【问题标题】:How to apply numpy.linalg.norm to each row of a matrix?如何将 numpy.linalg.norm 应用于矩阵的每一行?
【发布时间】:2011-12-06 05:06:26
【问题描述】:

我有一个二维矩阵,我想对每一行取范数。但是当我直接使用numpy.linalg.norm(X) 时,它需要整个矩阵的范数。

我可以通过使用 for 循环对每一行取范数,然后对每个 X[i] 取范数,但由于我有 30k 行,这需要很长时间。

有什么建议可以找到更快的方法吗?或者是否可以将np.linalg.norm 应用于矩阵的每一行?

【问题讨论】:

    标签: python numpy


    【解决方案1】:

    对于 numpy 1.9+

    请注意,与 perimosocordiae shows 一样,从 NumPy 版本 1.9 开始,np.linalg.norm(x, axis=1) 是计算 L2 范数的最快方法。

    对于 numpy

    如果您正在计算 L2 范数,您可以直接计算它(使用 axis=-1 参数沿行求和):

    np.sum(np.abs(x)**2,axis=-1)**(1./2)
    

    Lp-norms 当然可以类似地计算。

    它比np.apply_along_axis 快得多,虽然可能不那么方便:

    In [48]: %timeit np.apply_along_axis(np.linalg.norm, 1, x)
    1000 loops, best of 3: 208 us per loop
    
    In [49]: %timeit np.sum(np.abs(x)**2,axis=-1)**(1./2)
    100000 loops, best of 3: 18.3 us per loop
    

    norm 的其他 ord 形式也可以直接计算(具有类似的加速):

    In [55]: %timeit np.apply_along_axis(lambda row:np.linalg.norm(row,ord=1), 1, x)
    1000 loops, best of 3: 203 us per loop
    
    In [54]: %timeit np.sum(abs(x), axis=-1)
    100000 loops, best of 3: 10.9 us per loop
    

    【讨论】:

    • 如果你把 x 平方,你为什么要做 np.abs(x)?
    • @Patrick:如果x 的dtype 很复杂,那么它会有所不同。例如,如果x = np.array([(1+1j,2+1j)]) 那么np.sum(np.abs(x)**2,axis=-1)**(1./2)array([ 2.64575131]),而np.sum(x**2,axis=-1)**(1./2)array([ 2.20320266+1.36165413j])
    • @perimosocordiae posted 更新 numpy.linalg.norm 及其 new axis 参数是目前最快的方法。
    • 如果我想将规范列应用于矩阵,该怎么做?
    • @user3515225:np.linalg.norm(x, axis=0)axis 指的是被求和的轴。对于二维数组,0 轴指的是行,因此axis=0 导致norm 对每个固定列的行求和。
    【解决方案2】:

    由于 numpy 更新而复活了一个老问题。从 1.9 版本开始,numpy.linalg.norm 现在接受 axis 参数。 [code, documentation]

    这是镇上最快的新方法:

    In [10]: x = np.random.random((500,500))
    
    In [11]: %timeit np.apply_along_axis(np.linalg.norm, 1, x)
    10 loops, best of 3: 21 ms per loop
    
    In [12]: %timeit np.sum(np.abs(x)**2,axis=-1)**(1./2)
    100 loops, best of 3: 2.6 ms per loop
    
    In [13]: %timeit np.linalg.norm(x, axis=1)
    1000 loops, best of 3: 1.4 ms per loop
    

    为了证明它计算的是同样的东西:

    In [14]: np.allclose(np.linalg.norm(x, axis=1), np.sum(np.abs(x)**2,axis=-1)**(1./2))
    Out[14]: True
    

    【讨论】:

      【解决方案3】:

      使用 NumPy 的einsum 比接受的答案快得多,

      numpy.sqrt(numpy.einsum('ij,ij->i', a, a))
      

      注意对数比例:


      重现情节的代码:

      import numpy
      import perfplot
      
      
      def sum_sqrt(a):
          return numpy.sqrt(numpy.sum(numpy.abs(a) ** 2, axis=-1))
      
      
      def apply_norm_along_axis(a):
          return numpy.apply_along_axis(numpy.linalg.norm, 1, a)
      
      
      def norm_axis(a):
          return numpy.linalg.norm(a, axis=1)
      
      
      def einsum_sqrt(a):
          return numpy.sqrt(numpy.einsum("ij,ij->i", a, a))
      
      
      b = perfplot.bench(
          setup=lambda n: numpy.random.rand(n, 3),
          kernels=[sum_sqrt, apply_norm_along_axis, norm_axis, einsum_sqrt],
          n_range=[2 ** k for k in range(20)],
          xlabel="len(a)",
      )
      b.save("out.png")
      

      【讨论】:

      • 想解释一下这是什么/做什么?
      • einsum 永远不会失败:)
      【解决方案4】:

      尝试以下方法:

      In [16]: numpy.apply_along_axis(numpy.linalg.norm, 1, a)
      Out[16]: array([ 5.38516481,  1.41421356,  5.38516481])
      

      a 是您的二维数组。

      上面计算了 L2 范数。对于不同的规范,您可以使用以下内容:

      In [22]: numpy.apply_along_axis(lambda row:numpy.linalg.norm(row,ord=1), 1, a)
      Out[22]: array([9, 2, 9])
      

      【讨论】:

        猜你喜欢
        • 2021-07-22
        • 1970-01-01
        • 2013-11-22
        • 2011-01-19
        • 1970-01-01
        • 2013-02-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多