pandasのSettingWithCopyWarningの意味と直し方
pandasでデータをいじっていると、代入のたびにこの警告が出ることがある。
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.Try using .loc[row_indexer,col_indexer] = value insteadエラーではないので動いてしまうが、放置すると「代入したはずなのに反映されない」という地味で厄介なバグにつながる。意味と直し方を最短でまとめる。
なぜ出るのか
原因はほぼ一つ、チェーンインデックス(連続した角かっこ)だ。
# 警告が出る書き方df[df["age"] > 20]["flag"] = 1この書き方は、pandasから見ると2段階の操作になっている。まず df[df["age"] > 20] で一部を取り出し、その結果に対して ["flag"] = 1 を代入している。問題は、取り出した結果が元のDataFrameの「ビュー(参照)」なのか「コピー」なのかが、状況によって変わることだ。
コピーに代入していた場合、元の df には何も反映されない。pandasは「あなたはコピーに代入しているかもしれませんよ」と警告している。つまりこれは、代入が効かない可能性への注意喚起だ。
直し方1:.loc で一度に指定する
正しくは、行と列を一つの .loc でまとめて指定する。
# 正しい書き方df.loc[df["age"] > 20, "flag"] = 1.loc[行条件, 列] の形にすると、取り出しと代入が分かれず、元の df に確実に書き込まれる。チェーンインデックスをやめるだけで、警告も実害も消える。これが基本の直し方だ。
直し方2:意図して部分集合を作るなら .copy()
一部を抜き出して別物として扱いたい場合は、コピーであることを明示する。
# 元データと切り離した別物として扱うadults = df[df["age"] > 20].copy()adults["flag"] = 1 # これは adults だけを変更する。df は変わらない.copy() を付けると「これはコピーです」と宣言したことになり、警告は出ない。元データを変えたいのか、切り離した部分集合を変えたいのか、自分の意図をコードで表しているわけだ。
やってはいけない対処
警告がうるさいからといって、出力そのものを止めるのは避けたい。
# 非推奨:警告を握りつぶすだけ。バグは残るpd.options.mode.chained_assignment = Noneこれは警告を消すだけで、代入が効いていない問題は残ったままになる。原因のチェーンインデックスを直すのが正解だ。
まとめ
- 原因:
df[条件]["列"] = 値のようなチェーンインデックス - 直し方:
df.loc[条件, "列"] = 値でまとめて指定する - 部分集合を別物として扱うなら
.copy()を明示する - 警告を抑制する設定で握りつぶさない
この警告は、pandasが「その代入、効いていないかもしれない」と教えてくれているサインだ。.loc か .copy() のどちらが自分の意図かを決めれば、警告も実害も同時に消える。