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

Nic*_*las 5 python datetime pandas 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
Run Code Online (Sandbox Code Playgroud)

jez*_*ael 7

使用W-MON替代W,检查锚定偏移

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
Run Code Online (Sandbox Code Playgroud)


toz*_*CSS 5

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

# 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
Run Code Online (Sandbox Code Playgroud)
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

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

# the accepted answer
> s.groupby(pd.Grouper(key='dt',freq='W-Mon')).count()
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)

或者我们可以这样做:

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

第三种解决方案,可以说是最具可读性的解决方案,是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()
Run Code Online (Sandbox Code Playgroud)

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

dt 计数
2020-03-02 00:00:00 1
2020-03-09 00:00:00 7
2020-03-16 00:00:00 1

说明:当freq提供给时pd.Grouperclosedlabelkwargs 都默认为right。设置freqW( 的缩写W-Sun)有效,因为我们希望一周在星期日结束(包括星期日,并g.closed == 'right'处理此问题)。不幸的是,pd.Grouper文档字符串不显示默认值,但您可以像这样看到它们:

g = pd.Grouper(key='dt', freq='W')
print(g.closed, g.label)
> right right
Run Code Online (Sandbox Code Playgroud)