Step 42:総合プロジェクト 実データを分析しよう

🎯 ステップ42: 総合プロジェクト:実データを分析しよう

これまで学んだスキルを総動員して、本格的なデータ分析に挑戦!

ステップ1〜41で学んだすべてのスキルを使って、実践的なデータ分析プロジェクトに挑戦します。「売上データ分析」と「アンケートデータ分析」の2つのプロジェクトを通じて、データ分析の一連の流れを体験しましょう。

📖 このステップで学ぶこと

・データ分析プロジェクトの進め方

・プロジェクト1:売上データ分析

・プロジェクト2:アンケートデータ分析

・分析レポートの作成方法

🌟 1. データ分析プロジェクトの進め方

実際のデータ分析は、決まった5つのステップで進めていきます。この流れを覚えておくと、どんなデータでも分析できるようになります。

📌 データ分析の5ステップ

ステップ 内容 使う機能
1. データの読み込み CSVファイルなどからデータを取得 pd.read_csv()
2. データの確認 形状、型、欠損値などをチェック head(), info(), describe()
3. データの加工 クリーニング、変換、集計 groupby(), pivot_table()
4. データの可視化 グラフで傾向を把握 matplotlib, seaborn
5. 結果の解釈 分析結果から何が言えるか考える print()でレポート出力

💡 プロジェクトに取り組むコツ

・コードを写すだけでなく、自分なりの分析も追加してみましょう

・データを変えたり、違うグラフを作ったりして、いろいろ試してください

・エラーが出ても大丈夫!エラーメッセージを読んで解決しましょう

💰 2. プロジェクト1:売上データ分析

ある店舗の1年間の売上データを分析します。以下の点を明らかにするのが目標です。

📌 分析の目標

・月別の売上推移はどうなっているか?

・どの商品が一番売れているか?

・季節による売上の違いはあるか?

・今後の改善提案は何か?

Step 1: サンプルデータの作成

まず、分析用のサンプルデータを作成します。実際のプロジェクトではCSVファイルを読み込みますが、ここでは練習用にPythonでデータを生成します。

コード:ライブラリのインポートとデータ準備

# 必要なライブラリをインポート
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 日本語フォントの設定(Google Colab用)
plt.rcParams['font.family'] = 'DejaVu Sans'

# Seabornのスタイル設定
sns.set_style("whitegrid")

# 乱数のシードを固定(同じ結果が再現できるように)
np.random.seed(42)

print("ライブラリの準備完了!")

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

np.random.seed(42)

 ・乱数のシード(種)を固定する

 ・同じシードを使えば、毎回同じ乱数が生成される

 ・分析結果を再現できるようにするために重要

次に、1年間365日分の売上データを作成します。

コード:売上データの生成

# 日付の範囲を作成(2024年1月1日から365日間)
dates = pd.date_range('2024-01-01', periods=365, freq='D')

# 商品リスト
products = ['りんご', 'みかん', 'バナナ', 'いちご', 'ぶどう']

# データを格納するリスト
data = []

# 各日付・各商品の売上データを生成
for date in dates:
    # 月を取得
    month = date.month
    
    # 季節による基本売上の変動
    if month in [6, 7, 8]:      # 夏
        base_sales = 150
    elif month in [12, 1, 2]:   # 冬
        base_sales = 80
    else:                        # 春・秋
        base_sales = 100
    
    # 各商品のデータを作成
    for product in products:
        # 商品ごとの売上係数
        if product == 'りんご':
            product_base = base_sales * 1.2
        elif product == 'みかん':
            product_base = base_sales * 1.0
        elif product == 'バナナ':
            product_base = base_sales * 1.5
        elif product == 'いちご':
            product_base = base_sales * 0.8
        else:  # ぶどう
            product_base = base_sales * 1.1
        
        # ランダムな変動を追加(±20の範囲)
        sales = int(product_base + np.random.normal(0, 20))
        sales = max(0, sales)  # マイナスにならないように
        
        # データをリストに追加
        data.append({
            '日付': date,
            '商品名': product,
            '売上金額': sales * 100  # 1個100円と仮定
        })

# DataFrameに変換
df = pd.DataFrame(data)

print("データの準備完了!")
print(f"データ件数: {len(df)}件")
print()
print("最初の10行:")
df.head(10)

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

