DataFrame同士で計算を行っていると、次のWarningが出る事があります。
このエラーは、例えば、「カラム名を同じもので上書きしようとした場合」に出るはずです。
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
Warningの意味は、「もとのDataFrameからコピーした。(=もとのDFは上書きしなかった)」というものです。
しかし、欲しい結果は得られているのではないでしょうか。
結果は正しいのですが、Pythonは「この結果は、あなたが望んだ結果ではないかもしれないので、注意して」と言っているのです。
※ここではDataFrameを「df」(またはDF)と表記します。
※ここで使用したcsvファイル、コード全文は、ページ下部の参考ページのリンクから参照可能です。
内容
- どんな時に起きるか
- 原因
- 解決方法
- 参考ページ
https://shilabo.com/python/web_self/
https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html
【どんな時に起きるか】
これは、例えば、%表示の変換で、「1.00(%)」となっているものを数値「0.01」に同じカラム名で変換(上書き)しようと、
df2['pct'] = df2['pct'] / 100
と書いた場合に起きると思います。それは、コードを辿ると、df2が他のDFを参照し
df2 = df1[['clm0', 'clm1']]
のように書いているのではないでしょうか?
【原因】
これは chained indexing と呼ばれるものです。
dfを連鎖的にカラム名を指定して参照(複製)した場合に起きます。
例えば、df1が所与の時、新しいdf2に対して、
df2 = df1[['clm0', 'clm1']]
と書いて、更にdf2['clm0'] =1
のように、
辿った先の、元のdf1を、df1のカラム名を使って上書きする(上書きするのと同じコードを書いた)ような場合に起きます。
df2 = df1
と書けば、df2['clm0']=1
(カラム’clm0’を全部1にする)と書いても問題は起きません。それは、df1の全てがdf2に複製され、df2をいじると、df1も同時に上書きされるからです。
ところが、カラム名を指定して、df2 = df1[['clm0', 'clm1']]
と書くと、df2には写しが渡されますが、ここでdf1に存在するカラム名’clm0’を指定してdf2の方からdf2['clm0']=1
と書くと、Pythonはdf1は上書きしない、という判断をします。
本来、df2 = df1
という書き方は、df2をいじれば、df1も上書きする、という仕様です。しかし、df2['clm0']=1
と書くと、この仕様とは異なるので、Warningを出すと思われます。
Warningの指示通り、df.loc[]を使っても(例えば、df.loc[:,'pct']
)、同じWarningが出るはずです。
まず、仕様を確認します。
import pandas as pd
f1 = r'C:\Users\shilabo\Documents\SHiLABO_python\a004_007a.csv'
df1 = pd.read_csv(f1)
# 1.1(a) example
df2A = pd.read_csv(f1)
df2a = df2A
df2a['clm0'] = 99
print('#df2a\n', df2a)
# df2a
# clm0 clm1 clm2 clm3
# 0 99 -1 12 105
# 1 99 -2 22 205
# 2 99 -3 32 305
# 3 99 -4 42 405
# 4 99 -5 52 505
print('#df2A\n', df2A)
# df2A
# clm0 clm1 clm2 clm3 #<=df2Aも'clm0'が上書きされる。
# 0 99 -1 12 105
# 1 99 -2 22 205
# 2 99 -3 32 305
# 3 99 -4 42 405
# 4 99 -5 52 505
view rawa004_007_02.py hosted with ❤ by GitHub
次にWaningを再現します。
df2B = pd.read_csv(f1)
df2b = df2B[['clm0', 'clm1']]
df2b['clm0'] = 99
print('#df2b\n',df2b)
# df2b #<=df2bはWarningが出て、上書きされる。
# clm0 clm1
# 0 99 -1
# 1 99 -2
# 2 99 -3
# 3 99 -4
# 4 99 -5
# 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
print('#df2B\n',df2B)
# df2B
# clm0 clm1 clm2 clm3 #<=df2Bは'clm0'が上書きされない。
# 0 1 -1 12 105
# 1 2 -2 22 205
# 2 3 -3 32 305
# 3 4 -4 42 405
# 4 5 -5 52 505
【解決方法】
- 同じ名前のカラム名で、結果を入れた新しいカラムを作るのを「避ければ」、このWarningは回避できます。
または、 - 同じカラム名にしたければ、以下のように、
df2 = df1['clm0'].copy()
と書いて、コピーである事を明確にすれば、Warningは出ません。
df2C = pd.read_csv(f1)
df2c = df2C[['clm0', 'clm1']].copy() #.copy()とする。
df2c['clm0'] = 99
print('#df2c\n',df2c)
# df2c
# clm0 clm1 #<=df2cで'clm0'を上書きしてもWarningは出ない。
# 0 99 -1
# 1 99 -2
# 2 99 -3
# 3 99 -4
# 4 99 -5
print('#df2C\n',df2C)
# df2C
# clm0 clm1 clm2 clm3 #<=df2Cは上書きされない。
# 0 1 -1 12 105
# 1 2 -2 22 205
# 2 3 -3 32 305
# 3 4 -4 42 405
# 4 5 -5 52 505
勿論、Warningの内容を分かっていて使っていれば、多くの場合、問題ありません。
参考ページ
- 実践で使うPythonの技術が掲載されています。書籍のように構成されています。
- pandasの該当部分のドキュメント(英語)です。
https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html - コードに用いたcsvファイル(GitHub) : a004_007a.csv
- ここに掲載したコード全文