【问题标题】:How do you get the magnitude of a vector in Numpy?你如何在 Numpy 中获得向量的大小?
【发布时间】:2012-02-28 13:55:14
【问题描述】:

根据“只有一种明显的方法可以做到”,如何在 Numpy 中获得向量(一维数组)的大小?

def mag(x): 
    return math.sqrt(sum(i**2 for i in x))

上述方法可行,但我无法相信我必须自己指定这样一个微不足道的核心功能。

【问题讨论】:

  • 我通常使用linalg.norm,如下所述。但比你的 lambda 东西稍微简单一点,不需要导入,只是 sum(x*x)**0.5
  • 顺便说一句,将 lambda 函数分配给名称从来没有任何充分的理由。
  • lambda 旨在成为一个匿名函数,所以给它一个名字你做错了。那么它只是def的残缺版本。而且,如果你坚持,你也可以在一行上放一个 def。您可能有理由使用 lambda 的通常地方是用于将某些参数列表作为可调用对象传递。人们误用它,如上所示,这是它进入 guido 的python regrets 列表的原因之一(见幻灯片 4)
  • 链接已失效! Long live the link!
  • 链接是PowerPoint! Long live the PDF!

标签: python numpy


【解决方案1】:

您所追求的功能是numpy.linalg.norm。 (我认为它应该在基本 numpy 中作为数组的属性——比如x.norm()——但是哦,好吧)。

import numpy as np
x = np.array([1,2,3,4,5])
np.linalg.norm(x)

您还可以输入可选的ord 以获得所需的第 n 阶标准。假设你想要 1 范数:

np.linalg.norm(x,ord=1)

等等。

【讨论】:

  • “应该是数组的属性:x.norm()”我完全同意。通常在使用 numpy 时,我会使用自己的 Array 和 Matrix 子类,这些子类将我常用的所有函数都作为方法引入。 Matrix.randn([5,5])
  • 另外,对于由向量组成的矩阵,np.linalg.norm 现在有一个新的axis 参数,在此讨论:stackoverflow.com/a/19794741/1959808
【解决方案2】:

如果您完全担心速度,您应该改用:

mag = np.sqrt(x.dot(x))

以下是一些基准:

