熊猫.DataFrame.copy(deep=True) 实际上并不创建深层拷贝



我已经用pd做了一段时间的实验。系列和 pd。数据帧并面临一些奇怪的问题。假设我有以下 pd。数据帧:

df = pd.DataFrame({'col':[[1,2,3]]})

请注意,此数据帧包含包含列表的列。我想修改此数据帧的副本并返回其修改后的版本,以便初始版本保持不变。为了简单起见,假设我想在其单元格中添加整数"4"。

我尝试了以下代码:

def modify(df):
dfc = df.copy(deep=True)
dfc['col'].iloc[0].append(4)
return dfc
modify(df)
print(df)

问题是,除了新的复制dfc,初始数据帧df也被修改。为什么?我应该怎么做才能防止初始数据帧被修改?我的熊猫版本是0.25.0

从此处的文档,在注释部分:

当 deep=True 时,将复制数据,但不会递归复制实际的 Python 对象,只会复制对该对象的引用。这与标准库中的copy.deepcopy形成对比,后者递归复制对象数据(请参阅下面的示例(。

在GitHub 上的本期中再次引用了这一点,开发人员指出:

在 a 中嵌入可变对象。 数据帧是一种反模式

因此,此函数正在按照开发人员的意图工作 - 列表等可变对象不应嵌入到数据帧中。

我找不到一种方法可以让copy.deepcopy在数据帧上按预期工作,但我确实使用 pickle 发现了一个相当糟糕的解决方法:

import pandas as pd
import pickle
df = pd.DataFrame({'col':[[1,2,3]]})
def modify(df):
dfc = pickle.loads(pickle.dumps(df))
print(dfc['col'].iloc[0] is df['col'].iloc[0]) #Check if we've succeeded in deepcopying
dfc['col'].iloc[0].append(4)
print(dfc)
return dfc
modify(df)
print(df)

输出:

False
col
0  [1, 2, 3, 4]
col
0  [1, 2, 3]

这是一个有趣的问题。发生这种情况的原因是,即使您正在创建数据帧的副本,这也不会改变内部列表仍在引用同一对象的事实,或者换句话说,这些对象不会递归复制。这可以通过检查列表id看到:

df = pd.DataFrame({'col':[[1,2]]})
dfc = df.copy()
dfc['col'].iloc[0].append(4)
id(df.iloc[0,0])
# 1734189849288
id(dfc.iloc[0,0])
# 1734189849288

我能想到的一种解决方法是沿感兴趣的列应用list.copy

id(df.iloc[0,0])
# 1734174279432
dfc = df.copy()
dfc['col'] = df.col.apply(list.copy)
id(dfc.iloc[0,0])
# 1734186015688

最新更新