每周分组 python pandas 数据帧(从星期一开始)

Posted

技术标签:

【中文标题】每周分组 python pandas 数据帧(从星期一开始)【英文标题】:Group python pandas dataframe per weeks (starting on Monday) 【发布时间】:2018-03-15 16:50:53 【问题描述】:

我有一个包含每天值的数据框(请参阅下面的 df)。 我想每周对“预测”字段进行分组,但将星期一作为一周的第一天。

目前我可以通过 pd.TimeGrouper('W') 来完成(参见下面的 df_final),但它从星期日开始对一周进行分组(参见下面的 df_final)

import pandas as pd
data = [("W1","G1",1234,pd.to_datetime("2015-07-1"),8),
        ("W1","G1",1234,pd.to_datetime("2015-07-30"),2),
        ("W1","G1",1234,pd.to_datetime("2015-07-15"),2),
        ("W1","G1",1234,pd.to_datetime("2015-07-2"),4),
        ("W1","G2",2345,pd.to_datetime("2015-07-5"),5),
        ("W1","G2",2345,pd.to_datetime("2015-07-7"),1),
        ("W1","G2",2345,pd.to_datetime("2015-07-9"),1),
        ("W1","G2",2345,pd.to_datetime("2015-07-11"),3)]

labels = ["Site","Type","Product","Date","Forecast"]
df = pd.DataFrame(data,columns=labels).set_index(["Site","Type","Product","Date"])
df


                              Forecast
Site Type Product Date                
W1   G1   1234    2015-07-01         8
                  2015-07-30         2
                  2015-07-15         2
                  2015-07-02         4
     G2   2345    2015-07-05         5
                  2015-07-07         1
                  2015-07-09         1
                  2015-07-11         3



df_final = (df
     .reset_index()
     .set_index("Date")
     .groupby(["Site","Product",pd.TimeGrouper('W')])["Forecast"].sum()
     .astype(int)
     .reset_index())
df_final["DayOfWeek"] = df_final["Date"].dt.dayofweek
df_final

  Site  Product       Date  Forecast  DayOfWeek
0   W1     1234 2015-07-05        12          6
1   W1     1234 2015-07-19         2          6
2   W1     1234 2015-08-02         2          6
3   W1     2345 2015-07-05         5          6
4   W1     2345 2015-07-12         5          6

【问题讨论】:

我认为 W-MON 而不是 W 应该会有所帮助。 【参考方案1】:

使用W-MON 代替W,检查anchored offsets:

df_final = (df
     .reset_index()
     .set_index("Date")
     .groupby(["Site","Product",pd.Grouper(freq='W-MON')])["Forecast"].sum()
     .astype(int)
     .reset_index())

df_final["DayOfWeek"] = df_final["Date"].dt.dayofweek
print (df_final)
  Site  Product       Date  Forecast  DayOfWeek
0   W1     1234 2015-07-06        12          0
1   W1     1234 2015-07-20         2          0
2   W1     1234 2015-08-03         2          0
3   W1     2345 2015-07-06         5          0
4   W1     2345 2015-07-13         5          0

【讨论】:

TimeGrouper 是now deprecated【参考方案2】:

我对这个问题有三个解决方案,如下所述。首先,我应该声明前接受的答案是不正确的。原因如下:

# let's create an example df of length 9, 2020-03-08 is a Sunday
s = pd.DataFrame('dt':pd.date_range('2020-03-08', periods=9, freq='D'),
                  'counts':0)
> s
dt counts
0 2020-03-08 00:00:00 0
1 2020-03-09 00:00:00 0
2 2020-03-10 00:00:00 0
3 2020-03-11 00:00:00 0
4 2020-03-12 00:00:00 0
5 2020-03-13 00:00:00 0
6 2020-03-14 00:00:00 0
7 2020-03-15 00:00:00 0
8 2020-03-16 00:00:00 0

这九天跨越三个周一至周日。 3 月 2 日、9 日和 16 日这几周。让我们试试公认的答案:

# the accepted answer
> s.groupby(pd.Grouper(key='dt',freq='W-Mon')).count()
dt counts
2020-03-09 00:00:00 2
2020-03-16 00:00:00 7

这是错误的,因为 OP 希望在结果数据框中将“星期一作为一周的第一天”(而不是作为一周的最后一天)。让我们看看当我们尝试使用 freq='W' 时会得到什么

> s.groupby(pd.Grouper(key='dt', freq='W')).count()
dt counts
2020-03-08 00:00:00 1
2020-03-15 00:00:00 7
2020-03-22 00:00:00 1

这个石斑鱼实际上是按照我们的要求进行分组的(周一到周日),但将“dt”标记为一周的结束,而不是开始。因此,为了得到我们想要的,我们可以将索引移动 6 天,例如:

w = s.groupby(pd.Grouper(key='dt', freq='W')).count()
w.index -= pd.Timedelta(days=6)

或者我们可以这样做:

s.groupby(pd.Grouper(key='dt',freq='W-Mon',label='left',closed='left')).count()

第三种解决方案,可以说是最具可读性的解决方案,首先将dt 转换为句点,然后进行分组,最后(如果需要)转换回时间戳

s.groupby(s.dt.dt.to_period('W'))['counts'].count().to_timestamp()
# a variant of this solution is: s.set_index('dt').to_period('W').groupby(pd.Grouper(freq='W')).count().to_timestamp()

所有这些解决方案都会返回 OP 要求的内容:

dt counts
2020-03-02 00:00:00 1
2020-03-09 00:00:00 7
2020-03-16 00:00:00 1

【讨论】:

以上是关于每周分组 python pandas 数据帧(从星期一开始)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用带有 Pandas 的时间戳按小时对数据帧进行分组

一次将 pandas 数据帧随机分组以进行 x 折交叉验证

计算 pandas 数据框中的重叠时间帧,按人分组

使用 pandas 在数据帧上执行 groupby,按计数排序并获取 python 中的前 2 个计数

分组数据帧然后在pandas中过滤的最有效方法

根据 pandas 中的字典对数据帧的行进行分组并对相应的分子求和