基于if-else条件合并两个DataFrame的内存优化方案
问题描述
- 现有两个Pandas数据集:
- DF1:共106063行,含
posts(文本内容)、type(类型标签)两个字段 - DF2:共14万余行,含
word(词汇)、emotion(情感分类)、value(情感权重)三个字段
- DF1:共106063行,含
- 期望输出的新DataFrame含3个字段:
type:直接取DF1的type字段值posts_tok:DF1.posts字段按空格拆分后的词汇列表emotions:逐行匹配,若DF2中的word存在于当前行posts_tok列表中,收集对应emotion组成列表;无任何匹配时取值为'O'
- 原有实现问题:采用三层嵌套循环+
iterrows遍历实现逻辑,运行时触发内存溢出(Out of Memory),无法得到结果,需要高性能优化方案。
原有方案的性能瓶颈
这种写法跑不动是必然的:iterrows每遍历一行都会生成一个单独的Series对象,遍历开销是原生列表遍历的上百倍;再加三层嵌套逻辑,相当于每处理1条DF1的文本,就要全量遍历14万行的DF2做匹配,总计算量达到千亿级,过程中生成的大量临时Series、列表对象会快速占满内存,必然触发OOM。
优化实现方案
核心优化逻辑:
- 把DF2的词汇-情感映射提前转成Python字典(哈希表结构),把单步匹配的时间复杂度从O(n)降到O(1),彻底避免逐行遍历DF2的开销
- 全程用Python原生列表做遍历处理,完全弃用
iterrows、逐行apply、逐行concat等高开销写法 - 所有字段数据批量生成后,一次性组装成最终DataFrame,减少中间对象的内存占用
可直接运行的实现代码:
import pandas as pd # 第一步:构建词汇到情感的哈希映射,查询复杂度O(1) # 提前去重避免重复键,丢弃无用字段省内存 word2emotion = ( DF2.drop_duplicates(subset=['word']) .set_index('word')['emotion'] .to_dict() ) emo_word_set = set(word2emotion.keys()) default_emo = ['O'] # 第二步:批量处理所有文本,全程用原生列表遍历 type_list = DF1['type'].tolist() posts_tok_list = [] emotions_list = [] for post_content in DF1['posts'].tolist(): # 按空格拆词 tokens = post_content.split() posts_tok_list.append(tokens) # 用集合交集快速找匹配词,比逐词判断快数倍 matched_words = set(tokens) & emo_word_set if matched_words: emotions_list.append([word2emotion[w] for w in matched_words]) else: emotions_list.append(default_emo) # 第三步:一次性组装结果,避免逐行拼接的内存开销 result_df = pd.DataFrame({ 'type': type_list, 'posts_tok': posts_tok_list, 'emotions': emotions_list })
可选进阶优化
- 若存在同一个word对应多个emotion的场景,构建
word2emotion字典时把value存为列表即可,匹配时直接extend结果列表 - 若文本存在大小写不一致问题,提前把DF2的所有word转小写,拆分后的词汇也转小写后再做匹配,避免漏匹配
- 内存极度紧张时,读取数据阶段就直接丢弃无用字段(比如本次逻辑不需要用到DF2的
value字段,读入时就可以不加载该列),进一步压缩内存占用 - 若需要对emotion去重,匹配完成后把结果列表转集合再转回列表即可
- 该方案在普通办公本上运行耗时通常在10秒以内,内存占用不超过1G,完全不会触发OOM。
内容的提问来源于stack exchange,提问作者Davide GmailJob
相关产品推荐
相关产品推荐

