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

时间:2017-10-04 10:17:24

标签: python pandas datetime pandas-groupby

我有一个每天都有值的数据框(参见下面的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

2 个答案:

答案 0 :(得分:4)

使用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

答案 1 :(得分:1)

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

# 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 计数
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

这 9 天跨越三个周一至周日。 3 月 2 日、9 日和 16 日这三个星期。让我们尝试接受的答案:

# the accepted answer
> s.groupby(pd.Grouper(key='dt',freq='W-Mon')).count()
<头>
dt 计数
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 计数
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 计数
2020-03-02 00:00:00 1
2020-03-09 00:00:00 7
2020-03-16 00:00:00 1