7日目のゴール
7日目のテーマは
「CSVファイル読み書きアプリの“全体像”を、自分の言葉で説明できるようになること」 です。
ここまで 6 日間であなたは、
csv.reader / csv.writer
csv.DictReader / csv.DictWriter
集計・フィルタ・ソート
エラー行のスキップ・分離
小さなヘルパー関数によるリファクタリング
を通して、
「CSVを使った実務データ処理」を一通り体験してきました。
今日はそれを「バラバラのテクニック」ではなく、
ひとつのアプリとして頭の中でつながった状態にする
ことを目指します。
アプリ全体を一文で説明してみる
どんなアプリを作ったのか
まず、この 7 日間で作ったアプリを
あえて一文で表現してみます。
「社員情報のCSVを読み込み、集計・抽出・エラー分離などをメニューから選んで実行できる、小さな実務用CSVユーティリティ」
もう少し分解すると、こうなります。
ひとつの入力CSV(employees_dirty.csv)を対象にする
起動時に一度だけCSVを読み込む
メニューから機能を選ぶ
選んだ機能が、読み込んだデータに対して処理を行う
必要に応じて、別CSVに結果を書き出す
つまり、
「CSVを中心にした、対話型のバッチツール」
のようなイメージです。
コアとなる「データの流れ」を整理する
読む → 加工する → 出す
このアプリの本質的な流れは、たったこれだけです。
CSVファイルからデータを読む
Pythonの中で加工・集計・フィルタする
必要なら、別のCSVとして書き出す
これを、もう少し具体的に追ってみます。
最初に load_employees(INPUT_FILE) が呼ばれ、
csv.DictReader で全行を辞書のリストとして読み込む。
その後は、メニューで選ばれた機能に応じて、
そのリスト(rows)を渡して処理する。
表示系の機能(一覧表示・集計)は、
rows を読みながら print するだけで完結する。
書き出し系の機能(30歳以上の抽出・エラー行の出力)は、
rows から条件に合う行だけを集め、
csv.DictWriter で別CSVに書き出す。
どの機能も、
「入力は rows(辞書のリスト)」
「出力は print か CSVファイル」
という共通の形になっています。
ヘルパー関数が担っている役割を理解する
to_int_or_none と is_valid_employee_row
6日目で導入したヘルパー関数は、
アプリの「見通し」を良くするための重要なピースです。
to_int_or_none(text) は、
空文字や変な文字列を None に変換する
数値として使えるものだけ int にして返す
という「数値変換のルール」を一箇所にまとめた関数でした。
これによって、
どの機能でも「数値変換の仕方」が統一される
仕様変更(例:空文字は 0 とみなしたい)があっても、この関数だけ直せばよい
という状態になっています。
is_valid_employee_row(row) は、
年齢が数値として解釈できるか
給与が数値として解釈できるか
部署が空欄ではないか
といった「行の妥当性チェック」を一箇所にまとめた関数でした。
これによって、
「どの行を“正常”とみなすか」が明確になる
エラー行の抽出・スキップが一貫したルールで行える
ようになっています。
この二つのヘルパーは、
「データのルールをコードの真ん中に置く」
という、とても大事な設計の一歩です。
各機能を「言葉で説明できるか」確認する
例 1: 部署ごとの給与集計
この機能を、コードを見ずに説明してみます。
rows の各行について、
まず is_valid_employee_row で「有効な行かどうか」を判定する。
有効な行だけを対象に、
部署名をキーにした辞書(stats)に
給与の合計と件数をためていく。
最後に、部署ごとに
合計と平均を計算して表示する。
ここで大事なのは、
「どの行を使うか」と「どう集計するか」が
頭の中で分かれていることです。
例 2: 30歳以上の社員を別CSVに書き出す
この機能も、言葉で追ってみます。
rows の各行について、
is_over_30(row) で「30歳以上かどうか」を判定する。
True の行だけを filtered に集める。
filtered が空でなければ、
元のヘッダー(fieldnames)を使って
csv.DictWriter で output_over30.csv に書き出す。
ここでも、
「条件判定」と「書き出し」が
きれいに分かれているのが分かると思います。
例 3: エラー行の書き出し
この機能は、こう説明できます。
rows の各行について、
is_valid_employee_row(row) が False のものだけを
error_rows に集める。
error_rows があれば、
元のヘッダーを使って
csv.DictWriter で error.csv に書き出す。
つまり、
「正常行の定義」と「エラー行の定義」が
同じ関数を基準にしている、ということです。
「どこを変えれば何が変わるか」をイメージする
仕様変更を頭の中でシミュレーションしてみる
ここからは、少し“設計者の目線”で考えてみます。
例えば、
「部署が空欄でも集計に含めたい」
という仕様変更が来たとします。
どこを直せばいいでしょうか。
答えは、is_valid_employee_row です。
部署が空欄なら False にしていた部分を、
条件から外せばいいだけです。
if department == "":
return False
Pythonこの行を削るかコメントアウトすれば、
部署が空欄でも「有効な行」とみなされるようになります。
逆に、
「給与が 0 の行はエラー扱いにしたい」
という仕様変更なら、こう足せばいいです。
if salary is not None and salary <= 0:
return False
Pythonこのように、
「仕様変更があったときに、
どの関数を触ればいいかがすぐ分かる」
という状態になっているかどうかが、
設計が整理されているかどうかの目安になります。
7日目のまとめ 今日つかんでほしい感覚
今日、そしてこの 7 日間でつかんでほしい本質は、これです。
csvモジュールは、「テキストとしてのCSV」を「Pythonのデータ」に変換してくれる橋渡し役。
DictReader / DictWriter を使うと、「列名でアクセスできる」実務的なコードになる。
実務データはきれいではないので、「変換できない値」「空欄」「エラー行」を前提に設計する。
ヘルパー関数(to_int_or_none, is_valid_employee_row など)に“ルール”を集約すると、コードが直しやすくなる。
アプリ全体を「読む → 加工する → 出す」という流れで捉えられれば、機能追加も怖くなくなる。
ここまで来たあなたは、
「csvモジュールを知っている人」ではなく、
“CSVを使った実務データ処理を、自分で設計できる人” の入り口に立っています。
この先は、
複数ファイルの突き合わせ、
日付の扱い、
クラス化やGUI化、
データベースとの連携
など、いくらでも広げていけます。
どこに進むにしても、
今日までに身につけた
「データをよく観察すること」
「ルールをコードに落とし込むこと」
「エラーを前提に設計すること」
この感覚は、ずっとあなたの武器になります。

