Eu tenho uma matriz numpy como esta: [1 2 2 0 0 1 3 5]
É possível obter o índice dos elementos como uma matriz 2D? Por exemplo, a resposta para a entrada acima seria[[3 4], [0 5], [1 2], [6], [], [7]]
Atualmente, tenho que repetir os diferentes valores e chamar numpy.where(input == i)
cada valor, que tem um desempenho terrível com uma entrada grande o suficiente.
python
numpy
numpy-ndarray
Frederico Schardong
fonte
fonte
np.argsort([1, 2, 2, 0, 0, 1, 3, 5])
dáarray([3, 4, 0, 5, 1, 2, 6, 7], dtype=int64)
. então você pode apenas comparar os próximos elementos.Respostas:
Aqui está uma abordagem O (max (x) + len (x)) usando
scipy.sparse
:Isso funciona criando uma matriz esparsa com entradas nas posições (x [0], 0), (x [1], 1), ... Usando o formato
CSC
(coluna esparsa compactada) isso é bastante simples. A matriz é então convertida para o formatoLIL
(lista vinculada). Esse formato armazena os índices da coluna para cada linha como uma lista em seurows
atributo; portanto, tudo o que precisamos fazer é pegar isso e convertê-lo em lista.Observe que, para pequenas matrizes, as
argsort
soluções baseadas são provavelmente mais rápidas, mas em um tamanho não insanamente grande, isso passará.EDITAR:
argsort
numpy
única solução baseada em :Se a ordem dos índices dentro dos grupos não importar, você também pode tentar
argpartition
(isso não faz diferença neste pequeno exemplo, mas isso não é garantido em geral):EDITAR:
@Divakar recomenda contra o uso de
np.split
. Em vez disso, um loop é provavelmente mais rápido:Ou você pode usar o novo operador de morsa (Python3.8 +):
EDITAR (EDITADO):
(Não é puro numpy): Como alternativa ao numba (consulte a publicação de @ senderle), também podemos usar o pythran.
Ajuntar com
pythran -O3 <filename.py>
Aqui
numba
vence por um bigode em termos de desempenho:Coisas mais antigas:
Horários vs. numba (antigo)
fonte
np.split
.Uma opção potencial, dependendo do tamanho dos seus dados, é desistir
numpy
e usarcollections.defaultdict
:Então você acaba com um dicionário de
{value1: [index1, index2, ...], value2: [index3, index4, ...]}
. A escala de tempo é quase linear com o tamanho da matriz, então 10.000.000 demoram ~ 2.7s na minha máquina, o que parece bastante razoável.fonte
Embora o pedido seja uma
numpy
solução, decidi ver se há umanumba
solução interessante . E de fato existe! Aqui está uma abordagem que representa a lista particionada como uma matriz irregular armazenada em um único buffer pré-alocado. Isso se inspira naargsort
abordagem proposta por Paul Panzer . (Para uma versão mais antiga que não foi tão bem, mas era mais simples, veja abaixo.)Isso processa uma lista de dez milhões de itens em 75ms, que é quase uma aceleração de 50x de uma versão baseada em lista escrita em Python puro.
Para uma versão mais lenta, mas um pouco mais legível, eis o que eu tinha antes, com base no suporte experimental recentemente adicionado a "listas digitadas" de tamanho dinâmico, que nos permitem encher cada lixeira de maneira fora de ordem muito mais rapidamente.
Isso luta
numba
um pouco com o mecanismo de inferência de tipos e tenho certeza de que há uma maneira melhor de lidar com essa parte. Isso também é quase 10 vezes mais lento que o acima.Eu testei estes contra o seguinte:
Também os testei em uma versão cython pré-compilada semelhante a
enum_bins_numba_buffer
(descrita em detalhes abaixo).Em uma lista de dez milhões de ints aleatórios (
ints = np.random.randint(0, 100, 10000000)
), obtenho os seguintes resultados:De maneira impressionante, essa maneira de trabalhar
numba
supera umacython
versão da mesma função, mesmo com a verificação de limites desativada. Ainda não tenho familiaridade suficientepythran
para testar essa abordagem, mas estaria interessado em ver uma comparação. Parece provável, com base nessa aceleração, que apythran
versão também seja um pouco mais rápida com essa abordagem.Aqui está a
cython
versão para referência, com algumas instruções de construção. Depois decython
instalar, você precisará de umsetup.py
arquivo simples como este:E o módulo cython
enum_bins_cython.pyx
:Com esses dois arquivos em seu diretório de trabalho, execute este comando:
Você pode importar a função usando
from enum_bins_cython import enum_bins_cython
.fonte
Aqui está uma maneira realmente muito estranha de fazer isso que é terrível, mas achei engraçado demais para não compartilhar - e tudo
numpy
!EDIT: este é o melhor método que eu poderia encontrar nesse caminho. Ainda é 10 vezes mais lento que a
argsort
solução da @PaulPanzer :fonte
Você pode fazer isso criando um dicionário de números, as chaves seriam os números e os valores deveriam ser os índices que o número visualizado, esta é uma das maneiras mais rápidas de fazer isso, você pode ver o código abaixo:
fonte
Pseudo-código:
obtenha o "número de matrizes 1d na matriz 2d", subtraindo o valor mínimo da sua matriz numpy do valor máximo e depois mais um. No seu caso, será 5-0 + 1 = 6
inicialize uma matriz 2d com o número de matrizes 1d dentro dela. No seu caso, inicialize uma matriz 2d com 6 1d. Cada matriz 1d corresponde a um elemento único na sua matriz numpy, por exemplo, a primeira matriz 1d corresponde a '0', a segunda matriz 1d corresponde a '1', ...
loop através de sua matriz numpy, coloque o índice do elemento na matriz 1d correspondente à direita. No seu caso, o índice do primeiro elemento na sua matriz numpy será colocado na segunda matriz 1d, o índice do segundo elemento na sua matriz numpy será colocado na terceira matriz 1d, ....
Esse pseudocódigo levará um tempo linear para ser executado, pois depende do comprimento da sua matriz numpy.
fonte
Isso fornece exatamente o que você deseja e levaria cerca de 2,5 segundos para 10.000.000 na minha máquina:
fonte
Portanto, dada uma lista de elementos, você deseja criar pares (elemento, índice). Em tempo linear, isso pode ser feito como:
Isso deve levar tempo O (n). Não consigo pensar em uma solução mais rápida a partir de agora, mas atualizarei aqui se o fizer.
fonte