Python中如何加速计算DataFrame行的自定义距离矩阵
高效实现对称自定义距离矩阵的方案
你当前手写的双重循环方案性能瓶颈主要来自两点:一是Python层循环本身开销大,二是循环内反复调用iloc、切片取数的冗余操作多。结合你给出的距离函数逻辑(数值欧氏距离+分类特征匹配距离),完全可以通过向量化运算把性能提升几十到上百倍,不需要逐对样本循环计算。
最优实现(针对你的距离逻辑专属优化,速度最快)
你的距离函数可以拆成完全独立的两部分,分别做向量化计算后直接相加即可,天然满足对称性,不需要手动给对称位赋值:
import numpy as np from scipy.spatial.distance import cdist # 提前把两部分列转成numpy数组,避免循环内反复切片、取数 num_array = df[numerical_indices].to_numpy(dtype=np.float64) cat_array = df[categorical_indices].to_numpy() n_samples = len(df) # 一步计算所有样本对的数值部分欧氏距离,底层C实现无Python循环 num_dist_matrix = cdist(num_array, num_array, metric="euclidean") # 利用numpy广播机制,一步计算所有样本对的分类特征不匹配个数 # 维度扩展后做比较:(n,1,特征数) vs (1,n,特征数),沿特征轴求和就是不匹配计数 cat_dist_matrix = np.sum(cat_array[:, None, :] != cat_array[None, :, :], axis=-1) # 两部分相加就是最终的距离矩阵 M = num_dist_matrix + cat_dist_matrix
性能对比说明
- 所有运算均在numpy/scipy的C/Fortran底层实现,完全去掉了Python层双重循环,性能比手写Python循环快50~200倍,样本量越大优势越明显
- 提前把DataFrame转为numpy数组,省掉了循环内反复调用
iloc、按索引切片的冗余开销 - 计算结果天然对称,不需要手动执行
M[i][j] = M[j][i]的重复赋值操作
通用方案(适配任意无法拆解的对称自定义距离)
如果后续你更换了无法拆分为向量化运算的复杂自定义距离,不要手写双重循环,用scipy内置的pdist即可,它会自动利用距离对称性只计算上三角部分,且循环逻辑跑在C层,比手写Python循环快3~10倍:
from scipy.spatial.distance import pdist, squareform # 提前转numpy数组减少取数开销 data_array = df.to_numpy() # pdist返回压缩的上三角距离结果,只计算n*(n-1)/2次,比全量循环少一半计算量 condensed_dist = pdist( data_array, metric=lambda a, b: custom_distance(a, b, categorical_indices, numerical_indices) ) # 把压缩结果转为完整的n*n方阵 M = squareform(condensed_dist)
注:该方案因为要逐对调用Python层的自定义距离函数,性能比前面的专属向量化方案差,但依然显著优于手写双重循环。
内容的提问来源于stack exchange,提问作者Pandora
相关产品推荐
相关产品推荐