pd.date_range(‘2024-01-01′, periods=365, freq=’D’)

 ・2024年1月1日から365日分の日付を生成

 ・freq=’D’は「日単位」を意味する

date.month

 ・日付から月(1〜12)を取り出す

np.random.normal(0, 20)

 ・平均0、標準偏差20の正規分布からランダムな数値を生成

 ・売上に自然な変動を加えるため

max(0, sales)

 ・salesと0の大きい方を返す

 ・売上がマイナスにならないようにする

実行結果

データの準備完了!

データ件数: 1825件

(365日 × 5商品 = 1825件)

Step 2: データの確認

分析を始める前に、データの基本情報を確認します。これは分析の第一歩として必ず行う作業です。

コード:データの基本確認

# データの形状を確認
print("=== データの形状 ===")
print(f"行数: {df.shape[0]}, 列数: {df.shape[1]}")
print()

# データ型を確認
print("=== データ型 ===")
print(df.dtypes)
print()

# 基本統計量を確認
print("=== 売上金額の基本統計量 ===")
print(df['売上金額'].describe())
print()

# 欠損値を確認
print("=== 欠損値の確認 ===")
print(df.isnull().sum())
print()

# 商品の種類を確認
print("=== 商品の種類 ===")
print(df['商品名'].unique())

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 確認ポイントの解説

df.shape:データの大きさ(行数、列数)を確認

df.dtypes:各列のデータ型を確認(数値か文字列か)

df[‘列名’].describe():数値データの統計量(平均、標準偏差、最小、最大など)

df.isnull().sum():欠損値(空のセル)の数を確認

df[‘列名’].unique():その列に含まれるユニークな値を確認

実行結果

=== データの形状 ===

行数: 1825, 列数: 3

=== データ型 ===

日付   datetime64[ns]

商品名  object

売上金額 int64

=== 欠損値の確認 ===

日付   0

商品名  0

売上金額 0

→ 欠損値なし、データはきれいな状態です

Step 3: 月別売上の集計

まず、月ごとの売上を集計して、売上の推移を把握します。

コード:月別売上の集計

# 日付から「月」の列を追加
df['月'] = df['日付'].dt.month

# 月別の売上合計を集計
monthly_sales = df.groupby('月')['売上金額'].sum()

print("=== 月別売上合計 ===")
for month, sales in monthly_sales.items():
    print(f"{month:2d}月: {sales:>10,}円")

print()

# 月別の売上平均(1日あたり)
monthly_avg = df.groupby('月')['売上金額'].mean()

print("=== 月別平均売上(1日・1商品あたり)===")
for month, avg in monthly_avg.items():
    print(f"{month:2d}月: {avg:>8,.0f}円")

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

df[‘日付’].dt.month

 ・datetime型の列から月(1〜12)を抽出

 ・.dtアクセサを使うとdatetime型の様々な要素を取り出せる

df.groupby(‘月’)[‘売上金額’].sum()

 ・月でグループ化して、売上金額の合計を計算

f”{month:2d}”

 ・2桁で表示(1月なら「 1」、12月なら「12」)

f”{sales:>10,}”

 ・10桁で右寄せ、3桁ごとにカンマ区切り

Step 4: 商品別売上の集計

コード:商品別売上ランキング

# 商品別の売上合計を集計(降順でソート)
product_sales = df.groupby('商品名')['売上金額'].sum().sort_values(ascending=False)

print("=== 商品別売上ランキング ===")
for rank, (product, sales) in enumerate(product_sales.items(), 1):
    print(f"{rank}位: {product:8s} {sales:>12,}円")

print()

# 商品別の平均売上
product_avg = df.groupby('商品名')['売上金額'].mean().sort_values(ascending=False)

print("=== 商品別平均売上(1日あたり)===")
for product, avg in product_avg.items():
    print(f"{product:8s} {avg:>8,.0f}円")

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

.sort_values(ascending=False)

 ・値の大きい順(降順)にソート

 ・ascending=Trueだと昇順(小さい順)

enumerate(product_sales.items(), 1)

 ・items()でキーと値のペアを取得

 ・enumerate()で番号をつける(1から開始)

Step 5: 月別・商品別のクロス集計

