Python | 1 日 120 分 × 7 日アプリ学習:CSVファイル読み書きアプリ(中級編)

Web APP Python
スポンサーリンク

3日目のゴール

3日目のテーマは
「CSVを“ただ読む・書く”から一歩進めて、“集計・ソート・レポート作成”までできるようになること」 です。

1日目で「reader / writer」、
2日目で「DictReader / DictWriter」を押さえました。

今日はそこに、

合計・平均などの集計
条件付きの集計(例:30歳以上だけ)
CSVの内容を並び替えて別ファイルに出力

といった「実務データ処理の一歩目」を乗せていきます。


今日使うサンプルCSVを決めておく

people.csv を少し“仕事寄り”にする

3日目では、少しだけ実務寄りのデータを使います。

例えば、こんな employees.csv を用意したとします。

id,name,age,department,salary
1,Taro,25,Sales,300000
2,Hanako,30,HR,320000
3,Ken,22,Sales,250000
4,Naomi,35,Engineering,400000
5,Shin,28,Engineering,380000

ここでのポイントは、
「数値(age, salary)と文字列(name, department)が混ざっている」
ということです。

実務データはだいたいこういう形なので、
これを題材にしていきます。


基本の読み込み DictReader で辞書として扱う

まずは「1行=辞書」の感覚を再確認

最初に、employees.csv を DictReader で読み込んで、
中身をそのまま表示してみます。

import csv

