データ可視化(matplotlib基礎)

2025-11-22

はじめに

データ分析において、数値データを視覚的に表現することは非常に重要です。数字の羅列だけでは気づきにくいパターンや傾向も、グラフにすることで一目で理解できるようになります。Pythonでデータ可視化を行う際、最も広く使われているライブラリがmatplotlibです。

本記事では、Pythonの基本的な文法を習得した方が、matplotlibを使ってデータを効果的に可視化する方法を基礎から詳しく解説します。データ分析の結果を説得力のある形で伝える技術を身につけましょう。

matplotlibとは

matplotlibは、Pythonでプロフェッショナルな品質のグラフや図を作成するための包括的なライブラリです。2003年から開発が始められ、現在では科学計算やデータ分析の分野で事実上の標準となっています。

主な特徴

  • 多様なグラフタイプ: 折れ線グラフ、棒グラフ、散布図、ヒストグラムなど
  • 高いカスタマイズ性: 色、サイズ、ラベル、凡例などを細かく調整可能
  • 公開品質の出力: 学術論文やビジネスレポートで使用できる高品質な画像を生成
  • 簡単な操作: 基本的なグラフは数行のコードで作成可能

インストール

まずはmatplotlibをインストールしましょう。

pipコマンドは、Pythonのパッケージ管理システムです。サードパーティ製ライブラリを簡単に導入・管理するための標準ツールです。

pip install matplotlib

インストールが完了したら、Pythonスクリプトでインポートして使用します。

import matplotlib.pyplot as plt
import numpy as np

基本的なグラフ作成

最もシンプルな折れ線グラフ

まずは基本的な折れ線グラフから始めましょう。グラフ描画用のライブラリ matplotlib.pyplot と数値計算用の numpy を読み込みます。次に、横軸の値 x と縦軸の値 y をリストで準備し、それらのデータを plt.plot を使って折れ線グラフとして描画します。最後に、plt.show() でグラフを画面に表示する、非常に基本的な可視化のコードです。

import matplotlib.pyplot as plt
import numpy as np

# データの準備
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

# グラフの作成
plt.plot(x, y)

# グラフの表示
plt.show()

このコードを実行すると、x値とy値に対応する点を結んだ折れ線グラフが表示されます。

複数系列のグラフ

複数のデータ系列を1つのグラフにプロットすることも簡単です。matplotlib.pyplotnumpy を読み込み、横軸の値 x と縦軸の値 y1y2 を準備します。その後、plt.plot を使って y1y2 の2つの系列をそれぞれ「系列A」「系列B」として描画します。さらに plt.legend() によってグラフ上に凡例を表示し、最後に plt.show() でグラフを画面に表示します。

import matplotlib.pyplot as plt
import numpy as np

# データの準備
x = [1, 2, 3, 4, 5]
y1 = [2, 4, 6, 8, 10]
y2 = [1, 3, 5, 7, 9]

# 複数系列のグラフ作成
plt.plot(x, y1, label='系列A')
plt.plot(x, y2, label='系列B')

# 凡例の表示
plt.legend()

plt.show()

これにより、2つのデータ系列を比較できる折れ線グラフが作成されます。

グラフの装飾とカスタマイズ

タイトルと軸ラベルの追加

グラフをわかりやすくするために、タイトルと軸ラベルを追加しましょう。numpy を使って0から10までの100点のデータを生成し、その値に対して sin 関数を適用して縦軸の値 y を計算します。次に matplotlib.pyplot を使って折れ線グラフを描き、plt.titleplt.xlabelplt.ylabel でタイトルや軸ラベルを設定し、plt.grid(True) でグリッド線を表示します。最後に plt.show() でグラフを画面に表示し、正弦波の形を確認できるようにしています。

import matplotlib.pyplot as plt
import numpy as np

# データ生成
x = np.linspace(0, 10, 100)  # 0から10まで100点のデータ
y = np.sin(x)  # 正弦波

# グラフ作成
plt.plot(x, y)

# グラフの装飾
plt.title('正弦波のグラフ')  # タイトル
plt.xlabel('x軸')           # x軸ラベル
plt.ylabel('y軸')           # y軸ラベル
plt.grid(True)              # グリッド線の表示

plt.show()

色と線種の変更

グラフの見た目を変更して、より見やすくしましょう。numpy で0から10までの100点のデータを作成し、sin(x)cos(x)sin(x)+cos(x) の3つの関数をそれぞれ色・線種・線幅を変えて plt.plot で描画します。さらに、plt.titleplt.xlabelplt.ylabel でタイトルや軸ラベルを設定し、plt.legend で凡例を表示、plt.grid で半透明のグリッド線を追加しています。最後に plt.show() で画面にグラフを表示し、三角関数の比較が一目でわかるようにしています。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

# 異なるスタイルで複数系列をプロット
plt.plot(x, np.sin(x), color='red', linestyle='-', linewidth=2, label='sin(x)')
plt.plot(x, np.cos(x), color='blue', linestyle='--', linewidth=2, label='cos(x)')
plt.plot(x, np.sin(x) + np.cos(x), color='green', linestyle=':', linewidth=3, label='sin(x)+cos(x)')

# 装飾
plt.title('三角関数のグラフ')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True, alpha=0.3)  # グリッド線を半透明に

plt.show()

様々なグラフタイプ

棒グラフ

カテゴリ別のデータ比較には棒グラフが適しています。categories に果物の種類、sales に売上数量のデータを用意します。plt.bar を使って各果物を色分けした棒グラフを作成し、plt.titleplt.xlabelplt.ylabel でタイトルや軸ラベルを設定します。さらに、plt.xticks(rotation=45) で x 軸ラベルを斜め表示にし、plt.ylim で y 軸に余白を作って見やすく調整します。最後に plt.tight_layout() でラベルのはみ出しを防ぎ、plt.show() でグラフを画面に表示します。

import matplotlib.pyplot as plt

# データの準備
categories = ['りんご', 'バナナ', 'オレンジ', 'ぶどう', 'いちご']
sales = [35, 28, 45, 32, 40]

# 棒グラフの作成
plt.bar(categories, sales, color=['red', 'yellow', 'orange', 'purple', 'pink'])

# 装飾
plt.title('果物の売上')
plt.xlabel('果物の種類')
plt.ylabel('売上数量')
plt.xticks(rotation=45)  # x軸ラベルを45度回転

# y軸の範囲を調整(下部に余白を作る)
plt.ylim(0, max(sales) + 5)

plt.tight_layout()  # ラベルがはみ出ないように調整
plt.show()

散布図

2変数間の関係を調べるには散布図が有効です。numpy で乱数を生成し、x を正規分布から取得、yx に線形関係を持たせつつノイズを加えて作成します。plt.scatter で各点を青色かつ半透明で描画し、点の大きさも指定しています。さらに、plt.titleplt.xlabelplt.ylabel でタイトルや軸ラベルを設定し、plt.grid で半透明のグリッド線を追加します。最後に plt.show() で散布図を画面に表示し、xy の関係性を視覚的に確認できるようにしています。

import matplotlib.pyplot as plt
import numpy as np

# ランダムなデータ生成
np.random.seed(42)  # 再現性のためのシード設定
x = np.random.randn(100)
y = 2 * x + np.random.randn(100) * 0.5  # 線形関係にノイズを追加

# 散布図の作成
plt.scatter(x, y, alpha=0.6, color='blue', s=30)  # alpha: 透明度, s: 点のサイズ

# 装飾
plt.title('散布図: xとyの関係')
plt.xlabel('xの値')
plt.ylabel('yの値')
plt.grid(True, alpha=0.3)

plt.show()

ヒストグラム

データの分布を確認するにはヒストグラムを使用します。numpy で平均0・標準偏差1の正規分布から1000個のデータを生成します。plt.hist を使ってヒストグラムを描画し、棒の色や透明度、枠線の色を指定しています。さらに、plt.titleplt.xlabelplt.ylabel でタイトルや軸ラベルを設定し、plt.grid で半透明のグリッド線を追加します。最後に plt.show() でヒストグラムを画面に表示し、データの分布の形を視覚的に確認できるようにしています。

import matplotlib.pyplot as plt
import numpy as np

# 正規分布に従うランダムデータ生成
np.random.seed(42)
data = np.random.normal(0, 1, 1000)  # 平均0, 標準偏差1の正規分布

# ヒストグラムの作成
plt.hist(data, bins=30, alpha=0.7, color='skyblue', edgecolor='black')