ピボットテーブルを使って、月と商品の組み合わせで売上を集計します。

コード:ピボットテーブルの作成

# ピボットテーブルを作成
pivot_table = df.pivot_table(
    values='売上金額',      # 集計する値
    index='月',             # 行に配置
    columns='商品名',       # 列に配置
    aggfunc='sum'           # 集計方法(合計)
)

print("=== 月別・商品別売上(ピボットテーブル)===")
print(pivot_table)
print()

# 各月の売上トップ商品を表示
print("=== 各月の売上トップ商品 ===")
for month in range(1, 13):
    top_product = pivot_table.loc[month].idxmax()  # 最大値のインデックス(商品名)
    top_sales = pivot_table.loc[month].max()       # 最大値
    print(f"{month:2d}月: {top_product} ({top_sales:,.0f}円)")

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

df.pivot_table()

 ・データをクロス集計する関数

 ・Excelのピボットテーブルと同じ機能

pivot_table.loc[month]

 ・特定の月の行を取得

.idxmax()

 ・最大値を持つインデックス(この場合は商品名)を返す

Step 6: 売上推移の可視化

集計したデータをグラフで視覚化します。まずは月別の売上推移を折れ線グラフで表示します。

コード:月別売上の折れ線グラフ

# グラフのサイズを設定
plt.figure(figsize=(12, 6))

# 折れ線グラフを描画
plt.plot(monthly_sales.index, monthly_sales.values, 
         marker='o', linewidth=2, markersize=8, color='steelblue')

# グラフの装飾
plt.title('Monthly Sales Trend (月別売上推移)', fontsize=16, fontweight='bold')
plt.xlabel('Month (月)', fontsize=12)
plt.ylabel('Sales (円)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.xticks(range(1, 13))

# 最大値と最小値にマーカーを追加
max_month = monthly_sales.idxmax()
min_month = monthly_sales.idxmin()
plt.scatter(max_month, monthly_sales[max_month], color='red', s=200, zorder=5, label='Max')
plt.scatter(min_month, monthly_sales[min_month], color='blue', s=200, zorder=5, label='Min')
plt.legend()

plt.tight_layout()
plt.show()

# 最大・最小の月を表示
print(f"売上が最も多かった月: {max_month}月 ({monthly_sales[max_month]:,}円)")
print(f"売上が最も少なかった月: {min_month}月 ({monthly_sales[min_month]:,}円)")

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

monthly_sales.idxmax()

 ・最大値を持つインデックス(月)を返す

plt.scatter(…, zorder=5)

 ・散布図で点を追加

 ・zorder=5で他の要素より前面に表示

s=200

 ・点のサイズを200に設定(目立つように大きく)

Step 7: 商品別売上の棒グラフ

コード:商品別売上の棒グラフ

# グラフのサイズを設定
plt.figure(figsize=(10, 6))

# 色のリスト
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8']

# 棒グラフを描画
bars = plt.bar(product_sales.index, product_sales.values, color=colors, edgecolor='black')

# 各棒の上に金額を表示
for bar in bars:
    height = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2., height,
             f'{int(height):,}',
             ha='center', va='bottom', fontsize=10, fontweight='bold')

# グラフの装飾
plt.title('Sales by Product (商品別売上)', fontsize=16, fontweight='bold')
plt.xlabel('Product', fontsize=12)
plt.ylabel('Sales (円)', fontsize=12)
plt.grid(axis='y', alpha=0.3)

plt.tight_layout()
plt.show()

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

for bar in bars:

 ・各棒グラフのオブジェクトをループ処理

bar.get_height()

 ・棒の高さ(値)を取得

bar.get_x() + bar.get_width()/2.

 ・棒の中央のX座標を計算

ha=’center’, va=’bottom’

 ・テキストの配置(水平:中央、垂直:下揃え)

Step 8: 商品別の月次推移

コード:商品別・月別売上推移

# グラフのサイズを設定
plt.figure(figsize=(14, 7))

# 各商品の月別売上を折れ線グラフで表示
for product in products:
    # 特定の商品のデータを抽出して月別に集計
    product_monthly = df[df['商品名'] == product].groupby('月')['売上金額'].sum()
    plt.plot(product_monthly.index, product_monthly.values, 
             marker='o', label=product, linewidth=2)

