Pandas左合并DataFrame遇内存错误与性能瓶颈的优化咨询
碰到这种大数据量的Pandas合并问题,我之前也踩过不少坑,给你几个实用的优化方案,亲测能解决内存溢出和速度慢的问题:
先修复去重操作的bug
你当前的代码里unique_b.drop_duplicates()其实没生效!因为Pandas的drop_duplicates()默认inplace=False,不会修改原DataFrame,必须赋值回去。而且要指定按id去重(毕竟我们是按id匹配),这样能最大程度压缩b的规模:unique_b = b[['id', 'name']].drop_duplicates(subset='id', keep='first')用
map替代merge(速度提升最明显)
左连接按id匹配name的场景,本质是一个哈希映射操作,map的效率比merge高得多,尤其是大数据量下。先把去重后的b转成id到name的映射Series,再直接映射到a中:# 构建id到name的映射 name_mapping = unique_b.set_index('id')['name'] # 直接给a新增name列 a['name'] = a['id'].map(name_mapping)这个操作比
pd.merge快好几倍,内存占用也低很多,因为不需要创建中间合并表。压缩DataFrame的内存占用
大数据量下,dtype的选择对内存影响极大。可以把列转成更节省空间的类型:# 优化id列:如果是整数,用int32代替默认的int64(只要数值范围允许) unique_b['id'] = unique_b['id'].astype('int32') a['id'] = a['id'].astype('int32') # 优化name列:如果name的重复率高,转成category类型 unique_b['name'] = unique_b['name'].astype('category')这样能把内存占用降低30%-70%,从根源避免MemoryError。
分块处理(极端内存不足时用)
如果内存实在不够装下整个a,可以把a切成小块,逐个处理后再拼接:# 按10万行一块拆分a(可根据内存调整chunk_size) chunk_size = 100000 chunks = [] for i in range(0, len(a), chunk_size): chunk = a.iloc[i:i+chunk_size].copy() chunk['name'] = chunk['id'].map(name_mapping) chunks.append(chunk) # 拼接所有块 a = pd.concat(chunks, ignore_index=True)从MongoDB数据源端优化
既然数据是从MongoDB读的,不如在读取前就完成去重,减少传输到Python的数据量:
用MongoDB的聚合管道直接返回去重后的id和name:// MongoDB聚合命令 db.b.aggregate([ { $group: { _id: "$id", name: { $first: "$name" } } }, { $project: { id: "$_id", name: 1, _id: 0 } } ])这样读到Pandas里的b已经是去重后的,省掉了Pandas里的去重步骤,效率更高。
优先试试前三个方案,尤其是用map替代merge,应该能快速解决你的问题!
内容的提问来源于stack exchange,提问作者daiyue

