【问题标题】:Iterative outer addition Numpy迭代外加 Numpy
【发布时间】:2019-12-07 16:05:37
【问题描述】:

我想应用多个向量/矩阵的外部加法。让我们说四次:

import numpy as np 

x = np.arange(100)
B = np.add.outer(x,x)
B = np.add.outer(B,x)
B = np.add.outer(B,x)

我希望加法的数量可以是一个变量,例如a=4 --> 加法的 4 倍。这可能吗?

【问题讨论】:

  • 简单循环有什么问题?通过广播,您可以:x[:,None,None]+x[None,:,None]+x ,但将其推广到a 次会更有效。
  • @hpaulj 我认为否则可能。

标签: python numpy addition


【解决方案1】:

方法#1

这是一个数组初始化 -

n = 4 # number of iterations to add outer versions
l = len(x)
out = np.zeros([l]*n,dtype=x.dtype)
for i in range(n):
    out += x.reshape(np.insert([1]*(n-1),i,l))

为什么在每次迭代时使用这种方法而不是迭代添加来创建新数组?

在每次迭代中迭代地创建新数组将需要更多内存,因此需要更多内存开销。通过数组初始化,我们将x 的元素添加到已经初始化的数组中。因此,它尝试使用它来提高内存效率。

备选方案#1

我们可以通过使用x 进行初始化来删除一次迭代。因此,更改将是 -

out = np.broadcast_to(x,[l]*n).copy()
for i in range(n-1):

方法#2:使用np.add.reduce -

另一种方法是使用np.add.reduce,它也不会创建任何中间数组,但在这里作为一种归约方法可能会更好,因为它就是为此实现的 -

l = len(x); n = 4
np.add.reduce([x.reshape(np.insert([1]*(n-1),i,l)) for i in range(n)])

时间安排 -

In [17]: x = np.arange(100)

In [18]: %%timeit
    ...: n = 4 # number of iterations to add outer versions
    ...: l = len(x)
    ...: out = np.zeros([l]*n,dtype=x.dtype)
    ...: for i in range(n):
    ...:     out += x.reshape(np.insert([1]*(n-1),i,l))
829 ms ± 28.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [19]: l = len(x); n = 4

In [20]: %timeit np.add.reduce([x.reshape(np.insert([1]*(n-1),i,l)) for i in range(n)])
183 ms ± 2.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

【讨论】:

    【解决方案2】:

    我认为没有内置参数可以多次重复此过程,但您可以相当轻松地为其定义自定义函数

    def recursive_outer_add(arr, num):
        if num == 1:
            return arr
    
        x = np.add.outer(arr, arr)
    
        for i in range(num - 1):
            x = np.add.outer(x, arr)
        return x
    

    作为一个警告:数组变得非常大非常快

    【讨论】:

      【解决方案3】:

      短且相当快:

      n = 4
      l = 10
      x = np.arange(l)
      
      sum(np.ix_(*n*(x,)))
      
      timeit(lambda:sum(np.ix_(*n*(x,))),number=1000)
      # 0.049082988989539444
      

      我们可以通过回到前面来加快速度:

      timeit(lambda:sum(reversed(np.ix_(*n*(x,)))),number=1000)
      # 0.03847671199764591
      

      我们也可以构建自己的逆向np.ix_:

      from operator import getitem
      from itertools import accumulate,chain,repeat
      
      sum(accumulate(chain((x,),repeat((slice(None),None),n-1)),getitem))
      
      timeit(lambda:sum(accumulate(chain((x,),repeat((slice(None),None),n-1)),getitem)),number=1000)
      # 0.02427654700295534
      

      【讨论】:

        猜你喜欢
        • 2011-10-03
        • 2020-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-04
        • 2013-01-21
        • 2015-09-14
        • 2013-05-05
        相关资源
        最近更新 更多