美文网首页Anacanda
Pandas时间序列切片(范围选取)前先将日期按升序排序!

Pandas时间序列切片(范围选取)前先将日期按升序排序!

作者: askka | 来源:发表于2019-12-25 12:09 被阅读0次
《DK Science year by year》

在数据分析中,时间序列数据所占的比例应该不在少数。《利用Python进行数据分析》一书中就专辟一章对数据序列的切片、重采样、绘图、移动窗口等逐一加以介绍。


《利用Python进行数据分析》

我手头的是该书的第一版,其中《第10章 时间序列》的“索引、选取、子集构造”一节中,专门提及了下面这一句(见原书P309):

通过日期进行切片的方式只对规则Series有效

一开始不知何意,“规则Series”也不知其具体所指,就稀里糊涂的翻过去了。
直到某次按照书中的方法对时间序列数据传入日期字符串进行切片(范围选取)发现不奏效,在网上搜索答案才发现,上面这句的确是时间序列切片的关键步骤所在。而且强烈怀疑这句是译者为避免读者走弯路自己加上去的(因为后来有机会看到该书的英文版本,也没有找到对应这一句的出处。当然这个也仅仅是怀疑没有实锤)。

日期索引数据的切片关键在于先排序

这里所谓的切片实际上就是按照起止日期等对数据集进行筛选。因此,能够实现的方式多种多样,turncate、query等均能实现pandas范围选取(切片)。只不过我个人看来,直接用日期作为表格的索引,传入日期字符串的方式最为便捷
来看具体的实例:

trainDF = pd.read_csv('train.csv',  parse_dates = ['Dates'])
trainDF = trainDF.drop(['Descript','Resolution'], axis =1)
trainDF_datesIndex = trainDF.set_index('Dates')  #日期索引、未排序
trainDF_Ascending = trainDF.set_index('Dates').sort_index(ascending=True) #日期升序
trainDF_Descending = trainDF.set_index('Dates').sort_index(ascending=False) #日期降序

载入'2003-01-06 00:01:00'至'2015-05-13 23:53:00'期间San Fransisco市的犯罪记录报告数据。筛选'2014-1-10'至'2014-1-30'之间的数据。


原数据集中的日期字段 query 与 loc、turncatre命令均可实现范围选取
索引未排序时无法用'起始日期:结束日期'的简便方式切片

从执行结果可见,索引未排序前,用query这些都可以筛选出正确的子数据集,只不过需要键入的代码会稍多些;若直接用代表日期的字符串选取具体的某一个具体的日期('Feb-2013'、'20130104'、'2005Q3'……)都可以被pandas识别;但要指定起止日期,比如pd['01/10/2014': '01/30/2014']或是['2004Q1': '2004Q3']这种时间段的方式,就不一定能成功。用turncate筛选索引时也是一样的效果:在对索引排序之前,用truncate进行单侧截取是可以的,但要用起止时间来掐头去尾就不行了。
换言之,写成:

trainDF_datesIndex.truncate(before='1/30/2004 00:00').truncate(after='1/10/2004 00:00')

是可以得到结果的,但写成:

trainDF_datesIndex.truncate('1/10/2004', '1/30/2004')

就不行了。参考资料7中也明确提到了这一点,truncate requires a sorted index。但既然对日期索引排序后可以直接用更少的代码实现按时间段切片,其实也就用不着turncate命令了。少打几个字母何乐不为呢。

#排序后下面两条指令是等效的
trainDF_Ascending['1/10/2004': '1/30/2004']
trainDF_Ascending .truncate('1/10/2004', '1/30/2004')
truncate同样需要先对索引排序

值得一提的是,pandas可以识别多种格式的表示日期的字符串,但也需注意比如'DD/MM/YYYY'与'MM/DD/YYYY',日期在前还是月份在前等对于日期的不同表示法。
看起来不少人也和我一样遇到过同样的问题,看到在stackoverflow回复pandas时间序列切片失效问题的也有不少。


stackoverflow上对此问题的回答

部分教程上的示例是自己构建的一个DatetimeIndex序列,也就不容易碰到类似的问题。但处理实际中的数据的过程中还是很容易忽视这一点的。

日期升序或降序排列执行结果会有不同

有意思的是,按照日期的升序排序或降序排序,pandas的结果会稍有不同。


将原数据按不同的顺序排序

首先需要指出的是,按照升序或者降序排序后,切片时起止日期的写法必须与排序的升降序保持一致!

  • trainDF_Ascending['20040110':'20040130'] #升序 √
  • trainDF_Ascending['20040130':'20040110'] #升序 ×
  • trainDF_Descending['20040130':'20040110'] #降序 √
  • trainDF_Descending['20040110':'20040130'] #降序 ×

一个不同之处在于,降序排序时,只用年来切片,[YYYY: YYYY]或[YYYYMM: YYYYMM]得不到想要的结果;升序排序时无此问题。


降序排序[yyyy:yyyy]筛选不到数据
降序排序时YYYYMM会报错

第二个不同在于,升序或降序排列不同切片时起止日期范围是否封闭也是不一样的。原数据集中只有'2014-1-10'至'2014-1-30'这20天内周数为偶数的数据(下图1),故将日期范围进一步精确到'2014-1-19'至'2014-1-26'这一周(下图2)。

