python - 将函数应用于 ndarray 的每一行

标签 python arrays numpy vectorization

我有这个函数来计算向量 x 的平方马氏距离以表示:

def mahalanobis_sqdist(x, mean, Sigma):
   '''
    Calculates squared Mahalanobis Distance of vector x 
    to distibutions' mean 
   '''
   Sigma_inv = np.linalg.inv(Sigma)
   xdiff = x - mean
   sqmdist = np.dot(np.dot(xdiff, Sigma_inv), xdiff)
   return sqmdist

我有一个形状为 (25, 4) 的 numpy 数组。所以,我想在没有 for 循环的情况下将该函数应用于数组的所有 25 行。所以,基本上,我该如何编写这个循环的矢量化形式:

for r in d1:
    mahalanobis_sqdist(r[0:4], mean1, Sig1)

mean1Sig1 是:

>>> mean1
array([ 5.028,  3.48 ,  1.46 ,  0.248])
>>> Sig1 = np.cov(d1[0:25, 0:4].T)
>>> Sig1
array([[ 0.16043333,  0.11808333,  0.02408333,  0.01943333],
       [ 0.11808333,  0.13583333,  0.00625   ,  0.02225   ],
       [ 0.02408333,  0.00625   ,  0.03916667,  0.00658333],
       [ 0.01943333,  0.02225   ,  0.00658333,  0.01093333]])

我尝试了以下方法,但没有用:

>>> vecdist = np.vectorize(mahalanobis_sqdist)
>>> vecdist(d1, mean1, Sig1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/numpy/lib/function_base.py", line 1862, in __call__
    theout = self.thefunc(*newargs)
  File "<stdin>", line 6, in mahalanobis_sqdist
  File "/usr/lib/python2.7/dist-packages/numpy/linalg/linalg.py", line 445, in inv
    return wrap(solve(a, identity(a.shape[0], dtype=a.dtype)))
IndexError: tuple index out of range

最佳答案

要将函数应用于数组的每一行,您可以使用:

np.apply_along_axis(mahalanobis_sqdist, 1, d1, mean1, Sig1)    

然而,在这种情况下,有更好的方法。您不必对每一行都应用一个函数。相反,您可以将 NumPy 操作应用于整个 d1 数组以计算相同的结果。 np.einsum可以替换 for-loop 和对 np.dot 的两次调用:


def mahalanobis_sqdist2(d, mean, Sigma):
   Sigma_inv = np.linalg.inv(Sigma)
   xdiff = d - mean
   return np.einsum('ij,im,mj->i', xdiff, xdiff, Sigma_inv)

这里有一些基准:

import numpy as np
np.random.seed(1)

def mahalanobis_sqdist(x, mean, Sigma):
   '''
   Calculates squared Mahalanobis Distance of vector x 
   to distibutions mean 
   '''
   Sigma_inv = np.linalg.inv(Sigma)
   xdiff = x - mean
   sqmdist = np.dot(np.dot(xdiff, Sigma_inv), xdiff)
   return sqmdist

def mahalanobis_sqdist2(d, mean, Sigma):
   Sigma_inv = np.linalg.inv(Sigma)
   xdiff = d - mean
   return np.einsum('ij,im,mj->i', xdiff, xdiff, Sigma_inv)

def using_loop(d1, mean, Sigma):
    expected = []
    for r in d1:
        expected.append(mahalanobis_sqdist(r[0:4], mean1, Sig1))
    return np.array(expected)

d1 = np.random.random((25,4))
mean1 = np.array([ 5.028,  3.48 ,  1.46 ,  0.248])
Sig1 = np.cov(d1[0:25, 0:4].T)

expected = using_loop(d1, mean1, Sig1)
result = np.apply_along_axis(mahalanobis_sqdist, 1, d1, mean1, Sig1)
result2 = mahalanobis_sqdist2(d1, mean1, Sig1)
assert np.allclose(expected, result)
assert np.allclose(expected, result2)

In [92]: %timeit mahalanobis_sqdist2(d1, mean1, Sig1)
10000 loops, best of 3: 31.1 µs per loop
In [94]: %timeit using_loop(d1, mean1, Sig1)
1000 loops, best of 3: 569 µs per loop
In [91]: %timeit np.apply_along_axis(mahalanobis_sqdist, 1, d1, mean1, Sig1)
1000 loops, best of 3: 806 µs per loop

因此 mahalanobis_sqdist2for-loop 快 18 倍,比使用 np.apply_along_axis 快 26 倍。


请注意,np.apply_along_axisnp.vectorizenp.frompyfunc 是 Python 实用函数。在引擎盖下,他们使用 for-while-loop。这里没有真正的“矢量化”。它们可以提供语法帮助,但不要指望它们能让您的代码比您自己编写的 for-loop 表现得更好。

关于python - 将函数应用于 ndarray 的每一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22581763/

相关文章:

python - 如何使用 python 连接到外部 API?

java - 如何在 Java 中实例化一组 map ?

javascript - 多次转换路径

python - 沿多边形边界生成等距点,但 CW/CCW

python - 需要帮助将 Matlab 的 bsxfun 转换为 numpy

python - 如何在 Python 中调用 AutoIt 脚本

python - 如何在类中使用多处理管理器

python - 查找二维 numpy 数组中最大和的位置

python - 如何求解未知矩阵的矩阵方程?

python - 线性同余发生器 - 弱测试结果