Classificando com eficiência uma matriz numpy em ordem decrescente?

121

Estou surpreso que esta pergunta específica não tenha sido feita antes, mas eu realmente não a encontrei no SO nem na documentação do np.sort.

Digamos que eu tenha uma matriz numpy aleatória contendo inteiros, por exemplo:

> temp = np.random.randint(1,10, 10)    
> temp
array([2, 4, 7, 4, 2, 2, 7, 6, 4, 4])

Se eu classificar, obtenho ordem crescente por padrão:

> np.sort(temp)
array([2, 2, 2, 4, 4, 4, 4, 6, 7, 7])

mas quero que a solução seja classificada em ordem decrescente .

Agora, eu sei que sempre posso fazer:

reverse_order = np.sort(temp)[::-1]

mas esta última afirmação é eficiente ? Não cria uma cópia em ordem crescente e, em seguida, reverte essa cópia para obter o resultado na ordem reversa? Se for esse o caso, existe uma alternativa eficiente? Parece que não np.sortaceita parâmetros para alterar o sinal das comparações na operação de classificação para obter as coisas na ordem inversa.

Amelio Vazquez-Reina
fonte

Respostas:

139

temp[::-1].sort()classifica a matriz no local, enquanto np.sort(temp)[::-1]cria uma nova matriz.

In [25]: temp = np.random.randint(1,10, 10)

In [26]: temp
Out[26]: array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])

In [27]: id(temp)
Out[27]: 139962713524944

In [28]: temp[::-1].sort()

In [29]: temp
Out[29]: array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])

In [30]: id(temp)
Out[30]: 139962713524944
Padraic Cunningham
fonte
30
Obrigado, mas como temp[::-1].sort()sabe que tem que classificar na ordem inversa ?? A maneira como eu li isso é: inverta a matriz original e, em seguida, classifique-a (em ordem crescente). Por que inverter a matriz original (vindo em uma ordem aleatória) e, em seguida, classificá-la em ordem crescente retornaria a matriz na ordem inversa?
Amelio Vazquez-Reina
14
Este comportamento está documentado, visto que não é intuitivo.
ebarr
18
Parece que funciona porque [::-1]simplesmente diz a numpy para iterar no array de trás para frente, em vez de reordenar o array. Portanto, quando a classificação no local acontece, ela realmente classifica em ordem crescente e move partes, mas deixa a parte da iteração reversa intacta.
perimosocordiae
45
Com a=np.array((...))o idioma a[::-1]não reverte nada, é apenas uma nova visualização dos mesmos dados, mais especificamente uma visualização em espelho. O método a[::-1].sort() opera na imagem espelhada , o que implica que ao sortmover para a esquerda um item menor em sua imagem espelhada, na realidade ele o está movendo para a direita no bloco de memória real do aarray. A visualização espelhada é classificada em ordem crescente, os dados reais são classificados em ordem decrescente. Experimente você mesmo em casa, com algumas moedas diferentes e um espelho!
gboffi
30
Isso realmente deve ser adicionado como um parâmetro legível, em np.sort(temp,order='descending')vez de exigir esses tipos de hacks
Nathan
92
>>> a=np.array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])

>>> np.sort(a)
array([2, 2, 4, 4, 4, 4, 5, 6, 7, 8])

>>> -np.sort(-a)
array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])
Mike O'Connor
fonte
2
Melhor resposta - curta e doce, e nenhum conhecimento do axisqual np.sortfoi aplicado é necessário.
Luke Davis
2
Isso é diferente de np.sort(temp)[::-1]colocar nans na parte de trás da matriz em vez de na frente. Se isso é bom ou ruim está em debate.
Ben
15

Para matrizes curtas, sugiro usar np.argsort()encontrando os índices da matriz ordenada negativada, o que é um pouco mais rápido do que inverter a matriz classificada:

In [37]: temp = np.random.randint(1,10, 10)

In [38]: %timeit np.sort(temp)[::-1]
100000 loops, best of 3: 4.65 µs per loop

In [39]: %timeit temp[np.argsort(-temp)]
100000 loops, best of 3: 3.91 µs per loop
Kasravnd
fonte
a[np.argsort(-a)]é provavelmente a melhor abordagem para qualquer outra nesta página. Sem reversão de passo -1 e menos um sinal de menos para pensar.
Jarad
8

Infelizmente, quando você tem uma matriz complexa, só np.sort(temp)[::-1]funciona corretamente. Os dois outros métodos mencionados aqui não são eficazes.

anishtain4
fonte
@ anishtain4: Por "matriz complexa", você quis dizer uma matriz de números complexos? Ou você quis dizer uma matriz com algum outro tipo de complexidade (em caso afirmativo, especifique que tipo de complexidade). Em qualquer dos casos, acho que você poderia elaborar um pouco mais sua resposta, investigando como os outros métodos podem falhar. Obrigado.
nascente de
@fountainhead, quero dizer a matriz de números complexos. Como é uma pergunta antiga, não lembro do meu caso de teste a partir de então para elaborar mais.
anishtain4
8