# 装飾
plt.title('データの分布')
plt.xlabel('値')
plt.ylabel('頻度')
plt.grid(True, alpha=0.3)

plt.show()

subplotの使用

複数のグラフを1つの図に並べて表示するにはsubplotを使用します。numpy で横軸データ x を生成し、y1=sin(x)y2=cos(x)y3=減衰振動 の3つの関数データを用意します。plt.figure で図全体のサイズを指定し、plt.subplot を使って 2行2列の4つの領域にそれぞれグラフを配置します。最初の3つのサブプロットには折れ線グラフを描き、タイトルとグリッドを設定。4つ目には正規分布に従うランダムデータのヒストグラムを描画します。plt.tight_layout() でグラフ同士の重なりを防ぎ、plt.show() で全てのグラフを画面に表示します。これにより、異なる種類のグラフを一目で比較できる図が作成されます。

import matplotlib.pyplot as plt
import numpy as np

# データ準備
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.exp(-x/3) * np.sin(x)

# 2x2のサブプロットを作成
plt.figure(figsize=(12, 8))  # 図のサイズ指定

# 1つ目のグラフ
plt.subplot(2, 2, 1)  # (行数, 列数, 位置)
plt.plot(x, y1, 'r-')
plt.title('sin(x)')
plt.grid(True)

# 2つ目のグラフ
plt.subplot(2, 2, 2)
plt.plot(x, y2, 'b-')
plt.title('cos(x)')
plt.grid(True)

# 3つ目のグラフ
plt.subplot(2, 2, 3)
plt.plot(x, y3, 'g-')
plt.title('減衰振動')
plt.grid(True)

# 4つ目のグラフ(ヒストグラム)
plt.subplot(2, 2, 4)
data = np.random.normal(0, 1, 1000)
plt.hist(data, bins=20, alpha=0.7, color='orange', edgecolor='black')
plt.title('正規分布')
plt.grid(True, alpha=0.3)

plt.tight_layout()  # グラフ間の重なりを防止
plt.show()

実践的な例

実際のデータを使用した可視化

CSVファイルからデータを読み込んで可視化する実践的な例を見てみましょう。pandas を使って2024年1月1日から100日分の日付データを作成し、numpy で商品Aと商品Bの売上データを乱数と簡単な周期関数で生成します。これらを DataFrame にまとめ、plt.plot で日付を横軸、売上を縦軸にして2つの商品の売上推移を折れ線グラフとして描画します。さらに、タイトルや軸ラベル、凡例、グリッド線を設定し、plt.xticks(rotation=45) で日付ラベルを見やすく回転させ、plt.tight_layout() で表示が重ならないよう調整しています。最後に plt.show() でグラフを画面に表示し、商品の売上変動を一目で確認できるようにしています。

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

# サンプルデータの作成(実際のプロジェクトではCSVファイルを読み込みます)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
sales_a = np.random.randint(50, 150, 100) + np.sin(np.arange(100) * 0.1) * 20
sales_b = np.random.randint(30, 120, 100) + np.cos(np.arange(100) * 0.1) * 15

# データフレームの作成
df = pd.DataFrame({
    'date': dates,
    'sales_a': sales_a,
    'sales_b': sales_b
})

# 時系列データの可視化
plt.figure(figsize=(12, 6))

plt.plot(df['date'], df['sales_a'], label='商品A', linewidth=2)
plt.plot(df['date'], df['sales_b'], label='商品B', linewidth=2)

# 装飾
plt.title('商品別売上推移')
plt.xlabel('日付')
plt.ylabel('売上数量')
plt.legend()
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

月次売上の比較

months に1月から12月までの月を設定し、numpy で2022年~2024年の各月売上データを乱数で生成します。plt.bar を使って、3年間の売上を横に並べた棒グラフとして描画し、width で棒の幅と位置を調整しています。さらに、タイトルや軸ラベル、凡例、グリッド線を設定し、各棒の上に売上数値を表示して視覚的に比較しやすくしています。最後に plt.tight_layout() で表示が重ならないよう調整し、plt.show() でグラフを画面に表示します。

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

# 月次売上データの作成
months = ['1月', '2月', '3月', '4月', '5月', '6月', 
          '7月', '8月', '9月', '10月', '11月', '12月']

# 3年間のデータ
sales_2022 = np.random.randint(800, 1200, 12)
sales_2023 = np.random.randint(900, 1300, 12)
sales_2024 = np.random.randint(1000, 1400, 12)

# 棒グラフで比較
x = np.arange(len(months))  # 月の位置
width = 0.25  # 棒の幅

plt.figure(figsize=(14, 7))

plt.bar(x - width, sales_2022, width, label='2022年', alpha=0.8)
plt.bar(x, sales_2023, width, label='2023年', alpha=0.8)
plt.bar(x + width, sales_2024, width, label='2024年', alpha=0.8)

# 装飾
plt.title('月次売上比較(2022-2024年)')
plt.xlabel('月')
plt.ylabel('売上高(千円)')
plt.xticks(x, months)
plt.legend()
plt.grid(True, alpha=0.3)

# 数値を表示
for i, v in enumerate(sales_2022):
    plt.text(i - width, v + 20, str(v), ha='center', fontsize=8)
for i, v in enumerate(sales_2023):
    plt.text(i, v + 20, str(v), ha='center', fontsize=8)
for i, v in enumerate(sales_2024):
    plt.text(i + width, v + 20, str(v), ha='center', fontsize=8)

plt.tight_layout()
plt.show()

グラフの保存

作成したグラフを画像ファイルとして保存する方法も重要です。numpy で0から10までの100点の横軸データを生成し、sin(x) を計算して縦軸データとします。plt.plot で折れ線グラフを描き、タイトルや軸ラベル、グリッド線を設定します。その後、plt.savefig を使って高解像度のPNGファイルとPDFファイルとしてグラフを保存し、最後に plt.show() で画面にも表示します。これにより、作成したグラフをファイルとしても残すことができます。

import matplotlib.pyplot as plt
import numpy as np

# サンプルグラフの作成
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title('サイン波')
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.grid(True)

# グラフをファイルに保存
plt.savefig('sine_wave.png', dpi=300, bbox_inches='tight')  # 高解像度で保存
plt.savefig('sine_wave.pdf')  # PDF形式で保存

plt.show()

スタイルの適用

matplotlibには様々なスタイルが用意されており、簡単に見た目を変更できます。plt.style.available で利用可能なスタイル一覧を確認し、plt.style.use('seaborn-v0_8') でモダンなスタイルを適用します。その後、numpy で横軸データを作成し、上段のサブプロットに sin(x)cos(x) の折れ線グラフ、下段にカテゴリ別の棒グラフを描画します。タイトル、凡例、グリッド線、色設定を行い、plt.tight_layout() でレイアウトの重なりを防ぎ、plt.show() で画面に表示します。スタイルを変えることで、グラフ全体の配色や線のデザインが自動的に統一され、見やすくなります。

import matplotlib.pyplot as plt
import numpy as np

# 利用可能なスタイルの表示
print(plt.style.available)

# スタイルの適用
plt.style.use('seaborn-v0_8')  # モダンなスタイル

# グラフ作成
x = np.linspace(0, 10, 100)
plt.figure(figsize=(10, 6))

plt.subplot(2, 1, 1)
plt.plot(x, np.sin(x), label='sin(x)')
plt.plot(x, np.cos(x), label='cos(x)')
plt.title('スタイル適用例 - 折れ線グラフ')
plt.legend()
plt.grid(True)

plt.subplot(2, 1, 2)
categories = ['A', 'B', 'C', 'D']
values = [23, 45, 56, 78]
plt.bar(categories, values, color=plt.cm.Set3(np.arange(len(categories))))
plt.title('スタイル適用例 - 棒グラフ')

plt.tight_layout()
plt.show()

プログラム例】売上データの分析ダッシュボード

学んだ技術を組み合わせて、実践的な分析ダッシュボードを作成してみましょう。

numpy で乱数を使って日次の売上データを生成し、pandas で日付や月ごとの集計を行っています。ダッシュボードは2行2列の4つのグラフで構成されており、以下の内容を可視化します。

  1. 商品別売上推移(折れ線グラフ):電子機器、衣料品、食品、書籍の年間売上を日次で表示し、季節変動やトレンドを確認可能。
  2. 月次売上比較(棒グラフ):各商品の月ごとの売上合計を並べて比較。棒の幅と位置を調整して見やすくしている。
  3. 年間売上構成(円グラフ):年間売上の合計に基づき、各商品の売上比率を視覚化。
  4. 売上分布(ヒストグラム):各商品の日次売上の分布を密度表示で比較し、売上のばらつきを把握。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

