pandasのfillnaが反映されない原因と対処法を試した話

事務職として働く中でPythonを始めたのは、Excelの限界を感じたからでした。自動化はそれなりに順調で、少しずつできることが増えてきた頃のことです。ある日、どう考えても意味不明な現象にぶつかりました。

売上管理用のデータフレームを扱っていたときです。欠損値の「NaN」を消すためにfillnaを書いたのに、次の集計でまた「NaN」が出てきます。何度実行し直しても変わらず、30分以上を浪費した末に、隣の上司からの視線まで気になり始めました。

後から調べて分かったのですが、fillnaが反映されない場合はpandasの仕組みに対する誤解が原因でした。プログラミングの理屈を知れば納得できるのですが、現場で急いでいるときは本当に恐ろしい落とし穴です。この記事では自分が経験した3つの失敗パターンと、今実践している対処法を共有します。

NaN消えず!df.fillna(0)の落とし穴

まず私が最初に直面した絶望的な状況を詳しく振り返ります。当時の私は数千行に及ぶ複雑なCSVファイルを読み込みました。取引先から送られてきたデータです。金額が入っていないセルを「0」として処理しようとしました。そこで「df.fillna(0)」と意気揚々と入力します。入門書で最初に出てくるような標準的なコードです。実行ボタンを押してもエラーは出ませんでした。これで完璧に処理が終わったと思い込みます。そして次の集計ステップへ進んだのです。ところがそこで待っていたのは予想もしない結果でした。

CSVファイルは5000行ありました。NaNが含まれる列の数は12列に及びます。

何度やっても消えないNaN

集計結果の合計値は数百万円単位の数値になるはずでした。しかし画面には無情にも「NaN」とだけ表示されます。不思議に思ってデータの先頭を表示させてみました。先ほど消したはずの空欄が平然と並んでいたのです。私はパニックになり、もう一度同じコードを実行します。しかし何度やってもデータは1ミリも書き換わりません。パソコンのメモリが足りないのかと疑いました。ファイル自体が壊れているのではないかと本気で心配したのです。

fillnaのコードを10回以上繰り返し実行しました。それでもデータフレームにはNaNが残り続けていたのです。自分の目が信じられず画面を指でなぞって確認します。やはり空欄のままでした。

fillna:戻り値による非破壊操作

なぜfillnaを書いただけではデータが書き換わらないのでしょうか。その理由はpandasが持つ「戻り値」の設計思想にあります。fillnaという関数は元のデータを直接書き換えません。NaNを埋めた新しいデータを作成します。そしてそれを外に放り出すのがデフォルトの動きなのです。つまり元のデータフレームは綺麗なままで残されています。私たちは新しく生成された「修正済みのデータ」を保存していません。そのままゴミ箱へ捨ててしまっていたことになります。

日本語インターフェースのJupyter Notebook画面。fillna実行直

非破壊操作の誤解と真価

この仕組みを理解していないと空振りに終わります。いくら実行を繰り返しても意味がありません。プログラミングの世界では「非破壊的な操作」と呼ぶそうです。元のデータをうっかり壊さないように保護する親切な機能です。しかし当時の私には大きな壁となりました。「命令したのに無視された」と感じたのです。しかしこの仕様には明確なメリットもあります。元のデータが残っていれば失敗しても何度でもやり直せるからです。実務で結果を確定させたい場合は注意が必要です。「放り出された結果」をキャッチする記述が不可欠になります。

ここで一度立ち止まって考えてみてください

Pythonや自動化スキルを体系的に習得して、ITエンジニアとしてのキャリアを切り開きたい方には「Enjoy Tech!(エンジョイテック)」が選択肢のひとつです。現役エンジニアのサポートで、未経験から実践的なスキルを身につけられます。

プログラミングスクール Enjoy Tech!(エンジョイテック) →

fillnaが効かない原因:代入忘れ