# グラフの装飾
plt.title('Monthly Sales by Product (商品別・月別売上推移)', fontsize=16, fontweight='bold')
plt.xlabel('Month', fontsize=12)
plt.ylabel('Sales (円)', fontsize=12)
plt.legend(title='Product', fontsize=10)
plt.grid(True, alpha=0.3)
plt.xticks(range(1, 13))

plt.tight_layout()
plt.show()

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

df[df[‘商品名’] == product]

 ・特定の商品のデータだけを抽出

for product in products:

 ・各商品について順番にグラフを描画

 ・自動的に異なる色が割り当てられる

Step 9: 分析結果のまとめ(レポート作成)

分析結果をレポート形式でまとめます。数字だけでなく、ビジネス的な解釈と提案を加えることが重要です。

コード:分析レポートの出力

print("=" * 60)
print("売上データ分析レポート")
print("=" * 60)
print()

# 1. 年間総売上
total_sales = df['売上金額'].sum()
print("【1. 年間総売上】")
print(f"   {total_sales:,}円")
print()

# 2. 月別の傾向
max_month = monthly_sales.idxmax()
min_month = monthly_sales.idxmin()
print("【2. 月別傾向】")
print(f"   最高売上月: {max_month}月 ({monthly_sales[max_month]:,}円)")
print(f"   最低売上月: {min_month}月 ({monthly_sales[min_month]:,}円)")
print(f"   差額: {monthly_sales[max_month] - monthly_sales[min_month]:,}円")
print()

# 3. 季節性の分析
summer_sales = monthly_sales[[6, 7, 8]].sum()
winter_sales = monthly_sales[[12, 1, 2]].sum()
growth_rate = ((summer_sales / winter_sales) - 1) * 100
print("【3. 季節による違い】")
print(f"   夏季(6-8月)売上: {summer_sales:,}円")
print(f"   冬季(12-2月)売上: {winter_sales:,}円")
print(f"   夏季は冬季より {growth_rate:.1f}% 高い")
print()

# 4. 商品別の傾向
print("【4. 商品別ランキング】")
for rank, (product, sales) in enumerate(product_sales.items(), 1):
    share = (sales / total_sales) * 100
    print(f"   {rank}位: {product} - {sales:,}円 ({share:.1f}%)")
print()

# 5. 改善提案
print("【5. 改善提案】")
print(f"   ✓ 夏季は売上が{growth_rate:.1f}%増加 → 夏の在庫を増やす")
print(f"   ✓ {product_sales.index[0]}が売上トップ → 関連商品の展開を検討")
print(f"   ✓ {min_month}月の売上が低い → 冬季キャンペーンの実施")
print(f"   ✓ {product_sales.index[-1]}の売上が最下位 → プロモーション強化")
print()
print("=" * 60)

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

monthly_sales[[6, 7, 8]].sum()

 ・6月、7月、8月の売上を合計

 ・リストで複数のインデックスを指定できる

product_sales.index[0]

 ・ソート済みなので、最初の要素がトップ商品

product_sales.index[-1]

 ・最後の要素が最下位商品

💡 プロジェクト1のポイント

✓ データを多角的に集計して傾向を把握

✓ グラフで視覚的に表現することで理解しやすくする

✓ 数字だけでなく、ビジネス的な解釈を加える

✓ 分析結果から具体的なアクション(改善提案)を導く

📊 3. プロジェクト2:アンケートデータ分析

次は、顧客満足度アンケートのデータを分析します。

📌 分析の目標

・全体的な満足度の傾向はどうか?

・年齢層によって満足度に違いはあるか?

・改善が必要な項目はどれか?

・リピート意向はどのくらいか?

Step 1: アンケートデータの作成

コード:アンケートデータの生成

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

# 乱数シードを固定
np.random.seed(42)

# 回答者数
n_responses = 200

# 年齢層(確率で分布を設定)
age_groups = ['10代', '20代', '30代', '40代', '50代以上']
ages = np.random.choice(age_groups, n_responses, 
                        p=[0.1, 0.3, 0.3, 0.2, 0.1])

# 性別
genders = np.random.choice(['男性', '女性'], n_responses, 
                           p=[0.45, 0.55])