def create_sales_dashboard():
    """売上分析ダッシュボードの作成"""

    # サンプルデータの生成
    np.random.seed(42)
    dates = pd.date_range('2024-01-01', periods=365, freq='D')

    # 様々な商品の売上データ
    products = {
        '電子機器': np.random.normal(1000, 200, 365) + np.sin(np.arange(365) * 0.02) * 100,
        '衣料品': np.random.normal(800, 150, 365) + np.cos(np.arange(365) * 0.02) * 80,
        '食品': np.random.normal(1200, 100, 365),
        '書籍': np.random.normal(400, 50, 365)
    }

    # ダッシュボードの作成
    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    fig.suptitle('売上分析ダッシュボード (2024年)', fontsize=16, fontweight='bold')

    # 1. 時系列推移
    for product, sales in products.items():
        axes[0, 0].plot(dates, sales, label=product, linewidth=1.5)

    axes[0, 0].set_title('商品別売上推移')
    axes[0, 0].set_ylabel('売上高')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    axes[0, 0].tick_params(axis='x', rotation=45)

    # 2. 月次合計売上
    monthly_sales = {}
    for product, sales in products.items():
        df = pd.DataFrame({'date': dates, 'sales': sales})
        df['month'] = df['date'].dt.month
        monthly_sales[product] = df.groupby('month')['sales'].sum()

    months = range(1, 13)
    x = np.arange(len(months))
    width = 0.2

    for i, (product, sales) in enumerate(monthly_sales.items()):
        axes[0, 1].bar(x + i * width, sales, width, label=product, alpha=0.8)

    axes[0, 1].set_title('月次売上比較')
    axes[0, 1].set_xlabel('月')
    axes[0, 1].set_ylabel('売上高')
    axes[0, 1].set_xticks(x + width * 1.5)
    axes[0, 1].set_xticklabels([f'{m}月' for m in months])
    axes[0, 1].legend()
    axes[0, 1].grid(True, alpha=0.3)

    # 3. 年間売上構成(円グラフ)
    annual_totals = [sum(sales) for sales in products.values()]
    axes[1, 0].pie(annual_totals, labels=products.keys(), autopct='%1.1f%%', startangle=90)
    axes[1, 0].set_title('年間売上構成比')

    # 4. 売上の分布(ヒストグラム)
    for product, sales in products.items():
        axes[1, 1].hist(sales, bins=20, alpha=0.6, label=product, density=True)

    axes[1, 1].set_title('売上分布')
    axes[1, 1].set_xlabel('売上高')
    axes[1, 1].set_ylabel('密度')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

# ダッシュボードの実行
create_sales_dashboard()

各グラフにはタイトル、軸ラベル、凡例、グリッド線を設定し、plt.tight_layout() でレイアウトを整えています。最後に plt.show() でダッシュボード全体を表示します。
要するに、このプログラムは日次売上データから年間・月次・分布・構成比を一目で確認できる総合的な売上可視化ツールです。

ベストプラクティスと注意点

1. 適切なグラフの選択

  • 時系列データ: 折れ線グラフ
  • カテゴリ比較: 棒グラフ
  • 分布の確認: ヒストグラム
  • 相関関係: 散布図

2. 視覚的なわかりやすさ

  • 色の使い分け(色覚多様性への配慮)
  • 適切なフォントサイズ
  • 過度な装飾の回避
  • 明確な凡例とラベル

3. コードの整理

Pythonで整理された折れ線グラフを作成・表示します。xy=sin(x) のデータを用意し、plt.plot で線を描画。タイトルや軸ラベル、凡例、グリッドを設定し、plt.tight_layout() で見やすく調整してから plt.show() で表示します。

# 良い例:整理されたコード
def create_clean_chart():
    # データ準備
    x = np.linspace(0, 10, 100)
    y = np.sin(x)

    # グラフ設定
    plt.figure(figsize=(10, 6))
    plt.plot(x, y, 'b-', linewidth=2, label='sin(x)')

    # 装飾
    plt.title('整理されたグラフ', fontsize=14)
    plt.xlabel('x', fontsize=12)
    plt.ylabel('sin(x)', fontsize=12)
    plt.legend()
    plt.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

まとめ

matplotlibを使ったデータ可視化の基礎では、まず基本操作として簡単なコードで基本的なグラフを作成する方法を学びました。グラフタイプとしては、折れ線グラフ、棒グラフ、散布図、ヒストグラムなど、用途に応じた適切な種類の選択が重要です。

また、タイトルや軸ラベル、凡例の設定、色のカスタマイズなどの装飾技術を活用することで、グラフをより見やすく、理解しやすくすることができます。さらに、subplotを利用した複数グラフの表示により、ダッシュボード形式で情報を整理して提示することも可能です。

これらの技術は、実際のデータ分析に応用することで、複雑なデータパターンの直感的な理解、説得力のある結果提示、問題の発見と洞察の獲得、そして意思決定のサポートといった利点をもたらします。

データベース操作(SQLite)やデータの永続化などの、実践的なデータ処理パイプラインを構築すると、matplotlibで可視化したデータをデータベースから直接取得・処理できるようになると、さらに強力な分析が可能になります。

まずは簡単なグラフから始めて、徐々に複雑な可視化に挑戦してみましょう。実際のデータを使って練習することが、最も効果的な学習方法です。

演習問題

初級問題(3問)

初級1: 基本的な折れ線グラフ

"""
問題1: 以下のデータを使用して基本的な折れ線グラフを作成してください。

x = [0, 1, 2, 3, 4, 5]
y = [0, 1, 4, 9, 16, 25]

要件:
1. グラフにタイトル「二次関数のグラフ」を追加
2. x軸ラベルを「xの値」、y軸ラベルを「y = x²」と設定
3. グリッド線を表示
4. 線の色を赤色に設定
5. グラフを表示

注意: 基本的な装飾をすべて実装してください。
"""
import matplotlib.pyplot as plt

# ここにコードを書いてください

初級2: 棒グラフの作成

"""
問題2: 以下の果物の売上データを使用して棒グラフを作成してください。

fruits = ['りんご', 'バナナ', 'オレンジ', 'ぶどう', 'いちご']
sales = [120, 85, 90, 60, 110]

要件:
1. 各果物に対応する色を設定(りんご:赤, バナナ:黄, オレンジ:橙, ぶどう:紫, いちご:ピンク)
2. グラフタイトル「果物の売上比較」を追加
3. x軸ラベルを「果物の種類」、y軸ラベルを「売上数量」と設定
4. x軸のラベルを45度回転して表示
5. 各棒の上に売上数を表示

ヒント: plt.text() を使用して数値を表示
"""
import matplotlib.pyplot as plt

# ここにコードを書いてください

初級3: 複数系列の折れ線グラフ

"""
問題3: 以下の3つの関数を同じグラフにプロットしてください。

x = range(0, 101)  # 0から100までの整数
y1 = [i for i in x]                    # 直線
y2 = [i**2 for i in x]                 # 二次関数
y3 = [i**0.5 for i in x if i >= 0]     # 平方根

要件:
1. 各系列にラベルを設定('y=x', 'y=x²', 'y=√x')
2. 凡例を表示
3. 線種を変更(直線: 実線, 二次関数: 破線, 平方根: 点線)
4. グラフタイトル「関数の比較」を追加
5. グリッドを表示

注意: y3のデータポイント数が異なる点に注意
"""
import matplotlib.pyplot as plt
import math

# ここにコードを書いてください

中級問題(6問)

中級1: サブプロットの作成

"""
問題1: 2x2のサブプロットを作成し、以下の4種類のグラフを表示してください。

データ:
x = range(0, 11)
y1 = [i for i in x]                    # 直線
y2 = [i**2 for i in x]                 # 二次関数
y3 = [2**i for i in x]                 # 指数関数
categories = ['A', 'B', 'C', 'D']
values = [23, 45, 56, 78]

要件:
1. 左上: 直線の折れ線グラフ(タイトル: "線形関数")
2. 右上: 二次関数の折れ線グラフ(タイトル: "二次関数")
3. 左下: 指数関数の折れ線グラフ(タイトル: "指数関数")
4. 右下: 棒グラフ(タイトル: "カテゴリ別値")
5. 全体の図のサイズを(12, 8)に設定
6. tight_layout()でレイアウトを調整
"""
import matplotlib.pyplot as plt

