求Numpy优化方案:快速计算8000×8000数据框行对的Weighted Jaccard距离
大尺寸DataFrame加权Jaccard距离的Numpy优化方案
问题背景
需要计算8000×8000的Pandas数据框中每一对行的加权Jaccard距离,原有的双重循环实现(基于iterrows迭代)速度极慢,需通过Numpy向量化计算优化。
原方案的核心问题
双重循环(尤其是iterrows行迭代)属于Python级别的逐次操作,8000行数据需计算约3200万次行对,循环本身的开销会导致计算效率极低;同时每次调用函数处理单行数据,进一步增加了额外开销。
优化思路
利用Numpy的向量化计算和广播机制,将所有行对的计算一次性完成,避免Python循环的开销;同时通过数学变换减少内存占用,适配大尺寸数据:
- 利用公式转换:
min(a,b) = (a+b - |a-b|)/2、max(a,b) = (a+b + |a-b|)/2,将行对的min/max总和转换为行总和与元素差绝对值总和的组合计算,避免生成超大三维数组。
完整优化代码
import pandas as pd import numpy as np # 替换为你的8000×8000数据框 matrix = pd.DataFrame([[1, 2 ,3], [2, 1, 1], [3, 1, 1]]) arr = matrix.values # 将DataFrame转为Numpy数组,减少Pandas迭代开销 n_rows = arr.shape[0] # 1. 计算每行的元素总和 row_sums = arr.sum(axis=1) # 2. 计算所有行对的sum(a) + sum(b),利用广播生成(n_rows, n_rows)矩阵 sum_ab = row_sums[:, np.newaxis] + row_sums # 3. 计算所有行对的元素差的绝对值之和,广播后按列求和得到(n_rows, n_rows)矩阵 abs_diff_sum = np.abs(arr[:, None, :] - arr[None, :, :]).sum(axis=2) # 4. 推导每对行的min总和与max总和 sum_min = (sum_ab - abs_diff_sum) / 2 sum_max = (sum_ab + abs_diff_sum) / 2 # 5. 生成加权Jaccard相似度矩阵(若需距离则计算 1 - weighted_jaccard_sim) weighted_jaccard_sim = sum_min / sum_max # 6. 提取上三角部分(仅保留i<j的行对结果,避免重复计算) upper_triangle_results = weighted_jaccard_sim[np.triu_indices(n_rows, k=1)]
关键说明
- 内存优化:通过数学转换,仅生成
(8000,8000)的中间数组,内存占用约512MB(float64类型),完全适配常规机器内存。 - 速度提升:所有计算均为Numpy底层C实现的向量化操作,比Python循环快几个数量级,8000行的计算可在合理时间内完成。
- 结果提取:
np.triu_indices(n_rows, k=1)用于获取上三角索引,仅保留未重复的行对结果,与原循环中other_index>index的逻辑一致。
内容的提问来源于stack exchange,提问作者Tim Kirkwood
相关产品推荐
相关产品推荐

