PythonのCSVで空欄と0を混同しない:DictReaderで欠損値を見分ける

Python

この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。Python公式ドキュメントのcsvモジュールを参照し、架空の売上データで空欄と数値0の違いを確認する手順を整理しました。

検証ステータス:📘 公式仕様確認済み・掲載コードの実行は未確認
入力例と期待結果は示していますが、掲載コードを実際にPythonで動かした結果ではありません。

PythonのCSVで空欄と0を混同しない:DictReaderで欠損値を見分ける

CSVで売上実績を受け取ったとき、「0円」と「未入力」を同じ意味にしてしまうと、集計結果が変わります。たとえば担当者が売上を確認して0円と入力した行と、まだ報告されていない行は、どちらも売上がないように見えても業務上の状態は違います。前者は確定した値であり、後者は確認が必要なデータです。

PythonでCSVを扱うときによく使うのが、標準ライブラリの csv.DictReader です。これはCSVの見出しを辞書のキーにして、各行を読み取る仕組みです。ところが、読み取った値は基本的に文字列なので、空欄をどう扱うかはプログラム側で決めなければなりません。 、元ファイルを変更しない小さなサンプルを使い、0、空欄、そして列そのものが足りない行を区別します。期待値を先に示し、1条件を変えた場合と、業務での検査方法まで確認します。

使う環境と安全な準備

対象はPython 3の標準ライブラリです。追加パッケージやクラウド契約は不要です。作業用のフォルダーで架空データを扱い、実際の売上台帳や個人情報を含むCSVを直接変更しないでください。以下の例は文字列をメモリ上で読むため、ファイルを作る必要もありません。

確認したいことは三つあります。一つ目は文字列の "0" を数値のゼロとして扱えること。二つ目は "" という空文字列を未入力として残せること。三つ目は列が不足した行を、普通の空欄と区別できることです。

CSVの入力値を最初から数値へ一括変換するのではなく、読み取ったあとに条件を確認して変換します。数値へ変換できない値が来た場合に、黙って0へ置き換えないことも重要です。

最小例:0と空欄を区別して読む

次のコードは、Python 3で実行する想定です。コピーして csv_missing_demo.py という名前で保存し、python csv_missing_demo.py で実行します。Windowsで python が使えない環境では、導入済みのPython起動方法を確認してください。

import csv
import io

sample = """item,amount
A,0
B,
C,150
D
"""

reader = csv.DictReader(io.StringIO(sample))
for row in reader:
    raw = row["amount"]
    if raw is None:
        status = "列不足"
    elif raw == "":
        status = "未入力"
    else:
        try:
            amount = int(raw)
        except ValueError:
            status = "数値不正"
        else:
            status = f"確定値 {amount}"
    print(row["item"], status)

期待する表示は次のとおりです。これは掲載コードの想定出力であり、実行済みのログではありません。

A 確定値 0
B 未入力
C 確定値 150
D 列不足

A行の0は、ゼロという数値が確定した状態です。B行はカンマの後ろに何もないので、amount の値は空文字列です。D行はカンマも値もないため、見出しで指定された列数より項目が少なくなります。Python公式仕様では、DictReader は不足したフィールドを restval で埋め、既定値は None です。

ここで大切なのは、空文字列と None を最初から同じものにしないことです。業務によってはどちらも「確認待ち」に分類できますが、原因を調べるときには、入力者が値を空欄にしたのか、出力システムが列を欠落させたのかを区別したほうが役立ちます。

なぜ「if not raw」では危ないのか

Pythonでは空文字列は偽と判定されます。そこで if not raw と書くと、空文字列と None をまとめて検出できます。しかし、今回のように原因別の記録を残したい場合には情報が失われます。

一方、文字列の "0" は空ではないので真と判定されます。したがって、DictReader で読み取った直後の "0" と空文字列を区別すること自体は可能です。問題になるのは、途中で int(raw or 0) のような式に変えてしまうことです。この式は空欄を勝手に数値0へ変換してしまい、未入力だった事実を消します。

数値0は「計測して0だった」、未入力は「まだ分からない」という意味を持ちます。平均値を出す場合も、未入力を0にして分母へ含めるか、欠損として除外するかで結果が違います。どちらが正しいかは業務ルールによりますが、読み取り段階で区別を失うと、後から判断し直せません。

1条件を変える:数値ではない文字が混ざったら

CSVを手作業で編集すると、amount 列に 未確定 や - と入力される場合があります。上のサンプルで C,150 を C,未確定 に変えてみてください。期待する結果は、C行だけが「数値不正」と表示されることです。