# ここにコードを書いてください

中級2: 散布図とヒストグラム

"""
問題2: ランダムなデータを生成して、散布図とヒストグラムを作成してください。

要件:
1. 100個のランダムなデータポイントを生成(正規分布)
   x = np.random.normal(0, 1, 100)
   y = 2*x + np.random.normal(0, 0.5, 100)
2. 2x2のサブプロットを作成
3. 左上: xとyの散布図(透明度0.6、点のサイズ30)
4. 右上: xのヒストグラム(ビン数20、透明度0.7)
5. 左下: yのヒストグラム(ビン数20、透明度0.7)
6. 右下: 散布図に回帰直線を追加
7. 各グラフに適切なタイトルとラベルを追加

ヒント: 回帰直線にはnp.polyfit()を使用
"""
import matplotlib.pyplot as plt
import numpy as np

# ここにコードを書いてください

中級3: 円グラフの作成

"""
問題3: 以下の支出データを使用して円グラフを作成してください。

支出カテゴリ:
categories = ['家賃', '食費', '交通費', '娯楽', '貯蓄', 'その他']
amounts = [80000, 40000, 15000, 20000, 30000, 10000]

要件:
1. 円グラフを作成し、各要素にパーセンテージを表示
2. スタート角度を90度に設定
3. 割合が5%未満の要素をまとめて「その他」として表示
4. 凡例をグラフの右側に表示
5. タイトル「月間支出の内訳」を追加
6. グラフのアスペクト比を等しく設定

発展課題: 特定のスライスを少し引き出して強調表示
"""
import matplotlib.pyplot as plt

# ここにコードを書いてください

中級4: 時系列データの可視化

"""
問題4: 架空の株価データを作成して時系列グラフを描画してください。

要件:
1. 2024年1月1日から30日間の日次データを生成
2. 初期株価1000円から始まるランダムウォークを模拟
3. 移動平均線(5日間)を追加
4. 以下の要素を含む:
   - 元の株価(折れ線グラフ)
   - 移動平均線(太い折れ線)
   - グリッド線
   - 日付ラベルの回転
   - 凡例
5. タイトル「架空株価の推移」を追加

ヒント: 
- ランダムウォーク: price[t] = price[t-1] + random noise
- 移動平均: np.convolve() を使用
"""
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

# ここにコードを書いてください

中級5: 箱ひげ図とバイオリンプロット

"""
問題5: 3つの異なる分布を持つデータセットを作成し、比較してください。

要件:
1. 以下の3つのデータセットを生成:
   data1 = np.random.normal(50, 10, 100)  # 正規分布
   data2 = np.random.exponential(50, 100) # 指数分布
   data3 = np.random.uniform(30, 70, 100) # 一様分布
2. 2x1のサブプロットを作成
3. 上部: 箱ひげ図(3つのデータセットを並べて表示)
4. 下部: バイオリンプロット(3つのデータセットを並べて表示)
5. 各グラフに適切なラベルとタイトルを追加
6. x軸にデータセット名を表示

注意: バイオリンプロットにはplt.violinplot()を使用
"""
import matplotlib.pyplot as plt
import numpy as np

# ここにコードを書いてください

中級6: カスタムスタイルの適用

"""
問題6: matplotlibのスタイル機能を使用して、プロフェッショナルなレポートを作成してください。

要件:
1. 'seaborn-v0_8' スタイルを適用
2. 以下のデータで4つの異なるグラフを作成:
   - 折れ線グラフ: sin波とcos波
   - 棒グラフ: 月次売上
   - 散布図: 相関データ
   - ヒストグラム: 正規分布
3. 2x2のサブプロットレイアウト
4. 各グラフに適切な装飾を施す
5. 全体のタイトルを設定
6. グラフを高解像度で保存

使用データ:
x = np.linspace(0, 10, 100)
months = ['1月', '2月', '3月', '4月', '5月', '6月']
sales = [120, 135, 115, 145, 130, 160]
x_scatter = np.random.randn(100)
y_scatter = x_scatter * 2 + np.random.randn(100) * 0.5
hist_data = np.random.normal(0, 1, 1000)
"""
import matplotlib.pyplot as plt
import numpy as np

# ここにコードを書いてください

上級問題(3問)

上級1: インタラクティブなダッシュボード

"""
問題1: 複数の関連グラフからなるインタラクティブな分析ダッシュボードを作成してください。

シナリオ: 小売店の販売分析ダッシュボード

要件:
1. 以下のサンプルデータを生成:
   - 1年間の日次販売データ
   - 複数商品カテゴリの売上
   - 顧客層別の購買データ
2. 以下のグラフを含む3x2のダッシュボード:
   - 時系列売上推移(全カテゴリ)
   - 月次売上比較(棒グラフ)
   - カテゴリ別売上構成(円グラフ)
   - 価格帯別販売数量(ヒストグラム)
   - 曜日別売上分析(棒グラフ)
   - 顧客層別平均購入額(棒グラフ)
3. プロフェッショナルなスタイルを適用
4. 各グラフに適切なタイトル、ラベル、凡例を追加
5. レイアウトを調整して見やすくする

発展課題: マウスオーバーで数値を表示する機能を追加
"""
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

# ここにコードを書いてください

上級2: カスタムビジュアライゼーション

"""
問題2: 高度なカスタマイズを行った専門的な可視化を作成してください。

テーマ: プロジェクトの進捗状況とリソース使用率の可視化

要件:
1. ガントチャート風の進捗状況図:
   - 複数タスクの開始日、終了日、進捗率を表示
   - カラーマップで進捗率を表現
2. リソース使用率の積み上げ面グラフ:
   - 時間経過に伴うリソース使用率の変化
   - 複数リソースを積み上げて表示
3. 以下のカスタマイズを実装:
   - カスタムカラーパレットの定義
   - アノテーションの追加
   - 二軸グラフの作成
   - カスタム凡例
4. 出版品質の出力(高解像度、適切なフォントサイズ)

使用データ例:
tasks = ['設計', '開発', 'テスト', 'デプロイ']
start_dates = [1, 5, 15, 25]
durations = [10, 15, 8, 5]
progress = [1.0, 0.8, 0.3, 0.0]  # 進捗率
"""
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle

# ここにコードを書いてください

上級3: リアルタイムデータ可視化

"""
問題3: リアルタイムデータを模拟し、動的に更新されるグラフを作成してください。

シナリオ: センサーデータのモニタリングシステム

要件:
1. 以下のシミュレーションデータを生成:
   - 温度データ(ゆっくり変動する信号+ノイズ)
   - 湿度データ(異なる周波数で変動)
   - 気圧データ(ランダムウォーク)
2. matplotlibのアニメーション機能を使用
3. 3つのサブプロットで各センサーデータをリアルタイム表示
4. 以下の機能を実装:
   - 過去50ポイントのデータを保持・表示
   - 現在値の数値表示
   - 異常値の色分け表示(閾値を超えた場合)
   - スムーズなアニメーション
5. カスタムコールバック関数によるデータ更新

発展課題: ユーザーインタラクション(一時停止、再開)を追加
"""
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import random

# ここにコードを書いてください

解答作成のヒント

初級問題のヒント

  • plt.plot() の基本的な使い方をマスター
  • label, color, linestyle などのパラメータを活用
  • plt.legend() で凡例を表示

中級問題のヒント

  • plt.subplot() で複数グラフを配置
  • np.random モジュールでデータ生成
  • スタイル設定で見た目を改善

上級問題のヒント

  • FuncAnimation でアニメーション作成
  • カスタム描画関数で専門的なビジュアライゼーション
  • データフレームを活用した実践的なデータ処理

演習問題 解答例

初級問題 解答

初級1: 基本的な折れ線グラフ

import matplotlib.pyplot as plt

def basic_line_plot():
    # データの準備
    x = [0, 1, 2, 3, 4, 5]
    y = [0, 1, 4, 9, 16, 25]

    # グラフの作成
    plt.plot(x, y, color='red')

    # グラフの装飾
    plt.title('二次関数のグラフ')
    plt.xlabel('xの値')
    plt.ylabel('y = x²')
    plt.grid(True)

    # グラフの表示
    plt.show()

# 実行
basic_line_plot()

初級2: 棒グラフの作成

import matplotlib.pyplot as plt