反映されない原因の1つ目は自分自身に代入し直していないことです。先ほど説明した通りfillnaは新しいデータを作成して返します。そのため元の変数にその結果を上書きしてあげなければなりません。具体的には「df = df.fillna(0)」と書く必要があります。この「df = 」という左側の部分を忘れてしまうミスは多いです。初心者から中級者までが経験します。最も基本的で最も多い落とし穴だと言えるでしょう。

教本を片手にコードを書いていました。代入を意味する「=」の重要性に全く気づきません。関数を呼び出すだけで満足していたのです。30分間も代入を忘れていることに気づきませんでした。ネットで「fillna 効かない」と検索し続けます。ようやく自分の記述ミスを発見しました。その瞬間の脱力感は今でも忘れられません。

NaNを0に置き換え

実際にこの修正を行ってみます。驚くほどあっさりと全てのNaNが0に置き換わりました。特定の列だけを埋めたい場合も考え方は同じで、「df[‘金額’] = df[‘金額’].fillna(0)」のように列を指定した上で代入します。これで狙った場所だけを確実に更新できます。

自分はここに気づくまでに30分かかりました。今となっては「なぜ気づかなかったのか」と思いますが、当時は本当に見えていなかったのです。

コピー操作の落とし穴:locで解決

2つ目の原因は操作の対象が「コピーされた別物」になるケースです。「元のデータ」に対して操作できていない状態を指します。これは少し複雑に聞こえますが実務では非常によく起こります。大きな表の中から特定の条件で抽出したデータを用意しました。これに対してfillnaを行おうとする際のことです。pandasは内部的に「元の表のコピー」と判断することがあります。このコピーに対していくら修正を加えたとします。それでも大元のデータフレームには一切影響が及びません。

日本語の文字が含まれるVS Codeのエディタ画面。抽出した変数へのfillna

df.locによる意図せぬ挙動と警告の回避

この現象を防ぐためには明示的な指定が有効です。「df.loc」という機能を使うのが最も確実な方法になります。例えば「df.loc[:, ‘列名’] = df[‘列名’].fillna(0)」と書きます。コピーではなく元のデータの特定の場所を指定するのです。直接書き換えることが可能になります。しかしこれを使わないと不気味な警告が出ます。「SettingWithCopyWarning」という警告です。今回のように反映されないこともあります。結果的に余計な手間が増えることに気づきました。

警告メッセージの文字数は150文字に及びました。それに対する調査時間は45分もかかってしまったのです。

確実性を求めるなら面倒でも「loc」を使うべきです。データの場所を正確に指し示す癖をつけましょう。これが意図しない挙動を防ぐ強力な防衛策になります。

inplace=Trueの罠と再代入

3つ目の原因は「inplace=True」に頼りすぎる点です。便利なはずのこの設定がトラブルを引き起こします。fillnaの括弧の中にこの呪文を書きます。すると代入を使わなくても元のデータを直接書き換えてくれるのです。一見するとコードが短くなってスマートに思えます。しかし実はこれが深刻なトラブルの種になることがあるのです。特に前述した「コピーされたデータ」に対して使うと危険です。反映されないばかりかエラーメッセージすら出ません。原因究明が極めて困難な状況に陥ってしまいます。

日本語インターフェースのブラウザで表示したpandas公式ドキュメント。inpl

実は最新のpandasではこの設定は非推奨になりつつあります。「inplace=True」の使用はあまり推奨されません。将来的にこの機能が廃止される可能性も示唆されています。熟練のエンジニアたちも再代入の形を推奨することが多いです。「df = df.fillna(0)」という書き方になります。

便利機能の罠と確実性

代入を書かなくて済むので楽だと思いました。そこで全てのコードをこの形式に書き換えてみたのです。しかし一部の複雑な集計用データフレームでだけ値が更新されません。原因究明に丸一日を費やすことになりました。結局全てを再代入の形に戻します。すると全ての不具合が魔法のように解消されました。

このように一見便利に見える機能には注意が必要です。実はデバッグを困難にする迷宮の入り口になることもあります。確実な動作と将来的な安全性を優先しましょう。

欠損値処理:実務の使い分け術

