- 技術 💻
SettingWithCopyWarningの原因と直し方|pandas
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. は、DataFrame から取り出した一部に値を入れたとき、「それは元のデータのコピーかもしれず、元のデータは変わらないかもしれない」と pandas が知らせる警告です。 警告なので処理は止まりませんが、「元を変えたつもりが変わっていない」不具合の手がかりです。
直し方は、元のデータを変えたいのか、取り出した部分だけを変えたいのかで決まります。
| 変えたいもの | 書き方 |
|---|---|
| 元の DataFrame | df.loc[条件, "列"] = 値(1回の代入で書く) |
| 取り出した部分だけ | sub = df[条件].copy() にしてから代入 |
確認環境:Python 3.12、pandas 2.2.3 と 3.0.6(確認日 2026年10月11日)。同じコードを両方の版で実行し、結果と警告を比べました。pandas 3.0 では SettingWithCopyWarning は出なくなり、代わりに結果そのものが変わるコードがあります(後半で説明します)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SettingWithCopyWarningの原因と直し方|pandas」
再現:どちらも警告が出る2つの書き方
import pandas as pd
df = pd.DataFrame({"name": ["佐藤", "鈴木", "高橋"],
"dept": ["営業", "開発", "開発"],
"salary": [300, 400, 500]})
dev = df[df["dept"] == "開発"] # ① 絞り込んだ結果に
dev["salary"] = dev["salary"] * 2 # 列を代入
df[df["dept"] == "開発"]["salary"] = 999 # ② 連鎖代入([ ][ ] = )
raw1.py:4: 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
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
dev["salary"] = dev["salary"] * 2
警告の最後の行に、原因の行が表示されます(ここでは dev["salary"] = ...)。
| 書き方 | 元の df の salary |
意味 |
|---|---|---|
① 絞り込んだ dev に代入 |
[300, 400, 500](変わらない) |
dev だけが変わった。元を変えたかったなら不具合 |
② df[条件]["salary"] = 999 |
[300, 400, 500](変わらない) |
代入が効いていない(元を変えたつもりなら確実に不具合) |
直し方1:元の DataFrame を変えたい → df.loc
df.loc[df["dept"] == "開発", "salary"] = 999
print(df["salary"].tolist()) # [300, 999, 999] 警告なし
行の条件と列名を、1つの loc[ ] にまとめて書きます。 [ ][ ] と2回に分けると、1回目の結果がコピーになりうるため、2回目の代入が元に届きません。
直し方2:取り出した部分だけを変えたい → .copy()
dev = df[df["dept"] == "開発"].copy() # 明示的にコピーする
dev["salary"] = dev["salary"] * 2
print(dev["salary"].tolist(), df["salary"].tolist())
# [800, 1000] [300, 400, 500] 警告なし。元はそのまま
.copy() を付けると、「これは独立したコピーで、元とは別物」と明示できるので、警告は出ません。新しい列を足すだけなら assign でも書けます。
dev = df[df["dept"] == "開発"].assign(salary=lambda d: d["salary"] * 2) # 警告なし
pandas 2.2 と 3.0 で結果が変わるコード
pandas 3.0 では Copy-on-Write(コピーオンライト)が標準になりました。取り出した結果は常に元とは別物として振る舞い、SettingWithCopyWarning のような「コピーかもしれない」という曖昧さがなくなりました。そのぶん、2.2 では元が変わっていたコードが、3.0 では変わらなくなることがあります。
| # | コード | pandas 2.2.3 | pandas 3.0.6 |
|---|---|---|---|
| 1 | df[条件]["salary"] = 999 |
元は変わらない+SettingWithCopyWarning | 元は変わらない+ChainedAssignmentError |
| 2 | dev = df[条件]; dev["salary"] = ... |
dev だけ変わる+SettingWithCopyWarning |
dev だけ変わる・警告なし |
| 3 | df.loc[条件, "salary"] = 999 |
元が変わる | 元が変わる |
| 4 | .copy() してから代入 |
dev だけ変わる |
dev だけ変わる |
| 5 | s = df["salary"]; s[0] = 1 |
元の df も 1 に変わる+警告 | 元の df は変わらない・警告なし |
| 6 | df["salary"].fillna(0, inplace=True) |
元の df が 0 に埋まる+FutureWarning | 元の df は埋まらない(NaN のまま)+ChainedAssignmentError |
5 と 6 は、版を上げると結果が黙って変わる書き方です。特に 6 の inplace=True は、2.2 では動いていたため、多くのコードに残っています。
3.0 で出る ChainedAssignmentError
raw1.py:5: ChainedAssignmentError: A value is being set on a copy of a DataFrame or Series through chained assignment.
Such chained assignment never works to update the original DataFrame or Series, because the intermediate object
on which we are setting values always behaves as a copy (due to Copy-on-Write).
Try using '.loc[row_indexer, col_indexer] = value' instead, to perform the assignment in a single step.
名前は Error ですが、警告として表示され、処理は止まりません。メッセージのとおり、連鎖代入は 3.0 では元に一度も届きません。
inplace を使わない書き方に直す
# 2.2 では動いたが、3.0 では元が変わらない
df["salary"].fillna(0, inplace=True)
# どちらの版でも元が変わる
df["salary"] = df["salary"].fillna(0) # 代入し直す
df.fillna({"salary": 0}, inplace=True) # DataFrame に対して inplace
pandas の公式ドキュメント(Copy-on-Write)でも、列を取り出して inplace=True で変える書き方は元に反映されないとして、DataFrame に対して実行するか、代入し直す書き方が案内されています。
3.0 に上げる前に確かめる方法
pandas 2.2 でも、Copy-on-Write を有効にして動かせます。確認環境では、次の1行を先頭に入れると、表の 5・6 が 3.0 と同じ結果(元が変わらない)になりました。
import pandas as pd
pd.options.mode.copy_on_write = True # 2.2 で 3.0 と同じ動きにする
テストをこの設定で通しておけば、3.0 に上げても結果が変わりません。公式ドキュメントでは、変わる可能性のある箇所をすべて警告する pd.options.mode.copy_on_write = "warn" も案内されています(出る警告は多めです)。
警告を消すだけの書き方は使わない
pd.options.mode.chained_assignment = None # 警告を出さない設定
pandas 2.2 では、この設定で SettingWithCopyWarning を止められますが、代入が効いていない不具合はそのまま残ります。表の 1 のように、元が変わらないことに気づけなくなるので、警告を消すより loc・.copy() で書き直します。
確認の手順(チェックリスト)
| 順番 | 確認すること | 方法 |
|---|---|---|
| 1 | どの行で出たか | 警告の最後の行に表示されるコード |
| 2 | [ ][ ] = の連鎖代入か |
df.loc[条件, "列"] = 値 に書き換える |
| 3 | 絞り込んだ結果に代入しているか | 元を変えたいなら loc、部分だけなら .copy() |
| 4 | 列に inplace=True を使っていないか |
df["列"] = df["列"].fillna(...) に書き換える |
| 5 | pandas の版 | pd.__version__。3.0 では警告が出ずに結果が変わる書き方がある |
| 6 | 3.0 に上げる前 | 2.2 で copy_on_write = True にしてテスト |
よくある質問
Q. 警告が出ても、結果は正しいように見えます。
A. 取り出した部分だけを使うなら、結果は正しいことがあります。その場合でも .copy() を付けて、元とは別物であることを明示すると、3.0 でも同じ結果になり、警告も出ません。
Q. df.loc を使っているのに警告が出ます。
A. loc で取り出した結果に、もう一度代入していないかを確認します。sub = df.loc[条件] → sub["列"] = 値 は、表の 2 と同じ形です。元を変えるなら df.loc[条件, "列"] = 値 と1回で書きます。
まとめ
- 取り出した一部への代入が、元に届かないかもしれないという警告
- 元を変えるなら
df.loc[条件, "列"] = 値、部分だけなら.copy() [ ][ ] =の連鎖代入は、どの版でも元に届かない- pandas 3.0 は Copy-on-Write。SettingWithCopyWarning は出ず、
s[0] = 1や列のinplace=Trueは元を変えなくなる - 3.0 に上げる前に、2.2 で
pd.options.mode.copy_on_write = Trueにしてテストする - 警告を消す設定は使わない。不具合が見えなくなる
参考・出典
確認日はいずれも 2026年10月11日です。
- pandas ドキュメント「Copy-on-Write (CoW)」:https://pandas.pydata.org/docs/user_guide/copy_on_write.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SettingWithCopyWarningの原因と直し方|pandas」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。