Python 使用开始日期重新采样

Python 使用开始日期重新采样,python,pandas,dataframe,time-series,pandas-resample,Python,Pandas,Dataframe,Time Series,Pandas Resample,我想使用特定的日期(或月份)作为第一个箱子的边缘对熊猫对象重新采样。例如,在下面的代码片段中,我希望我的第一个索引值是2020-02-29,我很乐意指定start=2或start=“2020-02-29” 日期=pd.日期范围(“2020-01-29”、“2021-07-04”) >>>s=pd.系列(范围(长度(日期)),索引=日期) >>>s.重新采样('4M')。计数() 2020-01-31 3 2020-05-31 121 2020-09-30 122 2021-

我想使用特定的日期(或月份)作为第一个箱子的边缘对熊猫对象重新采样。例如,在下面的代码片段中,我希望我的第一个索引值是
2020-02-29
,我很乐意指定
start=2
start=“2020-02-29”

日期=pd.日期范围(“2020-01-29”、“2021-07-04”) >>>s=pd.系列(范围(长度(日期)),索引=日期) >>>s.重新采样('4M')。计数() 2020-01-31 3 2020-05-31 121 2020-09-30 122 2021-01-31 123 2021-05-31 120 2021-09-30 34 频率:4M,数据类型:int64 到目前为止,这是我能想到的最干净的用法
pd.cut
groupby

>>规则=“4M”
>>>开始=pd.时间戳(“2020-02-29”)-pd.序列频率至偏移量(规则)
>>>end=s.index.max()+pd.tseries.frequencies.to_offset(规则)
>>>bins=pd.日期\范围(开始、结束、频率=规则)
>>>gb=s.groupby(pd.cut(s.index,bin)).count()
>>>gb.index=gb.index.categories.right
>>>国标
2020-02-29     32
2020-06-30    122
2020-10-31    123
2021-02-28    120
2021-06-30    122
2021-10-31      4
数据类型:int64

您只需使用
pd.cut
如下所示:

>>> gb = pd.cut(s.index, bins).value_counts()
>>> gb.index = gb.index.categories.right
>>> gb
2020-02-29     32
2020-06-30    122
2020-10-31    123
2021-02-28    120
2021-06-30    122
2021-10-31      4
dtype: int64

没有必要使用
groupby

我的答案感觉有点粗糙,但使用
重采样
并给出所需的输出。在指定日期之前查找一个bin长度的日期(例如,4个月,或具体的月末),将其附加到
s
,然后
重新采样

rule = '4M'
date = '02-29-2020'

base_date = pd.to_datetime(date) - pd.tseries.frequencies.to_offset(rule)
s.loc[base_date] = np.nan
output = s.resample(rule=rule, label='right',).count()
output=output[output.index >= date]
结果:

2020-02-29     32
2020-06-30    122
2020-10-31    123
2021-02-28    120
2021-06-30    122
2021-10-31      4
Freq: 4M, dtype: int64
我添加了
output=output[output.index>=date]
b/c,否则您会得到一个额外的空箱子:

2019-10-31      0
2020-02-29     32
2020-06-30    122
2020-10-31    123
2021-02-28    120
2021-06-30    122
2021-10-31      4
Freq: 4M, dtype: int64

处理月间隔时的另一种方法是将datetime索引转换为年和月的整数,删除定义的开始日期和规则中的一些模值。在groupby中使用此选项

rule = '4M'
start = "2020-02-29"

# change types of value
d = pd.Timestamp(start)
nb = int(rule[:-1])

gr = s.groupby(d+(1+((s.index.year*12+s.index.month) #convert datetime index to int
                      -(d.year*12+d.month+1))//nb) # remove start and modulo rule
                  *pd.tseries.frequencies.to_offset(rule) # get rule freq
              ).count()
print (gr)
2020-02-29     32
2020-06-30    121
2020-10-31    123
2021-02-28    120
2021-06-30    122
2021-10-31      4
dtype: int64
现在,与您的方法相比,假设您使用相同的规则(4M)定义了一个日期,该日期不在您的规则定义的前X个月内,如2020-07-31。使用此方法,它给出:

2020-03-31     63 #you get this interval
2020-07-31    121
2020-11-30    122
2021-03-31    121
2021-07-31     95
dtype: int64 
使用您的方法时,您可以获得:

2020-07-31    121  #you loose info from before the 2020-03-31
2020-11-30    122
2021-03-31    121
2021-07-31     95
dtype: int64

我知道您在问题中指出,您定义了第一个日期,但使用此方法,您可以定义任何日期,只要规则以月份为单位

这不是原始答案,而是将@ALollz(注释)和@MhdMedf(答案)的改进合并到一个单一答案中,因为它们代表兼容的改进。另请参见下面的计时说明

rule = "4M"
start = pd.Timestamp("2020-02-29") - pd.tseries.frequencies.to_offset(rule)
end = s.index.max() + pd.tseries.frequencies.to_offset(rule)
bins = pd.date_range(start, end, freq=rule)
gb = pd.cut(s.index, bins, labels=bins[1:]).value_counts()
(上面的最后一行替换了OP中答案的最后两行。前四行没有改变,但为了清晰起见,此处包含。)

结果:

2020-02-29     32
2020-06-30    122
2020-10-31    123
2021-02-28    120
2021-06-30    122
2021-10-31      4

速度/计时:考虑到只有524行(在我的机器上是6毫秒),OP中的代码花费了大量的时间。使用OP数据,这两项改进结合起来可实现约3倍的加速。当然,在更大的系列/数据帧上,计时结果可能与这里看到的结果有很大不同。

您可以使用
pd.cut(s.index,bin,labels=bin[1:])
进行分组;指定剪切中的仓可以节省重新定义索引的步骤。此外,由于日期日期与“4M”偏移量完全无关,您可以通过仅指定起始日期的YM来消除歧义:
pd.Timestamp(“2020-02”)
。除此之外,你的切入点很重要。谢谢你的反馈。据我所知,@jsignell正在寻找一种更干净的方法来获得相同的答案。你有更好的答案的建议吗?再次感谢您的时间,我只是说您的答案可能会产生误导,因为看起来您将6行替换为2行,但您的答案也需要6行。您只缩短了6行中的1行(这很好,而且速度也更快)。我唯一的建议是更清楚地说明你的答案与OP中的答案有什么不同,以及如何改进。你也可以展示速度的提高。(我在我的“答案”中这样做了,但我使用的计时是您的改进和@ALollz的组合。)这是一个巧妙的技巧,似乎是最简单的代码,因为它使用重采样。在快速计时测试中,我发现它也比OP答案快,但不如@ALolz/MhdMedfa答案组合快。
output=output.loc[date://code>或
output[1://code>也可以。我喜欢使用
resample
和与我最初的问题陈述相同的聚合方法。谢谢