Em relação a esta resposta , existe uma maneira rápida de calcular medianas em uma matriz que possui grupos com um número desigual de elementos?
Por exemplo:
data = [1.00, 1.05, 1.30, 1.20, 1.06, 1.54, 1.33, 1.87, 1.67, ... ]
index = [0, 0, 1, 1, 1, 1, 2, 3, 3, ... ]
E então eu quero calcular a diferença entre o número e a mediana por grupo (por exemplo, mediana do grupo 0
é 1.025
o primeiro resultado 1.00 - 1.025 = -0.025
). Portanto, para a matriz acima, os resultados apareceriam como:
result = [-0.025, 0.025, 0.05, -0.05, -0.19, 0.29, 0.00, 0.10, -0.10, ...]
Como np.median.reduceat
ainda não existe, existe outra maneira rápida de conseguir isso? Minha matriz conterá milhões de linhas, portanto a velocidade é crucial!
Pode-se supor que os índices sejam contíguos e ordenados (é fácil transformá-los se não forem).
Dados de exemplo para comparações de desempenho:
import numpy as np
np.random.seed(0)
rows = 10000
cols = 500
ngroup = 100
# Create random data and groups (unique per column)
data = np.random.rand(rows,cols)
groups = np.random.randint(ngroup, size=(rows,cols)) + 10*np.tile(np.arange(cols),(rows,1))
# Flatten
data = data.ravel()
groups = groups.ravel()
# Sort by group
idx_sort = groups.argsort()
data = data[idx_sort]
groups = groups[idx_sort]
python
performance
numpy
median
numpy-ufunc
Jean Paul
fonte
fonte
scipy.ndimage.median
sugestão na resposta vinculada? Não me parece que ele precise de um número igual de elementos por rótulo. Ou eu perdi alguma coisa?Respostas:
Às vezes, você precisa escrever um código numpy não-idiomático, se realmente deseja acelerar seu cálculo, o que não pode ser feito com o numpy nativo.
numba
compila seu código python para o nível C. C. Como muitos numpy em si são geralmente tão rápidos quanto C, isso acaba sendo útil se o seu problema não se presta à vetorização nativa com numpy. Este é um exemplo (onde eu assumi que os índices são contíguos e classificados, o que também é refletido nos dados de exemplo):E aqui estão alguns horários usando a
%timeit
magia do IPython :Usando os dados de exemplo atualizados na pergunta, esses números (ou seja, o tempo de execução da função python vs. o tempo de execução da função acelerada por JIT) são
Isso equivale a uma aceleração de 65x no caso menor e uma aceleração de 26x no caso maior (comparado ao código de loop lento, é claro) usando o código acelerado. Outra vantagem é que (ao contrário da vetorização típica com numpy nativo), não precisamos de memória adicional para atingir essa velocidade, trata-se de código de baixo nível otimizado e compilado que acaba sendo executado.
A função acima pressupõe que matrizes numpy int sejam
int64
por padrão, o que não é realmente o caso no Windows. Portanto, uma alternativa é remover a assinatura da chamada paranumba.njit
, acionando uma compilação just-in-time adequada. Mas isso significa que a função será compilada durante a primeira execução, o que pode interferir nos resultados de temporização (podemos executar a função uma vez manualmente, usando tipos de dados representativos, ou apenas aceitar que a primeira execução de temporização será muito mais lenta, o que deve ser ignorado). Isso é exatamente o que tentei impedir especificando uma assinatura, que aciona a compilação antecipada.De qualquer forma, no caso JIT, o decorador de que precisamos é apenas
Observe que os tempos mostrados acima para a função jit-compiled somente se aplicam quando a função foi compilada. Isso acontece na definição (com compilação ansiosa, quando uma assinatura explícita é passada para
numba.njit
) ou durante a primeira chamada de função (com compilação lenta, quando nenhuma assinatura é passada paranumba.njit
). Se a função for executada apenas uma vez, o tempo de compilação também deve ser considerado para a velocidade desse método. Geralmente, vale a pena compilar funções se o tempo total de compilação + execução for menor que o tempo de execução não compilado (o que é realmente verdade no caso acima, onde a função python nativa é muito lenta). Isso acontece principalmente quando você está chamando sua função compilada várias vezes.Como max9111 observou em um comentário, um recurso importante
numba
é acache
palavra - chave parajit
. Passarcache=True
paranumba.jit
armazena a função compilada em disco, para que durante a próxima execução do módulo python fornecido, a função seja carregada a partir daí, em vez de recompilada, o que novamente pode poupar o tempo de execução a longo prazo.fonte
index
dados de roganjosh . Vou deixar uma observação sobre este, graças :)cache=True
- chave para evitar a recompilação em cada reinicialização do intérprete.Uma abordagem seria usar
Pandas
aqui puramente para fazer usogroupby
. Aumentei um pouco os tamanhos de entrada para entender melhor os tempos (já que há sobrecarga na criação do DF).Fornece o seguinte
timeit
:Para o mesmo tamanho de amostra, obtenho a abordagem de ditado de Aryerez :
No entanto, se aumentarmos as entradas por outro fator de 10, os tempos se tornarão:
No entanto, às custas de alguma reatibilidade, a resposta de Divakar usando numpy puro chega em:
À luz do novo conjunto de dados (que realmente deveria ter sido definido no início):
fonte
Talvez você já tenha feito isso, mas se não, veja se isso é rápido o suficiente:
Resultado:
fonte
np.vectorize
existe um invólucro muito fino para um loop, então eu não esperaria que essa abordagem fosse particularmente rápida.data
eindex
comonp.array
está na pergunta.Aqui está uma abordagem baseada no NumPy para obter mediana binned para valores positivos de caixas / índice -
Para resolver nosso caso específico de subtraídos -
fonte
df.groupby('index').transform('median')
?