def create_bar_chart():
    # データの準備
    fruits = ['りんご', 'バナナ', 'オレンジ', 'ぶどう', 'いちご']
    sales = [120, 85, 90, 60, 110]
    colors = ['red', 'yellow', 'orange', 'purple', 'pink']

    # 棒グラフの作成
    bars = plt.bar(fruits, sales, color=colors)

    # グラフの装飾
    plt.title('果物の売上比較')
    plt.xlabel('果物の種類')
    plt.ylabel('売上数量')
    plt.xticks(rotation=45)

    # 各棒の上に数値を表示
    for bar, value in zip(bars, sales):
        plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1, 
                str(value), ha='center', va='bottom')

    # y軸の範囲を調整
    plt.ylim(0, max(sales) + 20)

    # レイアウト調整
    plt.tight_layout()
    plt.show()

# 実行
create_bar_chart()

初級3: 複数系列の折れ線グラフ

import matplotlib.pyplot as plt
import math

def multiple_line_plots():
    # データの準備
    x = list(range(0, 101))
    y1 = [i for i in x]                    # 直線
    y2 = [i**2 for i in x]                 # 二次関数
    y3 = [math.sqrt(i) for i in x if i >= 0]  # 平方根
    x3 = list(range(0, 101))               # y3用のx値

    # グラフの作成
    plt.plot(x, y1, label='y=x', linestyle='-', linewidth=2)      # 実線
    plt.plot(x, y2, label='y=x²', linestyle='--', linewidth=2)    # 破線
    plt.plot(x3[:len(y3)], y3, label='y=√x', linestyle=':', linewidth=2)  # 点線

    # グラフの装飾
    plt.title('関数の比較')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.grid(True, alpha=0.3)

    # 表示
    plt.show()

# 実行
multiple_line_plots()

中級問題 解答

中級1: サブプロットの作成

import matplotlib.pyplot as plt

def create_subplots():
    # データの準備
    x = list(range(0, 11))
    y1 = [i for i in x]                    # 直線
    y2 = [i**2 for i in x]                 # 二次関数
    y3 = [2**i for i in x]                 # 指数関数
    categories = ['A', 'B', 'C', 'D']
    values = [23, 45, 56, 78]

    # サブプロットの作成
    plt.figure(figsize=(12, 8))

    # 1. 直線の折れ線グラフ
    plt.subplot(2, 2, 1)
    plt.plot(x, y1, 'b-', linewidth=2)
    plt.title('線形関数')
    plt.grid(True, alpha=0.3)

    # 2. 二次関数の折れ線グラフ
    plt.subplot(2, 2, 2)
    plt.plot(x, y2, 'r-', linewidth=2)
    plt.title('二次関数')
    plt.grid(True, alpha=0.3)

    # 3. 指数関数の折れ線グラフ
    plt.subplot(2, 2, 3)
    plt.plot(x, y3, 'g-', linewidth=2)
    plt.title('指数関数')
    plt.grid(True, alpha=0.3)

    # 4. 棒グラフ
    plt.subplot(2, 2, 4)
    bars = plt.bar(categories, values, color=['skyblue', 'lightcoral', 'lightgreen', 'gold'])
    plt.title('カテゴリ別値')

    # 棒グラフに数値を表示
    for bar, value in zip(bars, values):
        plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1, 
                str(value), ha='center', va='bottom')

    # レイアウト調整
    plt.tight_layout()
    plt.show()

# 実行
create_subplots()

中級2: 散布図とヒストグラム

import matplotlib.pyplot as plt
import numpy as np

def scatter_histogram_plots():
    # 乱数シードの設定(再現性のため)
    np.random.seed(42)

    # データの生成
    x = np.random.normal(0, 1, 100)
    y = 2 * x + np.random.normal(0, 0.5, 100)

    # 回帰直線の計算
    coefficients = np.polyfit(x, y, 1)
    regression_line = np.poly1d(coefficients)
    x_line = np.linspace(min(x), max(x), 100)
    y_line = regression_line(x_line)

    # サブプロットの作成
    plt.figure(figsize=(12, 8))

    # 1. 散布図
    plt.subplot(2, 2, 1)
    plt.scatter(x, y, alpha=0.6, s=30, color='blue')
    plt.title('xとyの散布図')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.grid(True, alpha=0.3)

    # 2. xのヒストグラム
    plt.subplot(2, 2, 2)
    plt.hist(x, bins=20, alpha=0.7, color='red', edgecolor='black')
    plt.title('xの分布')
    plt.xlabel('xの値')
    plt.ylabel('頻度')
    plt.grid(True, alpha=0.3)

    # 3. yのヒストグラム
    plt.subplot(2, 2, 3)
    plt.hist(y, bins=20, alpha=0.7, color='green', edgecolor='black')
    plt.title('yの分布')
    plt.xlabel('yの値')
    plt.ylabel('頻度')
    plt.grid(True, alpha=0.3)

    # 4. 回帰直線付き散布図
    plt.subplot(2, 2, 4)
    plt.scatter(x, y, alpha=0.6, s=30, color='blue', label='データ点')
    plt.plot(x_line, y_line, 'r-', linewidth=2, label='回帰直線')
    plt.title('回帰直線付き散布図')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.grid(True, alpha=0.3)

    # レイアウト調整
    plt.tight_layout()
    plt.show()

    # 相関係数の表示
    correlation = np.corrcoef(x, y)[0, 1]
    print(f"相関係数: {correlation:.3f}")

# 実行
scatter_histogram_plots()

中級3: 円グラフの作成

import matplotlib.pyplot as plt

def create_pie_chart():
    # データの準備
    categories = ['家賃', '食費', '交通費', '娯楽', '貯蓄', 'その他']
    amounts = [80000, 40000, 15000, 20000, 30000, 10000]

    # 割合の計算
    total = sum(amounts)
    percentages = [amount/total * 100 for amount in amounts]

    # 5%未満の要素をまとめる
    explode = [0] * len(categories)  # 引き出し量
    new_categories = []
    new_amounts = []
    new_percentages = []
    new_explode = []

    other_amount = 0
    for i, (category, amount, percentage) in enumerate(zip(categories, amounts, percentages)):
        if percentage < 5:
            other_amount += amount
        else:
            new_categories.append(category)
            new_amounts.append(amount)
            new_percentages.append(percentage)
            new_explode.append(0.1 if category == '貯蓄' else 0)  # 貯蓄を強調

    # 「その他」を追加
    if other_amount > 0:
        new_categories.append('その他')
        new_amounts.append(other_amount)
        new_percentages.append(other_amount/total * 100)
        new_explode.append(0)

    # 円グラフの作成
    plt.figure(figsize=(10, 8))

    # 円グラフを描画
    wedges, texts, autotexts = plt.pie(
        new_amounts, 
        labels=new_categories,
        autopct='%1.1f%%',
        startangle=90,
        explode=new_explode,
        colors=plt.cm.Set3(np.arange(len(new_categories)))
    )

    # パーセンテージのスタイル調整
    for autotext in autotexts:
        autotext.set_color('white')
        autotext.set_fontweight('bold')

    # タイトルと凡例
    plt.title('月間支出の内訳', fontsize=14, fontweight='bold')
    plt.legend(wedges, [f'{c}: {a:,}円 ({p:.1f}%)' 
                       for c, a, p in zip(new_categories, new_amounts, new_percentages)],
              title="支出内訳",
              loc="center left",
              bbox_to_anchor=(1, 0, 0.5, 1))

    # アスペクト比を等しく
    plt.axis('equal')

    # レイアウト調整
    plt.tight_layout()
    plt.show()

# 実行
create_pie_chart()

中級4: 時系列データの可視化

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

def create_stock_price_chart():
    # 乱数シードの設定
    np.random.seed(42)

    # 日付の生成
    start_date = datetime(2024, 1, 1)
    dates = [start_date + timedelta(days=i) for i in range(30)]

    # ランダムウォークで株価を生成
    prices = [1000]  # 初期価格
    for i in range(1, 30):
        # 前日の価格にランダムな変動を加える
        change = np.random.normal(0, 20)  # 平均0、標準偏差20の正規分布
        new_price = prices[-1] + change
        prices.append(new_price)

    # 移動平均の計算(5日間)
    moving_avg = []
    for i in range(len(prices)):
        if i < 4:  # 最初の4日は計算できない
            moving_avg.append(None)
        else:
            avg = sum(prices[i-4:i+1]) / 5
            moving_avg.append(avg)

    # グラフの作成
    plt.figure(figsize=(12, 6))

    # 株価の折れ線グラフ
    plt.plot(dates, prices, 'b-', linewidth=1.5, label='株価', alpha=0.7)

    # 移動平均線
    plt.plot(dates[4:], moving_avg[4:], 'r-', linewidth=2, label='5日移動平均')

    # グラフの装飾
    plt.title('架空株価の推移', fontsize=14, fontweight='bold')
    plt.xlabel('日付')
    plt.ylabel('株価(円)')
    plt.legend()
    plt.grid(True, alpha=0.3)

    # x軸の日付ラベルを回転
    plt.xticks(rotation=45)

    # y軸の範囲を調整
    plt.ylim(min(prices) * 0.98, max(prices) * 1.02)

    # レイアウト調整
    plt.tight_layout()
    plt.show()

    # 統計情報の表示
    print(f"初期価格: {prices[0]:.0f}円")
    print(f"最終価格: {prices[-1]:.0f}円")
    print(f"値幅: {max(prices) - min(prices):.0f}円")
    print(f"利益率: {(prices[-1] - prices[0]) / prices[0] * 100:.1f}%")

