PythonでRFM分析を行いたい

RFM分析は、過去に書いたデシル分析の拡張です。
金額、行動頻度、最近行動タイミングの3つの属性から、ユーザーをセグメント分けします。

esu-ko.hatenablog.com

分析と言っても、ここから示唆を出すのは難しいですが、、、
たいていの使い方としては、先にRFMの課題レベルを優先順位づけしておき、
それらに基づいて、3つを掛け合わせたグループの優先順位を決定、
施策をうつグループを決定する、と言う感じになります。

基本プロセス

手順としては、

  1. id✖️日付✖️売上のデータの準備
  2. 上記からidごとの、売上、最新日指標、間隔指標の算出
  3. グループへの分割(可能ならグループの詳細分析)

となります。

1. データの準備

Userクラスを作ってシミュレーションデータを発生させました。
細かい関数の説明は、前記事↓を参照してください。 esu-ko.hatenablog.com

import pandas as pd
import random
import numpy as np

class User:
  """
  ユーザー行動をシミュレーションしてデータを発生させるクラス
  """
  def __init__(self,idx):
    self.idx = idx
    # 最後に売上が上がった日をランダムに生成し、それをもとに最初に会計した日も生成
    self.last_date = random.choice(pd.date_range(start='2020-04-01',end='2020-06-30'))
    self.start_date = random.choice(pd.date_range(start='2020-01-01',end=self.last_date))
    # 売上が立つ日をランダムに選択
    self.freq = random.choice([3,7,14])
    # 売上の金額をベース(1500) + ランダム分で生成
    self.sales = random.gauss(2000,1000) + 1500

  def action_log(self):
    """
    初めて売上が立つ日から最後の日までをループし、購買行動データを作成
    """
    ids = []
    dates = []
    sales = []
    for i,d in enumerate(pd.date_range(start=self.start_date,end=self.last_date)):
        if i == 0 or i % self.freq == 0 or d == self.last_date:
          ids.append(self.idx)
          dates.append(d)
          sales.append(round(random.gauss(self.sales,700)))

    return pd.DataFrame({'id':ids,'date':dates,'sales':sales})


logs = []
for u in [User(i) for i in range(100)]:
  logs.append(u.action_log())

df = pd.concat(logs)

2.idごとの、売上、最新日指標、間隔指標の算出

id単位で売上を集計しつつ、 * 最新日: ここでは現状を2020/07/01として、そこから何日まえか * 間隔指標: 売上日数を最初に売上した日 ~ 最後に売上した日の日数で割ったもの とをつくります。

そのために、最初の集計の段階で、最初の売上日、最後の売上日、売上日数も作成しておきます。

# ユーザー単位に集計
agg_df = df.groupby('id').agg({'date':[min,max,np.size],'sales':[sum]})
agg_df.columns = ['date_min','date_max','sales_cnt','total_sales']

# 売上以外の指標をユーザー単位で作成
agg_df['date_range'] = list(map(lambda x : x.days,(agg_df['date_max'] - agg_df['date_min'])))
agg_df['freq'] = agg_df['date_range']/agg_df['sales_cnt']
agg_df['rec'] = list(map(lambda x : x.days,agg_df.date_max.apply(lambda x : pd.to_datetime('2020-07-01') - x)))

3. グループへの分割(可能ならグループの詳細分析)

グループ分けをする場合は、
グループ内のユーザー数が同数になる場合と、
指標ないの範囲が等しくなる場合があります。

今回は、施策範囲を決めるために、
ユーザー数が同数になるようにしました。
(ただしfrmからグループを分けると偏りが出るので、最終的なユーザー数を出すのは重要です。

rfm_df = agg_df.loc[:,['total_sales','freq','rec']]

rfm_df['m'] = pd.qcut(rfm_df.total_sales,3,[ 'm' + str(i) for i in range(3)])
rfm_df['r'] = pd.qcut(rfm_df.rec,3,[ 'r' + str(i) for i in range(3)])
rfm_df['f'] = pd.qcut(rfm_df.rec,3,[ 'f' + str(i) for i in range(3)])

このあと

ここまでは、分析というよりデータ処理の範囲でした。
このあと、それぞれがどんな属性かを見るために、、別の属性を結合し、
frmを組み合わせた分類グループを目的変数にした決定木分析を行うなどが有効です。

このとき用いる属性は、RFMを決定する行動とは異なる行動から作られた属性や、
FRM行動と直接的な関係性がない属性を使わないと、当たり前の結果しかでてこなくなるので注意が必要です。

この辺りはRの本ですが、こちらなどもご参考ください。(クリックするとamazonに飛びます)

またpythonの決定木に関しては、こちらも読んでいただけると幸いです。

esu-ko.hatenablog.com