皆さんは「ID-POSデータ」をご存じでしょうか。
ID-POSデータとは、POSデータに顧客IDを紐づけたデータのことです。
通常のPOSデータでは「何が・いつ・いくつ売れたか」は分かりますが、ID-POSデータでは「誰が買ったのか」まで追うことができます。
最近よく聞く「One to Oneマーケティング」や「会員獲得」、「顧客ロイヤルティ向上」といった取り組みも、このような顧客単位の購買データを活用できるようになったことが背景にあると思います。
ただ、実際にID-POSデータを手にしても、
「何から分析すればいいのか分からない」
「マーケティングにどう活かせばいいのか分からない」
「とりあえず売上を集計して終わってしまう」
というケースは少なくないのではないでしょうか。
本記事では、公開されているPOSデータを使って、ID-POSデータをどのように分析すればよいのかを整理していきます。
単なる集計ではなく、顧客理解、セグメンテーション、購買傾向の把握、施策への落とし込みまでを意識しながら、マーケティング視点で使える分析フレームワークを提案します。
はじめに
ID-POS分析の価値は、「売れた商品ランキング」を見ることだけではありません。
本当に使いたいのは、
- 誰に
- 何を
- いつ
- どの文脈で
提案するべきかを考えるための材料です。
今回は Kaggle の eCommerce purchase history from electronics store という公開POS-IDデータを使って実際に「マーケティングする」分析をしてみようと思います。
結論から言うと、このデータではスマートフォンが購買の中心にあり、そこからPC、AV機器、生活家電へ購買テーマが広がっていました。
また、RFMで見ると「優良顧客」と「休眠予備軍」が売上の大半を占めており、同じ販促を全員に出すより、顧客状態と購買テーマを掛け合わせた施策の方が考えやすい結果になりました。
この記事で使った分析は以下の通りです。
- RFM分析: 顧客の購買状態を分類する
- LDA: 顧客の購買テーマを抽出する
- 統計検定: トピックごとの購買金額・リピート率の差を確認する
- 機械学習: 後半期間の再購入を予測する
- 施策化: 分析結果から「誰に・何を・いつ」を考える
使ったライブラリ
主なライブラリは以下の通りです。
| ライブラリ | 用途 |
|---|---|
| pandas | CSV読み込み、欠損処理、集計、特徴量作成 |
| numpy | 数値計算、配列処理 |
| matplotlib / seaborn | グラフ作成 |
| scikit-learn | LDA、CountVectorizer、ロジスティック回帰、ランダムフォレスト |
| scipy | Kruskal-Wallis検定、カイ二乗検定 |
| statsmodels | Wilson法による比率の信頼区間 |
あまりメジャーじゃないライブラリだけ紹介します。
scipy は統計計算によく使われるライブラリです。今回は「グループ間に差がありそうか」を見る検定に使いました。
statsmodels は統計モデリング寄りのライブラリです。機械学習というより、検定・回帰・信頼区間など、統計分析で使う機能が多いです。今回はリピート率の95%信頼区間を出すために使いました。
データ概要
元CSVは 2,633,521 行でした。
ただし、このデータには user_id が欠損している行が多く含まれます。今回は user_id 単位のID-POS分析なので、顧客に紐づかない行は分析対象から外しました。
また、元データには 1970-01-01 の日時が混ざっていました。Kaggleデータの本体は2020年の購入履歴であり、今回の分析では 2020年の有効データをフルに使う方針にしました。
前処理後の分析対象は以下です。
| 項目 | 値 |
|---|---|
| 元データ行数 | 2,633,521 |
user_id あり・価格正・日時有効の行数 |
564,130 |
| 2020年の有効購入行数 | 562,823 |
| 顧客数 | 233,566 |
| 注文数 | 399,728 |
| 分析期間 | 2020-01-05 〜 2020-11-21 |
| 2020年の合計売上 | 117,320,205.17 |
| 平均注文金額 | 293.50 |
前処理の主なコードです。
import pandas as pd
df = pd.read_csv(
"data/ecommerce_purchase_history.csv",
dtype={
"order_id": "string",
"product_id": "string",
"category_id": "string",
"category_code": "string",
"brand": "string",
"user_id": "string",
},
)
df["event_time"] = pd.to_datetime(df["event_time"], errors="coerce", utc=True).dt.tz_convert(None)
df["price"] = pd.to_numeric(df["price"], errors="coerce")
df["brand"] = df["brand"].fillna("unknown_brand")
df["category_code"] = df["category_code"].fillna("unknown_category")
df = df.dropna(subset=["event_time", "user_id", "price"])
df = df[df["price"] > 0].copy()
# 今回は元データの実質的な購入期間である2020年をフル活用する
df = df[df["event_time"].dt.year == 2020].copy()
前処理で意識したこと
IDや注文番号は数値に見えますが、分析上は計算する値ではありません。そのため user_id や order_id は文字列として読みました。
ここを雑に読むと、大きなIDが浮動小数として扱われ、別ユーザーのIDが丸められる可能性があります。ID-POS分析では致命的なので、地味ですが重要なポイントです。
(いわゆる丸め誤差がおきる)
また、brand や category_code の欠損は削除せず、unknown_brand、unknown_category として残しました。欠損そのものが商品マスタや販売チャネルの課題を示す場合があるためです。
基礎集計
まず、月別売上を見ます。
2020年8月が最も大きく、売上は 27,982,605.44、客数(購入者数)は 47,592 人でした。
月別客数(購入者数)です。
顧客別の購入回数分布です。
顧客別の累計購入金額分布です。右に長い裾を持つため、グラフでは log1p を使っています。
(対数を取って正規分布にしてる)
今回のデータでは、顧客別の中央値は以下でした。
| 指標 | 値 |
|---|---|
| 顧客別購入回数の中央値 | 1.0 |
| 顧客別累計購入金額の中央値 | 208.31 |
| リピート顧客率 | 31.76% |
カテゴリ別売上の上位は以下です。
| 順位 | カテゴリ | 売上 |
|---|---|---|
| 1 | electronics.smartphone | 41,216,258.62 |
| 2 | computers.notebook | 14,788,035.02 |
| 3 | unknown_category | 9,772,035.47 |
| 4 | electronics.video.tv | 8,756,178.66 |
| 5 | appliances.kitchen.refrigerators | 8,625,193.15 |
ブランド別売上では Samsung と Apple が大きく、特に Samsung は 28,918,505.95、Apple は 25,951,286.48 でした。
マーケティング的ポイント
このデータではスマートフォンが売上の中心です。ただし、スマートフォン単体のランキングを見るだけではマーケティングに何の役にも立ちません。
重要なのは、スマホ購入者の中でも、
- 直近で買っているのか
- 何度も買っているのか
- 高単価なのか
- PCや家電も併買しているのか
を分けて見ることです。
RFM分析
RFM分析は顧客の状態を見るための手法です。
(ITパスポートとかでよく出るやつ)
- Recency: 最終購入から何日経っているか
- Frequency: 何回購入したか
- Monetary: 累計でいくら購入したか
今回は R/F/M を分位点でスコア化し、顧客をセグメントに分けました。
analysis_date = df["event_time"].max() + pd.Timedelta(days=1)
rfm = (
df.groupby("user_id")
.agg(
last_purchase=("event_time", "max"),
frequency=("order_id", "nunique"),
monetary=("price", "sum"),
)
.reset_index()
)
rfm["recency"] = (analysis_date - rfm["last_purchase"]).dt.days
RFMセグメント別の顧客数です。
RFMセグメント別の売上構成です。
今回の結果は以下の通りでした。
| セグメント | 客数 | 売上 | 平均Recency | 平均Frequency | 平均Monetary | リピート率 |
|---|---|---|---|---|---|---|
| 優良顧客 | 44,889 | 51,248,656.21 | 33.8 | 2.92 | 1,141.68 | 68.04% |
| 休眠予備軍 | 66,828 | 49,939,856.58 | 138.0 | 2.02 | 747.29 | 52.49% |
| 新規顧客 | 35,181 | 9,719,260.53 | 51.4 | 1.00 | 276.26 | 0.00% |
| 離反顧客 | 49,955 | 3,750,047.53 | 162.7 | 1.00 | 75.07 | 0.00% |
| 低単価リピート顧客 | 36,713 | 2,662,384.32 | 23.1 | 1.32 | 72.52 | 23.30% |
最も重要なのは、優良顧客と休眠予備軍の2セグメントで売上の大部分を占めていたことです。
特に休眠予備軍は平均Recencyが約138日と長い一方、平均Monetaryは 747.29、リピート率は 52.49% あります。つまり、完全に諦める顧客ではなく、復帰施策の優先度が高い顧客群です。
マーケティング的ポイント
今回のRFM結果からは、施策の優先順位は以下のように考えられます。
| セグメント | 施策仮説 |
|---|---|
| 優良顧客 | 値引きよりも上位モデル、保証、周辺機器、先行案内 |
| 休眠予備軍 | 過去購入カテゴリに合わせた新商品案内、復帰クーポン |
| 新規顧客 | 初回購入後の短期フォロー、関連商品提案 |
| 低単価リピート顧客 | セット販売、送料無料ライン、まとめ買い |
| 離反顧客 | 低コストな再接触テスト |
LDAによる購買トピック抽出
RFMは顧客の「状態」を表しますが、顧客が「何に興味を持っているか」は分かりません。
そこで LDA を使いました。
LDAは本来、文書に含まれる単語からトピックを推定する手法です。今回は以下のように置き換えました。
| LDAの考え方 | 今回の置き換え |
|---|---|
| 文書 | 顧客 |
| 単語 | brand_category |
| トピック | 購買テーマ |
特徴量エンジニアリングとして、brand と category_code を結合し、brand_category を作りました。
df["brand_category"] = (
df["brand"].str.lower().str.replace(r"[^0-9a-zA-Z_]+", "_", regex=True)
+ "__"
+ df["category_code"].str.lower().str.replace(r"[^0-9a-zA-Z_]+", "_", regex=True)
)
user_documents = (
df.sort_values(["user_id", "event_time"])
.groupby("user_id")["brand_category"]
.apply(lambda x: " ".join(x))
)
CountVectorizer は、文書を「単語の出現回数ベクトル」に変換する scikit-learn の機能です。今回は顧客ごとの購入語彙を数値化するために使いました。
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
vectorizer = CountVectorizer(
token_pattern=r"(?u)\b[\w]+\b",
max_features=3000,
min_df=2,
max_df=0.95,
)
X = vectorizer.fit_transform(user_documents.values)
lda = LatentDirichletAllocation(
n_components=5,
learning_method="batch",
random_state=42,
n_jobs=-1,
)
topic_prob = lda.fit_transform(X)
LDAトピック別の上位語です。
今回のLDA結果は、きれいに「スマホ型」「PC型」「家電型」と分かれるというより、全体としてスマートフォンが購買の起点になり、その周辺にPC・AV・生活家電が混ざる形でした。
そのため、上位語を見て以下のように名前を付けました。
| トピック | 顧客数 | 売上 | 平均Monetary | 中央Monetary | リピート率 | 解釈 |
|---|---|---|---|---|---|---|
| Apple/PC作業環境型 | 46,209 | 33,571,426.19 | 726.51 | 455.72 | 31.47% | Appleスマホ、ASUS/LenovoノートPC、マウスなど |
| Samsungスマホ起点・生活家電併買型 | 52,730 | 26,483,079.29 | 502.24 | 254.35 | 31.27% | Samsungスマホ、冷蔵庫、キッチン家電など |
| OPPO・AV/空調家電型 | 46,711 | 23,402,049.32 | 501.00 | 210.60 | 34.19% | OPPO、TV、洗濯機、イヤホン、空調など |
| Huawei・AV/掃除家電型 | 44,012 | 19,224,277.46 | 436.80 | 140.24 | 31.10% | Huawei、TV、掃除機、ヘッドホンなど |
| Xiaomi・ネットワーク/小型家電型 | 43,904 | 14,639,372.91 | 333.44 | 124.98 | 30.71% | Xiaomi、ルーター、タブレット、小型家電など |
マーケティング的ポイント
今回のデータでは、スマートフォンは単体商品というより、購買テーマの入口になっていました。
例えば Apple/PC作業環境型は平均Monetaryが最も高く、PC・周辺機器・保証・上位モデルの提案余地が大きそうです。
一方、Xiaomi・ネットワーク/小型家電型は平均Monetaryが低めなので、高額商品の強いアップセルより、ルーター、マウス、小型家電、送料無料ライン、セット販売の方が相性が良い可能性があります。
統計的検証
LDAで分けたトピックに意味があるのかを、統計検定で確認しました。
A. トピックごとに累計購入金額は違うか
購買金額は右に長い分布になりやすいため、正規分布を仮定しない Kruskal-Wallis検定を使いました。
from scipy import stats
groups = [
group["monetary"].values
for _, group in customer_topic.groupby("topic_label")
]
h_stat, p_value = stats.kruskal(*groups)
結果は以下です。
| 指標 | 値 |
|---|---|
| H統計量 | 9,016.03 |
| p値 | < 0.001 |
| epsilon squared | 0.0386 |
p値は非常に小さいため、トピック間で累計購入金額の分布に差はありそうです。
ただし、効果量である epsilon squared は 0.0386 なので、差は「非常に大きい」とまでは言えません。データ数が多いとp値は小さくなりやすいため、効果量も見るのが重要です。
箱ひげ図です。
B. トピックごとにリピート率は違うか
リピート有無は Frequency >= 2 としました。
トピックとリピート有無の関連は、カイ二乗検定で見ました。
from scipy import stats
import pandas as pd
table = pd.crosstab(customer_topic["topic_label"], customer_topic["repeat_flag"])
chi2, p_value, dof, expected = stats.chi2_contingency(table)
結果は以下の通りです。
| 指標 | 値 |
|---|---|
| カイ二乗統計量 | 166.12 |
| p値 | 7.13e-35 |
| Cramer's V | 0.0267 |
こちらもp値は小さいですが、Cramer's V は 0.0267 とかなり小さいです。
つまり「差はあるが、リピート率だけで見るとトピック差は大きくない」と解釈しました。
C. リピート率の95%信頼区間
リピート率は点推定だけでなく、Wilson法で95%信頼区間も出しました。
from statsmodels.stats.proportion import proportion_confint
low, high = proportion_confint(
count=repeat_count,
nobs=customer_count,
alpha=0.05,
method="wilson",
)
トピック別のリピート率は以下です。
| トピック | リピート率 | 95%信頼区間 |
|---|---|---|
| OPPO・AV/空調家電型 | 34.19% | 33.76% 〜 34.62% |
| Apple/PC作業環境型 | 31.47% | 31.05% 〜 31.90% |
| Samsungスマホ起点・生活家電併買型 | 31.27% | 30.88% 〜 31.67% |
| Huawei・AV/掃除家電型 | 31.10% | 30.67% 〜 31.53% |
| Xiaomi・ネットワーク/小型家電型 | 30.71% | 30.28% 〜 31.14% |
D. RFMセグメントとLDAトピックの関連
RFMセグメントとLDAトピックの関連もカイ二乗検定で確認しました。
| 指標 | 値 |
|---|---|
| カイ二乗統計量 | 5,976.85 |
| p値 | < 0.001 |
| Cramer's V | 0.0800 |
関連はありますが、Cramer's V は 0.0800 なので、こちらも強い関連というより「施策設計の補助情報」として使うのがよさそうです。
ヒートマップです。
マーケティング的ポイント
統計検定の結果からは、LDAトピックごとに購買金額やリピート率の差は確認できました。
ただし、効果量は大きくありませんでした。つまり「トピックだけで施策を決める」のではなく、RFMの状態と組み合わせる必要があります。
今回なら、同じApple/PC作業環境型でも、優良顧客には上位モデルや保証を出し、休眠予備軍には過去購入カテゴリに合わせた復帰訴求を出す、という使い分けが現実的だと考えます。
機械学習による再購入予測
次に、前半期間の購買履歴から後半期間の再購入を予測しました。
今回は元データの期間を活かし、以下のように分けました。
| 用途 | 期間 |
|---|---|
| 特徴量作成期間 | 2020-01-05 〜 2020-08-31 |
| 予測対象期間 | 2020-09-01 〜 2020-11-21 |
目的変数は「予測対象期間に購入したかどうか」です。
特徴量は以下を使いました。
| 特徴量 | 意味 |
|---|---|
| recency | 直近購入からの日数 |
| frequency | 注文回数 |
| monetary | 累計購入金額 |
| avg_order_value | 平均注文金額 |
| unique_category_count | 購入カテゴリ数 |
| unique_brand_count | 購入ブランド数 |
| LDA topic probability | 顧客が各購買トピックに属する確率 |
| main_topic | 最も確率が高い購買トピック |
特徴量エンジニアリングとして重要だったのは、LDAのトピックを単なるラベルではなく、確率として使ったことです。
例えば、ある顧客が「Apple/PC作業環境型 0.60、Samsungスマホ起点型 0.25、その他 0.15」のような混合的な興味を持つ場合、最大トピックだけに潰すより、確率をそのまま入れた方が情報が残ります。
比較したモデルは以下です。
- Model 1: RFMのみ
- Model 2: RFM + LDAトピック確率
- Model 3: RFM + LDA + カテゴリ/ブランド特徴量
feature_sets = {
"Model 1: RFM": ["recency", "frequency", "monetary"],
"Model 2: RFM + LDA": ["recency", "frequency", "monetary"] + topic_cols,
"Model 3: RFM + LDA + Category/Brand": [
"recency",
"frequency",
"monetary",
"avg_order_value",
"unique_category_count",
"unique_brand_count",
] + topic_cols + ["main_topic"],
}
モデル別ROC-AUCです。
結果は以下でした。
| モデル | ROC-AUC | Precision | Recall | F1 |
|---|---|---|---|---|
| Model 3 + LogisticRegression | 0.6595 | 0.2450 | 0.4739 | 0.3230 |
| Model 2 + LogisticRegression | 0.6517 | 0.2370 | 0.4618 | 0.3133 |
| Model 1 + LogisticRegression | 0.6427 | 0.2201 | 0.5053 | 0.3066 |
| Model 3 + RandomForest | 0.6256 | 0.2136 | 0.0639 | 0.0984 |
| Model 2 + RandomForest | 0.6031 | 0.2342 | 0.0629 | 0.0992 |
| Model 1 + RandomForest | 0.5949 | 0.1929 | 0.1622 | 0.1762 |
今回のベストは RFM + LDA + カテゴリ/ブランド特徴量 を使ったロジスティック回帰でした。
RFMのみのROC-AUCは 0.6427、LDAとカテゴリ・ブランド特徴量を足すと 0.6595 でした。改善幅は大きくありませんが、購買嗜好の情報を足すことで再購入予測が少し改善しました。
ベストモデルの混同行列です。
テストデータでは、実際の再購入率は 12.85% でした。ベストモデルは Recall が 47.39% なので、再購入者の約半分弱を拾う設定になっています。一方で Precision は 24.50% なので、予測陽性の中には再購入しない顧客も多く含まれます。
マーケティング的ポイント
このモデルは、高コストな個別DMよりも、メール、アプリ通知、レコメンド枠の出し分けのような低コスト施策に向いています。
Precisionが高くないため、「高額クーポンを配る対象者の厳密な選定」にはそのまま使いにくいです。一方、Recallがある程度あるため、「再購入しそうな顧客を広めに拾い、購買テーマに合わせて提案を変える」用途には使えそうです。
今回の結果から考えるマーケティング施策
ここからは、実際の分析結果に基づいて施策を考えます。
1. 優良顧客 × Apple/PC作業環境型
Apple/PC作業環境型は、平均Monetaryが 726.51 とトピック内で最も高く、売上も 33,571,426.19 と最大でした。
施策仮説:
- Mac/ノートPC、マウス、キーボード、モニターの関連提案
- Appleスマホ購入者へのイヤホン、スマートウォッチ、保証提案
- 値引きよりも延長保証、上位モデル、先行販売
この層は単価が高いため、割引で無理に買わせるより、作業環境全体を良くする提案が合いそうです。
2. 休眠予備軍 × Samsungスマホ起点・生活家電併買型
RFMでは休眠予備軍が 66,828 人、売上 49,939,856.58 と非常に大きいセグメントでした。
Samsungスマホ起点・生活家電併買型は、スマホだけでなく冷蔵庫やキッチン家電も上位に出ています。
施策仮説:
- 過去購入カテゴリに合わせた復帰クーポン
- 冷蔵庫、洗濯機、キッチン家電の買い替え通知
- Samsungスマホ購入者への周辺機器、保証、家電連動提案
休眠予備軍は過去の購買力が高いため、全体向けセールよりも、過去カテゴリに寄せた復帰施策が有望です。
3. OPPO・AV/空調家電型
OPPO・AV/空調家電型はリピート率が 34.19% と最も高いトピックでした。
上位語には OPPOスマホ、Samsung TV、Samsung洗濯機、Appleイヤホン、空調家電が出ています。
施策仮説:
- スマホ購入後にイヤホン、スマートウォッチ、TV連携商品を提案
- 空調家電購入者へ季節前メンテナンス・買い替え通知
- AV機器購入者へケーブル、設置、保証を提案
リピート率が高いので、購入後フォローやカテゴリ横断の提案を早めに出す価値がありそうです。
4. Xiaomi・ネットワーク/小型家電型
このトピックは平均Monetaryが 333.44 と低めでした。
上位語には Xiaomiスマホ、TP-Linkルーター、タブレット、マウス、小型家電が含まれます。
施策仮説:
- ルーター、マウス、充電器、ケーブルのセット販売
- 送料無料ラインまでの追加提案
- 小型家電のまとめ買い提案
高額アップセルよりも、買いやすい周辺商品を組み合わせて客単価を少し上げる施策が合いそうです。
5. 低単価リピート顧客
低単価リピート顧客は平均Recencyが 23.1 日と短く、最近も購入しています。一方で平均Monetaryは 72.52 と低いです。
施策仮説:
- カート内で送料無料ラインを提示
- 消耗品・小物のセット販売
- 月次キャンペーンでまとめ買い促進
この層は購入頻度の接点があるため、値引きよりも「ついで買い」を作る方が現実的です。
まとめ
今回の分析では、単なる商品ランキングではなく、ID-POSデータを顧客単位で見て施策に落とし込むことを重視しました。
結果として分かったことは以下です。
- 2020年の有効購入データは 562,823 行、顧客数は 233,566 人だった
- 売上の中心カテゴリは
electronics.smartphoneだった - RFMでは「優良顧客」と「休眠予備軍」が売上の大部分を占めていた
- LDAでは、スマホを起点にPC、AV、生活家電へ広がる購買テーマが見えた
- トピック別の購買金額・リピート率には統計的な差があったが、効果量は大きくなかった
- 再購入予測では、RFMにLDAとカテゴリ・ブランド特徴量を足すとROC-AUCが 0.6427 から 0.6595 に改善した
RFMは顧客の「状態」を表し、LDAは顧客の「興味・購買テーマ」を表します。
この2つを組み合わせることで、
- 優良顧客には上位提案
- 休眠予備軍には過去カテゴリに沿った復帰施策
- 新規顧客には初回購入直後の関連提案
- 低単価リピート顧客にはセット販売や送料無料ライン
のように、顧客ごとに施策を変えられます。
ID-POS分析の価値は、売上を見ること自体ではなく、分析結果から「誰に・何を・いつ提案するか」を考えられる点にあります。
ただし、今回の分析は公開データを使った観察分析であり、広告接触、キャンペーン、在庫、粗利、会員属性などは考慮できていません。また、因果推論ではないため、施策効果を断定することはできません。
実務で使う場合は、本記事のような分析で施策仮説を作り、その後A/Bテストやホールドアウト検証で本当に効果があるかを確認するのがよいと思います。
という事でID-POSデータを私にください