# 実行
create_stock_price_chart()

中級5: 箱ひげ図とバイオリンプロット

import matplotlib.pyplot as plt
import numpy as np

def create_box_violin_plots():
    # 乱数シードの設定
    np.random.seed(42)

    # 3つの異なる分布を持つデータセットを生成
    data1 = np.random.normal(50, 10, 100)  # 正規分布
    data2 = np.random.exponential(50, 100) # 指数分布
    data3 = np.random.uniform(30, 70, 100) # 一様分布

    # データをリストにまとめる
    data = [data1, data2, data3]
    labels = ['正規分布', '指数分布', '一様分布']

    # サブプロットの作成
    plt.figure(figsize=(12, 8))

    # 1. 箱ひげ図
    plt.subplot(2, 1, 1)
    box_plot = plt.boxplot(data, labels=labels, patch_artist=True)

    # 箱の色を設定
    colors = ['lightblue', 'lightgreen', 'lightcoral']
    for patch, color in zip(box_plot['boxes'], colors):
        patch.set_facecolor(color)

    plt.title('データ分布の比較 - 箱ひげ図', fontweight='bold')
    plt.ylabel('値')
    plt.grid(True, alpha=0.3)

    # 2. バイオリンプロット
    plt.subplot(2, 1, 2)
    violin_plot = plt.violinplot(data, showmeans=True, showmedians=True)

    # バイオリンの色を設定
    for i, pc in enumerate(violin_plot['bodies']):
        pc.set_facecolor(colors[i])
        pc.set_alpha(0.7)

    # 平均値と中央値のスタイル設定
    violin_plot['cmeans'].set_color('red')
    violin_plot['cmeans'].set_linewidth(2)
    violin_plot['cmedians'].set_color('blue')
    violin_plot['cmedians'].set_linewidth(2)

    plt.title('データ分布の比較 - バイオリンプロット', fontweight='bold')
    plt.xlabel('分布タイプ')
    plt.ylabel('値')
    plt.xticks([1, 2, 3], labels)
    plt.grid(True, alpha=0.3)

    # レイアウト調整
    plt.tight_layout()
    plt.show()

    # 統計情報の表示
    for i, (d, label) in enumerate(zip(data, labels)):
        print(f"\n{label}:")
        print(f"  平均: {np.mean(d):.2f}")
        print(f"  標準偏差: {np.std(d):.2f}")
        print(f"  最小値: {np.min(d):.2f}")
        print(f"  最大値: {np.max(d):.2f}")

# 実行
create_box_violin_plots()

中級6: カスタムスタイルの適用

import matplotlib.pyplot as plt
import numpy as np

def create_styled_dashboard():
    # スタイルの適用
    plt.style.use('seaborn-v0_8')

    # データの準備
    x = np.linspace(0, 10, 100)
    months = ['1月', '2月', '3月', '4月', '5月', '6月']
    sales = [120, 135, 115, 145, 130, 160]

    # 散布図用データ
    np.random.seed(42)
    x_scatter = np.random.randn(100)
    y_scatter = x_scatter * 2 + np.random.randn(100) * 0.5

    # ヒストグラム用データ
    hist_data = np.random.normal(0, 1, 1000)

    # サブプロットの作成
    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    fig.suptitle('データ可視化ダッシュボード', fontsize=16, fontweight='bold')

    # 1. 折れ線グラフ(sin波とcos波)
    axes[0, 0].plot(x, np.sin(x), 'b-', linewidth=2, label='sin(x)')
    axes[0, 0].plot(x, np.cos(x), 'r-', linewidth=2, label='cos(x)')
    axes[0, 0].set_title('三角関数')
    axes[0, 0].set_xlabel('x')
    axes[0, 0].set_ylabel('y')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)

    # 2. 棒グラフ(月次売上)
    bars = axes[0, 1].bar(months, sales, color=plt.cm.viridis(np.linspace(0, 1, len(months))))
    axes[0, 1].set_title('月次売上')
    axes[0, 1].set_xlabel('月')
    axes[0, 1].set_ylabel('売上高')

    # 棒グラフに数値を表示
    for bar, value in zip(bars, sales):
        axes[0, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1, 
                       str(value), ha='center', va='bottom', fontweight='bold')

    # 3. 散布図
    axes[1, 0].scatter(x_scatter, y_scatter, alpha=0.6, s=50, 
                      c=x_scatter, cmap='viridis')
    axes[1, 0].set_title('相関散布図')
    axes[1, 0].set_xlabel('x')
    axes[1, 0].set_ylabel('y')

    # 相関係数の計算と表示
    correlation = np.corrcoef(x_scatter, y_scatter)[0, 1]
    axes[1, 0].text(0.05, 0.95, f'相関係数: {correlation:.3f}', 
                   transform=axes[1, 0].transAxes, fontsize=12,
                   bbox=dict(boxstyle="round,pad=0.3", facecolor="white", alpha=0.8))

    # 4. ヒストグラム
    axes[1, 1].hist(hist_data, bins=30, alpha=0.7, color='purple', 
                   edgecolor='black', density=True)
    axes[1, 1].set_title('正規分布のヒストグラム')
    axes[1, 1].set_xlabel('値')
    axes[1, 1].set_ylabel('密度')

    # 正規分布曲線の追加
    x_norm = np.linspace(-4, 4, 100)
    y_norm = (1/(np.sqrt(2*np.pi))) * np.exp(-0.5*x_norm**2)
    axes[1, 1].plot(x_norm, y_norm, 'r-', linewidth=2, label='理論曲線')
    axes[1, 1].legend()

    # レイアウト調整
    plt.tight_layout()

    # グラフを保存
    plt.savefig('styled_dashboard.png', dpi=300, bbox_inches='tight')
    plt.savefig('styled_dashboard.pdf', bbox_inches='tight')

    plt.show()

# 実行
create_styled_dashboard()

上級問題 解答

上級1: インタラクティブなダッシュボード

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

