Kaggleの「Retailrocket recommender system dataset」でDID（差分の差分法）を練習してみた

# はじめに
今回は、「ChatGPTにハンズオンを作らせてみた」の第3弾で、DID（差分の差分法）を勉強しました。

第2弾はこちら↓

https://qiita.com/UKI_datascience/items/726cd22f7b5eb29e584a

# DID（差分の差分法）
「あるグループに何かしらの介入（政策や施策など）を実施したとき、実施しなかったグループと比べてどのくらい変化したのか？」を分析するための因果推論手法です。
処置群（介入を受けたグループ）の変化と対照群（介入を受けていないグループ）の変化の差分をとることで、DIDの推定値を算出するため、差分の差分法とも呼ばれます。

### 使用データ
Kaggleのデータセットにある、「[Retailrocket recommender system dataset](https://www.kaggle.com/datasets/retailrocket/ecommerce-dataset?select=events.csv)」というEコマースのレコメンデーションシステムで使用される商品プロパティの情報を記録したデータセットを用いました。その中でも、今回は次の3つの変数をピックアップして使用しました。

| 変数  | 説明                   |
|---------|----------------------|
| **timestamp** | イベントの発生時刻（ミリ秒単位） |
| **event** | ユーザーの行動（```view```、```addtocart```、```transaction```） |
| **transactionid** | 購入が発生した場合に記録される取引ID（NaNでない場合は購入） |

また、このデータを用いて、次の変数を作成しました。

| 変数  | 説明                   |
|---------|----------------------|
| **discounted** | 割引対象かどうか（0=対象外、1=対象） |
| **post_discount** | 割引後かどうか（0=割引前、1=割引後） |
| **interact** | DIDの交互作用項（割引後かつ割引対象の商品） |

### やること
- Eコマースのデータから、取引データ（実際に購入されたデータ）のみを抽出
- 介入するグループ（割引対象）を定義
- 介入のタイミングを定義
- ```介入を受けたグループの売上の変化```と```介入を受けなかったグループの売上の変化```の差分を取り、割引の効果があったかを測定

### 使用コード
```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
import seaborn as sns
```

```python
# データ読み込み
df = pd.read_csv("data/Retailrocket recommender system dataset/events.csv")

# ミリ秒のUNIX時間を日付に変換
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")

# 日付のみに変換
df["date"] = df["timestamp"].dt.date
```

今回のデータは、取引データ以外も含まれているため、取引データのみを抽出します。

```python
# 取引データのみを抽出
df_sales = df[df["event"] == "transaction"]
```

今回のデータでは、どの商品が割引対象かどうかが分からないので、こちら側が手動で割引対象の商品を定義します。（商品IDが10000から20000の間にある商品とします。）

```python
# 割引を受けた商品（処置群）を定義
df_sales["discounted"] = df_sales["itemid"].apply(lambda x: 1 if 100000 <= x <= 200000 else 0)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457746/42962610-6dda-45ce-afcb-9f5ec553fbb8.png)

割引が開始されたタイミングについても手動で定義していきます。（2015年7月1日を基準に設定します。）

```python
# 割引開始日
discount_start_date = pd.to_datetime("2015-07-01").date()

# 割引前・後のフラグ
df_sales["post_discount"] = df_sales["date"].apply(lambda x: 1 if x >= discount_start_date else 0)
```

処置群（介入を受けたグループ）と対照群（介入を受けなかったグループ）に分けて、売上回数をカウントして、一度可視化してみます。

```python
# 商品ごとに売上回数をカウント
df_did = df_sales.groupby(["date", "discounted", "post_discount"]).size().reset_index(name="sales")
```

```python
# 割引前後の売上トレンド
sns.lineplot(data=df_did, x="date", y="sales", hue="discounted")
plt.axvline(discount_start_date, color='red', linestyle="--", label="Discount Start")
plt.legend(title="Discounted Product")
plt.xticks(rotation=45)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457746/0d2e6efe-33c3-41c6-91a2-500130b3d635.png)

DIDでは、単純に「割引前と割引後の売上」を比較するのではなく、「通常商品の売上の変化」と「割引商品の売上の変化」を比較することが重要なため、交互作用項を追加したうえで、DIDを実装します。

```python
# 交互作用項の作成
df_did["interact"] = df_did["discounted"] * df_did["post_discount"]

# 回帰モデルの構築
X = sm.add_constant(df_did[["post_discount", "discounted", "interact"]])
y = df_did["sales"]

model = sm.OLS(y, X).fit()
print(model.summary())
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457746/d0800e19-a7fd-4f92-b243-7c82d79363a1.png)

### 分析結果
```post_discount```と```discounted```は、それぞれ単純な「割引前後の売上比較」と「商品ごとの比較」になってしまうため、交互作用項である```interact```のみに注目する。

- 係数: 1.11
    - 割引導入後の売上の変化はほぼゼロに近い
- P値: 0.896
    - 割引の影響は統計的に優位ではない（```P<0.05```であれば有意と判断）
- 解釈: この結果から、割引が売上に明確な影響を与えたとは言えない

# おわりに
今回は、Kaggleのデータセットから、割引対象と割引開始のタイミングを完全に手動で決めたので、有意な結果が出なくて当然かなと思います。