メインコンテンツへスキップ
Lesson6 / 6

演習:辞書の総合問題

目次

1. このレッスンで学ぶこと

  • 辞書を使った実践的な演習
  • データの集計と分析
  • 複合的な問題の解決
  • 実務に近いコーディング

2. なぜ演習が必要か?

辞書の操作を理解しただけでは、実際の問題解決には不十分です。

Python
# 理論だけでは不十分
理論 = "辞書はキーと値のペア"

# 実践で初めて理解が深まる
実践 = "データを効率的に管理・分析できる"

演習を通じて、辞書の実践力を身につけましょう。

💡 豆知識: 実務では、辞書はJSON APIのレスポンス処理、データベースレコードの表現、設定管理など、あらゆる場面で使われます。辞書を使いこなせることは、Pythonプログラマーにとって必須のスキルです。


3. 演習1: 在庫管理システム

問題

以下の商品在庫データを管理するシステムを作成してください。

Python
# 商品在庫データ
inventory = {
    "apple": {"price": 100, "stock": 50},
    "banana": {"price": 80, "stock": 30},
    "orange": {"price": 120, "stock": 20}
}

# 実装する機能:
# 1. 商品を追加する関数
# 2. 在庫を更新する関数
# 3. 商品を削除する関数
# 4. 在庫切れ商品を確認する関数
# 5. 在庫総額を計算する関数
💡 ヒント
  • 関数を使って機能を分ける
  • 存在確認をしてからアクセス
  • ループで全商品を処理
✅ 解答例
Python
inventory = {
    "apple": {"price": 100, "stock": 50},
    "banana": {"price": 80, "stock": 30},
    "orange": {"price": 120, "stock": 20}
}

# 機能1: 商品を追加
def add_product(name, price, stock):
    if name in inventory:
        print(f"{name}は既に存在します")
    else:
        inventory[name] = {"price": price, "stock": stock}
        print(f"{name}を追加しました")

# 機能2: 在庫を更新
def update_stock(name, quantity):
    if name in inventory:
        inventory[name]["stock"] += quantity
        new_stock = inventory[name]["stock"]
        print(f"{name}の在庫を更新: {new_stock}個")
    else:
        print(f"{name}が見つかりません")

# 機能3: 商品を削除
def remove_product(name):
    if name in inventory:
        removed = inventory.pop(name)
        print(f"{name}を削除しました(価格: {removed['price']}円)")
    else:
        print(f"{name}が見つかりません")

# 機能4: 在庫切れ確認
def check_out_of_stock():
    out_of_stock = []
    for name, info in inventory.items():
        if info["stock"] == 0:
            out_of_stock.append(name)

    if out_of_stock:
        print(f"在庫切れ: {', '.join(out_of_stock)}")
    else:
        print("在庫切れ商品なし")
    return out_of_stock

# 機能5: 在庫総額を計算
def calculate_total_value():
    total = 0
    for name, info in inventory.items():
        value = info["price"] * info["stock"]
        total += value
        print(f"{name}: {info['price']}円 × {info['stock']}個 = {value:,}円")
    return total

# テスト実行
print("=== 初期在庫 ===")
for name, info in inventory.items():
    print(f"{name}: {info['price']}円, 在庫{info['stock']}個")

print("\n=== 商品追加 ===")
add_product("grape", 200, 15)

print("\n=== 在庫更新 ===")
update_stock("apple", -10)
update_stock("banana", 20)

print("\n=== 在庫総額 ===")
total = calculate_total_value()
print(f"総額: {total:,}円")

print("\n=== 在庫切れ確認 ===")
check_out_of_stock()

print("\n=== 商品削除 ===")
remove_product("orange")

print("\n=== 最終在庫 ===")
for name, info in inventory.items():
    print(f"{name}: {info['price']}円, 在庫{info['stock']}個")

実行結果:

=== 初期在庫 ===
apple: 100円, 在庫50個
banana: 80円, 在庫30個
orange: 120円, 在庫20個

=== 商品追加 ===
grapeを追加しました

