A maneira mais eficiente de reverter uma matriz numpy

276

Acredite ou não, depois de traçar o perfil do meu código atual, a operação repetitiva da reversão numpy da matriz consumiu uma grande parte do tempo de execução. O que tenho agora é o método comum baseado em exibição:

reversed_arr = arr[::-1]

Existe alguma outra maneira de fazê-lo de forma mais eficiente, ou é apenas uma ilusão da minha obsessão por desempenho numpy irrealista?

nye17
fonte
27
Er ... arr[::-1]apenas retorna uma visão invertida. É o mais rápido possível, e não depende do número de itens na matriz, pois apenas altera os avanços. O que você está revertendo é realmente uma matriz numpy?
21811 Joe Kington
sim, de fato, arré uma matriz numpy.
precisa saber é
12
Hmmm ... Bem, no meu laptop leva cerca de 670 nanossegundos, independentemente do tamanho da matriz. Se esse é o seu gargalo, pode ser necessário mudar de idioma ... Tenho certeza de que você não encontrará uma maneira mais rápida de reverter uma matriz numpy. Boa sorte, pelo menos!
21811 Joe Kington
6
Bem, você necessariamente precisa executá-lo dentro de um loop? Em alguns casos, é melhor criar uma matriz numpy com milhões de itens e operar em toda a matriz. Mesmo se você estiver usando um método de diferenças finitas ou algo semelhante onde o resultado depende do resultado anterior, às vezes você pode fazer isso. (Ênfase em algumas vezes ...) De qualquer forma, se a velocidade é o objetivo principal, o fortran ainda é o rei. f2pyé seu amigo! Muitas vezes, vale a pena escrever partes críticas de desempenho de um algoritmo (especialmente em computação científica) em outra linguagem e chamá-lo de python. Boa sorte!
21411 Joe Kington
1
@berto. É mais lento, pois é um wrapper para arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py . Pesquise por def flipud. A função tem literalmente quatro linhas.
Mad físico

Respostas:

240

Quando você cria, reversed_arrestá criando uma exibição na matriz original. Você pode alterar a matriz original e a exibição será atualizada para refletir as alterações.

Você está recriando a exibição com mais frequência do que precisa? Você deve conseguir fazer algo assim:

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

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

Eu não sou um especialista numpy, mas isso parece ser a maneira mais rápida de fazer as coisas numpy. Se é isso que você já está fazendo, acho que não pode melhorar.

PS Ótima discussão sobre visões numpy aqui:

Ver em uma matriz numpy?

steveha
fonte
Ajuda criar um objeto de fatia e depois reutilizá-lo em muitas matrizes?
endolith 16/05
1
Na verdade, eu apenas testei e não vejo nenhuma diferença com o objeto de fatia criado fora do loop. (Oh espera, é muito ligeiramente mais rápido repetidamente 43,4 ms vs 44,3 ms para um 1000000 loop.)
endolith
O look_atque a função deve fazer?
mrgloom
1
@mrgloom Supõe-se que represente qualquer tarefa que analise os dados. O objetivo do exemplo era mostrar que a visualização reversed_arrainda é utilizável após a alteração dos dados subjacentes. A gravação de novos valores na matriz não invalida a exibição. Na verdade, você também pode usar a visualização para escrever novos valores na matriz. reversed_arr[0] = 99definiria o último elemento da matriz como 99, o mesmo que arr[-1] = 99faria.
steveha
60

Como mencionado acima, a[::-1]realmente cria apenas uma visualização, portanto é uma operação de tempo constante (e, como tal, não leva mais tempo à medida que a matriz cresce). Se você precisar que o array seja contíguo (por exemplo, porque você está executando muitas operações vetoriais), ascontiguousarrayé tão rápido quanto flipup/ fliplr:

insira a descrição da imagem aqui


Código para gerar o gráfico:

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
fonte
O perfplot requer pelo menos o Python 3.6, porque ele usa f-strings (Literal String Interpolation)
fivef
42

Porque isso parece não estar marcado como respondido ainda ... A resposta de Thomas Arildsen deve ser a correta: basta usar

np.flipud(your_array) 

se for uma matriz 1d (matriz da coluna).

Com matrizes fazer

fliplr(matrix)

se você deseja reverter linhas e flipud(matrix)se deseja inverter colunas. Não há necessidade de transformar seu array de colunas 1d em um array de linhas bidimensionais (matriz com uma camada None) e depois invertê-lo.

zauberfein
fonte
38

np.fliplr() vira a matriz da esquerda para a direita.

Observe que, para matrizes 1d, é necessário enganá-lo um pouco:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]
tooty44
fonte
34
reversed_arr = np.flipud(arr1d)parece funcionar diretamente.
Thomas Arildsen
3

Vou expandir a resposta anterior sobre np.fliplr(). Aqui está um código que demonstra a construção de uma matriz 1d, transformando-a em uma matriz 2d, invertendo-a e convertendo-a novamente em uma matriz 1d. time.clock()será usado para manter o tempo, que é apresentado em termos de segundos.

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

Com a declaração de impressão descomentada:

[2 1 0]
0.00203907123594

Com a declaração impressa comentada:

5.59799927506e-05

Então, em termos de eficiência, acho isso decente. Para aqueles de vocês que gostam de fazer isso em uma linha, aqui está essa forma.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]
M. Murphy
fonte
3
Cronometrar algo com uma matriz tão pequena é bastante inútil. Se você quiser comparar as coisas, seria melhor usar algo que demora um pouco, como 3000 ou talvez ainda mais elementos.
Barabas 13/05
0

Expandindo o que outros disseram, darei um pequeno exemplo.

Se você tem uma matriz 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])

Mas se você estiver trabalhando com uma matriz 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]])

Na verdade, isso não inverte a matriz.

Deve usar np.flip para realmente reverter os elementos

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

Se você deseja imprimir os elementos de uma matriz um por um, use o plano juntamente com o flip

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

Para que ele funcione com números negativos e uma lista longa, você pode fazer o seguinte:

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

Onde flipud é para 1d arra

Boris Stoyanov
fonte