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?


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?


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

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.


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]

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]

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

-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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.