=== 在庫更新 ===
appleの在庫を更新: 40個
bananaの在庫を更新: 50個

=== 在庫総額 ===
apple: 100円 × 40個 = 4,000円
banana: 80円 × 50個 = 4,000円
orange: 120円 × 20個 = 2,400円
grape: 200円 × 15個 = 3,000円
総額: 13,400円

=== 在庫切れ確認 ===
在庫切れ商品なし

=== 商品削除 ===
orangeを削除しました(価格: 120円)

=== 最終在庫 ===
apple: 100円, 在庫40個
banana: 80円, 在庫50個
grape: 200円, 在庫15個

4. 演習2: 成績管理システム

問題

生徒の成績データを管理し、以下の分析を行ってください。

Python
# 生徒の成績データ
students = {
    "太郎": {"math": 85, "english": 92, "science": 78},
    "花子": {"math": 95, "english": 88, "science": 90},
    "次郎": {"math": 72, "english": 85, "science": 80},
    "三郎": {"math": 88, "english": 78, "science": 85}
}

# 実装する機能:
# 1. 各生徒の平均点を計算
# 2. 科目別の平均点を計算
# 3. 最高点と最低点を持つ生徒を見つける
# 4. 全科目80点以上の生徒を抽出
✅ 解答例
Python
students = {
    "太郎": {"math": 85, "english": 92, "science": 78},
    "花子": {"math": 95, "english": 88, "science": 90},
    "次郎": {"math": 72, "english": 85, "science": 80},
    "三郎": {"math": 88, "english": 78, "science": 85}
}

# 機能1: 各生徒の平均点
print("=== 生徒別平均点 ===")
student_averages = {}
for name, scores in students.items():
    total = sum(scores.values())
    average = total / len(scores)
    student_averages[name] = average
    print(f"{name}: {average:.2f}点")

# 機能2: 科目別平均点
print("\n=== 科目別平均点 ===")
subjects = ["math", "english", "science"]
subject_averages = {}

for subject in subjects:
    total = 0
    count = 0
    for scores in students.values():
        total += scores[subject]
        count += 1
    average = total / count
    subject_averages[subject] = average
    print(f"{subject}: {average:.2f}点")

# 機能3: 最高点・最低点の生徒
print("\n=== 最高点・最低点 ===")
max_student = max(student_averages.items(), key=lambda x: x[1])
min_student = min(student_averages.items(), key=lambda x: x[1])
print(f"最高平均: {max_student[0]} ({max_student[1]:.2f}点)")
print(f"最低平均: {min_student[0]} ({min_student[1]:.2f}点)")

# 機能4: 全科目80点以上
print("\n=== 全科目80点以上の生徒 ===")
excellent_students = []
for name, scores in students.items():
    if all(score >= 80 for score in scores.values()):
        excellent_students.append(name)
        print(f"{name}: {scores}")

if not excellent_students:
    print("該当者なし")

# 追加分析: 科目別最高得点者
print("\n=== 科目別最高得点者 ===")
for subject in subjects:
    max_score = 0
    top_student = ""
    for name, scores in students.items():
        if scores[subject] > max_score:
            max_score = scores[subject]
            top_student = name
    print(f"{subject}: {top_student} ({max_score}点)")

# 全体統計
all_scores = []
for scores in students.values():
    all_scores.extend(scores.values())

print(f"\n=== 全体統計 ===")
print(f"総合平均: {sum(all_scores) / len(all_scores):.2f}点")
print(f"最高点: {max(all_scores)}点")
print(f"最低点: {min(all_scores)}点")

実行結果:

=== 生徒別平均点 ===
太郎: 85.00点
花子: 91.00点
次郎: 79.00点
三郎: 83.67点

=== 科目別平均点 ===
math: 85.00点
english: 85.75点
science: 83.25点

=== 最高点・最低点 ===
最高平均: 花子 (91.00点)
最低平均: 次郎 (79.00点)

=== 全科目80点以上の生徒 ===
花子: {'math': 95, 'english': 88, 'science': 90}

=== 科目別最高得点者 ===
math: 花子 (95点)
english: 太郎 (92点)
science: 花子 (90点)

