如何在Pandas/Numpy中用自定义函数实现类外积运算?
优化自定义距离矩阵的生成方式
问题描述
我拥有一个包含N行的DataFrame,以及M个质心(每个质心的形状与DataFrame的行完全一致)。需要生成一个N行M列的矩阵,其中第m列由将第m个质心应用到DataFrame后得到。当前通过手动遍历质心+apply的方式实现,希望找到更高效、规范的实现方式。
原实现代码:
df = pd.read_csv('test_data.csv') centroids = df.sample(n=2) centroids.reset_index(drop=True, inplace=True) def getDistanceMatrix(df, centroids): distanceMatrix = np.zeros((len(df), len(centroids))) distFunc = lambda centroid, row: sum(centroid != row) iCentroid = 0 for _, centroid in centroids.iterrows(): distanceMatrix[:, iCentroid] = df.apply( lambda row: distFunc(centroid, row), axis=1 ) iCentroid += 1 return distanceMatrix distanceMatrix = getDistanceMatrix(df, centroids)
示例test_data.csv数据:
A,B,C,D 1,2,1,1 2,1,1,2 1,2,3,4 2,2,1,2 2,3,3,4 1,1,3,1 4,2,1,2 2,3,3,3 4,1,1,2
更优实现方式
方法1:利用Numpy广播实现向量化计算
你的自定义距离是两个向量对应位置不同元素的个数之和(类似汉明距离),可以直接用Numpy的广播机制完成向量化计算,避免循环和apply这类低效操作:
import pandas as pd import numpy as np df = pd.read_csv('test_data.csv') centroids = df.sample(n=2).reset_index(drop=True) def getDistanceMatrix(df, centroids): # 将DataFrame和质心转为Numpy数组 df_arr = df.to_numpy() centroids_arr = centroids.to_numpy() # 广播后计算差异元素的总和:df_arr形状(N,D),centroids_arr形状(M,D) # 广播为(N,M,D)的布尔数组,沿最后一维求和得到(N,M)的距离矩阵 return (df_arr[:, None] != centroids_arr).sum(axis=2) distanceMatrix = getDistanceMatrix(df, centroids)
优势:
- 完全向量化操作,效率比原方法提升几个数量级(尤其是数据量较大时)
- 代码简洁,无需手动初始化矩阵和循环填充
方法2:适配复杂自定义距离的通用方案
如果你的自定义距离函数无法直接用向量化实现,可以用numpy.vectorize封装(注意:vectorize本质是循环的封装,效率不如纯向量化,但比pandas.apply更高效):
def getDistanceMatrix(df, centroids): df_arr = df.to_numpy() centroids_arr = centroids.to_numpy() # 定义自定义距离函数 def dist_func(row, centroid): return sum(row != centroid) # 封装为可广播的vectorized函数 vec_dist = np.vectorize(dist_func, signature='(d),(d)->()') return vec_dist(df_arr[:, None], centroids_arr)
原方法的问题
原方案中iterrows循环和df.apply都是逐行操作,属于Python级别的循环,在数据量较大时会非常缓慢。而Numpy的向量化操作是基于C语言实现的底层运算,能大幅提升执行效率。
内容的提问来源于stack exchange,提问作者P i
相关产品推荐
相关产品推荐