それでは結局どの方法を使うのが実務において正解なのでしょうか。私が数多くの失敗を経てたどり着いた使い分けの基準を紹介します。まずデータフレーム全体のNaNをまとめて埋めたい場合です。0や「欠損」などの固定値で埋めるケースを考えます。この場合は迷わず全列一括の再代入を使います。「df = df.fillna(0)」という書き方です。これが最もシンプルで分かりやすい方法になります。後からコードを読み返したときにも便利です。自分の意図が明確に伝わりやすいからです。

古い基幹システムから出力された日本語のCSVデータのイメージ。グレー背景の業務フ

データ修正トラブル激減

次に特定の列だけをピンポイントで修正したい場合を考えます。先ほど紹介した「loc」を使った形を徹底しています。「df.loc[:, ‘列名’] = df[‘列名’].fillna(値)」です。これを使うようになってからトラブルが激減しました。「書いたのに反映されていない」という事態はほぼゼロです。複数の列に対して異なるルールで埋めたいときもあります。その場合は辞書形式を活用するのが非常に効率的です。「df = df.fillna({‘列A’: 0, ‘列B’: ‘不明’})」と書きます。

再代入方式とloc方式をルール化しました。以前はデータ修正の不備で手戻りが月10回発生していました。それが現在では0回になっています。

状況に応じて「再代入」と「loc」を適切に組み合わせます。これで意図通りにデータを制御できるようになるのです。

データ型の見えない落とし穴

最後に「見えない落とし穴」についてお伝えします。fillnaが正しく反映された後にも潜んでいる問題です。無事にNaNを埋めることができても安心はできません。データの「型」が勝手に変わってしまうことがあります。その影響でその後の計算に失敗するケースがあるのです。例えば整数の列にNaNが含まれているとします。pandasはその列を自動的に「浮動小数点数」として扱うのです。ここでfillna(0)を実行してみます。見た目は0になりますが内部的には小数です。「0.0」という小数として残ってしまいます。

日本語の文字が含まれるExcel画面。整数の列が勝手に小数として認識されてしまっ

その後に厳密な処理が続く場合は注意が必要です。「この列は必ず整数でなければ」という条件があると、ここでまた別のエラーが発生します。この場合はfillnaの直後に「.astype(int)」を繋げて型変換します。全列一括で埋める際は、日付データの列まで0で埋めてしまうミスにも要注意です。

型不一致トラブル、型確認の習慣

実は「型」の不一致による不具合はやっかいです。エラーメッセージを見ても原因がfillnaとは気づきにくく、調査だけで時間を取られます。データを埋める際は、その列が本来どんな型であるべきかをセットで確認する癖をつけておくと助かります。これを意識するようになってから、型のせいで集計がズレる事故はかなり減りました。

無料プレゼント

Excel業務を自動化する前に確認するチェックリスト(PDF)

自動化していい作業かどうか、VBAかPythonか、最初に避けるべき落とし穴。実務でよく迷うポイントを1枚にまとめました。メールアドレスだけで受け取れます。

無料でチェックリストを受け取る

¥980 ミニキット

コピペで動かせる3スクリプト+自動化チェックリスト

最新ファイルの自動選択・部署名ゆれの正規化・CSV文字コード確認の3本セット。今週の作業を1つだけ楽にするための最小キットです。

ミニキットを見る(¥980)

関連リンクとチェックリスト

関連書籍

Python・pandas関連書籍を、手元で見返せる形にしておきたい場合

pandasのエラー対応は、似た症状を何度も見返すことが多いです。データ前処理や型変換を手元で確認できる本を1冊置いておくと、調べ直しの時間を減らしやすいです。

Python・pandas関連書籍をAmazonで探す

学習サービスとアンケート

このスキルを活かしてさらに前へ進むなら

PythonやExcel自動化スキルを持ったまま、ITエンジニアとして転職したい方には「EBAエデュケーション」が選択肢です。企業が求めるエンジニア像に合わせたカリキュラムで、実務直結のスキルを習得できます。

ITエンジニア転職・EBAエデュケーション →

[アンケート] この記事は役に立ちましたか?


1問だけ回答する