web-dev-qa-db-ja.com

なぜ私はパンダのデータフレームのコピーを作るべきですか

親データフレームからサブデータフレームを選択するとき、.copy()メソッドを使ってデータフレームのコピーを作成するプログラマーがいることに気づきました。

なぜ彼らはデータフレームのコピーを作っているのですか?コピーしないとどうなりますか?

110

これはパウロの答えを拡大したものです。 Pandasでは、DataFrameのインデックスを作成すると、最初のDataFrameへの参照が返されます。したがって、サブセットを変更すると、最初のDataFrameも変更されます。したがって、最初のDataFrameが変更されないようにするには、コピーを使用します。次のコードを見てください。

df = DataFrame({'x': [1,2]})
df_sub = df[0:1]
df_sub.x = -1
print(df)

あなたは得るでしょう:

x
0 -1
1  2

対照的に、以下はdfを変更しません。

df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1
126
cgold

なぜなら、あなたがコピーをしないのであれば、dataFrameを別の名前に割り当てたとしても、インデックスは他の場所で操作することができます。

例えば:

df2 = df
func1(df2)
func2(df)

func1は、df2を変更することによってdfを変更できます。

df2 = df.copy()
func1(df2)
func2(df)
34
sparrow

コピーまたはビューを返すかどうかは、インデックスの種類によって異なることに注意する必要があります。

パンダのドキュメンテーションは言います:

コピーに対してビューを返す

データに対するビューがいつ返されるかに関する規則は、NumPyに完全に依存しています。ラベルの配列またはブールベクトルがインデックス付け操作に関係しているときはいつでも、結果はコピーになります。単一ラベル/スカラーのインデックス作成とスライシングでは、 df.ix [3:6]またはdf.ix [:, 'A']を指定すると、ビューが返されます。

11
Gusev Slava

主な目的は、連鎖インデックスを使用せずにSettingWithCopyWarningを削除することです。

ここで連鎖インデックスはdfc['A'][0] = 111のようなものです。

この文書では、連鎖索引付けは ビューとコピーの対比 で避けるべきだと述べています。これはその文書から少し修正された例です:

In [1]: import pandas as pd

In [2]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})

In [3]: dfc
Out[3]:
    A   B
0   aaa 1
1   bbb 2
2   ccc 3

In [4]: aColumn = dfc['A']

In [5]: aColumn[0] = 111
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [6]: dfc
Out[6]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

ここでaColumnはビューであり、元のDataFrameからのコピーではないため、aColumnを変更すると元のdfcも変更されます。次に、行に最初にインデックスを付けるとします。

In [7]: zero_row = dfc.loc[0]

In [8]: zero_row['A'] = 222
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [9]: dfc
Out[9]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

今回はzero_rowはコピーなので、元のdfcは変更されません。

上記の2つの例から、元のDataFrameを変更するかどうかはあいまいです。あなたが以下のような何かを書くならば、これは特に危険です:

In [10]: dfc.loc[0]['A'] = 333
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [11]: dfc
Out[11]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

今回はまったく機能しませんでした。ここではdfcを変更したいと思いましたが、実際にはコピーである中間値dfc.loc[0]を変更してすぐに破棄されます。 dfc.loc[0]dfc['A']のような中間値がビューなのかコピーなのかを予測するのは非常に難しいので、元のDataFrameが更新されるかどうかは保証されません。そのため、連鎖インデックスは避けるべきであり、パンダはこのような連鎖インデックスの更新のためにSettingWithCopyWarningを生成します。

今は.copy()の使い方です。警告を排除するには、あなたの意図を明示的に表すためにコピーを作成してください。

In [12]: zero_row_copy = dfc.loc[0].copy()

In [13]: zero_row_copy['A'] = 444 # This time no warning

あなたがコピーを修正しているので、あなたはオリジナルのdfcが決して変わらないことを知っています、そしてあなたはそれが変わることを期待していません。あなたの期待は振る舞いと一致し、そしてSettingWithCopyWarningは消えます。

元のDataFrameを変更したい場合は、locを使用することをお勧めします。

In [14]: dfc.loc[0,'A'] = 555

In [15]: dfc
Out[15]:
    A   B
0   555 1
1   bbb 2
2   ccc 3
5
Cosyn

一般的に、元のデータフレームよりもコピーの方が作業が安全です。ただし、元のデータフレームは不要になり、操作バージョンを続行したい場合は除きます。通常は、元のデータフレームを操作したバージョンと比較するなどの目的で使用する必要があります。したがって、ほとんどの人はコピーを作成して最後にマージします。

1
bojax