如何高效统计人名中双词组合的出现次数?
高效统计人名中单词共现次数的方法
问题背景
我有一个包含单列人名的DataFrame,数据如下:
name -------------- john doe john david doe doe henry john john henry
需求是统计每对单词在人名中的无序共现次数:即两个单词只要在同一条人名记录中出现就算一次,不考虑顺序。例如john和doe在3条记录中共同出现,共3次。
预期输出
name1 | name2 | count ---------------------- david | doe | 1 doe | henry | 1 doe | john | 3 henry | john | 2
注意:name1需为按字母顺序排在前面的单词。
当前采用暴力解法:
- 生成DataFrame中所有唯一单词的列表
- 对每个唯一单词
W,筛选出包含W的记录 - 从筛选后的记录中统计其他单词的出现频率,得到
W与其他单词的共现次数
但该方法为二次复杂度,数据量大时效率极低;尝试过NLP包的n-gram提取,但会将所有名字拼接成字符串,导致相邻名字的首尾单词被误统计,高估共现次数。
高效解决方案
方法1:词频矩阵+矩阵乘法
核心逻辑是通过词频矩阵的转置乘积直接计算共现次数,适合大数据量场景:
import pandas as pd import numpy as np # 构造示例DataFrame df = pd.DataFrame({ 'name': ['john doe', 'john david doe', 'doe henry john', 'john henry'] }) # 1. 提取所有唯一单词,建立单词-索引映射 all_words = df['name'].str.split(expand=True).stack().unique() word_idx_map = {word: idx for idx, word in enumerate(all_words)} # 2. 构建词频矩阵:每行对应一条人名,每列对应一个单词,值为1表示该单词出现在人名中 freq_matrix = np.zeros((len(df), len(all_words)), dtype=int) for row_idx, name in enumerate(df['name']): for word in name.split(): freq_matrix[row_idx, word_idx_map[word]] = 1 # 3. 计算共现矩阵:矩阵转置相乘,得到单词对的共现次数 cooccur_matrix = freq_matrix.T @ freq_matrix # 4. 转换为目标格式:提取非重复对、排序 cooccur_df = pd.DataFrame(cooccur_matrix, index=all_words, columns=all_words) # 提取上三角区域(排除自身共现和重复对) cooccur_df = cooccur_df.where(np.triu(np.ones(cooccur_df.shape), k=1).astype(bool)).stack().reset_index() cooccur_df.columns = ['name1', 'name2', 'count'] # 确保name1按字母顺序在前,然后排序输出 cooccur_df[['name1', 'name2']] = np.sort(cooccur_df[['name1', 'name2']], axis=1) cooccur_df = cooccur_df.sort_values(['name1', 'name2']).reset_index(drop=True) print(cooccur_df)
方法2:生成合法单词对+分组统计
直接针对每条人名生成所有无序单词对,再全局统计次数,内存占用更低:
import pandas as pd from itertools import combinations df = pd.DataFrame({ 'name': ['john doe', 'john david doe', 'doe henry john', 'john henry'] }) # 对每条人名生成排序后的单词对(保证统一顺序,避免重复统计) df['word_pairs'] = df['name'].apply( lambda x: [tuple(sorted(pair)) for pair in combinations(x.split(), 2)] ) # 展开所有单词对并统计次数 result = df.explode('word_pairs').value_counts('word_pairs').reset_index() # 拆分单词对为两列 result[['name1', 'name2']] = pd.DataFrame(result['word_pairs'].tolist(), index=result.index) # 整理格式并排序 result = result.drop('word_pairs', axis=1).rename(columns={0: 'count'}) result = result.sort_values(['name1', 'name2']).reset_index(drop=True) print(result)
两种方法都能高效得到符合要求的结果,其中方法1适合单词数量较多的场景,方法2适合人名拆分后单词数量较少的场景。
内容的提问来源于stack exchange,提问作者Stacker
相关产品推荐
相关产品推荐

