単元 7:欠損値とデータ型
isna()/notna()で欠損値の有無を確認できるfillna()で欠損値を補完できる(固定値・平均・前方埋め)dropna()で欠損値を含む行・列を除去できるastype()で列のデータ型を変換できる
欠損値は「データの正体不明」を表す
Section titled “欠損値は「データの正体不明」を表す”現実のデータには 「値が存在しない」「記録が取れなかった」「未回答」 といった状態が必ず出てきます。Excel ならセルが空欄、CSV なら値がない、アンケートなら「未回答」と書かれている。
pandas はこれを NaN(Not a Number) という特別な値で表します。NaN は単なる「空」ではなく、「あるべき値が無い」状態のマーカー です。集計関数(mean, sum など)はデフォルトで NaN を無視して計算してくれますが、そのままで意味のある結果になるとは限りません。欠損が多すぎる列をそのまま平均すると、サンプル数が見かけより少ない平均になり、ノイズが乗ります。
欠損とどう向き合うか(無視する/補完する/落とす)は、分析の質を左右する大事な判断です。
欠損値を検出する
Section titled “欠損値を検出する”まず欠損がどこに、どれくらいあるかを把握します:
df.isna() # 全セルに True/False を返す DataFramedf.isna().sum() # 列ごとの欠損数df.isna().any() # 列ごとに「1 つでも欠損があるか」df.notna() # isna の逆実務では df.isna().sum() を読み込み直後に必ず一回呼ぶのが定石です。「どの列がどれだけ欠けているか」が分かれば、次の対処法が決まります。
欠損値の補完戦略
Section titled “欠損値の補完戦略”欠損を 埋める か 落とす かは状況次第。代表的な補完戦略:
固定値で埋める
Section titled “固定値で埋める”df["備考"] = df["備考"].fillna("なし")df["年齢"] = df["年齢"].fillna(0)文字列列なら「なし」「未回答」、数値列なら 0 や中央値で埋めるのが一般的。
統計量で埋める
Section titled “統計量で埋める”df["年齢"] = df["年齢"].fillna(df["年齢"].mean()) # 平均df["年齢"] = df["年齢"].fillna(df["年齢"].median()) # 中央値平均は外れ値の影響を受けるので、分布が歪んでいるときは 中央値の方が安全 です。
前後の値で埋める(時系列向け)
Section titled “前後の値で埋める(時系列向け)”df.fillna(method="ffill") # forward fill:前の有効値で埋めるdf.fillna(method="bfill") # backward fill:後ろの有効値で埋める時系列データで「観測が一時的に欠けた」場合、直前の値を引き継ぐ前方埋めが自然なことが多いです。
欠損値の行・列を落とす
Section titled “欠損値の行・列を落とす”「欠損があったら捨てる」も立派な戦略です:
df.dropna() # 1 つでも欠損がある行を全部落とすdf.dropna(subset=["年齢", "収入"]) # 指定列のどれかが欠損なら落とすdf.dropna(axis=1) # 列を落とす(普通は使わない)df.dropna(thresh=3) # 有効値が 3 個未満の行を落とすただし、dropna() を雑に使うと データの大半が消えてしまう ことがあります(特に多列で欠損がバラけている場合)。先に isna().sum() で全体像を見てから判断するのが安全です。
データ型を変換する
Section titled “データ型を変換する”数値であるべき列が文字列として読み込まれることがあります(例:「1,000」のカンマ付き数字、ゼロ埋め郵便番号)。こうした列に集計を当てる前に、型を直す 必要があります。
df["年齢"] = df["年齢"].astype(int) # 整数にdf["価格"] = df["価格"].astype(float) # 浮動小数点にdf["郵便番号"] = df["郵便番号"].astype(str) # 文字列に(ゼロ埋め保持)文字列から数値への変換でエラーが出る場合(カンマや単位記号が混じっている)、str.replace で先に整形してから astype する流れになります:
df["価格"] = df["価格"].str.replace(",", "").astype(int)より柔軟な変換には pd.to_numeric が便利。エラー時の挙動を指定できます:
df["価格"] = pd.to_numeric(df["価格"], errors="coerce")# errors="coerce" は変換できない値を NaN にする欠損値と型のセットで考える
Section titled “欠損値と型のセットで考える”欠損値と型は 密接に関係 しています。
NaNは浮動小数点(float)の特殊値なので、NaNを含む整数列は 暗黙のうちに float になります(int64→float64)- 欠損を補完してから
astype(int)で整数に戻す、というセットで進めることが多い - pandas 1.x 以降は
Int64(大文字)型で「欠損を許容する整数」も使えるが、慣れるまでは float 経由が簡単
よく出る躓きどころ
Section titled “よく出る躓きどころ”- 欠損を確認せずに
mean()を呼ぶ — 結果が「サンプル数の少ない平均」になるが見た目には分からない fillna(0)を機械的に使う — 0 が「存在する値」と混同される(年齢 0 は 0 歳と区別がつかない)。0 が意味を持つ列では避けるdropna()で大量の行が消える — 必ずisna().sum()で確認してからastype(int)でNaNを含む列が変換できない —intはNaNを表せないため、先に補完するかInt64を使う
サンプルコード
Section titled “サンプルコード”- 題材データ:users.csv をダウンロード(「備考」列に「あり/なし」、空欄含む)
users.csv を題材に、次を順に行いなさい。
df.isna().sum()で各列の欠損数を表示する- 「備考」列の欠損値を「なし」で埋める(
fillna) - 「年齢」列を
intからfloatに変換し、その後またintに戻して挙動を確認する - 欠損のある行を
dropna()で落とし、行数の変化を確認する
最後の dropna() を呼ぶ前と後で df.shape を比較すると、「欠損で行が大量に消える」場面のイメージがつかめます。
発展課題(オプション)
Section titled “発展課題(オプション)”- 数値列に対して、欠損を中央値で補完するコードを書く
pd.to_numeric(..., errors="coerce")を使って、文字列が混入した「数値であるはずの列」を整えるInt64型(大文字)を試して、欠損を許容する整数列を作る