先頭ゼロ付きの試料IDは数値ではなく識別子として読み、欠測を含む計数値はpandasのnullable整数型Int64で読む。read_csvの型推定だけに任せると、IDのゼロが消えたり、整数列が浮動小数点になったりする。列の意味と欠測記号を読み込み時に指定するのが基本である。
最小例で確かめる
以下は説明用に作った小さなデータである。実測データや実行速度の測定結果ではない。コード全体をexample.pyとして保存すれば、入力ファイルを別途用意せずに実行できる。assertは、この例で成り立つべき形や値を確認するために入れてある。
from io import StringIO
import pandas as pd
csv_text = "sample_id,count\n001,12\n002,\nNA,0\n"
df = pd.read_csv(StringIO(csv_text),
dtype={"sample_id": "string", "count": "Int64"},
keep_default_na=False, na_values={"count": [""]})
print(df.to_string(index=False))
print("dtypes:", {k: str(v) for k, v in df.dtypes.items()})
print("missing count:", int(df["count"].isna().sum()))
print("positive IDs:", df.loc[(df["count"] > 0).fillna(False),
"sample_id"].tolist())
assert df["sample_id"].tolist() == ["001", "002", "NA"]
assert str(df["count"].dtype) == "Int64"
assert pd.isna(df.loc[1, "count"])
assert df.loc[2, "count"] == 0
assert not df["sample_id"].isna().any()
実行結果
sample_id count
001 12
002 <NA>
NA 0
dtypes: {'sample_id': 'string', 'count': 'Int64'}
missing count: 1
positive IDs: ['001']
IDの見た目を数値へ変えない
001と1が別の試料を表すなら、数値型へ変換してはいけない。先頭ゼロは表示上の飾りではなく識別子の一部だからである。読み込み後にastype(“string”)としても、既に数値1へ変わった列から元の桁数を復元することはできない。dtypeをread_csvへ渡し、最初から文字列として取り込む。
この例ではNAという文字列も有効なIDとして使っている。read_csvには標準の欠測記号があるため、単に文字列型を指定しただけでは意図しない欠測判定が起き得る。keep_default_na=Falseで標準の欠測記号を使わず、count列の空文字だけを欠測として指定した。実際のファイルに合わせ、どの列のどの表記を欠測とするかを決めよう。
Int64とint64は同じではない
大文字のIで始まるInt64はpandasのnullable整数型で、整数値とpd.NAを同じ列に保持できる。小文字のint64は通常のNumPy整数型を表し、この欠測表現をそのまま入れられない。列名や値だけでなく、df.dtypesを見て期待する型に読めたか確認するとよい。
例のcountは12、欠測、0である。欠測を0で埋めると、未測定と実際に0だった測定の区別が消える。計数値に小数が混ざった場合も、安易に丸めて整数へ押し込まず入力の意味を調べる。Int64という型を選ぶことは、「観測された値は整数である」という契約でもある。
欠測を含む比較は方針を決める
count > 0の比較では、欠測行の判定も欠測になる。例では「正の値が確認できた行だけ採用する」という方針でfillna(False)を使った。未測定行を別途確認する必要があるなら、count.isna()で取り出して一覧に残す。解析対象から外すことと、元の記録から消すことは分けて考える。
pd.NAの判定に==を使わず、isnaやnotnaを使う。NaN、None、pd.NAが混ざる入力でも、読み込んだ列の欠測状態を統一的に検査できる。合計や平均などの集約は欠測を除くことが多いので、結果には有効件数も添えると分母の違いを見落としにくい。
読み込み後の確認と受け渡し
最初に試料IDの件数、欠測、重複、期待する文字の種類を点検する。文字列型なら必ず正しいIDになるわけではなく、前後の空白や異なる表記が別IDとして残ることもある。ゼロを除く、空白を削る、英字を大文字にするなどの正規化は、元のID体系がそれを許すと分かってから行う。
CSVへ保存すると型情報自体は保存されないため、次に読む側でもdtypeと欠測規則が必要である。CSVとともに列定義を管理しておけば再現しやすい。また、ここで実行したのはpandas 2.2.3であり、最新版で文字列の自動推定が変わっても困らないよう、必要な型を明示した例としている。
動作確認環境と参考資料
Linux・CPython 3.12.14、NumPy 2.3.5、pandas 2.2.3、SciPy 1.17.0、Matplotlib 3.10.8の環境で掲載コードを実行した。使用するライブラリはコード冒頭のimportを参照してほしい。公式資料の最新版と、この実行確認版は区別している。数値の末尾や表の表示幅は環境によって変わることがある。