=== 全体統計 ===
総合平均: 84.67点
最高点: 95点
最低点: 72点

5. 演習3: アクセスログ分析

問題

Webサイトのアクセスログを分析してください。

Python
# アクセスログデータ
logs = [
    {"user": "user001", "page": "/home", "time": "10:00"},
    {"user": "user002", "page": "/about", "time": "10:05"},
    {"user": "user001", "page": "/products", "time": "10:10"},
    {"user": "user003", "page": "/home", "time": "10:15"},
    {"user": "user001", "page": "/contact", "time": "10:20"},
    {"user": "user002", "page": "/home", "time": "10:25"}
]

# 分析内容:
# 1. ユーザー別アクセス回数
# 2. ページ別アクセス回数
# 3. 最もアクティブなユーザー
# 4. 最も人気のあるページ
✅ 解答例
Python
logs = [
    {"user": "user001", "page": "/home", "time": "10:00"},
    {"user": "user002", "page": "/about", "time": "10:05"},
    {"user": "user001", "page": "/products", "time": "10:10"},
    {"user": "user003", "page": "/home", "time": "10:15"},
    {"user": "user001", "page": "/contact", "time": "10:20"},
    {"user": "user002", "page": "/home", "time": "10:25"}
]

# 分析1: ユーザー別アクセス回数
user_access = {}
for log in logs:
    user = log["user"]
    user_access[user] = user_access.get(user, 0) + 1

print("=== ユーザー別アクセス回数 ===")
for user, count in sorted(user_access.items()):
    print(f"{user}: {count}回")

# 分析2: ページ別アクセス回数
page_access = {}
for log in logs:
    page = log["page"]
    page_access[page] = page_access.get(page, 0) + 1

print("\n=== ページ別アクセス回数 ===")
for page, count in sorted(page_access.items(), key=lambda x: x[1], reverse=True):
    print(f"{page}: {count}回")

# 分析3: 最もアクティブなユーザー
most_active_user = max(user_access.items(), key=lambda x: x[1])
print(f"\n=== 最もアクティブなユーザー ===")
print(f"{most_active_user[0]}: {most_active_user[1]}回")

# 分析4: 最も人気のあるページ
most_popular_page = max(page_access.items(), key=lambda x: x[1])
print(f"\n=== 最も人気のあるページ ===")
print(f"{most_popular_page[0]}: {most_popular_page[1]}回")

# 追加分析: ユーザーの行動パターン
print("\n=== ユーザーの行動パターン ===")
user_pages = {}
for log in logs:
    user = log["user"]
    page = log["page"]
    if user not in user_pages:
        user_pages[user] = []
    user_pages[user].append(page)

for user, pages in user_pages.items():
    print(f"{user}: {' → '.join(pages)}")

# ユニークページ数
print("\n=== ユーザー別ユニークページ数 ===")
for user, pages in user_pages.items():
    unique_pages = len(set(pages))
    print(f"{user}: {unique_pages}ページ")

実行結果:

=== ユーザー別アクセス回数 ===
user001: 3回
user002: 2回
user003: 1回

=== ページ別アクセス回数 ===
/home: 3回
/about: 1回
/products: 1回
/contact: 1回

=== 最もアクティブなユーザー ===
user001: 3回

=== 最も人気のあるページ ===
/home: 3回

=== ユーザーの行動パターン ===
user001: /home → /products → /contact
user002: /about → /home
user003: /home

=== ユーザー別ユニークページ数 ===
user001: 3ページ
user002: 2ページ
user003: 1ページ

6. 演習4: データのマージと変換

問題

複数のデータソースから情報を統合してください。

Python
# ユーザー基本情報
users = {
    "user001": {"name": "太郎", "age": 25},
    "user002": {"name": "花子", "age": 30},
    "user003": {"name": "次郎", "age": 28}
}

# 購入履歴
purchases = [
    {"user_id": "user001", "product": "book", "price": 1500},
    {"user_id": "user001", "product": "pen", "price": 300},
    {"user_id": "user002", "product": "book", "price": 1500},
    {"user_id": "user003", "product": "notebook", "price": 500},
    {"user_id": "user001", "product": "eraser", "price": 100}
]

