为什么我要在pandas中复制一个数据框

Eli*_*eph 137 python pandas chained-assignment

从父数据帧中选择子数据帧时,我注意到一些程序员使用该.copy()方法复制数据帧.

他们为什么要复制数据框?如果我不复制会怎么样?

cgo*_*old 156

这扩展了保罗的答案.在Pandas中,索引DataFrame会返回对初始DataFrame的引用.因此,更改子集将更改初始DataFrame.因此,如果要确保初始DataFrame不应更改,则需要使用该副本.请考虑以下代码:

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

你会得到:

x
0 -1
1  2
Run Code Online (Sandbox Code Playgroud)

相反,以下叶子df不变:

df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1
Run Code Online (Sandbox Code Playgroud)

  • 这是深拷贝吗? (11认同)
  • 我发现这篇关于 panda/numpy 中的深/浅副本问题的文章非常清晰和全面:https://realpython.com/pandas-settingwithcopywarning/ (3认同)
  • 是。默认模式是“深层”复制!https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.copy.html (2认同)

spa*_*row 38

因为如果您不复制,那么即使您将dataFrame分配给其他名称,索引仍然可以在其他地方操作.

例如:

df2 = df
func1(df2)
func2(df)
Run Code Online (Sandbox Code Playgroud)

func1可以通过修改df2来修改df,所以为了避免这种情况:

df2 = df.copy()
func1(df2)
func2(df)
Run Code Online (Sandbox Code Playgroud)

  • 这是因为在第一个示例中,“df2 = df”,两个变量都引用同一个 DataFrame 实例。因此,对“df”或“df2”所做的任何更改都将针对同一对象实例进行。而在“df2 = df.copy()”中,创建了第二个对象实例,它是第一个对象实例的副本,但现在“df”和“df2”引用不同的对象实例,并且任何更改都将对其各自的 DataFrame 进行实例。 (7认同)

Gus*_*ava 12

有必要提一下,返回的副本或视图取决于索引的类型.

熊猫文档说:

返回视图与副本

关于何时返回数据视图的规则完全取决于NumPy.每当索引操作中涉及标签数组或布尔向量时,结果将是副本.使用单标签/标量索引和切片,例如df.ix [3:6]或df.ix [:,'A'],将返回视图.


WeN*_*Ben 8

假设您有如下数据框

df1
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
Run Code Online (Sandbox Code Playgroud)

当您想创建另一个df2与 相同的 时df1,没有copy

df2=df1
df2
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
Run Code Online (Sandbox Code Playgroud)

并且只想修改 df2 值,如下所示

df2.iloc[0,0]='changed'

df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
Run Code Online (Sandbox Code Playgroud)

同时 df1 也发生了变化

df1
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
Run Code Online (Sandbox Code Playgroud)

由于两个 df 相同object,我们可以使用id

id(df1)
140367679979600
id(df2)
140367679979600
Run Code Online (Sandbox Code Playgroud)

所以它们是同一个对象,一个改变另一个也会传递相同的值。


如果我们添加copy, 现在df1df2被认为是不同的object,如果我们对其中一个进行相同的更改,则另一个将不会更改。

df2=df1.copy()
id(df1)
140367679979600
id(df2)
140367674641232

df1.iloc[0,0]='changedback'
df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
Run Code Online (Sandbox Code Playgroud)

值得一提的是,当您对原始数据帧进行子集化时,添加副本也是安全的,以避免 SettingWithCopyWarning


Cos*_*syn 7

主要目的是避免链接索引并消除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
Run Code Online (Sandbox Code Playgroud)

这里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
Run Code Online (Sandbox Code Playgroud)

这次zero_row是副本,因此原始文件dfc没有被修改。

从上面的两个示例中,我们可以看出是否要更改原始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
Run Code Online (Sandbox Code Playgroud)

这次根本没有用。在这里我们想更改dfc,但实际上我们修改了一个中间值dfc.loc[0],该中间值是一个副本,被立即丢弃。很难预测中间值是dfc.loc[0]还是dfc['A']视图或副本,因此无法保证是否会更新原始DataFrame。这就是为什么应该避免链接索引的原因,而pandas会SettingWithCopyWarning为这种链接索引更新生成。

现在是的使用.copy()。要消除该警告,请复制一份以明确表达您的意图:

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

In [13]: zero_row_copy['A'] = 444 # This time no warning
Run Code Online (Sandbox Code Playgroud)

由于您正在修改副本,因此您知道原件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
Run Code Online (Sandbox Code Playgroud)