只有在空间受到严重限制时,才值得尝试原地操作。如果是这种情况,可以通过迭代数组的平坦视图来稍微加快代码的运行速度。由于reshape
在可能的情况下返回一个新视图,数据本身不会被复制(除非原始数据结构不寻常)。
我不知道有更好的方法来实现对任意Python函数的原地应用。
>>> def flat_for(a, f):
... a = a.reshape(-1)
... for i, v in enumerate(a):
... a[i] = f(v)
...
>>> a = numpy.arange(25).reshape(5, 5)
>>> flat_for(a, lambda x: x + 5)
>>> a
array([[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]])
一些时间:
>>> a = numpy.arange(2500).reshape(50, 50)
>>> f = lambda x: x + 5
>>> %timeit flat_for(a, f)
1000 loops, best of 3: 1.86 ms per loop
这个版本的速度是嵌套循环版本的大约两倍:
>>> a = numpy.arange(2500).reshape(50, 50)
>>> def nested_for(a, f):
... for i in range(len(a)):
... for j in range(len(a[0])):
... a[i][j] = f(a[i][j])
...
>>> %timeit nested_for(a, f)
100 loops, best of 3: 3.79 ms per loop
当然向量化仍然更快,所以如果你可以复制,那么就使用复制:
>>> a = numpy.arange(2500).reshape(50, 50)
>>> g = numpy.vectorize(lambda x: x + 5)
>>> %timeit g(a)
1000 loops, best of 3: 584 us per loop
如果您可以使用内置的ufunc重写dim
,那么请不要使用vectorize
:
>>> a = numpy.arange(2500).reshape(50, 50)
>>> %timeit a + 5
100000 loops, best of 3: 4.66 us per loop
numpy
通过就地操作(in-place)执行像+=
这样的操作,就像您所期望的那样-因此,您可以获得ufunc速度与无代价的原位应用。有时它甚至更快!请参见此处的示例。
顺便说一下,我对这个问题的最初回答(可以在其编辑历史记录中查看)是荒谬的,涉及到对a
索引进行向量化。它不仅必须绕过vectorize
的类型检测机制,而且结果和嵌套循环版本一样慢。所以聪明反被聪明误!
a_values = np.vectorize(dim)(a_values)
来避免使用嵌套循环,但这仍然不是一种原地操作的方法,所以它并不是答案。 - Dan D.vectorize
,但现在放弃了。赞成Bob的看法。 - senderlevectorize
进行就地工作会导致函数在第一个值上双重应用。所以你必须规避这种行为。其次,这种丑陋的操作没有任何回报;根据我的测试结果,结果并不比嵌套的 for 循环快。 - senderle