如何在Pandas中基于另一列多条件检测词汇并高效处理百万行数据?
问题背景
给定输入DataFrame:
import pandas as pd df_input = pd.DataFrame({'Keyword': {0: 'apple banana, orange', 1: 'apple orange ?banana "', 2: 'potato, piercing pot hole', 3: 'armor hard known'}, 'Returns': {0: 'Fruit; Banana Vendor', 1: 'Blendor :Kaka Orange', 2: 'piercing Fruit Banana takes a lot', 3: 'bullet jacket gun'}})
需要按以下规则生成三个得分列:
- Score:若
Keyword中的任意词汇出现在Returns列中,得1,否则0 - Score_before:若
Keyword中的任意词汇出现在Returns列词汇的前半部分,得1 - Score_after:若
Keyword中的任意词汇出现在Returns列词汇的后半部分,得1
预期输出:
import pandas as pd df_output = pd.DataFrame({'Keyword': {0: 'apple banana, orange', 1: 'apple orange ?banana "', 2: 'potato, piercing pot hole', 3: 'armor hard known'}, 'Returns': {0: 'Fruit; Banana Vendor', 1: 'Blendor :Kaka Orange', 2: 'piercing Fruit Banana takes a lot', 3: 'bullet jacket gun'}, 'Score': {0: 1, 1: 1, 2: 1, 3: 0}, 'Score_before': {0: 0, 1: 0, 2: 1, 3: 0}, 'Score_after': {0: 0, 1: 1, 2: 0, 3: 0}})
由于数据量达百万行,咨询以下问题:
- 如何高效进行词汇分词?
- 是否应使用字符串操作替代分词工具?
- 若使用自定义分词函数,如何在整个DataFrame上应用?
自定义分词函数:
import nltk from nltk.tokenize import word_tokenize nltk.download('punkt') import string def tokenize(s): translator = str.maketrans('', '', string.punctuation) s = s.translate(translator) s = word_tokenize(s) return s tokenize('Finity: Stocks%, Direct$ MF, ETF')
解决方案
一、分词效率与工具选择
百万级数据量下,优先用轻量字符串操作替代NLTK这类重型分词工具,原因如下:
- NLTK的
word_tokenize虽精准,但涉及复杂规则解析,速度远慢于原生字符串处理; - 你的场景仅需去除标点后按空格拆分,完全可以用字符串操作实现,无需调用分词库。
替代方案(比自定义函数快数倍):
import string def fast_tokenize(s): # 去除标点+按空格拆分+过滤空字符串+统一转小写(避免大小写匹配问题) s = s.translate(str.maketrans('', '', string.punctuation)) return [word.lower() for word in s.split() if word]
二、自定义分词函数的批量应用
如果坚持使用NLTK分词,要避免逐行循环(效率极低),推荐以下两种批量处理方式:
方法1:直接用pandas.Series.apply
import nltk from nltk.tokenize import word_tokenize import string nltk.download('punkt') def tokenize(s): translator = str.maketrans('', '', string.punctuation) s = s.translate(translator) return [word.lower() for word in word_tokenize(s)] # 批量处理两列 df_input['Keyword_tokens'] = df_input['Keyword'].apply(tokenize) df_input['Returns_tokens'] = df_input['Returns'].apply(tokenize)
方法2:用swifter加速(超大数据量首选)
swifter会自动判断用普通apply还是矢量化操作,大幅提升百万行数据的处理速度:
!pip install swifter import swifter df_input['Keyword_tokens'] = df_input['Keyword'].swifter.apply(tokenize) df_input['Returns_tokens'] = df_input['Returns'].swifter.apply(tokenize)
三、得分列的高效生成
基于分词结果,用矢量化操作生成得分列,避免Python循环:
步骤1:拆分Returns的前后半部分
def split_returns(tokens): half = len(tokens) // 2 return tokens[:half], tokens[half:] # 拆分后生成两列 df_input[['Returns_before', 'Returns_after']] = df_input['Returns_tokens'].apply(lambda x: pd.Series(split_returns(x)))
步骤2:用集合加速成员判断(核心优化)
列表的in操作是O(n),集合是O(1),先把分词结果转为集合:
df_input['Keyword_set'] = df_input['Keyword_tokens'].apply(set) df_input['Returns_set'] = df_input['Returns_tokens'].apply(set) df_input['Returns_before_set'] = df_input['Returns_before'].apply(set) df_input['Returns_after_set'] = df_input['Returns_after'].apply(set)
步骤3:生成得分列
# Score:判断词汇交集是否为空 df_input['Score'] = df_input.apply(lambda row: 1 if row['Keyword_set'] & row['Returns_set'] else 0, axis=1) # Score_before:判断与前半部分的交集 df_input['Score_before'] = df_input.apply(lambda row: 1 if row['Keyword_set'] & row['Returns_before_set'] else 0, axis=1) # Score_after:判断与后半部分的交集 df_input['Score_after'] = df_input.apply(lambda row: 1 if row['Keyword_set'] & row['Returns_after_set'] else 0, axis=1) # 保留目标列得到最终输出 df_output = df_input[['Keyword', 'Returns', 'Score', 'Score_before', 'Score_after']]
四、总结
- 百万行数据优先用原生字符串操作分词,比NLTK快数倍;
- 必须用NLTK时,搭配
swifter加速apply; - 用集合替代列表做成员判断,大幅提升得分计算效率;
- 全程避免Python循环,尽量用Pandas矢量化操作或优化后的
apply。
内容的提问来源于stack exchange,提问作者Mystic
相关产品推荐
相关产品推荐

