使用自定义时间段进行重采样
有没有一种简单的方法可以对一个数据框进行重采样,尤其是对于(半)不规则的时间段?
我有一个按天记录的数据集,现在想把它重采样成科学文献中有时提到的“十天期”。我觉得这个词在英语中没有一个准确的翻译,但基本上就是把一个月分成三个大约十天的部分,第三部分是剩下的8到11天之间的天数。
我自己想出了两个解决方案,一个是针对这种情况的特定方法,另一个是适用于任何不规则时间段的更通用的方法。但这两个方法都不是特别好,所以我很好奇其他人是怎么处理这种情况的。
首先,我们来创建一些示例数据:
import pandas as pd
begin = pd.datetime(2013,1,1)
end = pd.datetime(2013,2,20)
dtrange = pd.date_range(begin, end)
p1 = np.random.rand(len(dtrange)) + 5
p2 = np.random.rand(len(dtrange)) + 10
df = pd.DataFrame({'p1': p1, 'p2': p2}, index=dtrange)
我想到的第一个方法是按每个月(YYYYMM)进行分组,然后手动切割。比如:
def to_dec1(data, func):
# create the indexes, start of the ~10day period
idx1 = pd.datetime(data.index[0].year, data.index[0].month, 1)
idx2 = idx1 + datetime.timedelta(days=10)
idx3 = idx2 + datetime.timedelta(days=10)
# slice the period and perform function
oneday = datetime.timedelta(days=1)
fir = func(data.ix[:idx2 - oneday].values, axis=0)
sec = func(data.ix[idx2:idx3 - oneday].values, axis=0)
thi = func(data.ix[idx3:].values, axis=0)
return pd.DataFrame([fir,sec,thi], index=[idx1,idx2,idx3], columns=data.columns)
dfmean = df.groupby(lambda x: x.strftime('%Y%m'), group_keys=False).apply(to_dec1, np.mean)
这样得到的结果是:
print dfmean
p1 p2
2013-01-01 5.436778 10.409845
2013-01-11 5.534509 10.482231
2013-01-21 5.449058 10.454777
2013-02-01 5.685700 10.422697
2013-02-11 5.578137 10.532180
2013-02-21 NaN NaN
注意,你总是会得到完整的“十天期”,这不是问题,如果需要的话可以很容易去掉。
另一个解决方案是提供一个日期范围,在这个范围内你可以切割数据框,并对每个部分执行一个函数。这种方法在你想要的时间段上更灵活。
def to_dec2(data, dts, func):
chucks = []
for n,start in enumerate(dts[:-1]):
end = dts[n+1] - datetime.timedelta(days=1)
chucks.append(func(data.ix[start:end].values, axis=0))
return pd.DataFrame(chucks, index=dts[:-1], columns=data.columns)
dfmean2 = to_dec2(df, dfmean.index, np.mean)
注意,我使用了之前结果的索引作为日期范围,这样可以节省一些“自己构建”它的时间。
处理这些情况的最佳方法是什么呢?在Pandas中是否有更内置的方法可以使用?
2 个回答
2
使用HYRY的数据和解决方案,直到计算d
变量为止,我们还可以在pandas 0.11-dev或更高版本中做以下操作(与numpy版本无关):
In [18]: from datetime import timedelta
In [23]: pd.Series([ timedelta(int(i)) for i in d ])
Out[23]:
0 00:00:00
1 1 days, 00:00:00
2 2 days, 00:00:00
3 3 days, 00:00:00
4 4 days, 00:00:00
5 5 days, 00:00:00
6 6 days, 00:00:00
7 7 days, 00:00:00
8 8 days, 00:00:00
9 9 days, 00:00:00
10 00:00:00
47 6 days, 00:00:00
48 7 days, 00:00:00
49 8 days, 00:00:00
50 9 days, 00:00:00
Length: 51, dtype: timedelta64[ns]
日期的构造方式与上面类似
date = pd.Series(df.index) - pd.Series([ timedelta(int(i)) for i in d ])
df.groupby(date.values).mean()
11
如果你使用的是numpy 1.7版本,你可以用datetime64和timedelta64这两种数组来进行计算:
首先,创建一些示例数据:
import pandas as pd
import numpy as np
begin = pd.datetime(2013,1,1)
end = pd.datetime(2013,2,20)
dtrange = pd.date_range(begin, end)
p1 = np.random.rand(len(dtrange)) + 5
p2 = np.random.rand(len(dtrange)) + 10
df = pd.DataFrame({'p1': p1, 'p2': p2}, index=dtrange)
接下来,计算十天的日期:
d = df.index.day - np.clip((df.index.day-1) // 10, 0, 2)*10 - 1
date = df.index.values - np.array(d, dtype="timedelta64[D]")
df.groupby(date).mean()
最后,输出结果是:
p1 p2
2013-01-01 5.413795 10.445640
2013-01-11 5.516063 10.491339
2013-01-21 5.539676 10.528745
2013-02-01 5.783467 10.478001
2013-02-11 5.358787 10.579149