如何加速Pandas DataFrame的字符串相似度计算?
优化字符串相似度计算的超实用方案
兄弟,6000条Yahoo数据×13万条PTT数据的双重循环跑7天,这效率确实让人崩溃!咱们从替换低效计算库、并行化处理、避免Pandas逐行赋值这几个核心方向入手,把速度提上去。
核心问题分析
你当前的代码慢有两个关键原因:
difflib.SequenceMatcher是纯Python实现的相似度算法,计算单条就慢,乘以千万级别的计算量直接爆炸。- 双重循环里用
df_post.loc[j, ...]逐行赋值,Pandas的这种操作效率极低,相当于每次都要修改整个DataFrame的结构。
优化方案一:用C实现的Levenshtein库替换difflib
python-Levenshtein是基于C语言实现的字符串相似度计算库,比difflib快几十到上百倍,完全兼容你的需求(计算两个字符串的相似度比例)。
首先安装依赖:
pip install python-Levenshtein joblib
然后重构代码,用并行计算+数组操作替代双重循环:
import pandas as pd import Levenshtein from joblib import Parallel, delayed # 1. 读取并预处理数据 df_post = pd.read_csv('ptt_run.csv', encoding='utf8', header=0).fillna('null') df_yahoo = pd.read_csv('yahoo_movie_20180519_test.csv', encoding='utf8', header=0).fillna('null') # 2. 提取核心列转为Numpy数组(比Pandas列访问快很多) yahoo_texts = df_yahoo['yahoo_ch_nosign'].values post_texts = df_post['title_nosign'].values yahoo_ids = df_yahoo['yahoo_movie_id'].values # 3. 定义单条Yahoo数据的相似度计算函数 def compute_sim(yahoo_text, yahoo_id): # 用Levenshtein.ratio计算相似度,比difflib快N倍 sim_scores = [Levenshtein.ratio(yahoo_text, post_text) for post_text in post_texts] return (yahoo_id, sim_scores) # 4. 并行计算:利用所有CPU核心同时处理Yahoo数据 # verbose=10可以看到实时进度 results = Parallel(n_jobs=-1, verbose=10)( delayed(compute_sim)(text, idx) for text, idx in zip(yahoo_texts, yahoo_ids) ) # 5. 批量赋值到DataFrame(避免逐行修改) for yahoo_id, scores in results: df_post[yahoo_id] = scores # 6. 保存结果 df_post.to_csv('df_score_test.csv', encoding='utf8', index=False)
优化方案二:内存不足时的分块处理
如果你的机器内存不够(13万×6000的float64数据大概占6GB内存),可以把PTT数据分成小块处理,降低内存压力:
import pandas as pd import Levenshtein from joblib import Parallel, delayed df_post = pd.read_csv('ptt_run.csv', encoding='utf8', header=0).fillna('null') df_yahoo = pd.read_csv('yahoo_movie_20180519_test.csv', encoding='utf8', header=0).fillna('null') yahoo_texts = df_yahoo['yahoo_ch_nosign'].values post_texts = df_post['title_nosign'].values yahoo_ids = df_yahoo['yahoo_movie_id'].values # 把PTT数据分成10块(每块1.3万条),根据你的内存调整chunk_size chunk_size = 13000 post_chunks = [post_texts[i:i+chunk_size] for i in range(0, len(post_texts), chunk_size)] def compute_sim_chunk(yahoo_text, yahoo_id, post_chunk): sim_scores = [Levenshtein.ratio(yahoo_text, post_text) for post_text in post_chunk] return (yahoo_id, sim_scores) # 逐块计算并赋值 for chunk_idx, post_chunk in enumerate(post_chunks): print(f"正在处理第 {chunk_idx+1}/{len(post_chunks)} 块数据") start_row = chunk_idx * chunk_size end_row = start_row + len(post_chunk) # 并行计算当前块的相似度 chunk_results = Parallel(n_jobs=-1, verbose=5)( delayed(compute_sim_chunk)(text, idx, post_chunk) for text, idx in zip(yahoo_texts, yahoo_ids) ) # 把结果赋值到对应行 for yahoo_id, scores in chunk_results: df_post.loc[start_row:end_row-1, yahoo_id] = scores df_post.to_csv('df_score_test.csv', encoding='utf8', index=False)
效果预估
- 单条计算速度:
Levenshtein比difflib快50-100倍。 - 并行计算:如果你的机器有8核CPU,总时间可以降到原来的1/8左右。
- 综合下来,原来7天的计算量,现在应该能在几小时内完成(甚至更快,取决于CPU核心数)。
内容的提问来源于stack exchange,提问作者Steven
相关产品推荐
相关产品推荐

