メインコンテンツへスキップ
ブログ一覧に戻る
data

RFM 分析|最近性×頻度×金額で“使える”顧客セグメント

2025年11月3日
5分で読めます
RFM 分析|最近性×頻度×金額で“使える”顧客セグメント

RFM 分析|顧客価値を 3 指標で直感的にセグメント(実務テンプレ付き)

RFM は「最近性(R)」「頻度(F)」「金額(M)」の 3 指標で顧客価値を直感的に可視化し、施策の優先順位を付けるための定番手法です。

TL;DR

  • 目的: R(最近性)× F(頻度)× M(金額)で顧客をスコア化し、施策対象を即決
  • 手順: 期間定義 → 分位点スコア(R は逆符号)→ 合成スコア → セグメント施策
  • 注意: 業種の購買周期・キャンペーン影響・外れ値に敏感(期間と分位点を調整)

まずはここだけ(やさしい導入)

  • 何をする?: 「最近買ったか」「よく買うか」「いくら買うか」で顧客をざっくり分ける
  • いつ使う?: 休眠復帰、ロイヤル育成、クーポン配布、CRM 優先度付け
  • どう読む?: R は小さいほど良、F/M は大きいほど良。合成スコアで上位から順に見る

用語ミニ辞典(1 行で)

  • 最近性(R): 最終購買からの経過日数(小さい=最近買った)
  • 頻度(F): 一定期間の購買回数(多い=よく買う)
  • 金額(M): 一定期間の購買金額(大きい=高単価)
  • 分位点スコア: データを等しい人数のグループに分け、順位を 1–5 などで表す方法
  • 合成スコア: R/F/M を 3 桁で並べたり加重合算した総合指標

指標の定義(テンプレ)

  • R: 最終購買日からの経過日数(小さいほど良い)
  • F: 一定期間の購入回数(多いほど良い)
  • M: 一定期間の購買金額(多いほど良い)

スコアリング設計

  • 分位点(例: 五分位)で 1–5 を割当(R は逆符号に注意)
  • 合成スコア = 100×R + 10×F + M(表現はプロジェクトで統一)

最小コード(ダミーデータ)

import pandas as pd
import numpy as np

df = pd.DataFrame({
  'customer':['A','B','C','D','E','F','G','H'],
  'recency':[10,45,5,120,60,15,200,8],  # 日
  'frequency':[6,3,10,1,2,5,1,8],
  'monetary':[52000,18000,98000,3000,8000,42000,1500,76000]
})

def qcut_score(x, q=5, reverse=False):
    s = pd.qcut(x.rank(method='first'), q, labels=False) + 1
    return (q - s + 1) if reverse else s

df['R_score'] = qcut_score(df['recency'], reverse=True)
df['F_score'] = qcut_score(df['frequency'])
df['M_score'] = qcut_score(df['monetary'])
df['RFM'] = 100df['R_score'] + 10df['F_score'] + df['M_score']
print(df.sort_values('RFM', ascending=False)[['customer','R_score','F_score','M_score','RFM']])

読み方の例:

  • RFM 高: ロイヤル顧客。LTV 拡大型の施策
  • R 高 F/M 低: 直近来訪だが軽量。育成施策/クロスセル
  • R 低 F/M 高: 休眠危険の大口。復帰クーポン/リマーケ

可視化(表・ヒートマップ)

import matplotlib.pyplot as plt
import pandas as pd

# R×F の件数ヒートマップ
rf_table = pd.crosstab(df['R_score'], df['F_score']).sort_index().sort_index(axis=1)
plt.imshow(rf_table.values, cmap='Blues', origin='lower')
plt.xticks(range(len(rf_table.columns)), rf_table.columns)
plt.yticks(range(len(rf_table.index)), rf_table.index)
plt.xlabel('F_score'); plt.ylabel('R_score'); plt.title('R×F count heatmap')
plt.colorbar(); plt.tight_layout(); plt.show()

# RFM セグメント件数の棒グラフ
df['segment'] = df['R_score'].astype(str)+df['F_score'].astype(str)+df['M_score'].astype(str)
ax = df['segment'].value_counts().sort_index().plot(kind='bar', figsize=(6,3), title='RFM segment counts')
ax.set_xlabel('RFM'); ax.set_ylabel('count')
plt.tight_layout(); plt.show()

実務ケーススタディ(休眠危険を“先回り”で復帰)

目的: 直近の大口顧客(F/M 高)だが最近性が悪化した層に、期限付き復帰オファーを配布し LTV を守る。

  1. 期間とルール

  • 直近 6 か月を評価期間。キャンペーン期間は除外 or 別期間として評価
  • 外れ値(超大口)には Winsorize などで上限クリップ

  1. スコアと選定

  • 分位点で R/F/M のスコアを作成(R は逆符号)
  • ターゲット: R_score ≤ 2 かつ F_score ≥ 4 または M_score ≥ 4

  1. オファー設計

  • 14 日期限の復帰クーポン + パーソナライズ(過去カテゴリ)
  • テスト: 送信有無の A/B(回帰 to mean を避けるため無作為抽出)

  1. 評価指標

  • 復帰率、復帰後 60 日の累積粗利、オファーコスト差引の増分

最小コード(選定と件数確認)

target = df[(df['R_score'] <= 2) & ((df['F_score'] >= 4) | (df['M_score'] >= 4))]
print(len(target), '件を対象に復帰施策を検討')

落とし穴と対策

  • F/M の偏り(ヘビーユーザー依存)→ ロバスト指標や Winsorize
  • 業種差(購買周期)→ セグメント別に分位点を分ける
  • 一時的キャンペーン効果 → 期間設計を分けて比較

練習問題(理解を定着)

  1. R は日数なので「小さいほど良い」。分位点スコアをどう割り当てる?

  • ヒント: R は逆符号(最近買った人に高スコア)

  1. セール月を含むと F/M が急増した。何を調整する?

  • ヒント: 期間を分けて評価 or キャンペーンフラグで分位点を別計算

  1. 高単価だが頻度が低い VIP と、低単価だが頻度が高い常連。施策は同じで良い?

  • ヒント: 目的別(LTV 拡大 vs 滞在時間/来店頻度)で分ける

模範解答(クリックで展開)

  1. R は小さいほど良なので、分位点 1 が最低、5 が最高になるように逆割当。
  2. セール影響を切り出し、平常期と別計算。フラグで混ぜない。
  3. 異なる打ち手。VIP にはアップセル/会員特典、常連には来店頻度向上策やサブスク訴求。

よくある質問(FAQ)

  • Q: 分位点の段階数は固定?
  • A: データ量と施策解像度に応じて 3–7 段階で調整。比較可能性を優先
  • Q: 外れ値はどう扱う?
  • A: Winsorize などロバスト化や上限クリップで安定化。業務観点で上限設定
  • Q: R/F/M の重みは?
  • A: まず等重み。目的に応じて A/B テストや回帰で最適化


RFM分析についてのご相談はこちら

次の一手

状況に合わせて、選んでください。