Cara paling efisien untuk membalikkan array numpy

276

Percaya atau tidak, setelah memprofilkan kode saya saat ini, operasi berulang dari array array numpy memakan sepotong besar waktu berjalan. Apa yang saya miliki saat ini adalah metode berbasis tampilan umum:

reversed_arr = arr[::-1]

Apakah ada cara lain untuk melakukannya dengan lebih efisien, atau hanya ilusi dari obsesi saya dengan kinerja numpy yang tidak realistis?

nye17
sumber
27
Er ... arr[::-1]baru saja mengembalikan tampilan terbalik. Ini secepat yang Anda bisa dapatkan, dan tidak bergantung pada jumlah item dalam array, karena hanya mengubah langkahnya. Apakah yang Anda membalikkan sebenarnya array numpy?
Joe Kington
ya, memang, arradalah array numpy.
nye17
12
Hmmm ... Nah, di laptop saya dibutuhkan sekitar 670 nanodetik terlepas dari panjang array. Jika itu hambatan Anda, Anda mungkin perlu beralih bahasa ... Saya cukup yakin Anda tidak akan menemukan cara yang lebih cepat untuk membalikkan array yang numpy. Semoga berhasil, bagaimanapun juga!
Joe Kington
6
Nah, apakah Anda harus menjalankannya dalam satu lingkaran? Dalam beberapa kasus, lebih baik membuat array numpy dengan jutaan item dan kemudian beroperasi di seluruh array. Bahkan jika Anda melakukan metode beda hingga atau yang serupa di mana hasilnya tergantung pada hasil sebelumnya, Anda kadang-kadang dapat melakukan ini. (Penekanan pada kadang-kadang ...) Bagaimanapun, jika kecepatan adalah tujuan utama, fortran masih raja. f2pyadalah temanmu! Seringkali bermanfaat untuk menulis bagian-bagian penting dari suatu algoritma (terutama dalam komputasi ilmiah) dalam bahasa lain dan menyebutnya dari python. Semoga berhasil!
Joe Kington
1
@berto. Itu lebih lambat karena ini adalah pembungkus untuk arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py . Cari def flipud. Fungsinya secara harfiah empat baris.
Fisikawan Gila

Jawaban:

240

Saat Anda membuat, reversed_arrAnda membuat tampilan ke dalam array asli. Anda kemudian dapat mengubah array asli, dan tampilan akan diperbarui untuk mencerminkan perubahan.

Apakah Anda membuat ulang tampilan lebih sering dari yang seharusnya? Anda harus dapat melakukan sesuatu seperti ini:

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

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

Saya bukan ahli numpy, tapi sepertinya ini akan menjadi cara tercepat untuk melakukan hal-hal di numpy. Jika ini yang sudah Anda lakukan, saya pikir Anda tidak bisa memperbaikinya.

PS Diskusi hebat tentang pandangan numpy di sini:

Lihat ke array yang numpy?

steveha
sumber
Apakah itu membantu untuk membuat objek irisan dan kemudian menggunakannya kembali pada banyak array?
endolith
1
Sebenarnya saya baru saja mengujinya dan tidak melihat perbedaan dengan objek irisan yang dibuat di luar loop. (Oh, tunggu, ini sedikit lebih cepat. Diulang 43,4 ms vs 44,3 ms untuk loop 1000000)
endolith
Apa look_atfungsi yang seharusnya dilakukan?
mrgloom
1
@ mrgloom Seharusnya mewakili tugas apa pun yang melihat data. Inti dari contoh ini adalah untuk menunjukkan bahwa tampilan reversed_arrmasih dapat digunakan setelah data yang mendasarinya diubah. Menulis nilai baru ke dalam array tidak membuat tampilan menjadi tidak valid. Sebenarnya Anda juga bisa menggunakan tampilan untuk menulis nilai baru ke dalam array. reversed_arr[0] = 99akan mengatur elemen terakhir dalam array ke 99, sama seperti arr[-1] = 99akan.
steveha
60

Seperti yang disebutkan di atas, a[::-1]benar-benar hanya menciptakan tampilan, jadi ini adalah operasi waktu-konstan (dan karena itu tidak memakan waktu lebih lama ketika array tumbuh). Jika Anda membutuhkan array yang berdekatan (misalnya karena Anda melakukan banyak operasi vektor dengannya), ascontiguousarraykira-kira secepat flipup/ fliplr:

masukkan deskripsi gambar di sini


Kode untuk menghasilkan plot:

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,
)
Nico Schlömer
sumber
perfplot membutuhkan setidaknya Python 3.6 karena menggunakan f-string (Literal String Interpolation)
fivef
42

Karena ini sepertinya belum ditandai sebagai dijawab ... Jawaban Thomas Arildsen haruslah yang tepat: gunakan saja

np.flipud(your_array) 

jika itu adalah array 1d (array kolom).

Dengan matriks lakukan

fliplr(matrix)

jika Anda ingin membalikkan baris dan flipud(matrix)jika Anda ingin membalik kolom. Tidak perlu membuat array kolom 1d Anda menjadi array baris 2dimensi (matriks dengan satu lapisan Tidak Ada) dan kemudian membaliknya.

zauberfein
sumber
38

np.fliplr() membalik array ke kiri.

Perhatikan bahwa untuk array 1d, Anda perlu sedikit menipu:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]
tooty44
sumber
34
reversed_arr = np.flipud(arr1d)tampaknya bekerja secara langsung.
Thomas Arildsen
3

Saya akan memperluas jawaban tentang sebelumnya np.fliplr(). Berikut adalah beberapa kode yang menunjukkan membangun array 1d, mengubahnya menjadi array 2d, membaliknya, lalu mengubahnya kembali menjadi array 1d. time.clock()akan digunakan untuk menjaga waktu, yang disajikan dalam hitungan detik.

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

Dengan pernyataan cetak tanpa komentar:

[2 1 0]
0.00203907123594

Dengan pernyataan cetak berkomentar:

5.59799927506e-05

Jadi, dalam hal efisiensi, saya pikir itu layak. Bagi Anda yang suka melakukannya dalam satu baris, di sini adalah bentuk itu.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]
M. Murphy
sumber
3
Mengatur waktu sesuatu dengan array kecil seperti itu sangat tidak berguna. Jika Anda ingin membandingkan beberapa hal, akan lebih baik menggunakan sesuatu yang membutuhkan waktu, seperti 3000 atau bahkan lebih banyak elemen.
Barabas
0

Memperluas apa yang orang lain katakan saya akan memberikan contoh singkat.

Jika Anda memiliki larik 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])

Tetapi jika Anda bekerja dengan array 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]])

Ini sebenarnya tidak membalik Matrix.

Harus menggunakan np.flip untuk benar-benar membalik elemen

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

Jika Anda ingin mencetak elemen dari matriks satu per satu gunakan flat bersama dengan flip

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

Agar dapat bekerja dengan angka negatif dan daftar panjang, Anda dapat melakukan hal berikut:

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

Di mana flipud adalah untuk ar 1d

Boris Stoyanov
sumber