def create_retail_dashboard():
    # スタイルの適用
    plt.style.use('seaborn-v0_8')

    # サンプルデータの生成
    np.random.seed(42)

    # 1年間の日次データ生成
    start_date = datetime(2024, 1, 1)
    dates = pd.date_range(start_date, periods=365, freq='D')

    # 商品カテゴリの定義
    categories = ['電子機器', '衣料品', '食品', '家庭用品', '書籍']

    # 各カテゴリの売上データ生成(季節性を考慮)
    sales_data = {}
    for category in categories:
        # ベース売上 + 季節変動 + ランダムノイズ
        base_sales = np.random.randint(500, 2000)
        seasonal = 200 * np.sin(2 * np.pi * np.arange(365) / 365)  # 年間季節性
        trend = np.linspace(0, 100, 365)  # 上昇トレンド
        noise = np.random.normal(0, 50, 365)

        sales = base_sales + seasonal + trend + noise
        sales = np.maximum(sales, 0)  # 負の値にならないように
        sales_data[category] = sales

    # 顧客層データ
    customer_segments = ['10代', '20代', '30代', '40代', '50代以上']
    segment_sales = np.random.randint(500, 2000, len(customer_segments))
    segment_avg_purchase = [50, 85, 120, 150, 110]  # 平均購入額

    # 価格帯データ
    price_ranges = ['0-1000', '1001-3000', '3001-5000', '5001-10000', '10000+']
    price_sales = np.random.randint(100, 1000, len(price_ranges))

    # 曜日別データ
    weekdays = ['月', '火', '水', '木', '金', '土', '日']
    weekday_sales = np.random.randint(800, 1200, len(weekdays))
    weekday_sales[5] = 1500  # 土曜日を高く
    weekday_sales[6] = 1400  # 日曜日を高く

    # ダッシュボードの作成
    fig, axes = plt.subplots(3, 2, figsize=(16, 12))
    fig.suptitle('小売店販売分析ダッシュボード (2024年)', fontsize=18, fontweight='bold')

    # 1. 時系列売上推移
    for category in categories:
        axes[0, 0].plot(dates, sales_data[category], label=category, linewidth=1.5)

    axes[0, 0].set_title('時系列売上推移', fontsize=14, fontweight='bold')
    axes[0, 0].set_ylabel('売上高')
    axes[0, 0].legend(bbox_to_anchor=(1.05, 1), loc='upper left')
    axes[0, 0].tick_params(axis='x', rotation=45)
    axes[0, 0].grid(True, alpha=0.3)

    # 2. 月次売上比較
    monthly_totals = {}
    for category in categories:
        df = pd.DataFrame({'date': dates, 'sales': sales_data[category]})
        df['month'] = df['date'].dt.month
        monthly_totals[category] = df.groupby('month')['sales'].sum().values

    months = range(1, 13)
    x = np.arange(len(months))
    width = 0.15

    for i, category in enumerate(categories):
        axes[0, 1].bar(x + i * width, monthly_totals[category], width, label=category)

    axes[0, 1].set_title('月次売上比較', fontsize=14, fontweight='bold')
    axes[0, 1].set_xlabel('月')
    axes[0, 1].set_ylabel('売上高')
    axes[0, 1].set_xticks(x + width * 2)
    axes[0, 1].set_xticklabels([f'{m}月' for m in months])
    axes[0, 1].legend(bbox_to_anchor=(1.05, 1), loc='upper left')
    axes[0, 1].grid(True, alpha=0.3)

    # 3. カテゴリ別売上構成
    annual_totals = [sum(sales_data[cat]) for cat in categories]
    wedges, texts, autotexts = axes[1, 0].pie(
        annual_totals, 
        labels=categories, 
        autopct='%1.1f%%',
        startangle=90,
        colors=plt.cm.Set3(np.arange(len(categories)))
    )

    for autotext in autotexts:
        autotext.set_color('white')
        autotext.set_fontweight('bold')

    axes[1, 0].set_title('カテゴリ別売上構成', fontsize=14, fontweight='bold')

    # 4. 価格帯別販売数量
    axes[1, 1].bar(price_ranges, price_sales, color=plt.cm.viridis(np.linspace(0, 1, len(price_ranges))))
    axes[1, 1].set_title('価格帯別販売数量', fontsize=14, fontweight='bold')
    axes[1, 1].set_xlabel('価格帯(円)')
    axes[1, 1].set_ylabel('販売数量')
    axes[1, 1].tick_params(axis='x', rotation=45)
    axes[1, 1].grid(True, alpha=0.3)

    # 5. 曜日別売上分析
    axes[2, 0].bar(weekdays, weekday_sales, color=plt.cm.Paired(np.arange(len(weekdays))))
    axes[2, 0].set_title('曜日別売上分析', fontsize=14, fontweight='bold')
    axes[2, 0].set_xlabel('曜日')
    axes[2, 0].set_ylabel('売上高')
    axes[2, 0].grid(True, alpha=0.3)

    # 6. 顧客層別平均購入額
    axes[2, 1].bar(customer_segments, segment_avg_purchase, color=plt.cm.coolwarm(np.linspace(0, 1, len(customer_segments))))
    axes[2, 1].set_title('顧客層別平均購入額', fontsize=14, fontweight='bold')
    axes[2, 1].set_xlabel('顧客層')
    axes[2, 1].set_ylabel('平均購入額(円)')
    axes[2, 1].tick_params(axis='x', rotation=45)
    axes[2, 1].grid(True, alpha=0.3)

    # レイアウト調整
    plt.tight_layout()

    # グラフを保存
    plt.savefig('retail_dashboard.png', dpi=300, bbox_inches='tight')

    plt.show()

    # サマリー統計の表示
    total_annual_sales = sum(annual_totals)
    print(f"\n年間売上サマリー:")
    print(f"総売上: {total_annual_sales:,.0f}円")
    for category, total in zip(categories, annual_totals):
        percentage = (total / total_annual_sales) * 100
        print(f"  {category}: {total:,.0f}円 ({percentage:.1f}%)")

# 実行
create_retail_dashboard()

上級2: カスタムビジュアライゼーション

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
import matplotlib.dates as mdates
from datetime import datetime, timedelta

def create_project_visualization():
    # カスタムカラーパレットの定義
    colors = {
        'design': '#FF6B6B',
        'development': '#4ECDC4', 
        'testing': '#45B7D1',
        'deployment': '#96CEB4',
        'completed': '#FECA57',
        'resource1': '#FF9FF3',
        'resource2': '#54A0FF',
        'resource3': '#00D2D3'
    }

    # プロジェクトデータ
    tasks = ['設計', '開発', 'テスト', 'デプロイ']
    start_dates = [datetime(2024, 1, 1), datetime(2024, 1, 5), 
                   datetime(2024, 1, 15), datetime(2024, 1, 25)]
    durations = [10, 15, 8, 5]
    progress = [1.0, 0.8, 0.3, 0.0]  # 進捗率

    # リソース使用率データ
    dates = [datetime(2024, 1, 1) + timedelta(days=i) for i in range(40)]
    resource1 = np.sin(np.arange(40) * 0.2) * 30 + 50  # 開発リソース
    resource2 = np.cos(np.arange(40) * 0.15) * 20 + 40  # テストリソース
    resource3 = np.ones(40) * 25  # 管理リソース

    # 図の作成
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10))

    # 1. ガントチャート風の進捗状況図
    ax1.set_title('プロジェクト進捗状況', fontsize=16, fontweight='bold', pad=20)

    for i, (task, start, duration, prog) in enumerate(zip(tasks, start_dates, durations, progress)):
        # タスクの期間を表示
        end_date = start + timedelta(days=duration)
        task_color = colors[list(colors.keys())[i]]

        # 背景(全体期間)
        ax1.add_patch(Rectangle((mdates.date2num(start), i - 0.4), 
                               mdates.date2num(end_date) - mdates.date2num(start), 
                               0.8, 
                               facecolor=task_color, 
                               alpha=0.3,
                               edgecolor='black'))

        # 進捗部分
        progress_end = start + timedelta(days=duration * prog)
        ax1.add_patch(Rectangle((mdates.date2num(start), i - 0.4), 
                               mdates.date2num(progress_end) - mdates.date2num(start), 
                               0.8, 
                               facecolor=task_color,
                               edgecolor='black'))

        # タスクラベル
        ax1.text(mdates.date2num(start) - 2, i, task, 
                ha='right', va='center', fontweight='bold')

        # 進捗率の表示
        ax1.text(mdates.date2num(start) + (mdates.date2num(end_date) - mdates.date2num(start)) / 2, 
                i, f'{prog*100:.0f}%', 
                ha='center', va='center', fontweight='bold', color='white')

    # ガントチャートの設定
    ax1.set_xlim(mdates.date2num(datetime(2023, 12, 28)), 
                 mdates.date2num(datetime(2024, 2, 10)))
    ax1.set_ylim(-0.5, len(tasks) - 0.5)
    ax1.set_ylabel('タスク')
    ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m/%d'))
    ax1.xaxis.set_major_locator(mdates.WeekdayLocator(interval=1))
    ax1.grid(True, alpha=0.3)

    # 2. リソース使用率の積み上げ面グラフ
    ax2.set_title('リソース使用率の推移', fontsize=16, fontweight='bold', pad=20)

    # 積み上げ面グラフ
    ax2.stackplot([mdates.date2num(d) for d in dates], 
                  resource1, resource2, resource3,
                  labels=['開発リソース', 'テストリソース', '管理リソース'],
                  colors=[colors['resource1'], colors['resource2'], colors['resource3']],
                  alpha=0.7)

    # 現在日付の縦線
    current_date = datetime(2024, 1, 20)
    ax2.axvline(x=mdates.date2num(current_date), color='red', linestyle='--', linewidth=2)
    ax2.text(mdates.date2num(current_date), 90, '現在', 
            ha='center', va='top', fontweight='bold', color='red')

    # リソースグラフの設定
    ax2.set_xlim(mdates.date2num(datetime(2023, 12, 28)), 
                 mdates.date2num(datetime(2024, 2, 10)))
    ax2.set_ylim(0, 120)
    ax2.set_ylabel('リソース使用率 (%)')
    ax2.set_xlabel('日付')
    ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m/%d'))
    ax2.xaxis.set_major_locator(mdates.WeekdayLocator(interval=1))
    ax2.legend(loc='upper left')
    ax2.grid(True, alpha=0.3)

    # 二軸の作成(生産性指標)
    ax2_right = ax2.twinx()

    # 仮の生産性データ
    productivity = np.sin(np.arange(40) * 0.1) * 20 + 80
    ax2_right.plot([mdates.date2num(d) for d in dates], productivity, 
                  'k--', linewidth=2, label='生産性指標')
    ax2_right.set_ylabel('生産性指標', color='black')
    ax2_right.tick_params(axis='y', labelcolor='black')
    ax2_right.set_ylim(0, 100)
    ax2_right.legend(loc='upper right')

    # アノテーションの追加
    ax1.annotate('遅延リスク', 
                xy=(mdates.date2num(datetime(2024, 1, 22)), 2),
                xytext=(mdates.date2num(datetime(2024, 1, 25)), 2.5),
                arrowprops=dict(arrowstyle='->', color='red', lw=1.5),
                fontweight='bold', color='red')

    # レイアウト調整
    plt.tight_layout()

    # 高解像度で保存
    plt.savefig('project_visualization.png', dpi=300, bbox_inches='tight')
    plt.savefig('project_visualization.pdf', bbox_inches='tight')

    plt.show()

