287

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

X = my_dataframe[features_list].copy()

...而不仅仅是

X = my_dataframe[features_list]

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

4

7 回答 7

303

这扩展了保罗的回答。在 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
于 2014-12-28T20:01:49.483 回答
73

因为如果您不制作副本,那么即使您将 dataFrame 分配给不同的名称,索引仍然可以在其他地方进行操作。

例如:

df2 = df
func1(df2)
func2(df)

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

df2 = df.copy()
func1(df2)
func2(df)
于 2016-09-22T01:27:07.940 回答
25

有必要提到返回副本或视图取决于索引类型。

熊猫文档说:

返回视图与副本

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

于 2017-01-20T13:22:43.417 回答
22

主要目的是避免链式索引并消除SettingWithCopyWarning.

这里链式索引类似于dfc['A'][0] = 111

该文档说,在Returning a view vs a copy中应避免使用链式索引。这是该文档中稍作修改的示例:

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没有修改。

从上面这两个示例中,我们看到是否要更改原始 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 是否会被更新。这就是应该避免链式索引的原因,pandas 会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
于 2018-10-22T09:58:43.983 回答
13

假设您有如下数据框

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

当您想创建另一个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

并且只想修改 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

同时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

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

id(df1)
140367679979600
id(df2)
140367679979600

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


如果我们添加copy, and nowdf1df2被认为是不同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

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

于 2020-06-17T01:50:01.860 回答
2

一般来说,处理副本比处理原始数据帧更安全,除非您知道不再需要原始数据帧并希望继续处理经过处理的版本。通常,您仍然可以将原始数据框与操纵版本等进行比较。因此,大多数人在最后进行复制和合并。

于 2018-03-28T23:31:45.523 回答
1

Pandas Deep copy 保持初始 DataFrame 不变。

当您想要规范化 DataFrame 并希望保持初始 df 不变时,此功能特别有用。例如:

df = pd.DataFrame(np.arange(20).reshape(2,10))

然后你规范化数据:

# Using Sklearn MinMaxSacaler method
scaler = preprocessing.MinMaxScaler()

并且您基于第一个创建一个新的 df 并希望第一个保持不变,您必须使用 .copy() 方法

new_df = pd.DataFrame(df).copy() # Deep Copy
for i in range(10):
    pd_features[i] = scaler.fit_transform(unnormal_pd_features[i].values.reshape(-1,1))

否则你原来的 df 也会改变。

于 2021-06-02T06:07:57.403 回答