使用自定义时间段进行重采样

12 投票
2 回答
15179 浏览
提问于 2025-04-17 19:04

有没有一种简单的方法可以对一个数据框进行重采样,尤其是对于(半)不规则的时间段?

我有一个按天记录的数据集,现在想把它重采样成科学文献中有时提到的“十天期”。我觉得这个词在英语中没有一个准确的翻译,但基本上就是把一个月分成三个大约十天的部分,第三部分是剩下的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

撰写回答