【问题标题】:Iterate on an array with two implicit loops迭代具有两个隐式循环的数组
【发布时间】:2013-10-07 16:15:21
【问题描述】:

是否可以隐式迭代具有两个索引的数组? 这是我想做的一个非常简单的例子:

import numpy as np

x = np.arange(3)
y = np.zeros(3)

for i in range(3):
    y[i] = np.sum(x - x[i])

有一个隐式循环(总和)和一个显式循环(for i in range(3))...是否有可能有一个完全隐式的版本?

【问题讨论】:

  • 如果算作'implicit',你可以这样做y = np.array([sum(x - k) for k in x])
  • 这确实加快了速度并且看起来更好,@Roman,但我认为 thomleo 正在寻找 numpy 中的循环。
  • @askewchan 好的,我还在学习 numpy,可能有更好的方法来做到这一点,我会很高兴看到它
  • @Roman 我的目标是没有任何 python 循环,而是像 askewchan 所说的那样在 numpy 内循环!无论如何感谢您的建议,它看起来比我发布的更好!

标签: python loops numpy indexing


【解决方案1】:

以下应该有效:

y = np.sum(x - x[:,None], axis=1)

此解决方案使用 numpy 的广播工具。首先,我使用x[:,None] 将形状为(N,)x 重铸为(N,1)。您可能还会看到它写成x[:,np.newaxis]

x - x[:,None] 创建一个(N,N) 数组,其元素为tmp_{i,j} = x_i - x_j。然后,我只需使用np.sum 中的参数axis=1 对各行求和。

见:

In [13]: y = np.zeros(10)

In [14]: x = np.random.normal(size=(10,))

In [15]: for i in range(10):
        y[i] = np.sum(x - x[i])
   ....:

In [16]: y
Out[16]:
array([  7.99781458,   4.15114434, -17.24655912, -20.35606168,
        -5.0211756 ,   7.52062868,   8.2501526 ,   3.90397351,
        10.18746451,   0.61261819])

In [17]: np.sum(x - x[:,None], 1)
Out[17]:
array([  7.99781458,   4.15114434, -17.24655912, -20.35606168,
        -5.0211756 ,   7.52062868,   8.2501526 ,   3.90397351,
        10.18746451,   0.61261819])

In [18]: np.allclose(y, np.sum(x - x[:,None], 1))
Out[18]: True

时间安排: 需要指出的是,使用 numpy 提供的工具来操作数组通常比使用标准 Python 构造快得多:

In [48]: x = np.random.normal(size=(100,))

In [49]: %timeit y = np.array([sum(x - k) for k in x])
100 loops, best of 3: 6.86 ms per loop

In [67]: %timeit y = np.array([np.sum(x - k) for k in x])
1000 loops, best of 3: 1.54 ms per loop

In [50]: %timeit np.sum(x - x[:,None], 1)
10000 loops, best of 3: 59 µs per loop

In [51]:

In [51]: x = np.random.normal(size=(1000,))

In [52]: %timeit y = np.array([sum(x - k) for k in x])
1 loops, best of 3: 592 ms per loop

In [72]: %timeit y = np.array([np.sum(x - k) for k in x])
100 loops, best of 3: 17.2 ms per loop

In [53]: %timeit np.sum(x - x[:,None], 1)
100 loops, best of 3: 8.67 ms per loop

【讨论】:

  • 不错!唯一的缺点是它在形状(x.size, x.size)的过程中构建了一个大的二维数组。
  • 如果你和axis=1相加,你可以避免-ve。
  • @askewchan 当您试图摆脱 numpy 中的循环时,您通常最终会创建大型临时数组以向量化计算。此外,我更改了总和以使其更直观。就在您发表评论之前意识到这一点。谢谢。
  • 谢谢!与往常一样...权衡记忆与时间!
  • @RomanPekar 我添加了有关其工作原理的描述。它依赖于 numpy 广播
【解决方案2】:

如果可能,您应该始终尝试在计算机科学之前使用数学。你的表达式y[i] = np.sum(x - x[i]) 可以用一点代数重写为y[i] = np.sum(x) - x.size * x[i]。这清楚地表明您可以在没有任何循环的情况下重写代码:

y = np.sum(x) - x.size * x

显然,对于大型数组,它的运行速度比@JoshAdel 的解决方案快得多,对于大小为 1000 的输入,它的运行速度要快 x400:

>>> x = np.random.normal(size=(1000,))
>>> np.allclose(np.sum(x - x[:,None], 1), np.sum(x) - x.size * x)
True

%timeit np.sum(x - x[:,None], 1)
100 loops, best of 3: 6.33 ms per loop

%timeit np.sum(x) - x.size * x
100000 loops, best of 3: 16.5 us per loop

【讨论】:

  • 虽然我想说更智能的算法仍然在 CS 的范围内。 +1 用于识别更简单、更快捷的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
  • 2016-08-27
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多