如何高效实现Pandas DataFrame中列表的两两对比并生成叉积矩阵?
问题描述
给定如下包含列表类型列的DataFrame:
import pandas as pd df = pd.DataFrame({'Ind':['A','B','C','D'],'lists':[['dog','cat','horse','squirrel','bird'], ['dog','horse','fish','whale'], ['moose','cat','squirrel','ant','chicken'], ['dog','moose','cat','bird','ant']]})
需要生成一个对称的相似度矩阵,其中元素规则为:
- 列表完全相同时值为0
- 无共同元素时值为1
- 计算公式:
(min(len(list1), len(list2)) - 共同元素数量) / min(len(list1), len(list2))
当前使用双重循环实现需求,但数据规模增大时性能不足。已知矩阵是对称的,希望通过减少计算次数(仅计算N*(N+1)/2次而非N²次)来优化,同时保持输出与原结果一致。
原循环实现代码:
list_tot = [] for i in range(len(df)): list_temp = [] for j in range(len(df)): list1 = df.iloc[i]['lists'] list2 = df.iloc[j]['lists'] minlist = min(len(list1),len(list2)) dis = (minlist - len([el for el in list1 if el in list2]))/minlist list_temp.append(dis) list_tot.append(list_temp)
原输出结果:
[[0.0, 0.5, 0.6, 0.4], [0.5, 0.0, 1.0, 0.75], [0.6, 1.0, 0.0, 0.4], [0.4, 0.75, 0.4, 0.0]]
优化方案
1. 用集合优化交集计算效率
原代码中通过列表推导式找共同元素的效率较低,先将所有列表转为集合,利用集合的快速交集操作提升性能:
df['sets'] = df['lists'].apply(set)
2. 利用对称性减少计算量
由于矩阵是对称的(matrix[i][j] = matrix[j][i]),且对角线元素恒为0,只需计算上三角(含对角线)的元素,再填充对称位置即可,计算量直接减半:
n = len(df) # 初始化全0矩阵 result = [[0.0]*n for _ in range(n)] for i in range(n): set_i = df['sets'].iloc[i] len_i = len(df['lists'].iloc[i]) # 仅计算j >= i的部分 for j in range(i, n): set_j = df['sets'].iloc[j] len_j = len(df['lists'].iloc[j]) min_len = min(len_i, len_j) common_count = len(set_i & set_j) distance = (min_len - common_count) / min_len result[i][j] = distance # 对称位置赋值(跳过对角线) if i != j: result[j][i] = distance
3. 更简洁的迭代器实现(适用于大规模数据)
使用itertools.combinations_with_replacement生成所有i<=j的索引对,写法更简洁且底层实现更高效:
import itertools n = len(df) result = [[0.0]*n for _ in range(n)] # 遍历所有i<=j的索引组合 for i, j in itertools.combinations_with_replacement(range(n), 2): set_i = df['sets'].iloc[i] set_j = df['sets'].iloc[j] len_i = len(df['lists'].iloc[i]) len_j = len(df['lists'].iloc[j]) min_len = min(len_i, len_j) common_count = len(set_i & set_j) distance = (min_len - common_count) / min_len result[i][j] = distance if i != j: result[j][i] = distance
验证结果
运行上述优化代码后,输出与原循环结果完全一致:
[[0.0, 0.5, 0.6, 0.4], [0.5, 0.0, 1.0, 0.75], [0.6, 1.0, 0.0, 0.4], [0.4, 0.75, 0.4, 0.0]]
内容的提问来源于stack exchange,提问作者imburningbabe
相关产品推荐
相关产品推荐