# 実装:
# 1. ユーザーごとの購入総額を計算
# 2. ユーザー情報と購入情報を統合
# 3. 商品別の売上を集計
✅ 解答例
Python
users = {
    "user001": {"name": "太郎", "age": 25},
    "user002": {"name": "花子", "age": 30},
    "user003": {"name": "次郎", "age": 28}
}

purchases = [
    {"user_id": "user001", "product": "book", "price": 1500},
    {"user_id": "user001", "product": "pen", "price": 300},
    {"user_id": "user002", "product": "book", "price": 1500},
    {"user_id": "user003", "product": "notebook", "price": 500},
    {"user_id": "user001", "product": "eraser", "price": 100}
]

# 実装1: ユーザーごとの購入総額
user_totals = {}
for purchase in purchases:
    user_id = purchase["user_id"]
    price = purchase["price"]
    user_totals[user_id] = user_totals.get(user_id, 0) + price

print("=== ユーザー別購入総額 ===")
for user_id, total in user_totals.items():
    name = users[user_id]["name"]
    print(f"{name} ({user_id}): {total:,}円")

# 実装2: 情報の統合
user_profiles = {}
for user_id, info in users.items():
    profile = info.copy()
    profile["user_id"] = user_id
    profile["total_spent"] = user_totals.get(user_id, 0)
    profile["purchase_count"] = 0
    profile["products"] = []
    user_profiles[user_id] = profile

for purchase in purchases:
    user_id = purchase["user_id"]
    user_profiles[user_id]["purchase_count"] += 1
    user_profiles[user_id]["products"].append(purchase["product"])

print("\n=== 統合ユーザープロフィール ===")
for user_id, profile in user_profiles.items():
    print(f"\n{profile['name']} ({user_id}):")
    print(f"  年齢: {profile['age']}歳")
    print(f"  購入回数: {profile['purchase_count']}回")
    print(f"  購入総額: {profile['total_spent']:,}円")
    print(f"  購入商品: {', '.join(profile['products'])}")

# 実装3: 商品別売上
product_sales = {}
for purchase in purchases:
    product = purchase["product"]
    price = purchase["price"]
    if product not in product_sales:
        product_sales[product] = {"count": 0, "total": 0}
    product_sales[product]["count"] += 1
    product_sales[product]["total"] += price

print("\n=== 商品別売上 ===")
for product, data in sorted(product_sales.items(), key=lambda x: x[1]["total"], reverse=True):
    print(f"{product}: {data['count']}個, {data['total']:,}円")

# 最高額購入者
top_buyer = max(user_totals.items(), key=lambda x: x[1])
print(f"\n=== 最高額購入者 ===")
print(f"{users[top_buyer[0]]['name']}: {top_buyer[1]:,}円")

実行結果:

=== ユーザー別購入総額 ===
太郎 (user001): 1,900円
花子 (user002): 1,500円
次郎 (user003): 500円

=== 統合ユーザープロフィール ===

太郎 (user001):
  年齢: 25歳
  購入回数: 3回
  購入総額: 1,900円
  購入商品: book, pen, eraser

花子 (user002):
  年齢: 30歳
  購入回数: 1回
  購入総額: 1,500円
  購入商品: book

次郎 (user003):
  年齢: 28歳
  購入回数: 1回
  購入総額: 500円
  購入商品: notebook

=== 商品別売上 ===
book: 2個, 3,000円
pen: 1個, 300円
notebook: 1個, 500円
eraser: 1個, 100円

=== 最高額購入者 ===
太郎: 1,900円

7. 演習5: 設定管理システム

問題

階層的な設定データを管理するシステムを作成してください。

Python
# デフォルト設定
default_config = {
    "app": {
        "name": "MyApp",
        "version": "1.0.0",
        "debug": False
    },
    "database": {
        "host": "localhost",
        "port": 5432,
        "name": "mydb"
    },
    "cache": {
        "enabled": True,
        "ttl": 3600
    }
}