# 総合満足度(5段階評価)- 年齢により傾向を変える
satisfaction = []
for age in ages:
    if age in ['10代', '20代']:
        # 若い世代は満足度が高め
        score = np.random.choice([3, 4, 5], p=[0.2, 0.5, 0.3])
    elif age in ['30代', '40代']:
        # 中年層は標準的
        score = np.random.choice([2, 3, 4, 5], p=[0.1, 0.3, 0.4, 0.2])
    else:
        # シニア層は少し厳しめ
        score = np.random.choice([2, 3, 4], p=[0.2, 0.5, 0.3])
    satisfaction.append(score)

# 商品の質(5段階評価)
product_quality = np.random.choice([2, 3, 4, 5], n_responses, 
                                   p=[0.05, 0.25, 0.45, 0.25])

# 接客態度(5段階評価)
service_quality = np.random.choice([2, 3, 4, 5], n_responses, 
                                   p=[0.1, 0.2, 0.4, 0.3])

# 価格満足度(5段階評価)- やや厳しめの評価
price_satisfaction = np.random.choice([1, 2, 3, 4, 5], n_responses, 
                                      p=[0.05, 0.15, 0.35, 0.3, 0.15])

# リピート意向
repeat_intention = np.random.choice(['はい', 'いいえ', 'わからない'], 
                                    n_responses, p=[0.6, 0.1, 0.3])

# DataFrameに格納
survey_df = pd.DataFrame({
    '年齢層': ages,
    '性別': genders,
    '総合満足度': satisfaction,
    '商品の質': product_quality,
    '接客態度': service_quality,
    '価格満足度': price_satisfaction,
    'リピート意向': repeat_intention
})

print("アンケートデータの準備完了!")
print(f"回答者数: {len(survey_df)}人")
print()
print("最初の10行:")
survey_df.head(10)

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

np.random.choice(choices, size, p=[…])

 ・choicesから確率pに従ってランダムに選択

 ・sizeは選択する回数

 ・pは各選択肢の確率(合計が1になる必要あり)

例:p=[0.1, 0.3, 0.3, 0.2, 0.1]

 ・10代が10%、20代が30%、30代が30%、40代が20%、50代以上が10%

Step 2: 基本統計の確認

コード:アンケートデータの基本確認

# データ型と欠損値の確認
print("=== データの基本情報 ===")
print(survey_df.info())
print()

# 数値項目の統計量
print("=== 数値項目の統計量 ===")
numeric_cols = ['総合満足度', '商品の質', '接客態度', '価格満足度']
print(survey_df[numeric_cols].describe().round(2))
print()

# 年齢層の分布
print("=== 年齢層の分布 ===")
age_counts = survey_df['年齢層'].value_counts().sort_index()
for age, count in age_counts.items():
    print(f"{age}: {count}人 ({count/len(survey_df)*100:.1f}%)")
print()

# 性別の分布
print("=== 性別の分布 ===")
gender_counts = survey_df['性別'].value_counts()
for gender, count in gender_counts.items():
    print(f"{gender}: {count}人 ({count/len(survey_df)*100:.1f}%)")

※ 画面が小さい場合は、コードブロックを横にスクロールできます

Step 3: 満足度の集計

コード:満足度の詳細分析

# 総合満足度の分布
print("=== 総合満足度の分布 ===")
satisfaction_dist = survey_df['総合満足度'].value_counts().sort_index()
for score, count in satisfaction_dist.items():
    percentage = (count / len(survey_df)) * 100
    bar = '█' * int(percentage / 2)  # 簡易的な棒グラフ
    print(f"{score}点: {count:3d}人 ({percentage:5.1f}%) {bar}")
print()

# 各項目の平均点
print("=== 各項目の平均点 ===")
avg_scores = {
    '総合満足度': survey_df['総合満足度'].mean(),
    '商品の質': survey_df['商品の質'].mean(),
    '接客態度': survey_df['接客態度'].mean(),
    '価格満足度': survey_df['価格満足度'].mean()
}

for item, score in avg_scores.items():
    stars = '★' * int(score) + '☆' * (5 - int(score))
    print(f"{item:10s}: {score:.2f}点 {stars}")
print()

