pandas：ユーザごとに1週間単位で集計する

# 実行環境
* Python 3.10.2
* pandas 1.4.1

# やりたいこと
以下のデータを、`user`ごとに1週間単位（日曜日始まり）の合計の`count`を算出したいです。


```csvs: data.csv
user,date,count
alice,2022-03-01,1
alice,2022-03-05,2
alice,2022-03-06,3
alice,2022-03-07,4
alice,2022-03-12,5
alice,2022-03-13,6
alice,2022-03-14,7
bob,2022-03-12,100
bob,2022-03-13,101
```

```python
In [63]: df =pandas.read_csv("qiita-table.csv")

In [64]: df
Out[64]: 
    user        date  count
0  alice  2022-03-01      1
1  alice  2022-03-05      2
2  alice  2022-03-06      3
3  alice  2022-03-07      4
4  alice  2022-03-12      5
5  alice  2022-03-13      6
6  alice  2022-03-14      7
7    bob  2022-03-12    100
8    bob  2022-03-13    101

```


# 解決

まずは日付の計算ができるようにするため、`date`列の型をstring(object)から`datetime64`型に変換します。

```python
In [65]: df.dtypes
Out[65]: 
user     object
date     object
count     int64
dtype: object

In [70]: df["date"] = pandas.to_datetime(df["date"])

In [71]: df.dtypes
Out[71]: 
user               object
date       datetime64[ns]
count               int64
dtype: object
```

確認のため曜日を`day`列に追加します。

```python
In [77]: df["day"] = df["date"].dt.day_name()

In [80]: df
Out[80]: 
    user       date  count       day
0  alice 2022-03-01      1   Tuesday
1  alice 2022-03-05      2  Saturday
2  alice 2022-03-06      3    Sunday
3  alice 2022-03-07      4    Monday
4  alice 2022-03-12      5  Saturday
5  alice 2022-03-13      6    Sunday
6  alice 2022-03-14      7    Monday
7    bob 2022-03-12    100  Saturday
8    bob 2022-03-13    101    Sunday
```


`groupby`関数でユーザごとに分けて、`resample`関数で1週間単位に集計します。

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html

```python
In [85]: df.groupby("user").resample("W",on="date", label="left",closed="left").sum()
Out[85]: 
                  count
user  date             
alice 2022-02-27      3
      2022-03-06     12
      2022-03-13     13
bob   2022-03-06    100
      2022-03-13    101

```

以上です。

## `label`引数, `closed`引数を指定している理由
`label`引数, `closed`引数は、`rule="W"`のときはデフォルトで`right`です。

`label`引数, `closed`引数は以下の通りです。
>label='left'とすると開始日時、label='right'とすると終了日時がラベルとなる。

>closed='left'とすると開始日時 <= 期間 < 終了日時となり、closed='right'とすると開始日時 < 期間 <= 終了日時となる。

https://note.nkmk.me/python-pandas-time-series-resample-asfreq/


`label`引数, `closed`引数を指定しないと、日曜日終わりの1週間単位で集計されます。

```python
In [89]: df.groupby("user").resample("W",on="date").sum()
Out[89]: 
                  count
user  date             
alice 2022-03-06      6
      2022-03-13     15
      2022-03-20      7
bob   2022-03-13    201
```

日曜日始まりで集計したいので、`label="left", closed="left"`を指定しました。



>Which bin edge label to label bucket with. The default is ‘left’ for all frequency offsets except for ‘M’, ‘A’, ‘Q’, ‘BM’, ‘BA’, ‘BQ’, and ‘W’ which all have a default of ‘right’.

この説明だとよく分からなかったのですが、別サイトに分かりやすい説明がありました。


https://note.nkmk.me/python-pandas-time-series-resample-asfreq/

`label="right"`を指定すると、`date`が1週間ずれました。

```python
In [88]: df.groupby("user").resample("W",on="date", label="right",closed="left").sum()
Out[88]: 
                  count
user  date             
alice 2022-03-06      3
      2022-03-13     12
      2022-03-20     13
bob   2022-03-13    100
      2022-03-20    101
```


# 補足

## 日曜日以外で始まる場合

`rule="W-MON"`を指定すると、月曜日始まりの1週間単位で集計されます。

```python
In [91]: df.groupby("user").resample("W-MON",on="date",label="left",closed="left").sum()
Out[91]: 
                  count
user  date             
alice 2022-02-28      6
      2022-03-07     15
      2022-03-14      7
bob   2022-03-07    201
```

指定できる値は以下に掲載されています。

https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#anchored-offsets

## 感想
`rule="W"`のときは、なぜ`label`引数, `closed`引数はデフォルトで`right`なんでしょうかね？デフォルトが`left`の方が直観的な気がするのですが。。。