Pandas ユーザー行動分析実践
ユーザー行動分析はプロダクト最適化の重要な根拠です。本ケースでは、ユーザーの訪問、リテンション、コンバージョンなどの行動データを分析する方法を紹介します。
データ準備
ユーザー行動データのシミュレーション
例
import pandas as pd
import numpy as np
np.random.seed(42)
n_users = 500
n_events = 5000
# ユーザー基本情報
users = pd.DataFrame({
"ユーザーID": range(1, n_users + 1),
"登録日": pd.date_range("2024-01-01", periods=n_users, freq="D")[:n_users],
"チャネル": np.random.choice(["自然検索", "広告", "レコメンド", "ソーシャル"], n_users),
"デバイス": np.random.choice(["PC", "Mobile", "Tablet"], n_users, p=[0.3, 0.6, 0.1])
})
# ユーザー行動イベント
events = pd.DataFrame({
"イベントID": range(1, n_events + 1),
"ユーザーID": np.random.randint(1, n_users + 1, n_events),
"イベントタイプ": np.random.choice(
["閲覧", "クリック", "お気に入り", "カート追加", "注文", "コメント"],
n_events,
p=[0.4, 0.25, 0.1, 0.1, 0.1, 0.05]
),
"イベント時間": pd.date_range("2024-01-15", periods=n_events, freq="10min")
})
# データ結合
events = events.merge(users[["ユーザーID", "チャネル", "デバイス"]], on="ユーザーID")
print("ユーザーデータ:")
print(users.head())
print(f"\nユーザー数: {len(users)}")
print("\nイベントデータ:")
print(events.head())
print(f"\nイベント数: {len(events)}")
import numpy as np
np.random.seed(42)
n_users = 500
n_events = 5000
# ユーザー基本情報
users = pd.DataFrame({
"ユーザーID": range(1, n_users + 1),
"登録日": pd.date_range("2024-01-01", periods=n_users, freq="D")[:n_users],
"チャネル": np.random.choice(["自然検索", "広告", "レコメンド", "ソーシャル"], n_users),
"デバイス": np.random.choice(["PC", "Mobile", "Tablet"], n_users, p=[0.3, 0.6, 0.1])
})
# ユーザー行動イベント
events = pd.DataFrame({
"イベントID": range(1, n_events + 1),
"ユーザーID": np.random.randint(1, n_users + 1, n_events),
"イベントタイプ": np.random.choice(
["閲覧", "クリック", "お気に入り", "カート追加", "注文", "コメント"],
n_events,
p=[0.4, 0.25, 0.1, 0.1, 0.1, 0.05]
),
"イベント時間": pd.date_range("2024-01-15", periods=n_events, freq="10min")
})
# データ結合
events = events.merge(users[["ユーザーID", "チャネル", "デバイス"]], on="ユーザーID")
print("ユーザーデータ:")
print(users.head())
print(f"\nユーザー数: {len(users)}")
print("\nイベントデータ:")
print(events.head())
print(f"\nイベント数: {len(events)}")
ユーザーアクティブ分析
イベントタイプ分布
例
# イベントタイプ集計
event_dist = events["イベントタイプ"].value_counts()
event_pct = events["イベントタイプ"].value_counts(normalize=True) * 100
print("=== イベントタイプ分布 ===\n")
event_analysis = pd.DataFrame({
"回数": event_dist,
"割合": event_pct.round(2)
})
print(event_analysis)
print()
# 各イベントタイプのユーザー数
event_users = events.groupby("イベントタイプ")["ユーザーID"].nunique()
print("各イベントタイプの参加ユーザー数:")
print(event_users)
event_dist = events["イベントタイプ"].value_counts()
event_pct = events["イベントタイプ"].value_counts(normalize=True) * 100
print("=== イベントタイプ分布 ===\n")
event_analysis = pd.DataFrame({
"回数": event_dist,
"割合": event_pct.round(2)
})
print(event_analysis)
print()
# 各イベントタイプのユーザー数
event_users = events.groupby("イベントタイプ")["ユーザーID"].nunique()
print("各イベントタイプの参加ユーザー数:")
print(event_users)
ユーザーエンゲージメント
例
# ユーザーイベント集計
user_events = events.groupby("ユーザーID").agg({
"イベントID": "count",
"イベントタイプ": lambda x: x.nunique()
}).rename(columns={
"イベントID": "イベント総数",
"イベントタイプ": "イベントタイプ数"
})
print("=== ユーザーエンゲージメント ===\n")
print(f"ユーザー1人あたりの平均イベント数: {user_events['イベント総数'].mean():.1f}")
print(f"中央値: {user_events['イベント総数'].median():.0f}")
print(f"最大: {user_events['イベント総数'].max()}")
print()
# ユーザーセグメント化
user_events["アクティブ度"] = pd.cut(
user_events["イベント総数"],
bins=[0, 3, 10, 20, float("inf")],
labels=["低アクティブ", "普通", "アクティブ", "高アクティブ"]
)
print("ユーザーアクティブ度分布:")
print(user_events["アクティブ度"].value_counts())
user_events = events.groupby("ユーザーID").agg({
"イベントID": "count",
"イベントタイプ": lambda x: x.nunique()
}).rename(columns={
"イベントID": "イベント総数",
"イベントタイプ": "イベントタイプ数"
})
print("=== ユーザーエンゲージメント ===\n")
print(f"ユーザー1人あたりの平均イベント数: {user_events['イベント総数'].mean():.1f}")
print(f"中央値: {user_events['イベント総数'].median():.0f}")
print(f"最大: {user_events['イベント総数'].max()}")
print()
# ユーザーセグメント化
user_events["アクティブ度"] = pd.cut(
user_events["イベント総数"],
bins=[0, 3, 10, 20, float("inf")],
labels=["低アクティブ", "普通", "アクティブ", "高アクティブ"]
)
print("ユーザーアクティブ度分布:")
print(user_events["アクティブ度"].value_counts())
コンバージョンファネル分析
コンバージョンパス
例
# イベントタイプごとのユーザー数を計算
funnel = events.groupby("イベントタイプ")["ユーザーID"].nunique()
funnel = funnel.reindex(["閲覧", "クリック", "お気に入り", "カート追加", "注文", "コメント"])
# コンバージョン率を計算
funnel_df = pd.DataFrame({
"ユーザー数": funnel,
"絶対コンバージョン率": (funnel / funnel["閲覧"] * 100).round(2),
"ステップコンバージョン率": (funnel / funnel.shift(1) * 100).round(2)
}).fillna(100)
print("=== コンバージョンファネル ===\n")
print(funnel_df)
print()
# ファネルデータを可視化
print("ファネルデータ:")
for step, row in funnel_df.iterrows():
print(f"{step}: {row['ユーザー数']} 人 ({row['絶対コンバージョン率']:.1f}%)")
funnel = events.groupby("イベントタイプ")["ユーザーID"].nunique()
funnel = funnel.reindex(["閲覧", "クリック", "お気に入り", "カート追加", "注文", "コメント"])
# コンバージョン率を計算
funnel_df = pd.DataFrame({
"ユーザー数": funnel,
"絶対コンバージョン率": (funnel / funnel["閲覧"] * 100).round(2),
"ステップコンバージョン率": (funnel / funnel.shift(1) * 100).round(2)
}).fillna(100)
print("=== コンバージョンファネル ===\n")
print(funnel_df)
print()
# ファネルデータを可視化
print("ファネルデータ:")
for step, row in funnel_df.iterrows():
print(f"{step}: {row['ユーザー数']} 人 ({row['絶対コンバージョン率']:.1f}%)")
チャネル分析
各チャネルのパフォーマンス
例
# チャネル別ユーザー分析
channel_analysis = events.groupby("チャネル").agg({
"ユーザーID": "nunique",
"イベントID": "count"
}).rename(columns={
"ユーザーID": "ユーザー数",
"イベントID": "イベント数"
})
channel_analysis["ユーザー1人あたりのイベント数"] = (
channel_analysis["イベント数"] / channel_analysis["ユーザー数"]
).round(2)
print("=== チャネル分析 ===\n")
print(channel_analysis)
print()
# チャネル別コンバージョン比較
channel_funnel = events[events["イベントタイプ"].isin(["閲覧", "注文"])].groupby(
["チャネル", "イベントタイプ"]
)["ユーザーID"].nunique().unstack()
channel_funnel["コンバージョン率"] = (
channel_funnel["注文"] / channel_funnel["閲覧"] * 100
).round(2)
print("各チャネルのコンバージョン率:")
print(channel_funnel)
channel_analysis = events.groupby("チャネル").agg({
"ユーザーID": "nunique",
"イベントID": "count"
}).rename(columns={
"ユーザーID": "ユーザー数",
"イベントID": "イベント数"
})
channel_analysis["ユーザー1人あたりのイベント数"] = (
channel_analysis["イベント数"] / channel_analysis["ユーザー数"]
).round(2)
print("=== チャネル分析 ===\n")
print(channel_analysis)
print()
# チャネル別コンバージョン比較
channel_funnel = events[events["イベントタイプ"].isin(["閲覧", "注文"])].groupby(
["チャネル", "イベントタイプ"]
)["ユーザーID"].nunique().unstack()
channel_funnel["コンバージョン率"] = (
channel_funnel["注文"] / channel_funnel["閲覧"] * 100
).round(2)
print("各チャネルのコンバージョン率:")
print(channel_funnel)
デバイス分析
例
# デバイス分析
device_analysis = events.groupby("デバイス").agg({
"ユーザーID": "nunique",
"イベントID": "count"
}).rename(columns={
"ユーザーID": "ユーザー数",
"イベントID": "イベント数"
})
device_analysis["ユーザー1人あたりのイベント数"] = (
device_analysis["イベント数"] / device_analysis["ユーザー数"]
).round(2)
print("=== デバイス分布 ===\n")
print(device_analysis)
print()
# デバイス別コンバージョン率
device_funnel = events[events["イベントタイプ"].isin(["閲覧", "注文"])].groupby(
["デバイス", "イベントタイプ"]
)["ユーザーID"].nunique().unstack()
device_funnel["コンバージョン率"] = (
device_funnel["注文"] / device_funnel["閲覧"] * 100
).round(2)
print("各デバイスのコンバージョン率:")
print(device_funnel)
device_analysis = events.groupby("デバイス").agg({
"ユーザーID": "nunique",
"イベントID": "count"
}).rename(columns={
"ユーザーID": "ユーザー数",
"イベントID": "イベント数"
})
device_analysis["ユーザー1人あたりのイベント数"] = (
device_analysis["イベント数"] / device_analysis["ユーザー数"]
).round(2)
print("=== デバイス分布 ===\n")
print(device_analysis)
print()
# デバイス別コンバージョン率
device_funnel = events[events["イベントタイプ"].isin(["閲覧", "注文"])].groupby(
["デバイス", "イベントタイプ"]
)["ユーザーID"].nunique().unstack()
device_funnel["コンバージョン率"] = (
device_funnel["注文"] / device_funnel["閲覧"] * 100
).round(2)
print("各デバイスのコンバージョン率:")
print(device_funnel)
リテンション分析
例
# 簡易リテンション分析
# 登録後7日以内にアクティブであればリテンションとみなす
users_with_events = events.groupby("ユーザーID")["イベント時間"].agg(["min", "max"])
users_with_events.columns = ["初回アクティブ日", "最終アクティブ日"]
# 登録情報を結合
users_analysis = users.merge(users_with_events, left_on="ユーザーID", right_index=True, how="left")
# アクティブ日数を計算
users_analysis["アクティブ日数"] = (
users_analysis["最終アクティブ日"] - users_analysis["初回アクティブ日"]
).dt.days + 1
print("=== ユーザーリテンション分析 ===\n")
print(f"行動ありユーザー: {len(users_with_events)}")
print(f"平均アクティブ日数: {users_analysis['アクティブ日数'].mean():.1f} 日")
print()
# 翌日リテンション(簡易)
retention_1d = len(users_analysis[users_analysis["アクティブ日数"] >= 2]) / len(users_analysis) * 100
retention_3d = len(users_analysis[users_analysis["アクティブ日数"] >= 3]) / len(users_analysis) * 100
print(f"翌日リテンション率: {retention_1d:.1f}%")
print(f"3日リテンション率: {retention_3d:.1f}%")
# 登録後7日以内にアクティブであればリテンションとみなす
users_with_events = events.groupby("ユーザーID")["イベント時間"].agg(["min", "max"])
users_with_events.columns = ["初回アクティブ日", "最終アクティブ日"]
# 登録情報を結合
users_analysis = users.merge(users_with_events, left_on="ユーザーID", right_index=True, how="left")
# アクティブ日数を計算
users_analysis["アクティブ日数"] = (
users_analysis["最終アクティブ日"] - users_analysis["初回アクティブ日"]
).dt.days + 1
print("=== ユーザーリテンション分析 ===\n")
print(f"行動ありユーザー: {len(users_with_events)}")
print(f"平均アクティブ日数: {users_analysis['アクティブ日数'].mean():.1f} 日")
print()
# 翌日リテンション(簡易)
retention_1d = len(users_analysis[users_analysis["アクティブ日数"] >= 2]) / len(users_analysis) * 100
retention_3d = len(users_analysis[users_analysis["アクティブ日数"] >= 3]) / len(users_analysis) * 100
print(f"翌日リテンション率: {retention_1d:.1f}%")
print(f"3日リテンション率: {retention_3d:.1f}%")
分析まとめ
例
print("""
=== ユーザー行動分析まとめ ===
1. ユーザーエンゲージメント
- イベントタイプは閲覧が中心で、次いでクリックが多い
- ユーザー1人あたりの平均イベント数が少なく、ユーザーの粘着性を高める必要がある
2. コンバージョンファネル
- 離脱が大きく、ユーザー体験の最適化が必要
- 閲覧から注文までのコンバージョン率が低い
3. チャネル効果
- ユーザー数が多いチャネルのコンバージョンパスを重点的に最適化することを推奨
- チャネルごとにパフォーマンス差があるため、きめ細かな運用が必要
4. デバイスのパフォーマンス
- Mobile が主要デバイスであり、モバイル体験の最適化に重点的に取り組む必要がある
5. 最適化の提案
- 1) 商品表示を最適化し、閲覧→クリックのコンバージョンを向上させる
- 2) 購入フローを簡素化し、注文時の離脱を減らす
- 3) Mobile向けにページの読み込み速度を最適化する
- 4) ユーザーセグメントに基づく運用体制を構築する
""")
=== ユーザー行動分析まとめ ===
1. ユーザーエンゲージメント
- イベントタイプは閲覧が中心で、次いでクリックが多い
- ユーザー1人あたりの平均イベント数が少なく、ユーザーの粘着性を高める必要がある
2. コンバージョンファネル
- 離脱が大きく、ユーザー体験の最適化が必要
- 閲覧から注文までのコンバージョン率が低い
3. チャネル効果
- ユーザー数が多いチャネルのコンバージョンパスを重点的に最適化することを推奨
- チャネルごとにパフォーマンス差があるため、きめ細かな運用が必要
4. デバイスのパフォーマンス
- Mobile が主要デバイスであり、モバイル体験の最適化に重点的に取り組む必要がある
5. 最適化の提案
- 1) 商品表示を最適化し、閲覧→クリックのコンバージョンを向上させる
- 2) 購入フローを簡素化し、注文時の離脱を減らす
- 3) Mobile向けにページの読み込み速度を最適化する
- 4) ユーザーセグメントに基づく運用体制を構築する
""")