有没有比嵌套调用iterrows更快的Pandas行配对计算实现方法?
Pandas行两两配对计算的高效实现
你原有的双重iterrows循环属于Python级循环,数据量增大时效率极低,以下两种实现完全等价原逻辑,性能提升明显:
方案1:NumPy向量化实现(最高效推荐)
核心用底层C实现的矩阵运算代替Python循环,性能比原代码高数百倍,完全匹配你的计算规则:
- 行i与行j共同为1的元素个数直接通过矩阵乘法批量计算
- 每行非零个数批量计算后广播生成最大值矩阵
- 批量计算完概率后直接提取非自身配对的结果
import pandas as pd import numpy as np # 你的样例输入 data3 = pd.DataFrame({ 'ticket1': [0, 0, 0], 'ticket2': [0, 1, 1], 'ticket3': [0, 0, 0] }, index=['class1', 'class2', 'class3']) # 向量化计算逻辑 arr = data3.values # 计算每行非零元素个数 row_nonzero = np.count_nonzero(arr, axis=1) # 批量计算所有行对的共同为1的数量(矩阵乘法得到点积矩阵) intersect_mat = arr @ arr.T # 批量计算所有行对的非零数最大值矩阵,处理除零异常 max_mat = np.maximum(row_nonzero[:, None], row_nonzero[None, :]) max_mat[max_mat == 0] = 1 # 全零行对避免除零错误,最终结果为0 prob_mat = intersect_mat / max_mat # 提取所有i≠j的配对(如果你只需要无向配对i<j,替换下方为i,j = np.triu_indices(len(data3), k=1)即可) n = len(data3) mask = np.ones((n, n), dtype=bool) np.fill_diagonal(mask, False) i, j = np.where(mask) # 生成最终结果 res = pd.DataFrame({ 'col1': data3.index[i], 'col2': data3.index[j], 'col3': prob_mat[i, j] })
运行后输出和需求完全匹配,如果仅需要单向无重复配对(只保留class1-class2不保留class2-class1),替换提取配对的代码即可。
方案2:itertools适配实现(适合小数据集)
如果要使用itertools.product适配DataFrame行操作,可以提前预存行数据避免反复调用iterrows的开销:
import pandas as pd import numpy as np from itertools import product data3 = pd.DataFrame({ 'ticket1': [0, 0, 0], 'ticket2': [0, 1, 1], 'ticket3': [0, 0, 0] }, index=['class1', 'class2', 'class3']) affinity = [] idx_list = data3.index.tolist() # 预存所有行数据,避免循环中反复取值 row_data = data3.to_numpy() row_map = dict(zip(idx_list, row_data)) for idx1, idx2 in product(idx_list, idx_list): if idx1 == idx2: continue row1 = row_map[idx1] row2 = row_map[idx2] intersect = np.sum(row1 & row2) total = max(np.count_nonzero(row1), np.count_nonzero(row2)) prob = intersect / total if total != 0 else 0 affinity.append([idx1, idx2, prob]) res = pd.DataFrame(affinity, columns=['col1', 'col2', 'col3'])
该方案性能优于原双重iterrows实现,但远低于向量化方案,仅适合行数在百级以内的场景。
内容的提问来源于stack exchange,提问作者elchapo
相关产品推荐
相关产品推荐