このとき、int(raw) は整数へ変換できないため ValueError を発生させます。コードではその例外を捕まえ、数値不正という状態を付けています。数値不正を自動的にゼロへ変えないので、確認すべき行を見つけられます。

ただし、今回のコードは整数専用です。小数の金額やマイナス値を許すか、カンマを含む金額をどう扱うかは別途決める必要があります。通貨の計算では二進浮動小数点の誤差も考慮し、必要なら decimal.Decimal などを検討してください。用途を決めずにすべての入力を受け入れることは、データの正しさを保証しません。

値の状態が変わる場所を図で確認

次の図はCSVの1セルが、どの判定を通って業務上の状態に分かれるかを示しています。Python内部の実装を描いたものではなく、この記事のサンプルコードの判断順序を図にしたものです。

flowchart TD
    A["CSVのamount列"] --> B{"値がNone?"}
    B -->|"はい"| C["列不足"]
    B -->|"いいえ"| D{"空文字列?"}
    D -->|"はい"| E["未入力"]
    D -->|"いいえ"| F{"整数へ変換できる?"}
    F -->|"はい"| G["確定値 0など"]
    F -->|"いいえ"| H["数値不正"]

この順序なら、列不足、空欄、数値不正を混ぜずに扱えます。図の最後の「確定値」は、整数へ変換できたという意味に限ります。売上が業務上正しいか、取引先から承認されたかまでは、このコードでは判定していません。

想定どおりにならないときの切り分け

最初に、CSVの見出しが item,amount になっているかを確認してください。見出しに余分な空白があると、row["amount"] で KeyError になることがあります。列名を表示して確かめるなら、reader.fieldnames を使えます。見出しを勝手に修正する前に、入力元の仕様と照合します。

次に、区切り文字がカンマかどうかを確認します。タブ区切りやセミコロン区切りのファイルをカンマ区切りとして読むと、列が正しく分かれません。今回の例はカンマ区切り専用です。区切り文字を変更する場合は、csv.DictReader(..., delimiter=";") のように明示し、期待する列数を確認します。

余分な列がある場合にも注意が必要です。公式仕様では、見出しより多いフィールドは restkey で指定したキーにまとめられ、既定では None キーに格納されます。業務データでは「必要な列が読めたから正常」とせず、余分な列の有無も受入検査に含めると安全です。

ファイルを読む実務コードでは、文字コードと改行にも注意してください。Python公式のcsvドキュメントは、ファイルを開く際に newline="" を指定する例を示しています。文字コードは相手の仕様に合わせて encoding="utf-8" などを指定し、実ファイルの文字化けやBOMの有無は別途確認します。

業務に組み込むときの判断

毎月の売上CSVを取り込む業務では、単に合計金額を出す前に、未入力件数と列不足件数を報告するだけでも効果があります。たとえば未入力が3行なら入力担当者へ確認し、列不足が突然増えたなら出力元システムの仕様変更を疑う、といった切り分けができます。

実際の処理では、行番号、項目名、判定結果を監査用に記録し、元CSVを保管する設計が考えられます。ただし、実際の氏名や取引先情報をログに丸ごと出すと、別の情報漏えいリスクになります。検査ログには必要最小限の識別子とエラー種別だけを残してください。

さらに、合計に含めるのは「確定値」の行だけと決めるか、欠損が1件でもあれば集計を停止するかを業務責任者と決めておきます。プログラムが勝手にゼロ補完するより、業務上の判断を明示するほうが、後から集計の根拠を説明できます。

まとめ

CSVの0、空欄、列不足は似ていても同じ情報ではありません。DictReader で読み取った直後の値を確認し、None、空文字列、数値変換成功を分ければ、データが欠けた理由を追いやすくなります。業務データでは「数値として読めたか」と「正しい値か」を別々に検査することが重要です。

公式資料

文書情報

記事タイトル
PythonのCSVで空欄と0を混同しない:DictReaderで欠損値を見分ける
作成日
更新日
Source URL
https://papanda925.com/?p=18612

ライセンス: 本記事のうち、当サイトが権利を有する本文・自作図表は、特記なき限り CC BY 4.0 で利用できます。生成AIを活用して作成・編集した内容を含みます。コードについて、別途ライセンス表示またはリンク先GitHubリポジトリのライセンスがある場合は、その条件を優先します。引用・第三者資料・画像・商標等は本ライセンスの対象外です。 利用ポリシー

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