使用生成器构造pandas DataFrame出现数据全重复问题排查
异常原因
这个问题和pandas、生成器的原生迭代逻辑无关,根因是transform_xml生成器的实现存在对象引用错误:
- 你在写
transform_xml时,大概率在遍历XML节点的循环外部,提前初始化了一个存储商品字段的字典 - 遍历每个XML节点时,你只是修改这同一个字典的键值,之后直接
yield这个字典对象,没有在每次循环时新建独立的字典实例 - 前三次调用
next(trans)时,你拿到字典引用后立刻执行打印,此时字典还没被下一次迭代修改,所以输出的3条商品数据看起来是正常的。但这三次返回值本质上都是同一个字典对象的引用,如果你把这三个返回值存为变量,等生成器迭代完成后再查看,值也会被覆盖。 - 当你用
list(trans)批量收集生成器剩余内容时,列表里存储的全是指向这同一个字典的引用。等生成器迭代到最后一条Kenzo商品数据时,字典的内容就被固定为这条Kenzo商品的字段,最终列表里的所有元素、转成DataFrame后的所有行,就全是最后一条Kenzo数据的重复值。
错误写法复现
会触发该问题的生成器典型写法如下:
def transform_xml(eroot): item = {} # 循环外仅初始化1次字典 for node in eroot.iter("product"): # 每次迭代仅修改同一个字典的键值 item["MANUFACTURER"] = node.findtext("manufacturer") item["NAME"] = node.findtext("name") item["EAN"] = node.findtext("ean") item["id"] = node.findtext("id") item["SIZE"] = node.findtext("size") yield item # 每次返回的都是同一个字典的引用
修复方案
把字典的初始化逻辑移到循环内部,保证每次迭代都生成一个全新的独立字典实例再返回即可:
def transform_xml(eroot): for node in eroot.iter("product"): # 每次循环新建独立字典,不存在引用覆盖问题 item = { "MANUFACTURER": node.findtext("manufacturer"), "NAME": node.findtext("name"), "EAN": node.findtext("ean"), "id": node.findtext("id"), "SIZE": node.findtext("size") } yield item
你可以用一个小测试验证修复效果:修复后把前三次next(trans)的返回值分别存为变量,执行a is b判断会返回False,说明三个返回值是独立的字典对象,不会再出现值被覆盖、DataFrame行重复的问题。
内容的提问来源于stack exchange,提问作者Айдын
相关产品推荐
相关产品推荐