# リピート意向の集計
print("=== リピート意向 ===")
repeat_dist = survey_df['リピート意向'].value_counts()
for intention, count in repeat_dist.items():
    percentage = (count / len(survey_df)) * 100
    print(f"{intention:10s}: {count:3d}人 ({percentage:5.1f}%)")

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

‘█’ * int(percentage / 2)

 ・パーセンテージに応じた長さの棒を文字で表現

 ・簡易的なテキストグラフを作成

‘★’ * int(score) + ‘☆’ * (5 – int(score))

 ・スコアを星で視覚化

 ・例:3.5点なら「★★★☆☆」

Step 4: 年齢層別の分析

コード:年齢層別の満足度分析

# 年齢層別の総合満足度
age_order = ['10代', '20代', '30代', '40代', '50代以上']
age_satisfaction = survey_df.groupby('年齢層')['総合満足度'].agg(['mean', 'count'])
age_satisfaction = age_satisfaction.reindex(age_order)

print("=== 年齢層別の総合満足度 ===")
print(f"{'年齢層':8s} {'平均点':>8s} {'回答数':>8s}")
print("-" * 30)
for age, row in age_satisfaction.iterrows():
    print(f"{age:8s} {row['mean']:8.2f} {int(row['count']):8d}人")
print()

# 年齢層別の各項目平均
print("=== 年齢層別の各項目平均 ===")
age_analysis = survey_df.groupby('年齢層')[numeric_cols].mean()
age_analysis = age_analysis.reindex(age_order)
print(age_analysis.round(2))

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

.agg([‘mean’, ‘count’])

 ・複数の集計を一度に実行

 ・平均(mean)と件数(count)を同時に計算

.reindex(age_order)

 ・指定した順序でインデックスを並び替え

 ・年齢層を若い順に表示するため

Step 5: クロス集計

コード:クロス集計の作成

# 年齢層×リピート意向のクロス集計
print("=== 年齢層別リピート意向(%)===")
age_repeat = pd.crosstab(
    survey_df['年齢層'],
    survey_df['リピート意向'],
    normalize='index'  # 行方向で正規化(各年齢層の合計が100%)
) * 100

age_repeat = age_repeat.reindex(age_order)
print(age_repeat.round(1))
print()

# 性別×総合満足度のクロス集計
print("=== 性別×総合満足度(%)===")
gender_satisfaction = pd.crosstab(
    survey_df['性別'],
    survey_df['総合満足度'],
    normalize='index'
) * 100

print(gender_satisfaction.round(1))

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

pd.crosstab(行, 列)

 ・2つのカテゴリ変数の組み合わせで集計

 ・行に1つ目の変数、列に2つ目の変数

normalize=’index’

 ・行方向で正規化(各行の合計が1になる)

 ・’columns’なら列方向、’all’なら全体で正規化

Step 6: 満足度の可視化

コード:アンケート結果のダッシュボード

# 2×2のダッシュボードを作成
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('Customer Satisfaction Analysis', fontsize=18, fontweight='bold')

# 1. 総合満足度の分布(棒グラフ)
ax1 = axes[0, 0]
satisfaction_dist.plot(kind='bar', ax=ax1, color='steelblue', edgecolor='black')
ax1.set_title('Overall Satisfaction Distribution', fontsize=14)
ax1.set_xlabel('Score', fontsize=12)
ax1.set_ylabel('Count', fontsize=12)
ax1.set_xticklabels(ax1.get_xticklabels(), rotation=0)
# 各棒に人数を表示
for i, v in enumerate(satisfaction_dist.values):
    ax1.text(i, v + 2, str(v), ha='center', fontweight='bold')

# 2. 各項目の平均点(横棒グラフ)
ax2 = axes[0, 1]
items = list(avg_scores.keys())
scores = list(avg_scores.values())
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A']
bars = ax2.barh(items, scores, color=colors, edgecolor='black')
ax2.set_title('Average Score by Category', fontsize=14)
ax2.set_xlabel('Score', fontsize=12)
ax2.set_xlim(0, 5)
ax2.axvline(x=3, color='red', linestyle='--', alpha=0.5, label='Middle (3)')
# 各棒に点数を表示
for bar, score in zip(bars, scores):
    ax2.text(score + 0.1, bar.get_y() + bar.get_height()/2,
             f'{score:.2f}', va='center', fontweight='bold')

