pandas.resample(或groupby)的自定义间隔

3

假设我有这样一个数据框:

d = {'price': [10, 12, 8, 12, 14, 18, 10, 20],
     'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
df = pd.DataFrame(d)
df['a_date'] = pd.date_range('01/01/2018',
                             periods=8,
                             freq='W')

df
    price   volume  a_date
0   10      50      2018-01-07
1   12      60      2018-01-14
2   8       40      2018-01-21
3   12      100     2018-01-28
4   14      50      2018-02-04
5   18      100     2018-02-11
6   10      40      2018-02-18
7   20      50      2018-02-25

现在,我希望以约10天为一个时间间隔重新采样/分组数据,但要使用预定义的开始和结束日期,这些日期是每个月的第10天、第20天和最后一天,例如:
2018-01-01 to 2018-01-10
2018-01-11 to 2018-01-20
2018-01-21 to 2018-01-31
2018-02-01 to 2018-02-10
2018-02-11 to 2018-02-20
2018-02-21 to 2018-02-28

如果跨越这些时间间隔进行求和,结果将是:

             price  volume  
a_date
2018-01-10   10     50      
2018-01-20   12     60      
2018-01-31   20     140     
2018-02-10   14     50      
2018-02-20   28     140     
2018-02-28   20     50      

我能提供的最接近这个需求的方法是使用 df.resample('10D', on='a_date').sum(),但很明显我需要更加定制化的间隔方式。如果能够传入一个间隔数组我会很高兴,但我认为这是不可能的。
作为实验,我尝试过:
td = pd.to_datetime('2018-01-10') - pd.to_datetime('2018-01-01')
df.resample(td, on='a_date').sum()

但是 pandas.Timedelta 并不保留特定日期的信息。

编辑:

一个不同的数据框用于测试每个月的第一天:

d = {'price': np.arange(20)+1,
    'volume': np.arange(20)+5}
df = pd.DataFrame(d)
df['a_date'] = pd.date_range('01/01/2018',
                             periods=20,
                             freq='D')

应用被接受的答案后,结果为(第一天不计算在内):

      a_date  price  volume
0 2018-01-10     54      90
1 2018-01-20    155     195

与之比较(第一个时间段2018-01-01至2018-01-10):

df.iloc[:10].sum()

price     55
volume    95
dtype: int64
1个回答

1

尝试:

from pandas.tseries.offsets import MonthEnd

bins = []
end = df["a_date"].max()
current = df["a_date"].min()
current = pd.Timestamp(year=current.year, month=current.month, day=1)
while True:
    bins.append(current)
    bins.append(current + pd.Timedelta(days=9))
    bins.append(current + pd.Timedelta(days=19))
    bins.append(current + MonthEnd())
    if bins[-1] > end:
        break
    current = bins[-1] + pd.Timedelta(days=1)

x = (df.groupby(pd.cut(df["a_date"], bins)).sum()).reset_index()
x["a_date"] = x["a_date"].cat.categories.right
print(x[~(x.price.eq(0) & x.volume.eq(0))])

输出:

      a_date  price  volume
0 2018-01-10     10      50
1 2018-01-20     12      60
2 2018-01-31     20     140
4 2018-02-10     14      50
5 2018-02-20     28     140
6 2018-02-28     20      50

编辑:调整了桶:

from pandas.tseries.offsets import MonthEnd

end = df["a_date"].max()
current = df["a_date"].min()
bins = [
    pd.Timestamp(year=current.year, month=current.month, day=1) - MonthEnd(),
]
current = bins[-1]
while True:
    bins.append(bins[-1] + pd.Timedelta(days=10))
    bins.append(bins[-1] + pd.Timedelta(days=10))
    bins.append(current + MonthEnd())
    if bins[-1] > end:
        break
    current = bins[-1]

x = (df.groupby(pd.cut(df["a_date"], bins)).sum()).reset_index()
x["a_date"] = x["a_date"].cat.categories.right
print(x[~(x.price.eq(0) & x.volume.eq(0))])

输出:

      a_date  price  volume
0 2018-01-10     55      95
1 2018-01-20    155     195

看起来很不错,谢谢!我看到最后一行删除了“空”行。这是否也适用于具有不同日期的通用数据帧? - duff18
@duff18 当然可以,这是布尔索引。它会删除所有价格和交易量都等于零的行。 - Andrej Kesely
小问题,每个月的第一天不包括在该月的第一个区间内。可能使用 pandas.IntervalIndex 作为区间是正确的方法。 - duff18
@duff18 你能提供一个结果错误且期望输出的小输入吗?可能需要调整箱子的构建。 - Andrej Kesely
当然,我已经在帖子中添加了它。 - duff18

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接