# 実行
create_project_visualization()

上級3: リアルタイムデータ可視化

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import random

class RealTimeSensorMonitor:
    def __init__(self):
        # データバッファの初期化
        self.buffer_size = 50
        self.time_buffer = list(range(self.buffer_size))

        # センサーデータバッファ
        self.temperature_buffer = [20.0] * self.buffer_size
        self.humidity_buffer = [50.0] * self.buffer_size
        self.pressure_buffer = [1013.0] * self.buffer_size

        # 閾値設定
        self.temp_threshold_high = 25.0
        self.temp_threshold_low = 15.0
        self.humidity_threshold = 80.0

        # アニメーションの設定
        self.fig, self.axes = plt.subplots(3, 1, figsize=(12, 10))
        self.fig.suptitle('センサーデータリアルタイムモニタリング', fontsize=16, fontweight='bold')

        # アニメーションオブジェクト
        self.animation = None

    def generate_sensor_data(self):
        """新しいセンサーデータを生成"""
        # 温度データ(ゆっくり変動する信号+ノイズ)
        last_temp = self.temperature_buffer[-1]
        new_temp = last_temp + random.uniform(-0.5, 0.5) + 0.1 * np.sin(len(self.temperature_buffer) * 0.1)
        new_temp = max(10, min(30, new_temp))  # 10-30度の範囲に制限

        # 湿度データ(異なる周波数で変動)
        last_humidity = self.humidity_buffer[-1]
        new_humidity = last_humidity + random.uniform(-2, 2) + 5 * np.cos(len(self.humidity_buffer) * 0.05)
        new_humidity = max(30, min(90, new_humidity))  # 30-90%の範囲に制限

        # 気圧データ(ランダムウォーク)
        last_pressure = self.pressure_buffer[-1]
        new_pressure = last_pressure + random.uniform(-1, 1)
        new_pressure = max(1000, min(1030, new_pressure))  # 1000-1030 hPaの範囲に制限

        return new_temp, new_humidity, new_pressure

    def update_buffers(self, temp, humidity, pressure):
        """データバッファを更新"""
        self.temperature_buffer.append(temp)
        self.humidity_buffer.append(humidity)
        self.pressure_buffer.append(pressure)

        # バッファサイズを超えた古いデータを削除
        if len(self.temperature_buffer) > self.buffer_size:
            self.temperature_buffer.pop(0)
            self.humidity_buffer.pop(0)
            self.pressure_buffer.pop(0)
            self.time_buffer.pop(0)
            self.time_buffer.append(self.time_buffer[-1] + 1)

    def get_line_color(self, value, buffer, threshold_high=None, threshold_low=None):
        """値に基づいて線の色を決定"""
        if threshold_high is not None and value > threshold_high:
            return 'red'
        elif threshold_low is not None and value < threshold_low:
            return 'blue'
        else:
            return 'green'

    def update_plot(self, frame):
        """アニメーションの更新関数"""
        # 新しいデータを生成
        new_temp, new_humidity, new_pressure = self.generate_sensor_data()
        self.update_buffers(new_temp, new_humidity, new_pressure)

        # 各サブプロットをクリア
        for ax in self.axes:
            ax.clear()

        # 1. 温度データのプロット
        temp_colors = [self.get_line_color(val, self.temperature_buffer, 
                                         self.temp_threshold_high, self.temp_threshold_low) 
                      for val in self.temperature_buffer]

        for i in range(len(self.time_buffer)-1):
            self.axes[0].plot(self.time_buffer[i:i+2], self.temperature_buffer[i:i+2], 
                             color=temp_colors[i], linewidth=2)

        self.axes[0].axhline(y=self.temp_threshold_high, color='red', linestyle='--', alpha=0.7)
        self.axes[0].axhline(y=self.temp_threshold_low, color='blue', linestyle='--', alpha=0.7)
        self.axes[0].set_title(f'温度センサー - 現在値: {new_temp:.1f}°C', fontweight='bold')
        self.axes[0].set_ylabel('温度 (°C)')
        self.axes[0].set_ylim(10, 30)
        self.axes[0].grid(True, alpha=0.3)

        # 現在値の表示
        self.axes[0].text(0.02, 0.95, f'{new_temp:.1f}°C', 
                         transform=self.axes[0].transAxes, fontsize=12, fontweight='bold',
                         bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow", alpha=0.8))

        # 2. 湿度データのプロット
        humidity_colors = [self.get_line_color(val, self.humidity_buffer, self.humidity_threshold) 
                          for val in self.humidity_buffer]

        for i in range(len(self.time_buffer)-1):
            self.axes[1].plot(self.time_buffer[i:i+2], self.humidity_buffer[i:i+2], 
                             color=humidity_colors[i], linewidth=2)

        self.axes[1].axhline(y=self.humidity_threshold, color='red', linestyle='--', alpha=0.7)
        self.axes[1].set_title(f'湿度センサー - 現在値: {new_humidity:.1f}%', fontweight='bold')
        self.axes[1].set_ylabel('湿度 (%)')
        self.axes[1].set_ylim(30, 90)
        self.axes[1].grid(True, alpha=0.3)

        # 現在値の表示
        self.axes[1].text(0.02, 0.95, f'{new_humidity:.1f}%', 
                         transform=self.axes[1].transAxes, fontsize=12, fontweight='bold',
                         bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue", alpha=0.8))

        # 3. 気圧データのプロット
        for i in range(len(self.time_buffer)-1):
            self.axes[2].plot(self.time_buffer[i:i+2], self.pressure_buffer[i:i+2], 
                             color='green', linewidth=2)

        self.axes[2].set_title(f'気圧センサー - 現在値: {new_pressure:.1f} hPa', fontweight='bold')
        self.axes[2].set_ylabel('気圧 (hPa)')
        self.axes[2].set_xlabel('時間 (秒)')
        self.axes[2].set_ylim(1000, 1030)
        self.axes[2].grid(True, alpha=0.3)

        # 現在値の表示
        self.axes[2].text(0.02, 0.95, f'{new_pressure:.1f} hPa', 
                         transform=self.axes[2].transAxes, fontsize=12, fontweight='bold',
                         bbox=dict(boxstyle="round,pad=0.3", facecolor="lightgreen", alpha=0.8))

        # レイアウト調整
        plt.tight_layout()

        return self.axes

    def start_monitoring(self, interval=500):
        """モニタリングを開始"""
        self.animation = FuncAnimation(self.fig, self.update_plot, interval=interval, blit=False)
        plt.show()

    def stop_monitoring(self):
        """モニタリングを停止"""
        if self.animation:
            self.animation.event_source.stop()

# 使用例
def main():
    monitor = RealTimeSensorMonitor()
    print("センサーモニタリングを開始します...")
    print("Ctrl+Cで終了します")

    try:
        monitor.start_monitoring(interval=200)  # 200ms間隔で更新
    except KeyboardInterrupt:
        monitor.stop_monitoring()
        print("モニタリングを終了しました")

if __name__ == "__main__":
    main()