Pandas 重新采样开始日期

Posted

技术标签:

【中文标题】Pandas 重新采样开始日期【英文标题】:Pandas resample with start date 【发布时间】:2020-09-29 01:54:34 【问题描述】:

我想使用特定日期(或月份)作为第一个 bin 的边缘重新采样 pandas 对象。例如,在下面的 sn-p 中,我希望我的第一个索引值为 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

【问题讨论】:

可以使用pd.cut(s.index, bins, labels=bins[1:])进行分组;在剪切中指定 bin 可以为您节省重新定义索引的步骤。此外,由于日期的日期与“4M”偏移量完全无关,您可以通过仅指定 YM 作为开始来消除歧义:pd.Timestamp("2020-02")。除此之外,你的削减几乎是要走的路。 【参考方案1】:

我的回答感觉有点老套,但使用了resample 并给出了所需的输出。查找指定日期前一个 bin 长度的日期(例如 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).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

【讨论】:

这是一个巧妙的技巧,似乎是最简单的代码,因为它使用了重采样。在快速计时测试中,我发现它也比 OP 答案快,但不如 @ALolz/MhdMedfa 答案的组合快。 output = output.loc[date:]output[1:] 也可以。 我喜欢它使用resample 和与我原来的问题陈述相同的聚合方法。谢谢!【参考方案2】:

这不是原始答案,而是将@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 倍的加速。当然,在更大的系列/数据帧上,计时结果可能与此处看到的结果大不相同。

【讨论】:

【参考方案3】:

处理月份间隔的另一种方法是将日期时间索引从年份和月份转换为整数,删除定义的 start_date 和一些带有规则的模值。在 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

我知道您在问题中声明您定义了第一个日期,但是使用这种方法,您可以定义任何日期,只要规则在月份中即可

【讨论】:

【参考方案4】:

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

【讨论】:

感谢您的反馈。据我了解,@jsignell 正在寻找一种更简洁的方法来获得相同的答案。你有什么建议可以做出更好的回答吗?再次感谢您的宝贵时间 我只是说您的回答可能具有误导性,因为看起来您将 6 行替换为 2 行,但您的回答也需要 6 行。您只是缩短了 6 行中的 1 行(这很好,而且速度也更快)。我唯一的建议是更清楚地了解您的答案与 OP 中的答案有何不同和改进。您还可以显示速度增加。 (我在“答案”中这样做了,但我使用的时间是您的改进和@ALollz 的组合。)

以上是关于Pandas 重新采样开始日期的主要内容,如果未能解决你的问题,请参考以下文章

在 pandas 中有效地聚合重新采样的日期时间集合

Pandas 在日期列上重新采样

使用 Pandas 将每日数据重新采样为每月(日期格式)

根据日期创建每月重新采样的 Pandas DataFrame

Pandas 将 5 分钟数据重新采样为每小时平均值:日期问题 [重复]

如何在 Pandas/Numpy 中使用 dateOffset 对日内时间序列数据进行重新采样?