LESSON

演習:経営ダッシュボードを設計しよう

田中VPoE「来月の経営会議に向けて、NetShop社の現状を一目で把握できるダッシュボードを作ってほしい。経営層がデータドリブンな意思決定をするための基盤だ。」

あなた「どんな情報を載せればいいですか?」

田中VPoE「KPIツリーに基づいて、売上、顧客、商品の3つの観点でまとめてくれ。まずは設計図を描いて、それからPythonで実装しよう。」

ミッション概要

NetShop社の経営会議向けダッシュボードを設計・実装します。KPIの可視化、トレンド分析、アラートの3つの要素を盛り込みます。

サンプルデータの作成

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

np.random.seed(42)

# 月次サマリーデータ
months = pd.date_range('2024-01', '2025-12', freq='M')
n_months = len(months)

monthly = pd.DataFrame({
    'month': months,
    'revenue': np.cumsum(np.random.normal(200, 50, n_months)) + 4000,
    'orders': np.cumsum(np.random.normal(50, 20, n_months)) + 2000,
    'new_customers': np.random.poisson(300, n_months),
    'returning_customers': np.random.poisson(800, n_months),
    'avg_order_value': np.random.normal(3500, 300, n_months),
    'cvr': np.random.normal(0.032, 0.003, n_months),
    'visits': np.random.poisson(150000, n_months),
})
monthly['revenue'] = monthly['revenue'].clip(lower=3000)

# カテゴリ別月次データ
categories = ['家電', '書籍', '食品', 'ファッション', 'スポーツ']
cat_monthly = []
for m in months:
    for cat in categories:
        cat_monthly.append({
            'month': m, 'category': cat,
            'revenue': np.random.lognormal(7, 0.5),
            'orders': np.random.poisson(200)
        })
cat_df = pd.DataFrame(cat_monthly)

# 地域別データ
regions = ['関東', '関西', '中部', '北海道', '九州']
region_data = pd.DataFrame({
    'region': regions,
    'revenue': [2800, 1500, 900, 400, 600],
    'customers': [8000, 4500, 2500, 1200, 1800],
    'target': [3000, 1600, 1000, 500, 700]
})

Mission 1: ダッシュボードの設計書を作成する

実装の前に、ダッシュボードの設計書を作成してください。

要件

  1. ダッシュボードの全体レイアウト(どの位置に何を配置するか)
  2. 各パネルの目的、使用するグラフタイプ、データソース
  3. 経営層が最初に見るべき情報(ヒーローメトリクス)の選定
  4. フィルタリング・ドリルダウンの設計
解答例
## ダッシュボード設計書

### レイアウト(4行構成)

Row 1: ヒーローメトリクス(KPIカード 5枚)
  [月間売上] [注文数] [新規顧客数] [CVR] [AOV]
  ※ 前月比の矢印とパーセント変化を表示

Row 2: トレンド(2カラム)
  [左: 月次売上推移 - 折れ線グラフ]    [右: カテゴリ別売上構成 - 積み上げ棒グラフ]

Row 3: 詳細分析(2カラム)
  [左: 地域別売上 vs 目標 - 横棒グラフ]  [右: 顧客セグメント分布 - ドーナツチャート]

Row 4: アラート
  [目標未達KPIのリスト - テーブル形式]

### 各パネルの詳細

| パネル | グラフタイプ | データ | 目的 |
|--------|------------|--------|------|
| KPIカード | 数値+前月比 | monthly | 主要指標の瞬時把握 |
| 売上推移 | 折れ線+面 | monthly | トレンドの把握 |
| カテゴリ別 | 積み上げ棒 | cat_df | 構成比の変化を追跡 |
| 地域別 | 横棒+目標線 | region_data | 地域ごとの達成状況 |
| セグメント | ドーナツ | customer | 顧客構成の把握 |
| アラート | テーブル | 計算値 | 問題の早期発見 |

### フィルタリング
- 期間選択(月次/四半期/年次)
- カテゴリフィルタ
- 地域フィルタ

Mission 2: matplotlib/seabornでダッシュボードを実装する

設計書に基づいて、matplotlibでダッシュボードを実装してください。

