SPSS ModelerのRFM集計ノードを、pythonに書き換える

SPSS ModelerでRFM集計を行うノードをPythonで書き換えてみます。
RFM集計ノードは以下の記事で解説されていますが、購買取引データから顧客価値を示すRFMの値を算出するノードとなります。
[SPSS Modeler ノードリファレンス 2-13 RFM集計](https://qiita.com/416nishimaki/items/8c7cd38fb966b4ced05a)

- Recency : 最新の購入日から経過した日数
- Frequency : 購入回数
- Monetary : 購入金額の合計


![スクリーンショット 2022-06-23 19.48.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811188/82b7a37a-61b6-6043-fb5f-26557939f2b7.png)

SPSS Modelerでは下記の設定でRFMの集計を行います。
- Recencyの基準日を2019/12/31とする
- 2000以下の値を持つレコードを破棄
- 2019/09/01以降のトランザクションデータを用いて集計
- 2、3番目のリーセントトランザクション日付（基準日からの経過日数）を保存

![スクリーンショット 2022-09-08 15.40.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811188/f7488d0f-43a0-4142-d801-f148628cd90e.png)

これをPythonで実装します。

# データの読み込み
[トランザクションデータ](https://github.com/Takahashi0000/Modeler2Python/blob/main/data/sampletranDEPT4en2019.csv)を読み込みます。その際、`SDATE`を日付型として読み込みます。

```python
df_tran = pd.read_csv("sampletranDEPT4en2019.csv", parse_dates=['SDATE'])
```

読み込んだデータの上から5行を覗いてみます。
```python
df_tran.head()
```
![スクリーンショット 2022-06-23 19.36.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811188/ebcf8794-90b3-bfc2-da0a-7467b16429f5.png)

以下のカラムを使用し、集計をしていきます。
- `CUSTID` : 顧客ID
- `SDATE` : 購入日
- `SUBTOTAL` : 購入金額


# データの前処理
`NOW`にRecencyの基準日を代入します。
今回は、トランザクションデータ内の最新日時である2019/12/31が基準日となっています。

```python
# トランザクションデータ内の最新日時を取得し、Recencyの基準日(2019/12/31)とする
NOW=df_tran["SDATE"].max()
```
2019/09/01以降を集計の対象とします。
```python
# 指定日以降のトランザクションデータを集計の対象とする
df_tran=df_tran[df_tran["SDATE"]>="2019-09-01"]
```
2000以下の値をもつレコードを破棄します。
```python
# 指定の値以下のレコードを破棄
df_tran=df_tran[df_tran["SUBTOTAL"]>2000]
```

# RFM集計

`groupby()`によって`CUSTID`でグループ化し、`agg()`で集計を行います。
集計内容は下記の通りです。
- `SDATE`では、
    - 最後の購入日から基準日への経過日数（Recency）を算出します。
    - `count`を用いて購入回数（Frequency）を算出します。
    - 日付をソートし、2番目のリーセントトランザクション日付（R2）を算出します。値を持たない場合は欠損値`pd.NA`で埋めます。
    - 日付をソートし、3番目のリーセントトランザクション日付（R3）を算出します。値を持たない場合は欠損値`pd.NA`で埋めます。
- `SUBTOTAL`では、`sum`を用いて購入金額の合計（Monetary）を算出します。

集計後`reset_index()`によってDataFrameの行番号を振り直します。

```python
# Recency,Frequency,Monetary,R2,R3を集計する
ID="CUSTID"
df_rfm = (
    df_tran.groupby(ID)
    .agg(
        {
            "SDATE": [
                lambda date: (NOW - date.max()).days,
                "count",
                lambda date: (NOW - sorted(date)[-2]).days
                if len(date) > 1
                else pd.NA,
                lambda date: (NOW - sorted(date)[-3]).days
                if len(date) > 2
                else pd.NA,
            ],
            "SUBTOTAL": "sum",
        }
    )
    .reset_index()
)
```
最後にカラム名をそれぞれRecency,Frequency,Monetary,R2,R3に変更し、順番を並び替えて完成です。

```python
# カラム名の変更と順番の並び替え
df_rfm.columns=[ID,"Recency","Frequency","R2","R3","Monetary"]
df_rfm=df_rfm.reindex(columns=[ID,"Recency","Frequency","Monetary","R2","R3"])
```

# 集計結果を確認
PythonとSPSS Modelerでそれぞれ結果を見て、一致することを確認出来ました。

## Pythonの実行結果
```python
df_rfm
```
![スクリーンショット 2022-09-08 20.29.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811188/b135892b-99fa-b071-a9da-b72ec75173ed.png)



## SPSS Modelerの実行結果

![スクリーンショット 2022-09-08 15.40.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811188/c81ee726-baaf-b942-9303-e602cc949b03.png)



# サンプル
サンプルは以下に置きました。

- [ストリーム](https://github.com/Takahashi0000/Modeler2Python/blob/main/rfmAggregation/rfm_aggregation.str)
- [notebook](https://github.com/Takahashi0000/Modeler2Python/blob/main/rfmAggregation/rfm_aggregation.ipynb)
- [データ](https://github.com/Takahashi0000/Modeler2Python/blob/main/data/sampletranDEPT4en2019.csv)


■テスト環境
Modeler 18.4
MacOS Monterey 12.5.1
Python 3.10
pandas 1.4.4