# ユーザー設定
user_config = {
    "app": {
        "debug": True
    },
    "database": {
        "host": "prod-server"
    }
}

# 実装:
# 1. 設定をマージする関数(ユーザー設定を優先)
# 2. ドット記法で設定にアクセスする関数(例: "app.debug")
# 3. 設定を検証する関数
✅ 解答例
Python
import copy

default_config = {
    "app": {
        "name": "MyApp",
        "version": "1.0.0",
        "debug": False
    },
    "database": {
        "host": "localhost",
        "port": 5432,
        "name": "mydb"
    },
    "cache": {
        "enabled": True,
        "ttl": 3600
    }
}

user_config = {
    "app": {
        "debug": True
    },
    "database": {
        "host": "prod-server"
    }
}

# 機能1: 設定のマージ
def merge_config(default, user):
    result = copy.deepcopy(default)
    for key, value in user.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = merge_config(result[key], value)
        else:
            result[key] = value
    return result

# 機能2: ドット記法でアクセス
def get_config_value(config, path):
    keys = path.split(".")
    value = config
    for key in keys:
        if isinstance(value, dict) and key in value:
            value = value[key]
        else:
            return None
    return value

# 機能3: 設定の検証
def validate_config(config):
    errors = []

    # 必須項目のチェック
    required = [
        ("app.name", str),
        ("app.version", str),
        ("database.host", str),
        ("database.port", int),
        ("database.name", str)
    ]

    for path, expected_type in required:
        value = get_config_value(config, path)
        if value is None:
            errors.append(f"必須項目が不足: {path}")
        elif not isinstance(value, expected_type):
            errors.append(f"型エラー: {path}{expected_type.__name__} 型である必要があります")

    return errors

# 実行
final_config = merge_config(default_config, user_config)

print("=== デフォルト設定 ===")
for section, values in default_config.items():
    print(f"{section}: {values}")

print("\n=== ユーザー設定 ===")
for section, values in user_config.items():
    print(f"{section}: {values}")

print("\n=== マージ後の設定 ===")
for section, values in final_config.items():
    print(f"{section}: {values}")

print("\n=== ドット記法でアクセス ===")
print(f"app.name: {get_config_value(final_config, 'app.name')}")
print(f"app.debug: {get_config_value(final_config, 'app.debug')}")
print(f"database.host: {get_config_value(final_config, 'database.host')}")
print(f"database.port: {get_config_value(final_config, 'database.port')}")
print(f"cache.ttl: {get_config_value(final_config, 'cache.ttl')}")

print("\n=== 設定の検証 ===")
errors = validate_config(final_config)
if errors:
    print("エラーが見つかりました:")
    for error in errors:
        print(f"  - {error}")
else:
    print("設定は正常です")

実行結果:

=== デフォルト設定 ===
app: {'name': 'MyApp', 'version': '1.0.0', 'debug': False}
database: {'host': 'localhost', 'port': 5432, 'name': 'mydb'}
cache: {'enabled': True, 'ttl': 3600}

=== ユーザー設定 ===
app: {'debug': True}
database: {'host': 'prod-server'}

=== マージ後の設定 ===
app: {'name': 'MyApp', 'version': '1.0.0', 'debug': True}
database: {'host': 'prod-server', 'port': 5432, 'name': 'mydb'}
cache: {'enabled': True, 'ttl': 3600}

=== ドット記法でアクセス ===
app.name: MyApp
app.debug: True
database.host: prod-server
database.port: 5432
cache.ttl: 3600

=== 設定の検証 ===
設定は正常です

8. まとめ

このレッスンでは、辞書の基本操作を演習で統合して確認しました。

  • 参照、更新、削除、メソッド、ループを組み合わせて実装できました。
  • キーと値の扱いを明確にして処理を設計する力を確認しました。
  • 要件に応じて辞書操作を選択する判断力を強化できました。
  • 中間出力でデータ状態を確認するデバッグ手順を実践しました。
  • 実務に近いデータ管理処理へ応用できる基礎を固めました。