如何避免重复词形还原Pandas DataFrame中的重复句子以提升效率
高效处理Pandas中重复句子的词形还原方案
给定条件
如下是一个小型Pandas DataFrame示例:
import numpy as np import pandas as pd df = pd.DataFrame( { "user_ip": ["u7", "u3", "u1", "u9", "u4","u8", "u1", "u2", "u5"], "raw_sentence": ["First sentence!", np.nan, "I go to school everyday!", "She likes chips!", "I go to school everyday!", "This is 1 sample text!", "She likes chips!", "This is the thrid sentence.", "I go to school everyday!"], } )
对应的DataFrame展示:
| user_ip | raw_sentence |
|---|---|
| u7 | First sentence! |
| u3 | NaN |
| u1 | I go to school everyday! |
| u9 | She likes chips! |
| u4 | I go to school everyday! |
| u8 | This is 1 sample text! |
| u1 | She likes chips! |
| u2 | This is the thrid sentence. |
| u5 | I go to school everyday! |
目标
针对约10万行的大型Pandas DataFrame,避免对raw_sentence列中的重复句子重复执行词形还原操作,提升处理效率,替代当前使用map的策略。
低效的当前解决方案
目前使用.map()结合lambda遍历每一行,调用get_lm()函数获取词形还原结果,代码如下:
import nltk nltk.download('all', quiet=True, raise_on_error=True,) STOPWORDS = nltk.corpus.stopwords.words('english') wnl = nltk.stem.WordNetLemmatizer() tokenizer = nltk.tokenize.RegexpTokenizer(r'\w+') def get_lm(input_sent:str="my text!"): tks = [ w for w in tokenizer.tokenize(input_sent.lower()) if not w in STOPWORDS and len(w) > 1 and not w.isnumeric() ] lms = [ wnl.lemmatize(w, t[0].lower()) if t[0].lower() in ['a', 's', 'r', 'n', 'v'] else wnl.lemmatize(w) for w, t in nltk.pos_tag(tks)] return lms df["lemma"] = df["raw_sentence"].map(lambda raw: get_lm(input_sent=raw), na_action='ignore')
处理后的结果:
| user_ip | raw_sentence | lemma | 说明 |
|---|---|---|---|
| u7 | First sentence! | [first, sentence] | 首次出现,执行词形还原 |
| u3 | NaN | NaN | 通过na_action='ignore'忽略空值 |
| u1 | I go to school everyday! | [go, school, everyday] | 首次出现,执行词形还原 |
| u9 | She likes chips! | [like, chip] | 首次出现,执行词形还原 |
| u4 | I go to school everyday! | [go, school, everyday] | 重复句子,无需再次执行词形还原 |
| u8 | This is 1 sample text! | [sample, text] | 首次出现,执行词形还原 |
| u1 | She likes chips! | [like, chip] | 重复句子,无需再次执行词形还原 |
| u2 | This is the thrid sentence. | [thrid, sentence] | 首次出现,执行词形还原 |
| u5 | I go to school everyday! | [go, school, everyday] | 重复句子,无需再次执行词形还原 |
高效优化方案
方案1:字典缓存已处理结果
用字典存储已完成词形还原的句子结果,遍历列时先查缓存,存在则直接取用,不存在再处理并存入缓存,彻底避免重复计算。
# 初始化缓存字典 lemma_cache = {} def get_lm_cached(input_sent: str): if pd.isna(input_sent): return np.nan if input_sent in lemma_cache: return lemma_cache[input_sent] # 执行原词形还原逻辑 tks = [w for w in tokenizer.tokenize(input_sent.lower()) if w not in STOPWORDS and len(w) > 1 and not w.isnumeric()] lms = [wnl.lemmatize(w, t[0].lower()) if t[0].lower() in ['a', 's', 'r', 'n', 'v'] else wnl.lemmatize(w) for w, t in nltk.pos_tag(tks)] # 存入缓存 lemma_cache[input_sent] = lms return lms df["lemma"] = df["raw_sentence"].apply(get_lm_cached)
方案2:提取唯一句子处理后映射回原表
先提取raw_sentence中的非空唯一值,仅对这些值执行词形还原,再通过映射关系将结果同步回原DataFrame。
# 提取非空的唯一句子 unique_sents = df["raw_sentence"].dropna().unique() # 生成唯一句子的词形还原结果 unique_lemmas = pd.Series(unique_sents).apply(get_lm) # 创建句子与结果的映射字典 lemma_map = dict(zip(unique_sents, unique_lemmas)) # 映射回原DataFrame df["lemma"] = df["raw_sentence"].map(lemma_map)
方案3:分组广播结果
通过groupby按raw_sentence分组,对每组仅执行一次词形还原,再将结果广播到组内所有行。
df["lemma"] = df.groupby("raw_sentence")["raw_sentence"].transform( lambda x: get_lm(x.iloc[0]) if not pd.isna(x.iloc[0]) else np.nan )
效率说明
三种方案都能大幅减少重复计算:
- 方案1(字典缓存)在句子重复率较高时效率提升最明显,无需额外分组/合并操作,逻辑直接;
- 方案2适合重复句子占比极高的场景,仅处理唯一值的开销最小;
- 方案3代码最简洁,但分组操作在数据量极大时,性能略逊于前两者。
内容的提问来源于stack exchange,提问作者farid
相关产品推荐
相关产品推荐

