反转numpy数组的最有效方法

362

信不信由你,在对我的代码进行性能分析后,numpy数组翻转的重复操作占据了大量运行时间。我现在采用的是常见的基于视图的方法:

reversed_arr = arr[::-1]

有没有其他更有效的方法,还是这只是我对不切实际的numpy性能的执着的幻觉?


39
arr[::-1] 返回一个反转的视图,速度非常快,且不依赖于数组中的元素数量,因为它只是更改步幅。你要反转的是否真的是一个NumPy数组? - Joe Kington
14
嗯...在我的笔记本电脑上,无论数组长度如何,反转一个numpy数组大约需要670纳秒。如果这是您的瓶颈,您可能需要更换编程语言...我很确定您不会找到更快速反转numpy数组的方法。无论如何,祝你好运! - Joe Kington
6
你一定要在循环内运行吗?在某些情况下,最好制作一个拥有数百万项的numpy数组,然后对整个数组执行操作。即使你正在执行有限差分方法或类似的任务,其中结果依赖于先前的结果,有时也可以这样做。(强调有时候……)无论如何,如果速度是主要目标,FORTRAN仍然是王者。f2py是你的朋友!通常值得将算法(尤其是科学计算中的性能关键部分)用另一种语言编写,并从Python中调用它。祝好运! - Joe Kington
2
@berto。由于它是对 arr[::-1] 的封装,所以速度较慢:https://github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py. 搜索 def flipud。这个函数实际上只有四行代码。 - Mad Physicist
1
@berto,如果你希望你的链接能够保持超过几天的时间,那么这不是一个好主意。主分支中的行号非常频繁地更改。 - Mad Physicist
显示剩余4条评论
8个回答

320
reversed_arr = arr[::-1]

返回一个翻转的 视图,反映原始数组 arr 的情况。对原始数组 arr 的任何更改也将立即在 reversed_arr 中可见。对于 arrreversed_arr 的基础数据缓冲区是共享的,因此创建此视图始终是即时的,并且不需要为数组内容进行任何额外的内存分配或复制。

另请参见 NumPy 视图的讨论:如何创建 NumPy 数组的视图?


关于视图性能问题的可能解决方案

您是否比需要更频繁地重新创建视图?您应该能够执行类似以下的操作:

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中执行任务的最快方法。如果您已经在这样做,我认为您无法改进它。


创建一个切片对象,然后在许多数组中重复使用它是否有帮助? - endolith
1
实际上,我刚刚测试了一下,并没有看到在循环外创建的切片对象有任何区别。(哦,等等,它非常轻微地更快。对于1000000次循环,重复为43.4毫秒与44.3毫秒) - endolith
1
look_at 函数应该做什么? - mrgloom
1
@mrgloom 它应该代表查看数据的任何任务。示例的重点是展示在更改底层数据后,视图“reversed_arr”仍然可用。向数组中写入新值不会使视图无效。实际上,您还可以使用视图将新值写入数组中。reversed_arr [0] = 99将把数组中的最后一个元素设置为99,与arr [-1] = 99相同。 - steveha
有趣的是,使用视图似乎会导致scipy的lombscargle函数在一些之前的版本(但在我的情况下是1.8.1)中失败。例如,pgram = signal.lombscargle(x[::-1], y, w)会出现错误:TypeError: Invalid call to pythranized function \_lombscargle(float64[:] (is a view), float64[:], float64[:])'`。这是一个应该在更新的版本中解决的错误:Github问题 - Lu Kas

90
a[::-1]

只创建一个视图,因此它是常数时间操作(因此随着数组的增长不会变慢)。如果您需要数组连续(例如,因为正在执行许多向量操作),ascontiguousarrayflipud/fliplr 的速度大致相同:

enter image description here


生成该图的代码:

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)",
)

1
perfplot 需要至少 Python 3.6 版本,因为它使用了 f-strings(字面字符串插值)。 - fivef

49

因为似乎这个问题还没有被标记为已回答... Thomas Arildsen的回答应该是正确的:只需使用

np.flipud(your_array) 
如果它是一维数组(列数组)。
使用矩阵操作。
fliplr(matrix)

如果你想翻转行,使用flipud(matrix);如果你想翻转列,则不需要将1维列数组变成2维行数组(即带有一个None层的矩阵),然后再进行翻转。


41

np.fliplr()函数将数组从左到右翻转。

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


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

37
似乎reversed_arr = np.flipud(arr1d)可以直接使用。 - Thomas Arildsen
2
一个一维数组被认为是一列,而不是一行,因此应使用np.flipud(上下)而不是fliplr(左右)。 - mins

4

我将对之前关于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

注释掉print语句后:

5.59799927506e-05

就效率而言,我认为这还不错。对于那些喜欢一行代码的人,这是它的形式。

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

6
用如此小的数组来计时是相当无用的。如果要进行比较,最好使用需要一段时间的东西,比如3000个甚至更多的元素。 - Bas

2
np.flip的基于切片符号的类似方法是[ ::-1, ::-1]。
a = np.array([[1., 2.], [3., 4.], [5, 6]])
print(a)

out: [[1. 2.]
      [3. 4.]
      [5. 6.]]

b=a[::-1,::-1]
print(b)

out: [[1. 2.]
      [3. 4.]
      [5. 6.]]

1

在其他人所说的基础上,我会给出一个简短的例子。

如果你有一个一维数组...

>>> 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])

但是如果你正在使用一个二维数组...
>>> 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]])

如果您想逐个打印矩阵的元素,请使用flat和flip。
>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0

我认为你也可以使用 x[...,::-1] 来创建一个多维数组的反向视图,而不必使用 np.flip,就像这里演示的那样 https://dev59.com/kms05IYBdhLWcg3wJurp - phntm

0
为了让它支持负数和长列表,你可以按照以下步骤操作:
b = numpy.flipud(numpy.array(a.split(),float))

flipud 用于一维数组


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接