Cách hiệu quả nhất để đảo ngược một mảng numpy


276

Dù bạn có tin hay không, sau khi định hình mã hiện tại của tôi, hoạt động lặp đi lặp lại của đảo ngược mảng numpy đã ăn một khối lớn thời gian chạy. Những gì tôi có ngay bây giờ là phương pháp dựa trên chế độ xem phổ biến:

reversed_arr = arr[::-1]

Có cách nào khác để làm điều đó hiệu quả hơn không, hay đó chỉ là ảo ảnh từ nỗi ám ảnh của tôi với hiệu suất numpy không thực tế?


27
Er ... arr[::-1]chỉ trả về một cái nhìn đảo ngược. Nó nhanh như bạn có thể nhận được, và không phụ thuộc vào số lượng mục trong mảng, vì nó chỉ thay đổi các bước. Là những gì bạn đang đảo ngược thực sự là một mảng numpy?
Joe Kington

vâng, thực sự, arrlà một mảng numpy.
nye17

12
Hmmm ... Chà, trên máy tính xách tay của tôi, nó mất khoảng 670 nano giây bất kể độ dài của mảng. Nếu đó là nút cổ chai của bạn, bạn có thể cần phải chuyển đổi ngôn ngữ ... Tôi chắc chắn rằng bạn sẽ không tìm thấy cách nhanh hơn để đảo ngược một mảng khó chịu. Chúc may mắn, ở mức nào!
Joe Kington

6
Chà, bạn có nhất thiết phải chạy nó trong một vòng lặp không? Trong một số trường hợp, tốt hơn là tạo một mảng gọn gàng với hàng triệu mục và sau đó hoạt động trên toàn bộ mảng. Ngay cả khi bạn đang thực hiện một phương pháp khác biệt hữu hạn hoặc một cái gì đó tương tự trong đó kết quả phụ thuộc vào kết quả trước đó, đôi khi bạn có thể làm điều này. (Đôi khi nhấn mạnh ...) Ở bất kỳ giá nào, nếu tốc độ là mục tiêu chính, fortran vẫn là vua. f2pylà bạn của bạn! Việc viết các phần quan trọng của thuật toán (đặc biệt là trong tính toán khoa học) bằng ngôn ngữ khác và gọi nó từ python là rất đáng giá. Chúc may mắn!
Joe Kington

1
@berto. Nó chậm hơn vì nó là một trình bao bọc cho arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py . Tìm kiếm def flipud. Các chức năng có nghĩa đen là bốn dòng dài.
Nhà vật lý điên

Câu trả lời:


239

Khi bạn tạo, reversed_arrbạn đang tạo một khung nhìn vào mảng ban đầu. Sau đó, bạn có thể thay đổi mảng ban đầu và chế độ xem sẽ cập nhật để phản ánh các thay đổi.

Bạn có đang tạo lại chế độ xem thường xuyên hơn mức bạn cần không? Bạn sẽ có thể làm một cái gì đó như thế này:

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

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

Tôi không phải là một chuyên gia numpy, nhưng có vẻ như đây sẽ là cách nhanh nhất để làm mọi thứ trong numpy. Nếu đây là những gì bạn đang làm, tôi không nghĩ bạn có thể cải thiện nó.

PS Thảo luận tuyệt vời về quan điểm numpy ở đây:

Xem trên một mảng numpy?


Nó có giúp tạo ra một đối tượng lát và sau đó sử dụng lại nó trên nhiều mảng không?
endolith

1
Trên thực tế tôi chỉ thử nghiệm nó và không thấy bất kỳ sự khác biệt nào với đối tượng lát được tạo bên ngoài vòng lặp. (Oh chờ đợi, nó rất nhanh hơn một chút repeatably 43,4 ms vs 44,3 ms cho một vòng lặp 1000000.)
endolith

Những gì được look_atchức năng giả sử để làm gì?
mrgloom

1
@mrgloom Nó được cho là đại diện cho bất kỳ nhiệm vụ nào nhìn vào dữ liệu. Điểm của ví dụ là chỉ ra rằng chế độ xem reversed_arrvẫn có thể sử dụng được sau khi dữ liệu cơ bản được thay đổi. Viết các giá trị mới vào mảng không làm mất hiệu lực khung nhìn. Trên thực tế, bạn cũng có thể sử dụng khung nhìn để viết các giá trị mới vào mảng. reversed_arr[0] = 99sẽ đặt phần tử cuối cùng trong mảng thành 99, giống như arr[-1] = 99vậy.
steveha

60

Như đã đề cập ở trên, a[::-1]thực sự chỉ tạo ra một khung nhìn, vì vậy đó là một hoạt động liên tục (và như vậy sẽ không mất nhiều thời gian hơn khi mảng phát triển). Nếu bạn cần mảng liền kề (ví dụ vì bạn đang thực hiện nhiều thao tác vectơ với nó), ascontiguousarraythì nhanh như flipup/ fliplr:

nhập mô tả hình ảnh ở đây


Mã để tạo cốt truyện:

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 yêu cầu ít nhất Python 3.6 vì nó sử dụng chuỗi f (Nội suy chuỗi ký tự)
fivef

42

Bởi vì điều này dường như chưa được đánh dấu là đã trả lời ... Câu trả lời của Thomas Arildsen nên là câu trả lời thích hợp: chỉ cần sử dụng

np.flipud(your_array) 

nếu nó là mảng 1d (mảng cột).

Với matrize làm

fliplr(matrix)

nếu bạn muốn đảo ngược hàng và flipud(matrix)nếu bạn muốn lật cột. Không cần phải tạo mảng cột 1d của bạn thành mảng hàng 2 chiều (ma trận với một lớp Không có) và sau đó lật nó.


38

np.fliplr() lật mảng sang trái sang phải.

Lưu ý rằng đối với mảng 1d, bạn cần lừa nó một chút:

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

34
reversed_arr = np.flipud(arr1d)dường như làm việc trực tiếp
Thomas Arildsen

3

Tôi sẽ mở rộng về câu trả lời trước đó về np.fliplr(). Dưới đây là một số mã chứng minh việc xây dựng mảng 1d, chuyển đổi nó thành mảng 2d, lật nó, sau đó chuyển đổi lại thành mảng 1d. time.clock()sẽ được sử dụng để giữ thời gian, được trình bày dưới dạng giây.

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

Với tuyên bố in không bị thiếu:

[2 1 0]
0.00203907123594

Với tuyên bố in nhận xét:

5.59799927506e-05

Vì vậy, về mặt hiệu quả, tôi nghĩ đó là điều tốt. Đối với những bạn thích làm điều đó trong một dòng, đây là hình thức đó.

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

3
Thời gian một cái gì đó với một mảng nhỏ như vậy là khá vô dụng. Nếu bạn muốn so sánh mọi thứ, tốt hơn là sử dụng một cái gì đó mất một thời gian, như 3000 hoặc thậm chí nhiều yếu tố hơn.
Barabas

0

Mở rộng về những gì người khác đã nói tôi sẽ đưa ra một ví dụ ngắn.

Nếu bạn có mảng 1D ...

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

Nhưng nếu bạn đang làm việc với một mảng 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]])

Điều này không thực sự đảo ngược Ma trận.

Nên sử dụng np.flip để thực sự đảo ngược các yếu tố

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

Nếu bạn muốn in từng phần tử của ma trận, hãy sử dụng phẳng cùng với lật

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

-1

Để làm cho nó hoạt động với các số âm và một danh sách dài, bạn có thể làm như sau:

b = numpy.flipud(numpy.array(a.split(),float))

Trường hợp flipud là cho 1d arra

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.