# 3. 年齢層別の総合満足度(棒グラフ)
ax3 = axes[1, 0]
age_satisfaction['mean'].plot(kind='bar', ax=ax3, color='coral', edgecolor='black')
ax3.set_title('Satisfaction by Age Group', fontsize=14)
ax3.set_xlabel('Age Group', fontsize=12)
ax3.set_ylabel('Average Score', fontsize=12)
ax3.set_xticklabels(ax3.get_xticklabels(), rotation=45)
ax3.set_ylim(0, 5)
ax3.axhline(y=3, color='red', linestyle='--', alpha=0.5)
# 各棒に点数を表示
for i, v in enumerate(age_satisfaction['mean'].values):
    ax3.text(i, v + 0.1, f'{v:.2f}', ha='center', fontweight='bold')

# 4. リピート意向(円グラフ)
ax4 = axes[1, 1]
colors_pie = ['#90EE90', '#FFB6C1', '#FFD700']
repeat_dist.plot(kind='pie', ax=ax4, autopct='%1.1f%%', colors=colors_pie)
ax4.set_title('Repeat Intention', fontsize=14)
ax4.set_ylabel('')

plt.tight_layout()
plt.show()

※ 画面が小さい場合は、コードブロックを横にスクロールできます

Step 7: 分析結果のまとめ

コード:アンケート分析レポート

print("=" * 60)
print("アンケートデータ分析レポート")
print("=" * 60)
print()

# 1. 回答者の属性
print("【1. 回答者プロフィール】")
print(f"   総回答数: {len(survey_df)}人")
main_age = age_counts.idxmax()
print(f"   主な年齢層: {main_age}が最多 ({age_counts[main_age]}人)")
print(f"   性別比率: 女性{gender_counts['女性']/len(survey_df)*100:.0f}%、男性{gender_counts['男性']/len(survey_df)*100:.0f}%")
print()

# 2. 満足度の現状
high_satisfaction = (survey_df['総合満足度'] >= 4).sum()
low_satisfaction = (survey_df['総合満足度'] <= 2).sum()
print("【2. 満足度の現状】")
print(f"   総合満足度平均: {survey_df['総合満足度'].mean():.2f}点")
print(f"   満足層(4点以上): {high_satisfaction}人 ({high_satisfaction/len(survey_df)*100:.0f}%)")
print(f"   不満層(2点以下): {low_satisfaction}人 ({low_satisfaction/len(survey_df)*100:.0f}%)")
print()

# 3. 項目別の評価
best_item = max(avg_scores, key=avg_scores.get)
worst_item = min(avg_scores, key=avg_scores.get)
print("【3. 項目別評価】")
print(f"   最高評価: {best_item} ({avg_scores[best_item]:.2f}点)")
print(f"   最低評価: {worst_item} ({avg_scores[worst_item]:.2f}点)")
print(f"   改善余地: {worst_item}が他項目より{avg_scores[best_item] - avg_scores[worst_item]:.2f}点低い")
print()

# 4. 年齢層による違い
highest_age = age_satisfaction['mean'].idxmax()
lowest_age = age_satisfaction['mean'].idxmin()
print("【4. 年齢層による違い】")
print(f"   最も満足度が高い: {highest_age} ({age_satisfaction.loc[highest_age, 'mean']:.2f}点)")
print(f"   最も満足度が低い: {lowest_age} ({age_satisfaction.loc[lowest_age, 'mean']:.2f}点)")
print(f"   傾向: 若年層ほど満足度が高い")
print()

# 5. リピート意向
repeat_yes = repeat_dist['はい']
repeat_no = repeat_dist['いいえ']
repeat_unknown = repeat_dist['わからない']
print("【5. リピート意向】")
print(f"   リピート希望: {repeat_yes}人 ({repeat_yes/len(survey_df)*100:.0f}%)")
print(f"   リピート否定: {repeat_no}人 ({repeat_no/len(survey_df)*100:.0f}%)")
print(f"   保留層: {repeat_unknown}人 ({repeat_unknown/len(survey_df)*100:.0f}%)")
print()