要件

  1. KPIカード(数値と前月比)を表示するパネル
  2. 月次売上推移の折れ線グラフ(目標線付き)
  3. カテゴリ別売上の積み上げ棒グラフ
  4. 地域別の達成率を示す横棒グラフ
  5. 全体を1つのFigureにまとめる
解答例
fig = plt.figure(figsize=(18, 14))
fig.suptitle('NetShop社 経営ダッシュボード(2025年12月)', fontsize=18, fontweight='bold', y=0.98)

# === Row 1: KPIカード ===
kpi_data = [
    ('月間売上', f'{monthly.iloc[-1]["revenue"]:,.0f}万円', '+8.2%', 'green'),
    ('注文数', f'{monthly.iloc[-1]["orders"]:,.0f}件', '+5.1%', 'green'),
    ('新規顧客', f'{monthly.iloc[-1]["new_customers"]:,}人', '-2.3%', 'red'),
    ('CVR', f'{monthly.iloc[-1]["cvr"]:.1%}', '+0.3pt', 'green'),
    ('AOV', f'{monthly.iloc[-1]["avg_order_value"]:,.0f}円', '+3.5%', 'green'),
]

for i, (label, value, change, color) in enumerate(kpi_data):
    ax = fig.add_subplot(5, 5, i + 1)
    ax.text(0.5, 0.65, value, ha='center', va='center', fontsize=16, fontweight='bold')
    ax.text(0.5, 0.35, f'{label}', ha='center', va='center', fontsize=10, color='gray')
    ax.text(0.5, 0.15, change, ha='center', va='center', fontsize=11, color=color)
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.axis('off')
    ax.patch.set_facecolor('#f8f9fa')
    ax.patch.set_alpha(1)

# === Row 2 Left: 売上推移 ===
ax1 = fig.add_subplot(3, 2, 3)
ax1.plot(monthly['month'], monthly['revenue'], marker='o', linewidth=2, color='#1976D2')
ax1.fill_between(monthly['month'], monthly['revenue'], alpha=0.1, color='#1976D2')
target_line = 5000
ax1.axhline(y=target_line, color='red', linestyle='--', alpha=0.5, label=f'目標: {target_line}万円')
ax1.set_title('月次売上推移', fontsize=12, fontweight='bold', loc='left')
ax1.set_ylabel('売上(万円)')
ax1.legend()
ax1.grid(axis='y', alpha=0.3)
ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)

# === Row 2 Right: カテゴリ別構成 ===
ax2 = fig.add_subplot(3, 2, 4)
recent_months = cat_df[cat_df['month'] >= '2025-07']
pivot = recent_months.pivot_table(values='revenue', index='month', columns='category', aggfunc='sum')
pivot.plot(kind='bar', stacked=True, ax=ax2, colormap='Set2')
ax2.set_title('カテゴリ別売上構成(直近6ヶ月)', fontsize=12, fontweight='bold', loc='left')
ax2.set_ylabel('売上')
ax2.legend(loc='upper left', fontsize=8)
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)

# === Row 3 Left: 地域別達成率 ===
ax3 = fig.add_subplot(3, 2, 5)
achievement = (region_data['revenue'] / region_data['target'] * 100).round(1)
colors = ['#4CAF50' if a >= 100 else '#FF5722' for a in achievement]
bars = ax3.barh(region_data['region'], achievement, color=colors)
ax3.axvline(x=100, color='gray', linestyle='--', alpha=0.5)
ax3.bar_label(bars, fmt='%.1f%%', padding=3)
ax3.set_title('地域別目標達成率', fontsize=12, fontweight='bold', loc='left')
ax3.set_xlabel('達成率(%)')
ax3.spines['top'].set_visible(False)
ax3.spines['right'].set_visible(False)

# === Row 3 Right: 顧客構成 ===
ax4 = fig.add_subplot(3, 2, 6)
segments = ['新規', 'リピーター', '優良顧客', '休眠']
sizes = [30, 40, 15, 15]
colors_pie = ['#42A5F5', '#66BB6A', '#FFA726', '#EF5350']
wedges, texts, autotexts = ax4.pie(
    sizes, labels=segments, colors=colors_pie,
    autopct='%1.0f%%', startangle=90, pctdistance=0.8,
    wedgeprops=dict(width=0.4)
)
ax4.set_title('顧客セグメント構成', fontsize=12, fontweight='bold')

plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.savefig('dashboard.png', dpi=150, bbox_inches='tight', facecolor='white')
plt.show()

Mission 3: Plotlyでインタラクティブ版を作成する

同じダッシュボードをPlotlyで作成し、インタラクティブな操作を追加してください。

要件

  1. ホバーで詳細情報が見られる折れ線グラフ
  2. カテゴリをクリックでフィルタできる棒グラフ
  3. ドロップダウンで期間を切り替えられるUI
  4. HTMLファイルとしてエクスポート
解答例
import plotly.graph_objects as go
from plotly.subplots import make_subplots

fig = make_subplots(
    rows=2, cols=2,
    subplot_titles=[
        '月次売上推移', 'カテゴリ別売上構成',
        '地域別目標達成率', '顧客セグメント構成'
    ],
    specs=[
        [{'type': 'scatter'}, {'type': 'bar'}],
        [{'type': 'bar'}, {'type': 'pie'}]
    ],
    vertical_spacing=0.12,
    horizontal_spacing=0.1
)

# 売上推移
fig.add_trace(
    go.Scatter(
        x=monthly['month'], y=monthly['revenue'],
        mode='lines+markers', name='売上',
        line=dict(color='#1976D2', width=2),
        fill='tozeroy', fillcolor='rgba(25,118,210,0.1)',
        hovertemplate='%{x|%Y年%m月}<br>売上: %{y:,.0f}万円<extra></extra>'
    ), row=1, col=1
)
fig.add_hline(y=5000, line_dash='dash', line_color='red',
              annotation_text='目標: 5,000万円', row=1, col=1)

# カテゴリ別(直近6ヶ月)
for cat in categories:
    cat_data = cat_df[(cat_df['category'] == cat) & (cat_df['month'] >= '2025-07')]
    fig.add_trace(
        go.Bar(x=cat_data['month'], y=cat_data['revenue'], name=cat,
               hovertemplate='%{x|%Y年%m月}<br>' + cat + ': %{y:,.0f}<extra></extra>'),
        row=1, col=2
    )

# 地域別達成率
achievement_vals = (region_data['revenue'] / region_data['target'] * 100).round(1)
bar_colors = ['#4CAF50' if a >= 100 else '#FF5722' for a in achievement_vals]
fig.add_trace(
    go.Bar(x=achievement_vals, y=region_data['region'], orientation='h',
           marker_color=bar_colors, name='達成率',
           text=[f'{a}%' for a in achievement_vals], textposition='outside',
           hovertemplate='%{y}: %{x:.1f}%<extra></extra>'),
    row=2, col=1
)
fig.add_vline(x=100, line_dash='dash', line_color='gray', row=2, col=1)

# 顧客セグメント
fig.add_trace(
    go.Pie(labels=['新規', 'リピーター', '優良顧客', '休眠'],
           values=[30, 40, 15, 15], hole=0.4,
           marker_colors=['#42A5F5', '#66BB6A', '#FFA726', '#EF5350']),
    row=2, col=2
)

fig.update_layout(
    height=700, width=1100,
    title_text='NetShop社 経営ダッシュボード',
    title_font_size=18,
    showlegend=True,
    barmode='stack'
)

fig.write_html('dashboard_interactive.html', include_plotlyjs='cdn')
fig.show()
print("HTMLファイルを出力しました: dashboard_interactive.html")

達成度チェック

  • ダッシュボードの設計書(レイアウト、パネル定義)を作成できた
  • KPIカード(数値+前月比)を実装できた
  • 月次売上推移を目標線付きで可視化できた
  • カテゴリ別の構成変化を可視化できた
  • 地域別の達成状況を可視化できた
  • matplotlib版とPlotly版の両方を作成できた
  • 経営層が見てすぐに状況を把握できるデザインになっている

推定所要時間:90分