数値に変換できないセルを調べるなら、元の文字列を残したままto_numeric(errors=”coerce”)を使い、変換後に欠測となった場所を抽出する。元からの空欄と変換失敗を分けることが大切である。NaNになった行をすぐdropnaで捨てると、入力ミスを後から直せなくなる。
最小例で確かめる
以下は説明用に作った小さなデータである。実測データや実行速度の測定結果ではない。コード全体をexample.pyとして保存すれば、入力ファイルを別途用意せずに実行できる。assertは、この例で成り立つべき形や値を確認するために入れてある。
import numpy as np
import pandas as pd
df = pd.DataFrame({"sample_id": ["A", "B", "C", "D", "E", "F"],
"raw": pd.Series([" 1.5 ", "bad", None, "", "1,200", "inf"],
dtype="string")})
clean = df["raw"].str.strip()
missing = clean.isna() | clean.eq("").fillna(False)
numeric = pd.to_numeric(clean.mask(missing), errors="coerce")
failed = ~missing & numeric.isna()
finite = np.isfinite(numeric.to_numpy(dtype=float, na_value=np.nan))
nonfinite = numeric.notna() & ~finite
df["value"] = numeric
print("conversion failures:")
print(df.loc[failed, ["sample_id", "raw"]].to_string(index=False))
print("original missing IDs:", df.loc[missing, "sample_id"].tolist())
print("nonfinite IDs:", df.loc[nonfinite, "sample_id"].tolist())
assert df.loc[failed, "sample_id"].tolist() == ["B", "E"]
assert df.loc[missing, "sample_id"].tolist() == ["C", "D"]
assert df.loc[nonfinite, "sample_id"].tolist() == ["F"]
assert df.loc[0, "value"] == 1.5
assert df.loc[4, "raw"] == "1,200"
実行結果
conversion failures:
sample_id raw
B bad
E 1,200
original missing IDs: ['C', 'D']
nonfinite IDs: ['F']
変換前の列を残して比較する
rawは受け取った文字列、cleanは前後の空白を除いた変換用の値である。元列を上書きしなければ、どの表記が問題になったかを後で確認できる。例えば1,200を数値へ読めなかった場合、桁区切りなのか、小数点にカンマを使う表記なのかを、元の情報から判断できる。
errors=”coerce”は変換に失敗した値を欠測へ置き換える。これ自体は入力が妥当だったという判定ではない。例ではbadと1,200が変換失敗となり、元から欠測だったNoneと空文字は別の集合にした。入力仕様上は空文字も不正なら、missingの定義から外して失敗一覧へ含めればよい。
失敗マスクの作り方
failedは「元から欠測ではない」かつ「数値化した結果が欠測」である。この二条件により、欠測を含む列を数値へ変換したときに、すべての欠測を入力ミスと数えることを避けられる。入力が文字列型であることも前提なので、型が混在する場合は、どの値を文字列化するか先に整理する。
エラー一覧には値だけでなくsample_idを添えている。行を並べ替えた後でも、元ファイルのどの試料を確認すればよいか分かるためである。必要なら元ファイル名や読み込み時の行番号も保持する。行番号はヘッダーや空行の扱いで変わるので、DataFrameのindexがそのままCSVの行番号とは限らない点に注意する。
変換成功と有効な数値は違う
文字列infは浮動小数点の無限大へ変換できる。そのためisnaだけでは異常値として拾えない。例では有限値を採用する測定列を想定し、np.isfiniteを使って変換失敗とは別に非有限値を列挙した。文字列の解析、有限性、測定範囲という段階を分けると、どこで問題が起きたか説明しやすい。
負の値や極端な値も、数値として読めたから正常とは限らない。温度、濃度、回数などの単位と許容範囲を確認し、業務上の条件は変換後に追加する。識別子を数値へ変換すると先頭ゼロや大きな整数の精度を失うことがあるため、計算しない列まで一括で数値化しない。
自動修正は規則を決めてから
桁区切りを扱うとき、すべてのカンマを削ればよいとは限らない。小数区切り、複数値の誤入力、単位付き文字列などの可能性がある。read_csvのthousandsやdecimalなど入力仕様に沿う方法を使うか、認める書式を限定して変換する。修正規則と件数を残せば、その変更が意図的だったと分かる。
監査後に処理を止めたいなら、failed.any()などを見て明示的に例外を出す設計にできる。一部を除外して進める場合も、失敗件数と除外したIDを報告する。errors=”coerce”とdropnaだけで完結させず、無効値がなかった場合・全件無効だった場合も含めて出力の意味を確認しておこう。
動作確認環境と参考資料
Linux・CPython 3.12.14、NumPy 2.3.5、pandas 2.2.3、SciPy 1.17.0、Matplotlib 3.10.8の環境で掲載コードを実行した。使用するライブラリはコード冒頭のimportを参照してほしい。公式資料の最新版と、この実行確認版は区別している。数値の末尾や表の表示幅は環境によって変わることがある。