def show_employees_raw():
    with open("employees.csv", "r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f)

        for row in reader:
            print(row)

show_employees_raw()
Python

出力イメージはこうなります。

{'id': '1', 'name': 'Taro', 'age': '25', 'department': 'Sales', 'salary': '300000'}
{'id': '2', 'name': 'Hanako', 'age': '30', 'department': 'HR', 'salary': '320000'}
...

ここで改めて押さえたいのは、

CSVの1行が「キー: 文字列」の辞書として扱える
列名(id, name, age, department, salary)がそのままキーになっている

ということです。

この「列名でアクセスできる」状態が、
集計やソートの土台になります。


数値列をちゃんと「数値」として扱う

文字列のままだと計算できない

DictReader が返してくれる値は、
すべて「文字列」です。

例えば、row["salary"]"300000" という文字列です。

このままでは、足し算や平均ができません。

そこで、
「数値として扱いたい列は、必ず int や float に変換する」
という習慣をつけます。

import csv

def show_employees_with_cast():
    with open("employees.csv", "r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f)

        for row in reader:
            age = int(row["age"])
            salary = int(row["salary"])
            print(f"{row['name']} さんは {age} 歳、給与は {salary} 円です。")
Python

ここでの重要ポイントは、

row["age"]row["salary"] を、そのまま計算に使わない
必ず int() で整数に変換してから使う

ということです。

この一手間が、
「文字列のまま計算してバグる」事故を防いでくれます。


全社員の給与の合計と平均を出す

「合計」と「件数」を同時に数える

まずは、全社員の給与の合計と平均を出してみます。

import csv

def calc_total_and_average_salary():
    with open("employees.csv", "r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f)

        total_salary = 0
        count = 0

        for row in reader:
            salary = int(row["salary"])
            total_salary += salary
            count += 1

    if count == 0:
        print("社員データがありません。")
        return

    average_salary = total_salary / count

    print(f"社員数: {count} 人")
    print(f"給与合計: {total_salary} 円")
    print(f"給与平均: {average_salary:.1f} 円")
Python

ここで深掘りしたいポイントは三つです。

一つ目は、total_salarycount をループの外で 0 に初期化していること。
二つ目は、ループの中で「給与を足しながら、件数も1ずつ増やしている」こと。
三つ目は、ループが終わったあとで「合計 ÷ 件数」で平均を出していること。

このパターンは、
「何かの合計と平均を出したいときの基本形」
として、ぜひ体に染み込ませてほしいところです。


条件付き集計 30歳以上の平均給与を出す

if を挟むだけで「条件付き」に変わる

次は、「30歳以上の社員だけの平均給与」を出してみます。

やることは、さっきとほぼ同じです。
違うのは「if で条件を挟む」だけです。

import csv

def calc_average_salary_over_30():
    with open("employees.csv", "r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f)

        total_salary = 0
        count = 0

        for row in reader:
            age = int(row["age"])
            salary = int(row["salary"])

            if age >= 30:
                total_salary += salary
                count += 1

    if count == 0:
        print("30歳以上の社員がいません。")
        return

    average_salary = total_salary / count

    print(f"30歳以上の社員数: {count} 人")
    print(f"30歳以上の給与平均: {average_salary:.1f} 円")
Python

ここでの重要ポイントは、

「集計の対象にするかどうか」を if で決めていること
条件を満たしたときだけ、合計と件数を増やしていること

つまり、

「集計の基本形+if 条件」=条件付き集計

という構造になっています。


部署ごとの給与合計・平均を出す

「グループごとに集計する」という発想

実務でよくあるのが、
「部署ごとに集計したい」というニーズです。

例えば、
Sales / HR / Engineering ごとに
給与の合計と平均を出したい、というケースです。

ここでは、
「部署名をキーにした辞書」を使います。

import csv

def calc_salary_by_department():
    with open("employees.csv", "r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f)

        stats = {}

        for row in reader:
            dept = row["department"]
            salary = int(row["salary"])

            if dept not in stats:
                stats[dept] = {"total_salary": 0, "count": 0}

            stats[dept]["total_salary"] += salary
            stats[dept]["count"] += 1

    for dept, info in stats.items():
        total = info["total_salary"]
        count = info["count"]
        average = total / count
        print(f"[{dept}] 社員数: {count} 人, 合計: {total} 円, 平均: {average:.1f} 円")
Python

ここで深掘りしたいポイントは、かなり大事です。

stats は「部署名 → 集計情報(合計と件数)」を持つ辞書になっている。
部署ごとに「初めて出てきたとき」に、{"total_salary": 0, "count": 0} を作っている。
その後は、同じ部署が出てくるたびに「その部署の合計と件数」を更新している。

つまり、

「グループごとの集計=キー付きの辞書にためていく」

というパターンです。

この感覚は、
実務データ処理で本当に頻繁に使います。


CSVをソートして別ファイルに出力する

salary の高い順に並べ替える

次は、「給与の高い順に並べ替えたCSV」を作ってみます。

やることは、

一度全部読み込んでリストにためる
sorted で並べ替える
DictWriter で書き出す

という流れです。

import csv

def sort_employees_by_salary_desc(input_file, output_file):
    with open(input_file, "r", encoding="utf-8", newline="") as f_in:
        reader = csv.DictReader(f_in)
        rows = []

        for row in reader:
            row["age"] = int(row["age"])
            row["salary"] = int(row["salary"])
            rows.append(row)

    rows_sorted = sorted(rows, key=lambda r: r["salary"], reverse=True)

    fieldnames = ["id", "name", "age", "department", "salary"]

    with open(output_file, "w", encoding="utf-8", newline="") as f_out:
        writer = csv.DictWriter(f_out, fieldnames=fieldnames)
        writer.writeheader()

        for row in rows_sorted:
            row_to_write = {
                "id": row["id"],
                "name": row["name"],
                "age": str(row["age"]),
                "department": row["department"],
                "salary": str(row["salary"]),
            }
            writer.writerow(row_to_write)

    print(f"{output_file} に給与の高い順で書き出しました。")
Python

ここでの重要ポイントは、少し多めです。

一度 rows に全部ためてから sorted していること。
key=lambda r: r["salary"] で「salary を基準に並べ替えている」こと。
reverse=True で「降順(大きい順)」にしていること。
書き出すときに、数値を str() で文字列に戻していること。

このパターンを覚えると、
「年齢順」「名前順」「部署順」など、
いろんな並べ替えが書けるようになります。


ソートとフィルタを組み合わせる

「30歳以上を給与の高い順に並べる」

最後に、少しだけ欲張ってみます。

やりたいことは、

30歳以上の社員だけを対象にする
その中で給与の高い順に並べる
結果を別CSVに出す

というものです。

import csv

def filter_and_sort_employees(input_file, output_file):
    with open(input_file, "r", encoding="utf-8", newline="") as f_in:
        reader = csv.DictReader(f_in)
        rows = []

        for row in reader:
            age = int(row["age"])
            salary = int(row["salary"])

            if age >= 30:
                row["age"] = age
                row["salary"] = salary
                rows.append(row)

    rows_sorted = sorted(rows, key=lambda r: r["salary"], reverse=True)

    fieldnames = ["id", "name", "age", "department", "salary"]

    with open(output_file, "w", encoding="utf-8", newline="") as f_out:
        writer = csv.DictWriter(f_out, fieldnames=fieldnames)
        writer.writeheader()

        for row in rows_sorted:
            row_to_write = {
                "id": row["id"],
                "name": row["name"],
                "age": str(row["age"]),
                "department": row["department"],
                "salary": str(row["salary"]),
            }
            writer.writerow(row_to_write)

    print(f"{output_file} に 30歳以上を給与の高い順で書き出しました。")
Python

ここでの流れを、日本語だけで整理してみます。

入力CSVを DictReader で読み、1行ずつ辞書として受け取る。
年齢と給与を整数に変換する。
30歳以上なら、その行を rows に追加する。
rows を給与の高い順に sorted する。
結果を DictWriter で別CSVに書き出す。

これで、

「条件で絞り込み → 並べ替え → 別CSVに出力」

という、かなり実務的な処理が完成します。


3日目で絶対に押さえておきたいポイント

集計・ソート・レポートの“型”を体に入れる

今日の本質は、これです。

数値列は必ず intfloat に変換してから計算する。
合計と平均は「total と count をループでためて、最後に total / count」。
条件付き集計は「if 条件」を挟むだけで実現できる。
グループごとの集計は「キー付きの辞書(部署名 → 合計・件数)」にためていく。
ソートは「一度リストにためてから、sorted(key=…, reverse=…)」で並べ替える。

4日目以降は、
ここに「複数CSVの突き合わせ」「エラー行のスキップ」「ログ出力」などを足していきます。

でも、土台は今日のこれだけです。

「読む → 数値に変換 → 集計・ソート → 書き出す」

この流れが見えたら、
CSVはもう“ただのファイル”ではなく、
あなたが自由に料理できる「実務データ」になっています。

タイトルとURLをコピーしました