timedelta表示两个datetime对象之间的时间 差:
In [14]: delta = datetime(2011, 1, 7) - datetime(2008, 6, 24, 8, 15)
In [15]: delta
Out[15]: datetime.timedelta(926, 56700)
In [16]: delta.days
Out[16]: 926
In [17]: delta.seconds
Out[17]: 56700
给datetime对象加上(或减去)一个或多个timedelta,这样会产生一个新对 象:
In [18]: from datetime import timedelta
In [19]: start = datetime(2011, 1, 7)
In [20]: start + timedelta(12)
Out[20]: datetime.datetime(2011, 1, 19, 0, 0)
In [21]: start - 2 * timedelta(12)
Out[21]: datetime.datetime(2010, 12, 14, 0, 0)
利用str或strftime方法(传入一个格式化字符串),datetime对象和pandas的 Timestamp对象可以被格式化为字符串:
In [22]: stamp = datetime(2011, 1, 3)
In [23]: str(stamp)
Out[23]: '2011-01-03 00:00:00'
In [24]: stamp.strftime('%Y-%m-%d')
Out[24]: '2011-01-03'
datetime.strptime可以用这些格式化编码将字符串转换为日期:
In [25]: value = '2011-01-03'
In [26]: datetime.strptime(value, '%Y-%m-%d')
Out[26]: datetime.datetime(2011, 1, 3, 0, 0)
In [27]: datestrs = ['7/6/2011', '8/6/2011']
In [28]: [datetime.strptime(x, '%m/%d/%Y') for x in datestrs]
Out[28]: [datetime.datetime(2011, 7, 6, 0, 0), datetime.datetime(2011, 8, 6, 0, 0)]
datetime.strptime是通过已知格式进行日期解析的最佳方式。但是每次都要编写格 式定义是很麻烦的事情,尤其是对于一些常见的日期格式。这种情况下,你可以用 dateutil这个第三方包中的parser.parse方法(pandas中已经自动安装好了):
In [29]: from dateutil.parser import parse
In [30]: parse('2011-01-03')
Out[30]: datetime.datetime(2011, 1, 3, 0, 0)
dateutil可以解析几乎所有人类能够理解的日期表示形式:
In [31]: parse('Jan 31, 1997 10:45 PM')
Out[31]: datetime.datetime(1997, 1, 31, 22, 45)
注意:dateutil.parser是一个实用但不完美的工具。比如说,它会把一些原本不 是日期的字符串认作是日期(比如"42"会被解析为2042年的今天)。
在国际通用的格式中,日出现在月的前面很普遍,传入dayfirst=True即可解决这个 问题:
In [32]: parse('6/12/2011', dayfirst=True)
Out[32]: datetime.datetime(2011, 12, 6, 0, 0)
to_datetime方法可以解析多种不同的日期表示形式。不管这些日期是DataFrame的轴索引还是列
In [33]: datestrs = ['2011-07-06 12:00:00', '2011-08-06 00:00:00 ']
In [34]: pd.to_datetime(datestrs)
Out[34]: DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00 :00'], dtype='dat etime64[ns]', freq=None)
它还可以处理缺失值(None、空字符串等):
In [35]: idx = pd.to_datetime(datestrs + [None])
In [36]: idx
Out[36]: DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00 :00', 'NaT'], dty pe='datetime64[ns]', freq=None)
In [37]: idx[2] Out[37]: NaT
In [38]: pd.isnull(idx)
Out[38]: array([False, False, True], dtype=bool)
NaT(Not a Time)是pandas中时间戳数据的null值。
带有重复索引的时间序列
通过检查索引的is_unique属性,我们就可以知道它是不是唯一的:
In [66]: dup_ts.index.is_unique
Out[66]: False
对这个时间序列进行索引,要么产生标量值,要么产生切片,具体要看所选的时间 点是否重复。
假设你想要对具有非唯一时间戳的数据进行聚合。一个办法是使用groupby,并传 入level=0:
dup_ts.groupby(level=0)
生成日期范围
默认情况下,date_range会产生按天计算的时间点。如果只传入起始或结束日期, 那就还得传入一个表示一段时间的数字:
pd.date_range(start='2012-04-01', periods=20)
pd.date_range(end='2012-06-01', periods=20)
如果你想生成一个由每月最 后一个工作日组成的日期索引,可以传入"BM"频率(表示business end of month, 表11-4是频率列表),这样就只会包含时间间隔内(或刚好在边界上的)符合频率 要求的日期。
In [78]: pd.date_range('2000-01-01', '2000-12-01', freq='BM')
Out[78]: DatetimeIndex(['2000-01-31', '2000-02-29', '2000-03-31', '2000-0 4-28', '2000-05-31', '2000-06-30', '2000-07-31', '2000-0 8-31', '2000-09-29', '2000-10-31', '2000-11-30'], dtype='datetime64[ns]', freq='BM')
表11-4 基本的时间序列频率(不完整)
有时,虽然起始和结束日期带有时间信息,但你希望产生一组被规范化 (normalize)到午夜的时间戳。normalize选项即可实现该功能:
In [80]: pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True)
Out[80]: DatetimeIndex(['2012-05-02', '2012-05-03', '2012-05-04', '2012-0 5-05', '2012-05-06'], dtype='datetime64[ns]', freq='D')
频率和日期偏移量
pandas中的频率是由一个基础频率(base frequency)和一个倍数组成的。基础频 率通常以一个字符串别名表示,比如"M"表示每月,"H"表示每小时。对于每个基础 频率,都有一个被称为日期偏移量(date offset)的对象与之对应。例如,按小时 计算的频率可以用Hour类表示:
In [81]: from pandas.tseries.offsets import Hour, Minute
In [82]: hour = Hour()
In [83]: hour
Out[83]: <Hour>
在基础频率前面放上一个整数即可创建倍数:
pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4h')
大部分偏移量对象都可通过加法进行连接:
In [87]: Hour(2) + Minute(30)
Out[87]: <150 * Minutes>
同理,你也可以传入频率字符串(如"2h30min"),这种字符串可以被高效地解析 为等效的表达式:
pd.date_range('2000-01-01', periods=10, freq='1h30min')
有些频率所描述的时间点并不是均匀分隔的。例如,"M"(日历月末)和"BM"(每 月最后一个工作日)就取决于每月的天数,对于后者,还要考虑月末是不是周末。 由于没有更好的术语,我将这些称为锚点偏移量(anchored offset)。
表11-4列出了pandas中的频率代码和日期偏移量类。
表11-4 时间序列的基础频率
WOM日期
WOM(Week Of Month)是一种非常实用的频率类,它以WOM开头。它使你能获 得诸如“每月第3个星期五”之类的日期:
pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI')
移动(超前和滞后)数据
移动(shifting)指的是沿着时间轴将数据前移或后移。Series和DataFrame都有一 个shift方法用于执行单纯的前移或后移操作,保持索引不变:
In [91]: ts = pd.Series(np.random.randn(4),
index=pd.date_range('1/1/2000', periods=4 , freq='M'))
In [92]: ts
Out[92]:
2000-01-31 -0.066748
2000-02-29 0.838639
2000-03-31 -0.117388
2000-04-30 -0.517795
Freq: M, dtype: float64
In [93]: ts.shift(2) #这是后移
Out[93]:
2000-01-31 NaN
2000-02-29 NaN
2000-03-31 -0.066748
2000-04-30 0.838639
Freq: M, dtype: float64
In [94]: ts.shift(-2) #这是前移
Out[94]:
2000-01-31 -0.117388
2000-02-29 -0.517795
2000-03-31 NaN
2000-04-30 NaN
Freq: M, dtype: float64
当我们这样进行移动时,就会在时间序列的前面或后面产生缺失数据。
shift通常用于计算一个时间序列或多个时间序列(如DataFrame的列)中的百分比 变化。可以这样表达:
ts / ts.shift(1) - 1
由于单纯的移位操作不会修改索引,所以部分数据会被丢弃。因此,如果频率已 知,则可以将其传给shift以便实现对时间戳进行位移而不是对数据进行简单位移:
In [95]: ts.shift(2, freq='M')
Out[95]:
2000-03-31 -0.066748
2000-04-30 0.838639
2000-05-31 -0.117388
2000-06-30 -0.517795
Freq: M, dtype: float64
这里还可以使用其他频率,于是你就能非常灵活地对数据进行超前和滞后处理了:
In [96]: ts.shift(3, freq='D')
Out[96]:
2000-02-03 -0.066748
2000-03-03 0.838639
2000-04-03 -0.117388
2000-05-03 -0.517795
dtype: float64
In [97]: ts.shift(1, freq='90T')
Out[97]:
2000-01-31 01:30:00 -0.066748
2000-02-29 01:30:00 0.838639
2000-03-31 01:30:00 -0.117388
2000-04-30 01:30:00 -0.517795
Freq: M, dtype: float64
通过偏移量对日期进行位移
pandas的日期偏移量还可以用在datetime或Timestamp对象上:
In [98]: from pandas.tseries.offsets import Day, MonthEnd
In [99]: now = datetime(2011, 11, 17)
In [100]: now + 3 * Day()
Out[100]: Timestamp('2011-11-20 00:00:00')
如果加的是锚点偏移量(比如MonthEnd),第一次增量会将原日期向前滚动到符 合频率规则的下一个日期:
In [101]: now + MonthEnd()
Out[101]: Timestamp('2011-11-30 00:00:00')
In [102]: now + MonthEnd(2)
Out[102]: Timestamp('2011-12-31 00:00:00')
通过锚点偏移量的rollforward和rollback方法,可明确地将日期向前或向后“滚动”:
In [103]: offset = MonthEnd()
In [104]: offset.rollforward(now)
Out[104]: Timestamp('2011-11-30 00:00:00')
In [105]: offset.rollback(now)
Out[105]: Timestamp('2011-10-31 00:00:00')
日期偏移量还有一个巧妙的用法,即结合groupby使用这两个“滚动”方法:
In [106]: ts = pd.Series(np.random.randn(20),index=pd.date_range('1/15/2000',
period s=20, freq='4d'))
In [107]: ts
Out[107]:
2000-01-15 -0.116696
2000-01-19 2.389645
2000-01-23 -0.932454
2000-01-27 -0.229331
2000-01-31 -1.140330
2000-02-04 0.439920
2000-02-08 -0.823758
2000-02-12 -0.520930
2000-02-16 0.350282
2000-02-20 0.204395
2000-02-24 0.133445
2000-02-28 0.327905
2000-03-03 0.072153
2000-03-07 0.131678
2000-03-11 -1.297459
2000-03-15 0.997747
2000-03-19 0.870955
2000-03-23 -0.991253
2000-03-27 0.151699
2000-03-31 1.266151
Freq: 4D, dtype: float64
In [108]: ts.groupby(offset.rollforward).mean()
Out[108]:
2000-01-31 -0.005833
2000-02-29 0.015894
2000-03-31 0.150209
dtype: float64
更简单、更快速地实现该功能的办法是使用resample:
In [109]: ts.resample('M').mean() Out[109]: 2000-01-31 -0.005833 2000-02-29 0.015894 2000-03-31 0.150209 Freq: M, dtype: float64
网友评论