>>> import timeit
>>> timeit.timeit('np.linalg.norm(x)', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0450878
>>> timeit.timeit('np.sqrt(x.dot(x))', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0181372

编辑:当您必须采用许多向量的规范时,真正的速度提升就出现了。使用纯 numpy 函数不需要任何 for 循环。例如:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 4.23 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 18.9 us per loop

In [5]: np.allclose([np.linalg.norm(x) for x in a],np.sqrt((a*a).sum(axis=1)))
Out[5]: True

【讨论】:

  • 在发现np.linalg.norm 是一个瓶颈后,我确实使用了这种稍微不那么显式的方法,但后来我更进一步,只使用了math.sqrt(x[0]**2 + x[1]**2),这是另一个重大改进。跨度>
  • @NickT,查看我的编辑,了解使用纯 numpy 函数时的真正改进。
  • 点积的酷应用!
  • numpy.linalg.norm 包含防止此实现跳过的溢出的保护措施。例如,尝试计算[1e200, 1e200] 的范数。慢是有原因的……
  • @FedericoPoloni,至少使用 numpy 版本 1.13.3 我在计算 np.linalg.norm([1e200,1e200]) 时得到 inf
【解决方案3】:

另一种选择是在 numpy 中为任一数组使用 einsum 函数:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 3.86 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 15.6 µs per loop

In [5]: %timeit np.sqrt(np.einsum('ij,ij->i',a,a))
100000 loops, best of 3: 8.71 µs per loop

或向量:

In [5]: a = np.arange(100000)

In [6]: %timeit np.sqrt(a.dot(a))
10000 loops, best of 3: 80.8 µs per loop

In [7]: %timeit np.sqrt(np.einsum('i,i', a, a))
10000 loops, best of 3: 60.6 µs per loop

然而,调用它似乎会产生一些开销,这可能会使其在输入较小的情况下变慢:

In [2]: a = np.arange(100)

In [3]: %timeit np.sqrt(a.dot(a))
100000 loops, best of 3: 3.73 µs per loop

In [4]: %timeit np.sqrt(np.einsum('i,i', a, a))
100000 loops, best of 3: 4.68 µs per loop

【讨论】:

  • numpy.linalg.norm 包含防止此实现跳过的溢出的保护措施。例如,尝试计算[1e200, 1e200] 的范数。慢是有原因的……
【解决方案4】:

我发现最快的方法是通过 inner1d。以下是它与其他 numpy 方法的比较:

import numpy as np
from numpy.core.umath_tests import inner1d

V = np.random.random_sample((10**6,3,)) # 1 million vectors
A = np.sqrt(np.einsum('...i,...i', V, V))
B = np.linalg.norm(V,axis=1)   
C = np.sqrt((V ** 2).sum(-1))
D = np.sqrt((V*V).sum(axis=1))
E = np.sqrt(inner1d(V,V))

print [np.allclose(E,x) for x in [A,B,C,D]] # [True, True, True, True]

import cProfile
cProfile.run("np.sqrt(np.einsum('...i,...i', V, V))") # 3 function calls in 0.013 seconds
cProfile.run('np.linalg.norm(V,axis=1)')              # 9 function calls in 0.029 seconds
cProfile.run('np.sqrt((V ** 2).sum(-1))')             # 5 function calls in 0.028 seconds
cProfile.run('np.sqrt((V*V).sum(axis=1))')            # 5 function calls in 0.027 seconds
cProfile.run('np.sqrt(inner1d(V,V))')                 # 2 function calls in 0.009 seconds

inner1d 比 linalg.norm 快约 3 倍,比 einsum 快一点

【讨论】:

  • 实际上从您上面写的内容来看,linalg.norm 是最快的,因为它在 29 毫秒内完成了 9 次调用,因此在 3.222 毫秒内进行了 1 次调用,而 inner1d 在 4.5 毫秒内进行了 1 次调用。
  • @bisounours_tronconneuse 总执行时间的计时。如果您运行上面的代码,您将获得每个函数调用的时间细分。如果您仍有疑问,请将矢量计数更改为非常大的值,例如 ((10**8,3,)),然后手动运行 np.linalg.norm(V,axis=1),然后运行 ​​np.sqrt(inner1d(V,V)),您会发现 linalg.norm 与 inner1d 相比会滞后
  • 好的。谢谢你的澄清。
  • numpy.linalg.norm 包含防止此实现跳过的溢出的保护措施。例如,尝试计算[1e200, 1e200] 的范数。慢是有原因的……
  • @LarsH 不幸的是它不是。看起来numpy.core.umath_tests 正在被弃用。如果 inner1d 的现有功能在日落时分(这将是一种耻辱),那么您最好的选择(为了速度)将是使用 numba 编写一个并行化函数。
【解决方案5】:

scipy.linalg(或numpy.linalg)中使用norm函数

>>> from scipy import linalg as LA
>>> a = 10*NP.random.randn(6)
>>> a
  array([  9.62141594,   1.29279592,   4.80091404,  -2.93714318,
          17.06608678, -11.34617065])
>>> LA.norm(a)
    23.36461979210312

>>> # compare with OP's function:
>>> import math
>>> mag = lambda x : math.sqrt(sum(i**2 for i in x))
>>> mag(a)
     23.36461979210312

【讨论】:

    【解决方案6】:

    您可以使用工具带vg 简洁地执行此操作。它是 numpy 之上的一个轻量层,它支持单值和堆叠向量。

    import numpy as np
    import vg
    
    x = np.array([1, 2, 3, 4, 5])
    mag1 = np.linalg.norm(x)
    mag2 = vg.magnitude(x)
    print mag1 == mag2
    # True
    

    我在上一次创业时创建了这个库,它的动机是这样的:在 NumPy 中过于冗长的简单想法。

    【讨论】:

      【解决方案7】:
      v1 = np.array([ 1, 2 , 3 ])
      v1_size = len(v1)
      print(v1_size) // 3
      

      【讨论】:

      • print() 返回无,因此您不能将其除以 3。此外,我要使用的“幅度”是欧几里得范数(如问题示例中所示),而不是大小/向量的维度。
      • @NickT 我猜测// 的目的是作为注释(在类似 C 的语法中),指示 print 语句将打印的内容。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-10
      • 2013-09-28
      • 2014-06-03
      • 1970-01-01
      • 1970-01-01
      • 2011-04-21
      相关资源
      最近更新 更多