熊猫以开始日期重新采样

时间:2020-06-09 19:14:14

标签: python pandas dataframe time-series pandas-resample

我想使用特定的日期(或月份)作为第一个bin的边缘来重新采样pandas对象。例如,在以下代码段中,我希望我的第一个索引值为2020-02-29,并且很乐意指定start=2start="2020-02-29"

>>> dates = pd.date_range("2020-01-29", "2021-07-04")
>>> s = pd.Series(range(len(dates)), index=dates)
>>> s.resample('4M').count()
2020-01-31      3
2020-05-31    121
2020-09-30    122
2021-01-31    123
2021-05-31    120
2021-09-30     34
Freq: 4M, dtype: int64

到目前为止,这是我可以使用的pd.cutgroupby最干净的方法:

>>> 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 = s.groupby(pd.cut(s.index, bins)).count()
>>> 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

4 个答案:

答案 0 :(得分:7)

您只需使用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

答案 1 :(得分:6)

我的回答有点不客气,但使用resample并给出了所需的输出。在指定日期之前找到一个垃圾箱长度的日期(例如4个月,或者特别是一个月结束),将其附加到s,然后添加到resample

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

答案 2 :(得分:4)

处理月份间隔的另一种方法是将datetime索引转换为年份和月份中的整数,使用规则删除定义的start_date和一些取模值。分组使用。

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

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

答案 3 :(得分:1)

这不是原始答案,而是将@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行(我的机器上为6ms),OP中的代码花费了不小的时间。使用OP数据,这两项改进可以使速度提高大约3倍。当然,在更大的系列/数据帧上,时序结果可能与此处看到的结果大不相同。