# 6. 改善提案
print("【6. 改善提案】")
print(f"   ✓ {worst_item}が最も低い → 価格戦略の見直しが必要")
print(f"   ✓ {lowest_age}の満足度が低い → シニア層向けサービスの改善")
print(f"   ✓ {best_item}は高評価 → この強みをアピールすべき")
print(f"   ✓ 保留層が{repeat_unknown/len(survey_df)*100:.0f}% → 具体的な改善で取り込める可能性")
print()
print("=" * 60)

※ 画面が小さい場合は、コードブロックを横にスクロールできます

💡 コードの解説

max(avg_scores, key=avg_scores.get)

 ・辞書の値が最大のキーを取得

 ・key=avg_scores.getで値を比較基準にする

(survey_df['総合満足度'] >= 4).sum()

 ・条件を満たす行数をカウント

 ・TrueをSum()すると1として数えられる

💡 プロジェクト2のポイント

✓ クロス集計で属性別の違いを明らかにする

✓ 複数の視点からデータを分析する

✓ 数字の背景にある意味を考察する

✓ 改善につながる具体的な提案を行う

📝 チャレンジ課題

課題1:売上データの深掘り分析(発展)

📋 課題

プロジェクト1の売上データを使って、以下の分析を追加してください:

・曜日別の売上傾向を分析

・前月比の成長率を計算

・累計売上のグラフを作成

ヒントを見る

ヒント

・曜日の取得: df['日付'].dt.dayofweek または dt.day_name()

・前月比の計算: monthly_sales.pct_change()

・累計の計算: monthly_sales.cumsum()

課題2:アンケートデータの詳細分析(発展)

📋 課題

プロジェクト2のアンケートデータを使って、以下の分析を追加してください:

・満足度とリピート意向の関係を分析

・性別×年齢層のクロス集計

・ヒートマップで項目間の相関を可視化

ヒントを見る

ヒント

・相関行列の計算: survey_df[numeric_cols].corr()

・ヒートマップ: sns.heatmap(corr, annot=True, cmap='coolwarm')

・クロス集計: pd.crosstab(survey_df['性別'], survey_df['年齢層'])

課題3:オリジナルプロジェクト(応用)

📋 課題

興味のあるテーマで自分独自のデータを作成し、分析してみましょう:

・学校の成績データ

・スポーツチームの成績

・読書記録

・家計簿データ

分析の流れ: データ作成 → 集計 → 可視化 → 考察・提案

🎯 このステップのまとめ

✅ 学んだこと

✓ データ分析プロジェクトの5ステップ(読み込み→確認→加工→可視化→解釈)

✓ 売上データの分析手法(月別集計、商品別集計、ピボットテーブル)

✓ アンケートデータの分析手法(分布、クロス集計、属性別分析)

✓ 分析結果から改善提案を導く方法

✓ レポート形式でまとめる技術

💡 実務での活用

ビジネス: 売上分析、顧客分析、在庫管理

マーケティング: アンケート分析、A/Bテスト

教育: 成績分析、学習効果測定

個人: 家計管理、健康管理、趣味の記録

❓ よくある質問

Q1: 実際のCSVファイルを読み込むにはどうすればいいですか?

A: df = pd.read_csv('ファイル名.csv', encoding='utf-8')を使います。Google Colabの場合、左のファイルアイコンからアップロードできます。文字化けする場合はencoding='shift-jis'を試してください。

Q2: 分析結果をどのようにまとめればいいですか?

A: 結論→詳細→提案の順でまとめると良いです。最初に重要な発見を述べ、その根拠となるデータを示し、最後に具体的なアクションを提案します。

Q3: グラフが多すぎる気がします。どれを選ぶべきですか?

A: 伝えたいメッセージに合わせて選択します。すべてのグラフを載せる必要はありません。最も重要な発見を示すグラフ2〜3個に絞るのが効果的です。

Q4: データ分析で最も大切なことは何ですか?

A: データの背景を理解することです。数字だけ見るのではなく、「なぜそうなったのか」「これはビジネスにとって何を意味するのか」を考えることが重要です。

Q5: もっと複雑な分析をするには何を学べばいいですか?

A: 統計学と機械学習を学ぶと良いでしょう。回帰分析、クラスタリング、予測モデルなどに進むことができます。Scikit-learnというライブラリが役立ちます。

📝

学習メモ

Pythonデータ分析入門 - Step 42

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE