🏢 STEP 54: 実企業の分析事例ケーススタディ
Netflix、Amazon、Airbnb、Uberの成功事例から学ぶデータ分析の実践
📋 このステップで学ぶこと
- Netflix: レコメンデーション最適化とA/Bテスト文化
- Amazon: 需要予測と在庫最適化
- Airbnb: 価格最適化とダイナミックプライシング
- Uber: サージプライシングと需給マッチング
- 実際のデータ分析手法とビジネスインパクト
難易度: 応用レベル(世界的企業の分析手法を学ぶ)
📺 1. Netflix: レコメンデーション最適化
ビジネス背景
・膨大なコンテンツから最適なものを提案
・ユーザーの視聴継続率を高める
・解約率(チャーン)を下げる
データ分析アプローチ:
・視聴履歴データの収集(視聴時間、完視聴率、中断ポイント)
・協調フィルタリング(類似ユーザーの視聴パターン)
・A/Bテストによる継続的な改善
ビジネスインパクト:
・視聴時間の75%がレコメンデーションから
・年間約150億円のコスト削減効果
・解約率を大幅に改善
重要な指標(KPI)
| 指標 | 説明 | 重要性 |
|---|---|---|
| 視聴完了率 | コンテンツを最後まで視聴した割合 | コンテンツ品質の指標 |
| エンゲージメント率 | 再生開始から24時間以内の継続視聴 | ユーザー定着の指標 |
| 月間視聴時間 | 1ユーザーあたりの月間視聴時間 | サービス価値の指標 |
| 解約率 | 月間の解約ユーザー割合 | 最重要ビジネス指標 |
協調フィルタリングの実装
Netflixのレコメンデーションシステムの基本となる協調フィルタリングを実装します。
「あなたと似た視聴パターンの人が高評価したコンテンツ」を推薦する手法です。
ステップ:
1. ユーザー間の類似度を計算
2. 類似ユーザーが高評価したコンテンツを特定
3. 未視聴のコンテンツを推薦
# 必要なライブラリを読み込む
import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from scipy import stats
print("=" * 60)
print("【Case Study 1: Netflix レコメンデーションシステム】")
print("=" * 60)
サンプルデータの作成
# ユーザーとコンテンツの評価マトリックスを作成
# 実際のNetflixは暗黙的フィードバック(視聴時間、完視聴率)も使用
np.random.seed(42)
users = ['User_' + str(i) for i in range(1, 21)] # 20人のユーザー
contents = ['Content_' + str(i) for i in range(1, 16)] # 15本のコンテンツ
# 視聴履歴マトリックス(0=未視聴、1-5=評価)
# 約70%が未視聴(スパース性)
ratings_data = []
for user in users:
for content in contents:
# np.random.random(): 0〜1の乱数を生成
if np.random.random() > 0.7: # 30%のみ視聴
rating = np.random.randint(1, 6) # 1〜5の評価
ratings_data.append({
'user': user,
'content': content,
'rating': rating
})
ratings_df = pd.DataFrame(ratings_data)
print("【視聴履歴データ】")
print(f"総ユーザー数: {len(users)}")
print(f"総コンテンツ数: {len(contents)}")
print(f"総視聴記録: {len(ratings_df)}")
print(f"スパース率: {(1 - len(ratings_df)/(len(users)*len(contents)))*100:.1f}%")
ユーザー×コンテンツマトリックスの作成
# pivot_table(): 行×列のマトリックスに変換
# index: 行に配置する列
# columns: 列に配置する列
# values: セルに入れる値
# fill_value: 欠損値を埋める値
user_content_matrix = ratings_df.pivot_table(
index='user',
columns='content',
values='rating',
fill_value=0 # 未視聴は0
)
print("【ユーザー×コンテンツマトリックス(最初の5行5列)】")
print(user_content_matrix.iloc[:5, :5])
ユーザー間の類似度を計算
2つのベクトルの角度の近さを測る指標です。
・値の範囲: -1〜1
・1に近い: 視聴パターンが似ている
・0に近い: 関連がない
・-1に近い: 正反対のパターン
# cosine_similarity(): コサイン類似度を計算
# 各ユーザーの評価パターンを比較
user_similarity = cosine_similarity(user_content_matrix)
# DataFrameに変換(見やすくするため)
user_similarity_df = pd.DataFrame(
user_similarity,
index=user_content_matrix.index,
columns=user_content_matrix.index
)
print("【ユーザー類似度マトリックス(最初の5×5)】")
print(user_similarity_df.iloc[:5, :5].round(3))
特定ユーザーへのレコメンデーション
# User_1へのレコメンデーションを生成
target_user = 'User_1'
target_user_ratings = user_content_matrix.loc[target_user]
# 類似ユーザーTop5を取得
# sort_values(): 値でソート(ascending=False: 降順)
# [1:6]: 自分自身を除いて上位5人
similar_users = user_similarity_df[target_user].sort_values(ascending=False)[1:6]
print(f"【{target_user}に最も類似しているユーザーTop5】")
for similar_user, similarity in similar_users.items():
print(f" {similar_user}: 類似度 {similarity:.3f}")
レコメンデーションスコアの計算
# 未視聴コンテンツを特定
unwatched_contents = target_user_ratings[target_user_ratings == 0].index
# 各未視聴コンテンツのレコメンデーションスコアを計算
# スコア = 類似ユーザーの評価を類似度で重み付け平均
recommendation_scores = {}
for content in unwatched_contents:
weighted_sum = 0
similarity_sum = 0
for similar_user, similarity in similar_users.items():
rating = user_content_matrix.loc[similar_user, content]
if rating > 0: # 視聴済みのみ
weighted_sum += similarity * rating # 類似度×評価
similarity_sum += similarity
if similarity_sum > 0:
# 重み付け平均
recommendation_scores[content] = weighted_sum / similarity_sum
# スコア順にソート
recommended_contents = sorted(
recommendation_scores.items(),
key=lambda x: x[1],
reverse=True
)
print(f"【{target_user}へのレコメンデーションTop5】")
for i, (content, score) in enumerate(recommended_contents[:5], 1):
print(f" {i}. {content}: 予測スコア {score:.2f}")
A/Bテスト: アルゴリズムの比較
Netflixでは、週に数百のA/Bテストを並行実施しています。
# A/Bテスト: 既存アルゴリズム vs 新アルゴリズム
print("=" * 60)
print("【A/Bテスト: アルゴリズムの比較】")
print("=" * 60)
print("Group A: 既存アルゴリズム(協調フィルタリング)")
print("Group B: 新アルゴリズム(ハイブリッド: 協調+コンテンツベース)")
print()
# サンプルデータ(各グループ5000人)
np.random.seed(42)
n_users_per_group = 5000
# Group A: 既存アルゴリズム
# np.random.normal(): 正規分布に従う乱数を生成
group_a_engagement = np.random.normal(45, 8, n_users_per_group) # 平均45分/日
group_a_completion = np.random.normal(0.65, 0.15, n_users_per_group) # 完視聴率65%
# Group B: 新アルゴリズム(+10%改善を仮定)
group_b_engagement = np.random.normal(49.5, 8, n_users_per_group) # +10%
group_b_completion = np.random.normal(0.715, 0.15, n_users_per_group) # +10%
print("【結果サマリー】")
print(f"Group A: 平均視聴時間 {group_a_engagement.mean():.1f}分/日, 完視聴率 {group_a_completion.mean()*100:.1f}%")
print(f"Group B: 平均視聴時間 {group_b_engagement.mean():.1f}分/日, 完視聴率 {group_b_completion.mean()*100:.1f}%")
統計的検定
# t検定で差の有意性を検証
# stats.ttest_ind(): 2つの独立したグループの平均を比較
t_stat, p_value = stats.ttest_ind(group_a_engagement, group_b_engagement)
print("【視聴時間の比較(t検定)】")
print(f"差: {group_b_engagement.mean() - group_a_engagement.mean():.2f}分")
print(f"相対改善: {(group_b_engagement.mean() / group_a_engagement.mean() - 1)*100:.1f}%")
print(f"p値: {p_value:.6f}")
if p_value < 0.05:
print("✓ 統計的に有意(p < 0.05)→ 新アルゴリズムは効果あり")
else:
print("✗ 統計的に有意ではない")
ビジネスインパクト
# ビジネスインパクトの計算
total_users = 10000000 # 1000万人のユーザー
monthly_revenue_per_user = 1500 # 月額1500円
# 解約率の改善(10%改善を仮定)
current_monthly_churn = total_users * 0.05 # 5%解約
new_monthly_churn = total_users * 0.045 # 4.5%に改善
retained_users = current_monthly_churn - new_monthly_churn
# 年間収益保持額
annual_retained_revenue = retained_users * 12 * monthly_revenue_per_user
print("【ビジネスインパクト(年間)】")
print(f"総ユーザー数: {total_users:,}人")
print(f"月間解約削減: {retained_users:,.0f}人")
print(f"年間収益保持: ¥{annual_retained_revenue/100000000:.1f}億円")
・視聴開始・停止・巻き戻しなど全行動を記録
・暗黙的フィードバック(視聴時間)を重視
2. A/Bテスト文化
・全ての変更をA/Bテストで検証
・週に数百のテストを並行実施
3. パーソナライゼーション
・1人1人に異なるコンテンツを表示
・同じ作品でも異なるサムネイル
📦 2. Amazon: 需要予測と在庫最適化
ビジネス背景
・数億点の商品在庫を最適化
・配送時間の短縮(Prime配送の実現)
・在庫切れと過剰在庫の防止
データ分析アプローチ:
・過去の販売データから需要予測
・季節性・トレンドの考慮
・地域別の需要パターン分析
ビジネスインパクト:
・在庫コストの大幅削減
・当日配送の実現
・売上機会損失の最小化
重要な指標(KPI)
| 指標 | 説明 | 目標 |
|---|---|---|
| MAPE | 予測誤差率 | 10%以下 |
| 在庫回転率 | 年間の在庫回転回数 | 高いほど良い |
| 欠品率 | 在庫切れの発生率 | 5%以下 |
| 配送リードタイム | 注文から配達までの時間 | 当日〜翌日 |
販売データの準備
# 2年間の日次販売データを生成
print("=" * 60)
print("【Case Study 2: Amazon 需要予測と在庫最適化】")
print("=" * 60)
np.random.seed(42)
# pd.date_range(): 日付の連続データを生成
dates = pd.date_range('2022-01-01', '2023-12-31', freq='D')
# 販売数 = トレンド + 季節性 + 週次パターン + ノイズ
trend = np.linspace(100, 150, len(dates)) # 緩やかな上昇トレンド
seasonal = 30 * np.sin(2 * np.pi * np.arange(len(dates)) / 365) # 年次季節性
weekly = 10 * np.sin(2 * np.pi * np.arange(len(dates)) / 7) # 週次パターン
noise = np.random.normal(0, 10, len(dates)) # ランダムなノイズ
sales = trend + seasonal + weekly + noise
sales = np.maximum(sales, 0).astype(int) # 負の値を0に
sales_df = pd.DataFrame({'date': dates, 'sales': sales})
sales_df.set_index('date', inplace=True)
print("【販売データ】")
print(f"期間: {sales_df.index.min().date()} ~ {sales_df.index.max().date()}")
print(f"平均日販: {sales_df['sales'].mean():.1f}個")
print(f"最大日販: {sales_df['sales'].max()}個")
訓練データとテストデータに分割
# 80%を訓練、20%をテストに分割
train_size = int(len(sales_df) * 0.8)
train_data = sales_df[:train_size]
test_data = sales_df[train_size:].copy()
print(f"訓練データ: {len(train_data)}日")
print(f"テストデータ: {len(test_data)}日")
需要予測モデル: 指数平滑法
過去のデータに「重み」を付けて将来を予測する手法です。
最近のデータほど重みが大きく、古いデータは重みが小さくなります。
特徴:
・トレンド(上昇/下降傾向)を考慮
・季節性(週次、年次パターン)を考慮
・シンプルながら高精度
# 指数平滑法モデルの構築
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from sklearn.metrics import mean_absolute_percentage_error
# ExponentialSmoothing(): 指数平滑法モデルを作成
# seasonal_periods: 季節性の周期(7日=週次)
# trend: トレンドの種類('add'=加法的)
# seasonal: 季節性の種類('add'=加法的)
model_es = ExponentialSmoothing(
train_data['sales'],
seasonal_periods=7,
trend='add',
seasonal='add'
)
# fit(): モデルを訓練データに適合
fitted_es = model_es.fit()
# forecast(): 将来の値を予測
es_predictions = fitted_es.forecast(steps=len(test_data))
test_data['es_forecast'] = es_predictions.values
予測精度の評価
# 予測精度の評価指標
# MAPE: Mean Absolute Percentage Error(平均絶対パーセント誤差)
# → 予測がどれくらい外れているかを%で表す
mape = mean_absolute_percentage_error(test_data['sales'], test_data['es_forecast']) * 100
# MAE: Mean Absolute Error(平均絶対誤差)
# → 予測と実績の差の絶対値の平均
mae = np.abs(test_data['sales'] - test_data['es_forecast']).mean()
# RMSE: Root Mean Squared Error(平均二乗誤差の平方根)
# → 大きな誤差を重視した指標
rmse = np.sqrt(((test_data['sales'] - test_data['es_forecast'])**2).mean())
print("【予測精度の評価】")
print(f"MAPE: {mape:.2f}%")
print(f"MAE: {mae:.2f}個")
print(f"RMSE: {rmse:.2f}個")
在庫最適化パラメータの計算
安全在庫: 需要変動に備える緩衝在庫
発注点: この数量になったら発注するライン
EOQ: 1回の発注で最もコストが低くなる発注量
リードタイム: 発注から入荷までの日数
# 在庫最適化パラメータ
from scipy.stats import norm
lead_time = 3 # リードタイム(日)
service_level = 0.95 # サービスレベル(欠品確率5%以下)
# 需要の統計量
daily_demand_mean = test_data['es_forecast'].mean()
daily_demand_std = np.abs(test_data['sales'] - test_data['es_forecast']).std()
# norm.ppf(): 正規分布の累積分布関数の逆関数
# サービスレベル95%に対応するz値を取得
z_score = norm.ppf(service_level)
# 安全在庫 = z値 × リードタイム中の需要の標準偏差
lead_time_demand_std = daily_demand_std * np.sqrt(lead_time)
safety_stock = z_score * lead_time_demand_std
# 発注点 = リードタイム中の需要 + 安全在庫
lead_time_demand = daily_demand_mean * lead_time
reorder_point = lead_time_demand + safety_stock
print("【在庫管理パラメータ】")
print(f"リードタイム: {lead_time}日")
print(f"サービスレベル: {service_level*100:.0f}%")
print(f"安全在庫: {safety_stock:.1f}個")
print(f"発注点: {reorder_point:.1f}個")
経済的発注量(EOQ)の計算
# 経済的発注量(EOQ: Economic Order Quantity)
# 発注コストと保管コストの合計が最小になる発注量
annual_demand = daily_demand_mean * 365 # 年間需要
ordering_cost = 5000 # 1回の発注コスト(円)
holding_cost_per_unit = 10 # 在庫保管コスト(円/個/日)
holding_cost_annual = holding_cost_per_unit * 365 # 年間保管コスト
# EOQ公式: √(2 × 年間需要 × 発注コスト / 年間保管コスト)
eoq = np.sqrt((2 * annual_demand * ordering_cost) / holding_cost_annual)
# 年間発注回数とコスト
order_frequency = annual_demand / eoq
annual_ordering_cost = order_frequency * ordering_cost
annual_holding_cost = (eoq / 2) * holding_cost_annual
total_inventory_cost = annual_ordering_cost + annual_holding_cost
print("【経済的発注量(EOQ)】")
print(f"年間需要: {annual_demand:.0f}個")
print(f"EOQ: {eoq:.0f}個")
print(f"年間発注回数: {order_frequency:.1f}回")
print(f"総在庫コスト: ¥{total_inventory_cost:,.0f}")
・機械学習による予測精度向上
・季節性・トレンド・外部要因(天候、イベント)を考慮
2. 複数倉庫の最適配置
・顧客の近くに在庫を事前配置
・当日配送・翌日配送の実現
3. 先読み配送
・購入確率の高い顧客を予測
・購入前に商品を近隣倉庫に配置
🏠 3. Airbnb: 価格最適化とダイナミックプライシング
ビジネス背景
・ホストが適正価格を設定できない
・需要と供給のミスマッチ
・稼働率(占有率)の最適化
データ分析アプローチ:
・過去の予約データから需要パターン分析
・物件特性(立地、設備、レビュー)を機械学習で分析
・ダイナミックプライシング(動的価格調整)
ビジネスインパクト:
・ホストの収益13%向上
・予約率の向上
・新規ホストの参入障壁低下
重要な指標(KPI)
| 指標 | 説明 | 計算式 |
|---|---|---|
| 稼働率 | 予約された日数の割合 | 予約日数 / 提供日数 |
| ADR | 平均単価 | 総収益 / 予約日数 |
| RevPAR | 利用可能日あたり収益 | 稼働率 × ADR |
| 価格弾力性 | 価格変化に対する需要変化 | 需要変化% / 価格変化% |
物件データの準備
# Airbnb物件データの生成
print("=" * 60)
print("【Case Study 3: Airbnb ダイナミックプライシング】")
print("=" * 60)
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, r2_score
np.random.seed(42)
n_listings = 500
# 物件の特徴量を生成
listings_data = {
'listing_id': range(1, n_listings + 1),
'bedrooms': np.random.choice([1, 2, 3, 4], n_listings, p=[0.3, 0.4, 0.2, 0.1]),
'bathrooms': np.random.choice([1, 1.5, 2, 2.5], n_listings, p=[0.4, 0.3, 0.2, 0.1]),
'accommodates': np.random.randint(2, 9, n_listings),
'location_score': np.random.uniform(6, 10, n_listings),
'amenities_count': np.random.randint(5, 25, n_listings),
'review_score': np.random.uniform(3.5, 5.0, n_listings),
'num_reviews': np.random.randint(0, 200, n_listings),
'host_is_superhost': np.random.choice([0, 1], n_listings, p=[0.7, 0.3]),
'instant_bookable': np.random.choice([0, 1], n_listings, p=[0.6, 0.4])
}
listings_df = pd.DataFrame(listings_data)
価格の生成(特徴量に基づく)
# 実際の価格を特徴量から生成
# 基本価格 + 各特徴の影響
base_price = 8000
listings_df['price'] = (
base_price +
listings_df['bedrooms'] * 3000 + # ベッドルーム数の影響
listings_df['bathrooms'] * 2000 + # バスルーム数の影響
listings_df['accommodates'] * 500 + # 収容人数の影響
(listings_df['location_score'] - 8) * 2000 + # 立地スコアの影響
listings_df['amenities_count'] * 200 + # 設備数の影響
(listings_df['review_score'] - 4) * 3000 + # レビュースコアの影響
np.log1p(listings_df['num_reviews']) * 500 + # レビュー数の影響
listings_df['host_is_superhost'] * 2000 + # スーパーホストの影響
listings_df['instant_bookable'] * 1000 + # 即時予約の影響
np.random.normal(0, 1000, n_listings) # ノイズ
)
# 価格を5000〜50000円の範囲に制限
listings_df['price'] = listings_df['price'].clip(5000, 50000).astype(int)
print("【物件データ】")
print(f"物件数: {len(listings_df)}")
print(f"平均価格: ¥{listings_df['price'].mean():,.0f}")
print(f"価格範囲: ¥{listings_df['price'].min():,} ~ ¥{listings_df['price'].max():,}")
価格予測モデルの構築
複数の決定木を組み合わせた機械学習モデルです。
特徴:
・多数の特徴量を同時に考慮
・特徴量の重要度を自動計算
・過学習しにくい
# 特徴量とターゲットを定義
features = [
'bedrooms', 'bathrooms', 'accommodates', 'location_score',
'amenities_count', 'review_score', 'num_reviews',
'host_is_superhost', 'instant_bookable'
]
X = listings_df[features]
y = listings_df['price']
# 訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# ランダムフォレストモデルの構築
# n_estimators: 決定木の数
# max_depth: 木の深さの最大値
model = RandomForestRegressor(n_estimators=100, random_state=42, max_depth=10)
model.fit(X_train, y_train)
# 予測と評価
y_test_pred = model.predict(X_test)
test_mae = mean_absolute_error(y_test, y_test_pred)
test_r2 = r2_score(y_test, y_test_pred)
print("【モデル精度】")
print(f"MAE: ¥{test_mae:,.0f}")
print(f"R²: {test_r2:.3f}")
特徴量の重要度
# 特徴量の重要度を確認
# model.feature_importances_: 各特徴量が予測にどれだけ貢献したか
feature_importance = pd.DataFrame({
'feature': features,
'importance': model.feature_importances_
}).sort_values('importance', ascending=False)
print("【特徴量の重要度】")
for _, row in feature_importance.iterrows():
print(f" {row['feature']:20s}: {row['importance']:.3f}")
ダイナミックプライシング戦略
# サンプル物件の推奨価格を計算
sample_listing = listings_df.iloc[0]
base_recommended_price = model.predict([sample_listing[features]])[0]
print(f"【サンプル物件の推奨価格】")
print(f"基本推奨価格: ¥{base_recommended_price:,.0f}")
print()
# シナリオ別の動的価格調整
pricing_scenarios = {
'平日(需要低)': {'multiplier': 0.85, 'reason': '需要が低い'},
'週末': {'multiplier': 1.15, 'reason': '週末需要が高い'},
'大型連休': {'multiplier': 1.35, 'reason': '連休で需要急増'},
'イベント開催日': {'multiplier': 1.50, 'reason': '近隣でイベント'},
'直前予約(3日前)': {'multiplier': 0.80, 'reason': '空室を埋めるため割引'},
}
print("【シナリオ別価格設定】")
for scenario, params in pricing_scenarios.items():
adjusted_price = base_recommended_price * params['multiplier']
print(f" {scenario}: ¥{adjusted_price:,.0f} (×{params['multiplier']:.2f})")
価格弾力性による最適化
# 価格弾力性のシミュレーション
# 価格を上げると予約率が下がる関係をモデル化
# 価格弾力性: -1.5(価格が10%上がると予約率が15%下がる)
price_elasticity = -1.5
base_booking_probability = 0.70 # 基本予約確率70%
# 複数の価格オプションを評価
price_options = np.linspace(
base_recommended_price * 0.7,
base_recommended_price * 1.3,
13
)
results = []
for price in price_options:
# 価格変化率
price_change = (price - base_recommended_price) / base_recommended_price
# 需要変化率 = 価格弾力性 × 価格変化率
demand_change = price_elasticity * price_change
# 予約確率
booking_prob = base_booking_probability * (1 + demand_change)
booking_prob = np.clip(booking_prob, 0.1, 0.95)
# 期待収益 = 価格 × 予約確率
expected_revenue = price * booking_prob
results.append({
'price': price,
'booking_prob': booking_prob,
'expected_revenue': expected_revenue
})
# 最適価格を見つける
optimal = max(results, key=lambda x: x['expected_revenue'])
print("【価格最適化結果】")
print(f"最適価格: ¥{optimal['price']:,.0f}")
print(f"予約確率: {optimal['booking_prob']*100:.1f}%")
print(f"期待収益: ¥{optimal['expected_revenue']:,.0f}/泊")
・数百の特徴量から最適価格を予測
・ホストの価格設定の手間を削減
2. ダイナミックプライシング
・曜日、季節、イベントで価格を動的調整
・収益最大化と稼働率のバランス
3. RevPAR最適化
・単純な価格最大化ではない
・稼働率×単価の最適化
🚗 4. Uber: サージプライシングと需給マッチング
ビジネス背景
・需要と供給のリアルタイムマッチング
・ピーク時の供給不足
・待ち時間の最小化
データ分析アプローチ:
・リアルタイムの需要予測(エリア×時間帯)
・サージプライシング(需要に応じた価格変動)
・ドライバー配置の最適化
ビジネスインパクト:
・需給バランスの最適化
・ピーク時の供給確保
・顧客の待ち時間短縮
重要な指標(KPI)
| 指標 | 説明 | 目標 |
|---|---|---|
| ETA | 到着予想時間 | 5分以内 |
| マッチング率 | 配車成功率 | 95%以上 |
| ドライバー稼働率 | 乗客を乗せている時間の割合 | 高いほど良い |
| サージ倍率 | 通常価格に対する倍率 | 需給バランスで決定 |
1日の需給データの準備
# 24時間の時間帯別需給データを生成
print("=" * 60)
print("【Case Study 4: Uber サージプライシング】")
print("=" * 60)
np.random.seed(42)
hours = list(range(24))
base_demand = 100
demand_pattern = []
supply_pattern = []
for hour in hours:
# 需要パターン(通勤時間帯と夜にピーク)
if 7 <= hour <= 9: # 朝の通勤
demand_multiplier = 2.5
elif 17 <= hour <= 19: # 夕方の帰宅
demand_multiplier = 2.8
elif 22 <= hour <= 24 or 0 <= hour <= 2: # 夜の飲み会帰り
demand_multiplier = 2.0
else:
demand_multiplier = 1.0
demand = base_demand * demand_multiplier + np.random.normal(0, 10)
demand = max(demand, 20)
# 供給パターン(深夜は少ない)
if 0 <= hour <= 5:
supply_multiplier = 0.6
elif 7 <= hour <= 9:
supply_multiplier = 1.5
elif 17 <= hour <= 19:
supply_multiplier = 1.8
else:
supply_multiplier = 1.0
supply = base_demand * supply_multiplier + np.random.normal(0, 8)
supply = max(supply, 15)
demand_pattern.append(demand)
supply_pattern.append(supply)
uber_df = pd.DataFrame({
'hour': hours,
'demand': demand_pattern,
'supply': supply_pattern
})
uber_df['supply_demand_ratio'] = uber_df['supply'] / uber_df['demand']
サージ倍率の計算
供給/需要の比率に応じて価格を調整します。
効果:
1. 需要の抑制(価格上昇で不急の需要を抑制)
2. 供給の増加(ドライバーへのインセンティブ)
→ 需給バランスの最適化
# サージ倍率のルール
def calculate_surge(supply_demand_ratio):
if supply_demand_ratio >= 1.2:
return 1.0 # 供給過剰: 通常価格
elif supply_demand_ratio >= 0.9:
return 1.0 # バランス: 通常価格
elif supply_demand_ratio >= 0.7:
return 1.5 # やや不足: 1.5倍
elif supply_demand_ratio >= 0.5:
return 2.0 # 不足: 2.0倍
elif supply_demand_ratio >= 0.3:
return 2.5 # 大幅不足: 2.5倍
else:
return 3.0 # 深刻な不足: 3.0倍
uber_df['surge_multiplier'] = uber_df['supply_demand_ratio'].apply(calculate_surge)
print("【時間帯別サージ倍率(ピーク時のみ)】")
peak_hours = uber_df[uber_df['surge_multiplier'] > 1.0]
for _, row in peak_hours.iterrows():
print(f" {row['hour']:2d}時: 需要{row['demand']:.0f} / 供給{row['supply']:.0f} → {row['surge_multiplier']:.1f}x")
サージ効果のシミュレーション
# サージによる需給変化をシミュレーション
price_elasticity = -0.3 # 価格が10%上がると需要が3%減る
supply_elasticity = 0.5 # 価格が10%上がると供給が5%増える
base_fare = 1000 # 基本運賃1000円
# サージなしの場合
uber_df['matched_no_surge'] = uber_df[['demand', 'supply']].min(axis=1)
uber_df['unmatched_no_surge'] = uber_df['demand'] - uber_df['matched_no_surge']
# サージありの場合
uber_df['price_change'] = (uber_df['surge_multiplier'] - 1)
uber_df['demand_surge'] = uber_df['demand'] * (1 + price_elasticity * uber_df['price_change'])
uber_df['supply_surge'] = uber_df['supply'] * (1 + supply_elasticity * uber_df['price_change'])
uber_df['matched_surge'] = uber_df[['demand_surge', 'supply_surge']].min(axis=1)
# 全体のサマリー
total_matched_no_surge = uber_df['matched_no_surge'].sum()
total_matched_surge = uber_df['matched_surge'].sum()
match_rate_no_surge = total_matched_no_surge / uber_df['demand'].sum() * 100
match_rate_surge = total_matched_surge / uber_df['demand_surge'].sum() * 100
print("【1日全体のサマリー】")
print(f"サージなし: マッチング率 {match_rate_no_surge:.1f}%")
print(f"サージあり: マッチング率 {match_rate_surge:.1f}%")
print(f"改善: +{match_rate_surge - match_rate_no_surge:.1f}pp")
収益への影響
# 収益計算
commission_rate = 0.25 # Uberの手数料率25%
# サージなしの収益
revenue_no_surge = uber_df['matched_no_surge'].sum() * base_fare * commission_rate
# サージありの収益
uber_df['fare_surge'] = base_fare * uber_df['surge_multiplier']
uber_df['revenue_surge'] = uber_df['matched_surge'] * uber_df['fare_surge'] * commission_rate
revenue_surge = uber_df['revenue_surge'].sum()
print("【1日の収益比較】")
print(f"サージなし: ¥{revenue_no_surge:,.0f}")
print(f"サージあり: ¥{revenue_surge:,.0f}")
print(f"増加: +{(revenue_surge / revenue_no_surge - 1)*100:.1f}%")
・数分単位での需要予測
・天候、イベントを考慮
2. サージプライシングの効果
・需要の抑制 + 供給の増加
・Win-Win-Winの関係(乗客・ドライバー・Uber)
3. 透明性とコミュニケーション
・サージ発生理由を説明
・ユーザーの理解と納得を得る
📝 STEP 54 のまとめ
- Netflix: レコメンデーションとA/Bテスト文化
- Amazon: 需要予測と在庫最適化
- Airbnb: 価格最適化とダイナミックプライシング
- Uber: サージプライシングと需給マッチング
- 共通点: データドリブンな意思決定と継続的改善
| 要因 | 内容 |
|---|---|
| 1. データ収集の徹底 | ユーザー行動を詳細に記録、リアルタイムデータの活用 |
| 2. A/Bテストによる検証 | 全ての変更を実験として扱い、統計的に評価 |
| 3. 機械学習の活用 | 予測精度の向上、パーソナライゼーション |
| 4. リアルタイム最適化 | 動的な価格設定、需給バランスの調整 |
| 5. 顧客価値の最大化 | 単なる収益最大化ではなく、長期的な関係構築 |
・まず1つのプロジェクトで試す
・A/Bテストを1つ実施してみる
・シンプルな予測モデルから
・成功事例を積み重ねる
データ基盤を整備:
・ユーザー行動の記録
・分析ツールの導入
・データアクセスの民主化
文化の醸成:
・仮説検証の習慣化
・失敗を許容する文化
・データに基づく議論
❓ よくある質問
中小企業での実践方法:
・まずデータを集める(Googleアナリティクス、POSデータ、Excel記録)
・シンプルな分析から(売上推移、顧客セグメント別分析)
・小規模なA/Bテスト(メールタイトル、Webサイトのボタン色)
・無料ツール活用(Python、Google Colab、Tableau Public)
実例:
・小売店: RFM分析で優良顧客を特定→DM送付で売上20%向上
・飲食店: 天候データで需要予測→食材廃棄30%削減
・ECサイト: A/Bテストで購入ボタン改善→CVR 15%向上
機械学習なしでできること:
・記述統計(平均、中央値、標準偏差)
・相関分析、回帰分析(Excelでも可能)
・A/Bテスト(t検定、カイ二乗検定)
・RFM分析、コホート分析、ファネル分析
学習の順序:
1. Excel + 統計学の基礎(3ヶ月)
2. SQL + BIツール(3ヶ月)
3. Python基礎 + pandas(3ヶ月)
4. 機械学習入門(6ヶ月〜)
公式技術ブログ:
・Netflix Tech Blog: netflixtechblog.com
・Amazon Science: amazon.science
・Airbnb Engineering: medium.com/airbnb-engineering
・Uber Engineering: eng.uber.com
学習リソース:
・Coursera、edX、Udemy(オンラインコース)
・Kaggle(コンペ、チュートリアル)
・書籍:「仕事ではじめる機械学習」「前処理大全」など
ステップ:
1. 経営層のコミットメントを得る
2. 小規模なパイロットプロジェクトで成果を出す
3. 成果を数値で可視化し、共有する
4. 全社員向けのデータリテラシー教育
5. データに基づく議論を会議のルールにする
ポイント:
・「データで判断する」を習慣化
・失敗を許容し、学びとして活かす
・ツールを簡単にアクセスできるようにする
学習メモ
ビジネスデータ分析・意思決定 - Step 54