15

スライスに割り当てるときの pandas の動作は本質的に予測不可能であることはよく知られています(そして理解できます)。しかし、私は警告によってそれについて警告されることに慣れていSettingWithCopyます。

次の 2 つのコード スニペットのいずれでも警告が生成されないのはなぜですか?また、そのようなコードを意図せずに作成する可能性を減らすには、どのような手法を使用すればよいでしょうか?

# pandas 0.18.1, python 3.5.1
import pandas as pd
data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
new_data = data[['a', 'b']]
data = data['a']
new_data.loc[0, 'a'] = 100 # no warning, doesn't propagate to data

data[0] == 1
True


data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
new_data = data['a']
data = data['a']
new_data.loc[0] = 100 # no warning, propagates to data

data[0] == 100
True

説明は、現在のコンテキストから親 DataFrame にまだ到達できる場合にのみ、パンダが警告を生成するというものだと思いました。(前の例が示すように、これは検出アルゴリズムの弱点です。)

次のスニペットでは、元の 2 列の DataFrame に到達できなくなっていることがわかりますが、pandas の警告メカニズムは (幸いなことに) トリガーされます。

data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
new_data = data['a']
data = data[['a']]
new_data.loc[0] = 100 # warning, so we're safe

編集:

これを調査しているときに、警告が欠落している別のケースを見つけました。

data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
data = data.groupby('a')
new_data = data.filter(lambda g: len(g)==1)
new_data.loc[0, 'a'] = 100 # no warning, does not propagate to data
assert data.filter(lambda g: True).loc[0, 'a'] == 1

ほぼ同じ例でも警告がトリガーされます。

data = pd.DataFrame({'a': [1, 2, 2], 'b': ['a', 'b', 'c']})
data = data.groupby('a')
new_data = data.filter(lambda g: len(g)==1)
new_data.loc[0, 'a'] = 100 # warning, does not propagate to data
assert data.filter(lambda g: True).loc[0, 'a'] == 1

更新: コメントに入れるのが難しいため、ここで @firelynx の回答に返信します。

回答で、@firelynx は、データフレーム全体を取得しているため、最初のコード スニペットでは警告が発生しないと述べています。しかし、参加したとしても、警告は表示されません。

# pandas 0.18.1, python 3.5.1
import pandas as pd
data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c'], c: range(3)})
new_data = data[['a', 'b']]
data = data['a']
new_data.loc[0, 'a'] = 100 # no warning, doesn't propagate to data

data[0] == 1
True
4

1 に答える 1

2

やっていることを順を追って説明する

作成するデータフレームはビューではありません

data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
data._is_view
False

すべての列を取得しているため、 new_data もビューではありません

new_data = data[['a', 'b']]
new_data._is_view
False

今、あなたはデータをSeries 'a'

data = data['a']
type(data)
pandas.core.series.Series

ビューはどれですか

data._is_view
True

ここで、非コピーの値を更新しますnew_data

new_data.loc[0, 'a'] = 100 # no warning, doesn't propagate to data

これは警告を発するべきではありません。データフレーム全体です。

作成したSeriesはそれ自体をビューとしてフラグ付けしますが、それは DataFrame ではなく、DataFrame ビューとして動作しません。

このようなコードを書くことを避ける

シリーズ対データフレームの問題は、パンダでは非常に一般的な問題です[しばらくパンダを扱ったことがある場合は、引用は不要です]

問題は、常に書く必要があるということです

data[['a']]いいえdata['a']

左はデータフレーム ビューを作成し、右はシリーズを作成します。

一部の人々は、決して書くことはないと主張するかもしれませんdata['a']が、data.a代わりに書きます。したがって、コードの環境に警告を追加できますdata['a']

これは動作しません。まず、data.a構文を使用すると認知的不協和が生じます。

データフレームは、列のコレクションです。[]Python では、演算子を使用してコレクションのメンバーにアクセスします。演算子によって属性にアクセスします.。これらを切り替えると、Python プログラマーであれば誰でも認知的不協和を引き起こします。特に次のようなことを始めてdel data.a、うまくいかないことに気付くと。より広範な説明については、この回答を参照してください

レスキューのためのクリーンなコード

data[['a']]との違いがわかりにくいdata['a']

これはにおいです。どちらもすべきではありません

きれいなコードの原則とpython の禅を使用する適切な方法「明示的は暗黙的よりも優れている」

これは:

columns = ['a']
data[columns]

これはそれほど気にならないかもしれませんが、次の例を見てください。

data[['ad', 'cpc', 'roi']]

これは何を意味するのでしょうか?これらの列は何ですか? ここで得られるデータは何ですか?

これらは、このコード行を読んだときに頭に浮かぶ最初の質問です。

それを解決する方法は?コメントを言わないでください。

ad_performance_columns = ['ad', 'cpc', 'roi']
data[ad_performance_columns]

より明示的であるほど常に優れています。

詳細については、クリーン コードに関する本を購入することを検討してください。たぶんこれ

于 2017-05-29T08:38:02.600 に答える