如何复制含列表的Pandas DataFrame,避免原数据被修改?
解决Pandas DataFrame含列表列时深拷贝失效的问题
先还原一下你遇到的场景:当DataFrame里包含列表这种可变对象时,哪怕用copy.deepcopy()或者df.copy(deep=True),修改拷贝后的DataFrame里的列表元素,原DataFrame也会跟着被修改,就像下面这样:
首先创建带列表列的DataFrame:
import pandas as pd import copy a = pd.DataFrame({'a': [[1,1],[2, 2],[3,3]], 'b':[3,2,1]}) # 输出结果: # a b # 0 [1, 1] 3 # 1 [2, 2] 2 # 2 [3, 3] 1
执行深拷贝:
b = copy.deepcopy(a)
修改拷贝后的第一个列表元素:
b.a[0][0] = 0 # b的输出: # a b # 0 [0, 1] 3 # 1 [2, 2] 2 # 2 [3, 3] 1
结果原DataFrame a 也被修改了:
print(a) # 输出: # a b # 0 [0, 1] 3 # 1 [2, 2] 2 # 2 [3, 3] 1
这事儿其实是因为Pandas对可变对象(比如列表、字典)的深拷贝逻辑有点特殊——它默认只会拷贝DataFrame的外层结构,而里面的可变对象本身还是共享引用的。要彻底切断这种连锁影响,得手动把每个可变元素也单独拷贝一遍,下面给你几种可行的方法:
方法一:对列表列单独做深拷贝
用apply遍历包含列表的列,对每个元素单独执行copy.deepcopy,这样每个列表都会被完全复制,不再和原DataFrame共享引用:
import pandas as pd import copy a = pd.DataFrame({'a': [[1,1],[2, 2],[3,3]], 'b':[3,2,1]}) # 先做外层深拷贝,再单独处理列表列 b = a.copy(deep=True) b['a'] = b['a'].apply(copy.deepcopy) # 现在修改b的列表元素,原a完全不受影响 b.a[0][0] = 0 print(a) # 输出: # a b # 0 [1, 1] 3 # 1 [2, 2] 2 # 2 [3, 3] 1 print(b) # 输出: # a b # 0 [0, 1] 3 # 1 [2, 2] 2 # 2 [3, 3] 1
方法二:用JSON序列化+反序列化实现彻底拷贝
另一种思路是把整个DataFrame转成JSON字符串,再重新解析回来。这个过程会让所有可变对象被重新创建,完全切断和原数据的引用关联:
import pandas as pd import json a = pd.DataFrame({'a': [[1,1],[2, 2],[3,3]], 'b':[3,2,1]}) # 序列化转JSON,再反序列化回DataFrame b = pd.read_json(json.dumps(a.to_dict(orient='records')), orient='records') b.a[0][0] = 0 print(a) # 原DataFrame的列表完全没变化
方法三:逐行手动复制列表
如果你喜欢更直观的方式,也可以逐行处理,把每个列表单独复制一份:
import pandas as pd a = pd.DataFrame({'a': [[1,1],[2, 2],[3,3]], 'b':[3,2,1]}) b = a.copy(deep=True) # 遍历每行,复制列表元素 for idx in b.index: b.at[idx, 'a'] = b.at[idx, 'a'].copy() b.a[0][0] = 0 print(a) # 原数据保持初始状态
补充说明
为什么原来的深拷贝没用?因为Pandas的deepcopy在处理Series时,对于列表这类可变容器,只会拷贝Series的外层结构,容器内的元素还是引用原对象。所以必须对每个列表元素单独做深拷贝,才能彻底断开关联。
内容的提问来源于stack exchange,提问作者Azuuu
相关产品推荐
相关产品推荐

