反转numpy数组的最有效方法


276

信不信由你,在分析当前代码后,执行numpy数组还原的重复操作将占用大量运行时间。我现在拥有的是基于视图的常见方法:

reversed_arr = arr[::-1]

还有其他方法可以更有效地执行此操作,还是我对不切实际的numpy性能的痴迷所致的幻觉?


27
呃... arr[::-1]只是返回了相反的视图。它尽可能快,并且不依赖数组中项目的数量,因为它只是改变了步伐。您要反转的实际上是一个numpy数组吗?
乔·肯顿

是的,确实arr是一个numpy数组。
nye17 2011年

12
嗯...好吧,无论阵列的长度如何,在我的笔记本电脑上大约需要670纳秒。如果这是您的瓶颈,则可能需要切换语言...我很确定您不会找到更快的方法来反转numpy数组。祝你好运,无论如何!
乔·肯顿

6
好吧,您是否必须在循环中运行它?在某些情况下,最好制作一个包含数百万个项目的numpy数组,然后对整个数组进行操作。即使您使用的是有限差分法或类似方法,其结果取决于先前的结果,有时您也可以这样做。(有时强调...)无论如何,如果速度是主要目标,那么fortran仍然是王者。f2py是你的朋友!通常值得用另一种语言编写算法的性能关键部分(尤其是在科学计算中),然后从python调用它。祝好运!
乔·肯顿

1
@berto。这是因为速度较慢它是一个包装arr[::-1]github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py。搜索def flipud。该函数实际上是四行。
疯狂物理学家

Answers:


239

创建时,reversed_arr您正在创建原始数组的视图。然后,您可以更改原始数组,并且视图将更新以反映所做的更改。

您是否经常需要重新创建视图?您应该能够执行以下操作:

arr = np.array(some_sequence)
reversed_arr = arr[::-1]

do_something(arr)
look_at(reversed_arr)
do_something_else(arr)
look_at(reversed_arr)

我不是numpy专家,但这似乎是用numpy做事情的最快方法。如果这是您已经在做的,我认为您无法对此进行改进。

PS这里很好的numpy视图讨论:

查看到一个numpy的数组?


创建切片对象然后在许多数组上重用它是否有帮助?
endlith 2014年

1
实际上,我只是测试了一下,没有发现与在循环外部创建的slice对象有任何区别。(哦,等等,它稍微快一点。对于一个1000000循环,重复43.4 ms与44.3 ms)
Endolith 2014年

什么是look_at功能想干什么?
mrgloom

1
@mrgloom应该代表任何查看数据的任务。该示例的目的是表明reversed_arr在更改基础数据后,该视图仍然可用。将新值写入数组不会使视图无效。实际上,您还可以使用视图将新值写入数组。 reversed_arr[0] = 99会将数组中的最后一个元素设置为99,与之相同arr[-1] = 99
steveha

60

如上所述,a[::-1]实际上仅创建一个视图,因此它是一个恒定时间的操作(因此,随着数组的增长,它不需要花费更长的时间)。如果您需要数组是连续的(例如,因为您要对其执行许多矢量运算),ascontiguousarray则其速度大约与flipup/ 一样快fliplr

在此处输入图片说明


生成绘图的代码:

import numpy
import perfplot


perfplot.show(
    setup=lambda n: numpy.random.randint(0, 1000, n),
    kernels=[
        lambda a: a[::-1],
        lambda a: numpy.ascontiguousarray(a[::-1]),
        lambda a: numpy.fliplr([a])[0],
    ],
    labels=["a[::-1]", "ascontiguousarray(a[::-1])", "fliplr"],
    n_range=[2 ** k for k in range(25)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)

perfplot至少需要Python 3.6,因为它使用f字符串(文字字符串插值)
Fivef

42

因为这似乎还没有被标记为答案……托马斯·阿里德森的答案应该是正确的:只使用

np.flipud(your_array) 

如果是一维数组(列数组)。

用matrizs做

fliplr(matrix)

如果您想反转行和flipud(matrix)想要反转列。无需将一维列数组设置为二维行数组(具有一个“无”层的矩阵),然后对其进行翻转。


38

np.fliplr() 左右翻转数组。

请注意,对于一维数组,您需要进行一些技巧:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]

34
reversed_arr = np.flipud(arr1d)似乎直接起作用。
Thomas Arildsen

3

我将在前面有关的答案上进行扩展np.fliplr()。下面的代码演示了如何构建一个1d数组,将其转换为2d数组,翻转它,然后再转换回1d数组。time.clock()将用于保留时间,以秒为单位。

import time
import numpy as np

start = time.clock()
x = np.array(range(3))
#transform to 2d
x = np.atleast_2d(x)
#flip array
x = np.fliplr(x)
#take first (and only) element
x = x[0]
#print x
end = time.clock()
print end-start

带有未打印注释的注释:

[2 1 0]
0.00203907123594

随着打印语句注释掉:

5.59799927506e-05

因此,就效率而言,我认为这很不错。对于那些热爱一线做的人,这里就是这种形式。

np.fliplr(np.atleast_2d(np.array(range(3))))[0]

3
用如此小的数组来计时某些东西是毫无用处的。如果要比较事物,最好使用需要一段时间的事物,例如3000甚至更多的元素。
巴拉巴斯

0

扩展别人的说法,我将举一个简短的例子。

如果您有一维数组...

>>> import numpy as np
>>> x = np.arange(4) # array([0, 1, 2, 3])
>>> x[::-1] # returns a view
Out[1]: 
array([3, 2, 1, 0])

但是如果您正在使用2D阵列...

>>> x = np.arange(10).reshape(2, 5)
>>> x
Out[2]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> x[::-1] # returns a view:
Out[3]: array([[5, 6, 7, 8, 9],
               [0, 1, 2, 3, 4]])

这实际上并不会反转矩阵。

应该使用np.flip实际反转元素

>>> np.flip(x)
Out[4]: array([[9, 8, 7, 6, 5],
               [4, 3, 2, 1, 0]])

如果要一张一张地打印矩阵的元素,请同时使用平面和翻转

>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.