import pandas as pd
import numpy as np
时间日期
- 时间戳 tiimestamp:固定的时刻 -> pd.Timestamp
- 固定时期 period:比如 2016年3月份,再如2015年销售额 -> pd.Period
- 时间间隔 interval:由起始时间和结束时间来表示,固定时期是时间间隔的一个特殊
时间日期在 Pandas 里的作用
- 分析金融数据,如股票交易数据
- 分析服务器日志
Python datetime
python 标准库里提供了时间日期的处理。这个是时间日期的基础。
from datetime import datetime
from datetime import timedelta
now = datetime.now()
#输出当前年月日时分秒,还有秒的下一个计量单位
now.year, now.month, now.day
#输出当前年月日
时间差
date1 = datetime(2016, 3, 20)
date2 = datetime(2016, 3, 16)
delta = date1 - date2
#输出timedelta是4,默认为day
delta.days
#输出4
delta.total_seconds()
#转换成秒
date2 + delta
#输出一个datetime2016 3 20 0 0
date2 + timedelta(4.5)
#输出一个datetime2016 3 20 12 0
字符串和 datetime 转换
关于 datetime 格式定义,可以参阅 python 官方文档
date = datetime(2016, 3, 20, 8, 30)
str(date)
#变成字符串'2016-03-20 08:30:00'
date.strftime('%Y-%m-%d %H:%M:%S')
#可以自定义格式'2016-03-20 08:30:00'
datetime.strptime('2016-03-20 09:30', '%Y-%m-%d %H:%M')
#字符串转换为时间类型
Pandas 里的时间序列
Pandas 里使用 Timestamp 来表达时间
dates = [datetime(2016, 3, 1), datetime(2016, 3, 2), datetime(2016, 3, 3), datetime(2016, 3, 4)]
s = pd.Series(np.random.randn(4), index=dates)
type(s.index)
#输出pandas.tseries.index.DatetimeIndex
type(s.index[0])
#输出的是timestamp
日期范围
生成日期范围
pd.date_range('20160320', '20160331')
#生成DatetimeIndex(['2016-03-20', '2016-03-21', '2016-03-22', '2016-03-23',
# '2016-03-24', '2016-03-25', '2016-03-26', '2016-03-27',
# '2016-03-28', '2016-03-29', '2016-03-30', '2016-03-31'],
# dtype='datetime64[ns]', freq='D')
#freq = D表示天,可以自己改动
pd.date_range(start='20160320', periods=10)
#和上面差不多,但是数量为十天
## 规则化时间戳
pd.date_range(start='2016-03-20 16:23:32', periods=10, normalize=True)
时间频率
## 星期
pd.date_range(start='20160320', periods=10, freq='W')
# 月
pd.date_range(start='20160320', periods=10, freq='M')
## 每个月最后一个工作日组成的索引
pd.date_range(start='20160320', periods=10, freq='BM')
# 小时
pd.date_range(start='20160320', periods=10, freq='4H')
时期及算术运算
pd.Period 表示时期,比如几日,月或几个月等。比如用来统计每个月的销售额,就可以用时期作为单位。
p1 = pd.Period(2010)
p2 = p1 + 2
#输出2012年
p2 - p1
#输出2L
p1 = pd.Period(2016, freq='M')
#输出2016-01
p1 + 3
#输出Period('2016-04', 'M')
时期序列
pd.period_range(start='2016-01', periods=12, freq='M')
'''输出结果为
PeriodIndex(['2016-01', '2016-02', '2016-03', '2016-04', '2016-05', '2016-06',
'2016-07', '2016-08', '2016-09', '2016-10', '2016-11', '2016-12'],
dtype='int64', freq='M')
'''
pd.period_range(start='2016-01', end='2016-10', freq='M')
#输出1~10月
# 直接用字符串,输出季度
index = pd.PeriodIndex(['2016Q1', '2016Q2', '2016Q3'], freq='Q-DEC')
时期的频率转换
asfreq
- A-DEC: 以 12 月份作为结束的年时期
- A-NOV: 以 11 月份作为结束的年时期
- Q-DEC: 以 12 月份作为结束的季度时期
p = pd.Period('2016', freq='A-DEC')
#输出以12月为一年结束的时间,2016
p.asfreq('M', how='start')
#重新取freq,变成月份
p.asfreq('M', how='end')
#同上输出为12月
p = pd.Period('2016-04', freq='M')
#时间序列
p.asfreq('A-DEC')
#变换为年,以十二月为结束,所以是2016
# 以年为周期,以一年中的 3 月份作为年的结束(财年)输出为2017年
p.asfreq('A-MAR')
季度时间频率
Pandas 支持 12 种季度型频率,从 Q-JAN 到 Q-DEC
p = pd.Period('2016Q4', 'Q-JAN')
#2016年一月份为第四季度
# 以 1 月份结束的财年中,2016Q4 的时期是指 2015-11-1 到 2016-1-31
p.asfreq('D', how='start'), p.asfreq('D', how='end')
# 获取该季度倒数第二个工作日下午4点的时间戳
p4pm = (p.asfreq('B', how='end') - 1).asfreq('T', 'start') + 16 * 60
# 转换为 timestamp
p4pm.to_timestamp()
#Timestamp('2016-01-28 16:00:00')
Timestamp 和 Period 相互转换¶
ts = pd.Series(np.random.randn(5), index = pd.date_range('2016-01-01', periods=5, freq='M'))
ts.to_period()
#由timestamp转换成period
ts = pd.Series(np.random.randn(5), index = pd.date_range('2016-12-29', periods=5, freq='D'))
pts = ts.to_period(freq='M')
#由天转换成月
pts.groupby(level=0).sum()
# 转换为时间戳时,细部时间会丢失
pts.to_timestamp(how='end')
重采样
- 高频率 -> 低频率 -> 降采样:5 分钟股票交易数据转换为日交易数据
- 低频率 -> 高频率 -> 升采样
- 其他重采样:每周三 (W-WED) 转换为每周五 (W-FRI)
ts = pd.Series(np.random.randint(0, 50, 60), index=pd.date_range('2016-04-25 09:30', periods=60, freq='T'))
# 0-4 分钟为第一组,显示的是从9:30开始
ts.resample('5min', how='sum')
# 0-4 分钟为第一组,显示的是从9:35开始
ts.resample('5min', how='sum', label='right')
OHLC 重采样
金融数据专用:Open/High/Low/Close
ts.resample('5min', how='ohlc')
#五分钟为间隔重采样,有ohlc四列
### 通过 groupby 重采样
ts = pd.Series(np.random.randint(0, 50, 100), index=pd.date_range('2016-03-01', periods=100, freq='D'))
ts.groupby(lambda x: x.month).sum()
#以月份为单位求总和
ts.groupby(ts.index.to_period('M')).sum()
#也是以月份为单位求和
升采样和插值
# 以周为单位,每周五采样
df = pd.DataFrame(np.random.randint(1, 50, 2), index=pd.date_range('2016-04-22', periods=2, freq='W-FRI'))
df.resample('D')
#输出22-29所有天数
df.resample('D', fill_method='ffill', limit=3)
#向前填充,限制三个
# 以周为单位,每周一采样
df.resample('W-MON', fill_method='ffill')
时期重采样
df = pd.DataFrame(np.random.randint(2, 30, (24, 4)),
index=pd.period_range('2015-01', '2016-12', freq='M'),
columns=list('ABCD'))
adf = df.resample('A-DEC', how='mean')
#以十二月为一年的结尾,取每年的平均值
df.resample('A-MAY', how='mean')
#以五月份为一年的结尾,去每年的平均值
# 升采样
adf.resample('Q-DEC')
#以十二月为季度的结尾,升采样过后行数会变多,会出现很多空值
adf.resample('Q-DEC', fill_method='ffill')
#和上一个一样,空值变成了向前填充
性能
n = 1000000
ts = pd.Series(np.random.randn(n),
index=pd.date_range('2000-01-01', periods=n, freq='10ms'))
len(ts)
%timeit ts.resample('10min', how='ohlc')
#测试性能 10 loops, best of 3: 21.9 ms per loop
ts.resample('D', how='ohlc')
#以天为单位重采样
从文件中读取日期序列
df = pd.read_csv('data/002001.csv', index_col='Date')
df.index
#发现index并不是时间日期类型
df = pd.read_csv('data/002001.csv', index_col='Date', parse_dates=True)
df.index
#转换成时间日期类型
wdf = df['Adj Close'].resample('W-FRI', how='ohlc')
#以每周五为单位重采样,ohlc
wdf['Volume'] = df['Volume'].resample('W-FRI', how='sum')
#以每周五为单位,取总和
自定义时间日期解析函数
def date_parser(s):
s = '2016/' + s
d = datetime.strptime(s, '%Y/%m/%d')
return d
df = pd.read_csv('data/custom_date.csv', parse_dates=True, index_col='Date', date_parser=date_parser)
#指定函数
df.index
#确定index是时间日期类型