web-dev-qa-db-ja.com

パンダ:操作が元のデータフレームに影響するタイミングを知る

私はpandasを愛し、何年も使用しており、データフレームをサブセット化し、ビューとコピーを適切に処理する方法について十分な自信があると感じています(ただし、多くのアサーションを使用して PandasでSettingWithCopyWarningを処理する方法? と、それが起こったときに頭を包むことに関するいくつかの素晴らしいガイドがあります- パンダのSettingWithCopyWarningを理解する

しかし、 この回答 からの引用のような特定の事柄は、最新のドキュメント(0.22.0)に含まれておらず、多くの事柄が長年にわたって非推奨になっていることも知っています(一部の不適切な古いSO回答)、そしてそれは 変化し続ける です。

最近教えた後pandas非常に基本的な一般的な新人を完成させるためにPython連鎖インデックス作成の回避(および.iloc/.loc)、私はまだ一般的な経験則を提供するのに苦労しました。SettingWithCopyWarningに注意を払うことが重要なとき(たとえば、無視しても安全なとき)を知るためです。

私は個人的に、何らかのルール(たとえば、スライスまたはブール演算)に従ってデータフレームをサブセット化し、そのサブセットを変更する特定のパターン元のデータフレームに依存しないは、はるかに一般的な演算であることを発見しましたドキュメントが示唆しています。この状況では、オリジナルではなくコピーを変更にしたいので、警告は新規ユーザーを混乱させる/怖いです。

ビューとコピーのどちらが返されるかを事前に知ることは簡単ではありません。
どのルールがPandasビューとコピーの生成に使用しますか?
データフレームがパンダでコピーまたは表示されているかどうかを確認

代わりに、より一般的な(初心者に優しい)質問への答えを探しています:いつサブセット化されたデータフレームで操作を実行すると、それが作成された元のデータフレームに影響しますか?それらは独立していますか?.

私は合理的と思われるいくつかのケースを作成しましたが、私が見逃している「落とし穴」があるかどうか、またはこれを考える/チェックする簡単な方法があるかどうかはわかりません。上記の私の質問に関連して、次のユースケースに関する私の直感が正しいことを誰かが確認できることを望んでいました。

import pandas as pd
df1 = pd.DataFrame({'A':[2,4,6,8,10],'B':[1,3,5,7,9],'C':[10,20,30,40,50]})

1)警告:いいえ
元の変更:いいえ

# df1 will be unaffected because we use .copy() method explicitly 
df2 = df1.copy()
#
# Reference: docs
df2.iloc[0,1] = 100

2)警告:はい(その理由はよくわかりません)
元の変更:いいえ

# df1 will be unaffected because .query() always returns a copy
#
# Reference:
# https://stackoverflow.com/a/23296545/8022335
df2 = df1.query('A < 10')
df2.iloc[0,1] = 100

3)警告:はい
元の変更:いいえ

# df1 will be unaffected because boolean indexing with .loc
# always returns a copy
#
# Reference:
# https://stackoverflow.com/a/17961468/8022335
df2 = df1.loc[df1['A'] < 10,:]
df2.iloc[0,1] = 100

4)警告:いいえ
元の変更:いいえ

# df1 will be unaffected because list indexing with .loc (or .iloc)
# always returns a copy
#
# Reference:
# Same as 4)
df2 = df1.loc[[0,3,4],:]
df2.iloc[0,1] = 100

5)警告:いいえ
オリジナルの変更:はい(初心者にはわかりにくいですが、意味があります)

# df1 will be affected because scalar/slice indexing with .iloc/.loc
# always references the original dataframe, but may sometimes 
# provide a view and sometimes provide a copy
#
# Reference: docs
df2 = df1.loc[:10,:]
df2.iloc[0,1] = 100

tl; drオリジナルから新しいデータフレームを作成するとき、新しいデータフレームを変更します:
。loc/.ilocを使用したスカラー/スライスインデックス作成を使用して新しいデータフレームを作成する場合、元のファイルを変更します。
Will not .loc、.query()、または.copy()を使用したbooleanインデックス作成が使用される場合、オリジナルを変更します新しいデータフレームを作成します

37
ejolly

これはパンダのやや混乱し、イライラする部分ですが、ほとんどの場合、いくつかの単純なワークフロールールに従えば、これについて心配する必要はほとんどありません。特に、2つのデータフレームがあり、一方が他方のサブセットである場合、ここには2つの一般的なケースしかないことに注意してください。

これは、Zen of Pythonルール「明示的は暗黙的より優れている」が、従うべき優れたガイドラインである場合です。

ケースA:df2への変更はdf1に影響しない

もちろん、これは些細なことです。完全に独立した2つのデータフレームが必要なので、明示的にコピーを作成します。

df2 = df1.copy()

この後、df2に対して行うことはdf2にのみ影響し、df1には影響しません。

ケースB:df2への変更は、df1にも影響するはずです

この場合、あなたがやろうとしていることに正確に依存するため、問題を解決する一般的な方法はないと思います。ただし、非常に単純であり、それらがどのように機能するかについて曖昧さを持たないはずの標準的なアプローチがいくつかあります。

方法1:df1をdf2にコピーし、df2を使用してdf1を更新する

この場合、基本的に上記の例の1対1の変換を行うことができます。例#2は次のとおりです。

df2 = df1.copy()
df2 = df1.query('A < 10')
df2.iloc[0,1] = 100

df1 = df2.append(df1).reset_index().drop_duplicates(subset='index').drop(columns='index')

残念ながら、appendを介した再マージは少し冗長です。整数を浮動小数点数に変換するという副作用がありますが、次の方法でよりきれいに行うことができます。

df1.update(df2)   # note that this is an inplace operation

方法2:マスクを使用します(df2を作成しないでください)

ここでの最善の一般的なアプローチは、df2を作成することではなく、df1のマスクバージョンにすることです。多少残念なことに、locilocが混在しているため、上記のコードを直接変換することはできませんが、実際の使用ではおそらく非現実的です。

利点は、非常にシンプルで読みやすいコードを作成できることです。上記の例#2の代替バージョンは、df2が実際にマスクされたdf1のバージョンです。ただし、ilocを使用して変更する代わりに、列「C」== 10の場合に変更します。

df2_mask = df1['A'] < 10
df1.loc[ df2_mask & (df1['C'] == 10), 'B'] = 100

これでdf1またはdf1[df2_mask]を印刷すると、各データフレームの最初の行に「B」列= 100が表示されます。ここでは明らかにこれはそれほど驚くことではありませんが、「明示的は暗黙的よりも優れている」という固有の利点です。

6
JohnE

私も同じ疑問を持っています。過去にこの応答を検索しましたが、成功しませんでした。だから今、私はオリジナルが変更されていないことを証明し、警告を削除し始めたときにプログラムにこのコードの平和を使用します:

 import pandas as pd
 pd.options.mode.chained_assignment = None  # default='warn'
0
romulomadu

.iloc[0,1].iat[0,1]に置き換えるだけです。

より一般的には、1つの要素のみを変更する場合は、.iatまたは.atメソッドを使用する必要があります。代わりに、一度に複数の要素を変更する場合は、.locまたは.ilocメソッドを使用する必要があります。

このようにしてpandas警告をスローしないでください。

0
alububu