numpy & pandasの講座の演習課題でハマったのでメモ。
環境は、anaconda3でまとめてインストールした、python3.6.5、pandas0.23.0
DataFrameの値を変更したいときはqueryではなくlocを使う。
例えばこんなDataFrameがあったとする
In [2]: df = pd.DataFrame({"score":[10, 20, 30],
...: "address":["Kashiwa", "Matsudo", "Noda"],
...: "weight":[70, 80, 90]},
...: columns = ["score", "weight", "address"])
...: df.index = ["Taro", "Jiro", "Hanako"]
...: df
...:
Out[2]:
score weight address
Taro 10 70 Kashiwa
Jiro 20 80 Matsudo
Hanako 30 90 Noda
で、これをlocで絞り込んでから更新するとこう。
In [3]: df.loc["Taro", "weight"] = 123
...: df.loc["Taro", "weight"]
Out[3]: 123.0
これが正解。同じことをDataFrame.queryでできないのかな?と思ってやってみるとこんな感じになる
In [4]: df.loc["Taro", "weight"] = 70
...:
...: df.query("index == 'Taro'").weight = 123
...: df.loc["Taro", "weight"]
...:
...:
/anaconda3/lib/python3.6/site-packages/pandas/core/generic.py:4401: 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: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
self[name] = value
Out[4]: 70.0
なんかWarningが出て更新されない。
理由
出力されたWarningの中に示されているドキュメントhttp://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copyをみてみると、どうもloc
で絞り込んだ場合と []
によるスライスを連続して書いた場合ではインタプリタの評価が違うらしい。chained indexing
(インデックスの連鎖?)の問題というらしい。
以下、ドキュメントからの引用。
loc
だとこんな感じにインタプリタが解釈するが
dfmi.loc[:,('one','second')] = value
# becomes
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)
[]
を連続させるとこう。
dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)
で、こんな感じの説明が書いてある(だいぶ意訳)
__getitem__
があるのが分かるだろうか?シンプルな場合を除いては、後者の表現が参照(view)を返すかコピーを返すかを予測するのは難しい。(その時のメモリ配置による。で、その配置についてはpandasは保証できない) よって__setitem__
がdfmiを更新するか、一時オブジェクト(その後すぐ捨てられる)を更新するのかは誰にもわからない。そ ん な わ け で SettingWithCopy警告が出るのだ!
今回私がやってしまったようなquery
で絞り込み -> 列名でスライスした場合も同じことが起きると思われる。
ハンズオンの講座でやっていたときはなぜかWarningも出なかったので困った。後でDataFrame.queryのドキュメントを見て、そこから上記のindexingのページhttp://pandas.pydata.org/pandas-docs/stable/indexing.htmlに行って解決。