'2014-1-10'至'2014-1-30'的数据筛选 '2014-1-19'至'2014-1-26'的数据筛选

由上图运行结果可知,索引降序排列的数据表在'2014-1-19'至'2014-1-26'的数据筛选,没有包含'2014/01/26'这一天,而升序排序索引的数据表进行筛选时是包含了的。亦即升序排序切片左右均封闭,降序排序左开放右封闭(不含日期排在最后的数据)。


升序降序日期切片区别之二:左右是否封闭

日期索引排序的切片也可以传入变量来筛选数据。与前述提到的结论一致,降序排序筛选切片时需注意区间左右是否封闭等细节问题。值得一提的是,如果切片传入的时datetime变量就不存在区间封闭性左右不同的问题,但这样一来,又得多敲键盘打字了。


降序排列传入字符串变量切片与传入datetime变量切片结果不同

从这一点上说,个人认为升序排列时的处理方式更符合常规,好在pandas.sort_index()默认升序,要实在不放心就老老实实加上一个'ascending=True'来得保险。

at_time()与between_time()

另外还有两个与日期范围筛选有关的命令时at_time()和between_time()命令。对于筛选所有日期的某一个具体时刻用at_time(),两个时刻之间的范围筛选用between_time()。注意不论是升序还是降序,between_time()都写成早的时间在前、晚的时间在后!写反了结果可能出错。
这两个命令比较简单,有兴趣的自己找个表格自己一试便知,就不多啰嗦了。

trainDF_datesIndex['2015-05-13'].at_time('20:30')
trainDF_datesIndex['2015-04-13'].between_time('20:25','20:45')

# 不论是升序还是降序,between_time()都写成早的时间在前、晚的时间在后!
# 写反了结果可能出错
trainDF_Ascending.between_time('20:25','20:45')
trainDF_Descending.between_time('20:25','20:45')

本文只对时间序列表格的范围选取加以讨论。除此之外,重采样resample、移动窗口rolling等命令都是pandas中非常高效实用的处理时间序列的命令。在《Python for Data Analysis, 2nd Edition》 Chapter 11: Time Series一章提供了pandas处理时间序列相关指令的.ipynb文件(第一版中文版中时间序列是第10章),其实也可以去将上述提到的问题自己试一试。毕竟“纸上得来终觉浅”,虽然本文讨论的都是些细枝末节的问题。

结论
  • 通过日期进行切片的方式只对规则Series有效!所谓的“规则Series”个人的理解就是排过序的Series,姑妄言之。

  • 将日期改为DatetimeIndex方便切片、筛选、resample分组。但务必将日期按升序排列(ascending=True),否则有可能筛选不到数据、或是报错。

  • 降序排序不推荐!降序排列的表格需按照日期从大到小的方式排列才能可能的切片,且左开右闭的截取方式也可能造成不必要的误解。

  • 结合at_time()与between_time()命令可实现更为灵活的(日期时间段)范围选取。

参考资料
  1. Python for Data Analysis, 2nd Edition
  2. python pandas dataframe slicing by date conditions
  3. Select DataFrame rows between two dates
  4. 《Pandas Cookbook》第10章 时间序列分析
  5. pandas处理时间序列(2):DatetimeIndex、索引和选择……
  6. pandas.DataFrame.truncate
  7. pandas对时间索引进行分割(truncate requires a sorted index)
  8. Pandas日期数据处理:如何按日期筛选、显示及统计数据

相关文章

  • Pandas时间序列切片(范围选取)前先将日期按升序排序!

    在数据分析中,时间序列数据所占的比例应该不在少数。《利用Python进行数据分析》一书中就专辟一章对数据序列的切片...

  • pandas高级特性

    1.pandas排序 按标签排序使用sort_index方法实现按标签排序 sort_index方法默认升序排序,...

  • 内部排序

    1. 对以下分别按升序进行冒泡排序和快速排序。试写出每一趟排序完成后的序列内容: 2. 对以下按升序进行2-路归并...

  • pandas dataframe按日期排序?

    原文地址:https://cloud.tencent.com/developer/ask/195429 我通过导入...

  • JavaScript常用基础算法

    基础算法一、排序冒泡排序 插入排序 当目标是升序排序,最好情况是序列本来已经是升序排序,那么只需比较n-1次,时间...

  • 按离当前时间最近排序

    大于等于当前时间的,按离当前时间最近升序排序 小于当前时间的,按离当前时间最远升序排序 上sql 结果如图 第一个...

  • Pearls11 .排序

    [TOC] 插入排序 纸牌游戏,为将数组x[n]按升序排序,首先将第一个元素视为有序子数组x[0..0],然后插入...

  • 《利用Python进行数据分析》 11.3日期范围、频率和移位

    11.3 日期范围、频率和移位 pandas拥有一整套标准的时间序列频率和工具用于重新采样、推断频率以及生成固定...

  • 排序算法(java实现)

    本文均为升序算法 简单选择排序 将序列最小(or最大)值元素放在待排序序列首位, 从剩余待排序序列中选最小(or最...

  • Pandas层级索引

    Pandas层级索引 MultiIndex索引对象 选取子集 外层选取 内层选取 交换分层顺序 交换并排序分层

网友评论

    本文标题:Pandas时间序列切片(范围选取)前先将日期按升序排序!

    本文链接:https://www.haomeiwen.com/subject/bmdznctx.html