Cuidado com as dimensões.

Deixei

x  # initial numpy array
I = np.argsort(x) or I = x.argsort() 
y = np.sort(x)    or y = x.sort()
z  # reverse sorted array

Full Reverse

z = x[-I]
z = -np.sort(-x)
z = np.flip(y)
  • flipalterado em 1.15, versões anteriores necessárias . Solução: .1.14 axispip install --upgrade numpy

Primeira dimensão invertida

z = y[::-1]
z = np.flipud(y)
z = np.flip(y, axis=0)

Segunda dimensão invertida

z = y[::-1, :]
z = np.fliplr(y)
z = np.flip(y, axis=1)

Testando

Testando em uma matriz 100 × 10 × 10 1000 vezes.

Method       | Time (ms)
-------------+----------
y[::-1]      | 0.126659  # only in first dimension
-np.sort(-x) | 0.133152
np.flip(y)   | 0.121711
x[-I]        | 4.611778

x.sort()     | 0.024961
x.argsort()  | 0.041830
np.flip(x)   | 0.002026

Isso se deve principalmente à reindexação, e não a argsort.

# Timing code
import time
import numpy as np


def timeit(fun, xs):
    t = time.time()
    for i in range(len(xs)):  # inline and map gave much worse results for x[-I], 5*t
        fun(xs[i])
    t = time.time() - t
    print(np.round(t,6))

I, N = 1000, (100, 10, 10)
xs = np.random.rand(I,*N)
timeit(lambda x: np.sort(x)[::-1], xs)
timeit(lambda x: -np.sort(-x), xs)
timeit(lambda x: np.flip(x.sort()), xs)
timeit(lambda x: x[-x.argsort()], xs)
timeit(lambda x: x.sort(), xs)
timeit(lambda x: x.argsort(), xs)
timeit(lambda x: np.flip(x), xs)
A. West
fonte
np.flip () - super
Darius
6

Olá, eu estava procurando uma solução para reverter a classificação de um array numpy bidimensional e não consegui encontrar nada que funcionasse, mas acho que encontrei uma solução que estou enviando para o caso de alguém estar no mesmo barco.

x=np.sort(array)
y=np.fliplr(x)

np.sort classifica em ordem crescente, o que não é o que você deseja, mas o comando fliplr vira as linhas da esquerda para a direita! Parece funcionar!

Espero que te ajude!

Eu acho que é semelhante à sugestão sobre -np.sort (-a) acima, mas fiquei desanimado por comentar que nem sempre funciona. Talvez minha solução nem sempre funcione, no entanto, eu a testei com alguns arrays e parece estar OK.

Naz
fonte
1

Você pode classificar a matriz primeiro (crescente por padrão) e, em seguida, aplicar np.flip () ( https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html )

Para sua informação, também funciona com objetos datetime.

Exemplo:

    x = np.array([2,3,1,0]) 
    x_sort_asc=np.sort(x) 
    print(x_sort_asc)

    >>> array([0, 1, 2, 3])

    x_sort_desc=np.flip(x_sort_asc) 
    print(x_sort_desc)

    >>> array([3,2,1,0])
Maleckicoa
fonte
Para aqueles que têm NaN em suas matrizes, tome cuidado, vários métodos propostos produzem resultados diferentes. Por exemplo, se x = np.array([2,3,np.nan,1,0]) então a np.flip(np.sort(x))abordagem produz [nan 3. 2. 1. 0.], enquanto a -np.sort(-x)abordagem produz [3. 2. 1. 0. nan].
Uwe Mayer
1

Aqui está um truque rápido

In[3]: import numpy as np
In[4]: temp = np.random.randint(1,10, 10)
In[5]: temp
Out[5]: array([5, 4, 2, 9, 2, 3, 4, 7, 5, 8])

In[6]: sorted = np.sort(temp)
In[7]: rsorted = list(reversed(sorted))
In[8]: sorted
Out[8]: array([2, 2, 3, 4, 4, 5, 5, 7, 8, 9])

In[9]: rsorted
Out[9]: [9, 8, 7, 5, 5, 4, 4, 3, 2, 2]
Don Coder
fonte
-3

eu sugiro usar isso ...

np.arange(start_index, end_index, intervals)[::-1]

por exemplo:

np.arange(10, 20, 0.5)
np.arange(10, 20, 0.5)[::-1]

Em seguida, seu novo ataque:

[ 19.5,  19. ,  18.5,  18. ,  17.5,  17. ,  16.5,  16. ,  15.5,
    15. ,  14.5,  14. ,  13.5,  13. ,  12.5,  12. ,  11.5,  11. ,
    10.5,  10. ]
morteza omidipoor
fonte
1
Como isso resolve o problema? Você está apenas criando uma nova matriz completamente, não relacionado, (descendente) que - a propósito - poderia ser feito de uma forma mais eficiente: np.arange(20-0.5, 10-0.5, -0.5). Mas essa é uma história diferente e pode ser, por causa da pior legibilidade, discutível. Uma matriz de entrada não está classificada de forma alguma
Daniel