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

X = my_dataframe[features_list].copy()

...而不仅仅是

X = my_dataframe[features_list]

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


当前回答

假设您有如下数据帧

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,没有复制

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是同一个对象,我们可以使用id来检查它

id(df1)
140367679979600
id(df2)
140367679979600

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


如果我们添加副本,现在df1和df2被认为是不同的对象,如果我们对其中一个做相同的改变,另一个将不会改变。

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

其他回答

这扩展了保罗的回答。在Pandas中,索引一个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

这个答案在熊猫的新版本中已经被弃用了。看文档

因为如果你不复制,那么即使你把dataFrame赋给一个不同的名字,索引仍然可以在其他地方被操纵。

例如:

df2 = df
func1(df2)
func2(df)

Func1可以通过修改df2来修改df,为了避免:

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

假设您有如下数据帧

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,没有复制

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是同一个对象,我们可以使用id来检查它

id(df1)
140367679979600
id(df2)
140367679979600

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


如果我们添加副本,现在df1和df2被认为是不同的对象,如果我们对其中一个做相同的改变,另一个将不会改变。

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

熊猫深度拷贝保持初始数据框架不变。

当你想要规范化一个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也会改变。

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

熊猫的文件说:

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