numpy.amax () encontrará o valor máximo em uma matriz, e numpy.amin () faz o mesmo para o valor mínimo. Se eu quiser encontrar max e min, tenho que chamar as duas funções, o que requer a passagem da matriz (muito grande) duas vezes, o que parece lento.
Existe uma função na API numpy que encontra o máximo e o mínimo com apenas uma única passagem pelos dados?
amax
eamin
minmax
à biblioteca em questão ( github.com/numpy/numpy/issues/9836 ).Respostas:
Não. No momento da redação deste artigo, essa função não existia. (E sim, se não fosse essa função, seu desempenho seria significativamente melhor do que chamar
numpy.amin()
enumpy.amax()
sucessivamente em uma grande variedade.)fonte
Não acho que passar sobre o array duas vezes seja um problema.Considere o seguinte pseudocódigo:Embora haja apenas 1 loop aqui, ainda há 2 verificações. (Em vez de ter 2 loops com 1 cheque cada). Na verdade, a única coisa que você salva é a sobrecarga de 1 loop. Se os arrays realmente forem grandes, como você diz, a sobrecarga é pequena em comparação com a carga de trabalho do loop real. (Observe que tudo isso é implementado em C, então os loops são mais ou menos livres de qualquer maneira).
EDITAR Sinto muito para os 4 de vocês que votaram positivamente e tiveram fé em mim. Você definitivamente pode otimizar isso.
Aqui está um código fortran que pode ser compilado em um módulo Python via
f2py
(talvez umCython
guru possa vir e comparar isso com uma versão C otimizada ...):Compile-o via:
E agora estamos em um lugar onde podemos testá-lo:
Os resultados são um pouco surpreendentes para mim:
Devo dizer que não entendo completamente. Comparar apenas
np.min
versusminmax1
eminmax2
ainda é uma batalha perdida, então não é apenas um problema de memória ...notas - Aumentar o tamanho por um fator de
10**a
e diminuir a repetição por um fator de10**a
(mantendo o tamanho do problema constante) muda o desempenho, mas não de uma forma aparentemente consistente, o que mostra que há alguma interação entre o desempenho da memória e a sobrecarga da chamada de função em Pitão. Mesmo comparando umamin
implementação simples em fortran bate numpy por um fator de aproximadamente 2 ...fonte
i < minval
for verdadeiro, entãoi > maxval
será sempre falso, então você só precisa fazer 1,5 verificações por iteração em média quando o segundoif
é substituído por umelif
.f2py
apenas envolve o Fortran codificado manualmente para que seja chamado pelo Python. Um teste "mais justo" é provavelmente codificar manualmente em C e, em seguida, usarf2py
(!) Para envolvê-lo em Python. Se você está permitindo C ++, então o Shed Skin pode ser o ponto ideal para equilibrar a facilidade de codificação com o desempenho.Existe uma função para encontrar (max-min) chamada numpy.ptp se for útil para você:
mas não acho que haja uma maneira de encontrar o mínimo e o máximo com uma travessia.
EDIT: ptp apenas chama min e max sob o capô
fonte
Você poderia usar o Numba , que é um compilador Python dinâmico compatível com o NumPy usando LLVM. A implementação resultante é muito simples e clara:
Também deve ser mais rápido do que a
min() & max()
implementação de um Numpy . E tudo isso sem precisar escrever uma única linha de código C / Fortran.Faça seus próprios testes de desempenho, pois é sempre dependente de sua arquitetura, seus dados, suas versões de pacote ...
fonte
numba
função uma vez antes do benchmark para ter certeza de que é compilado por JIT ?. Além disso, se você usaripython
, para simplificar, sugiro que use%timeit whatever_code()
para medir a execução do tempo.elif
permite que seu mínimo seja maior do que seu máximo. Por exemplo, com uma matriz de comprimento 1, o máximo será qualquer que seja esse valor, enquanto o mínimo é + infinito. Não é grande coisa para um único, mas não é um bom código para jogar bem no fundo de uma besta de produção.Em geral, você pode reduzir a quantidade de comparações para um algoritmo minmax processando dois elementos por vez e comparando apenas o menor com o mínimo temporário e o maior com o máximo temporário. Em média, são necessários apenas 3/4 das comparações do que uma abordagem ingênua.
Isso pode ser implementado em c ou fortran (ou qualquer outra linguagem de baixo nível) e deve ser quase imbatível em termos de desempenho. estou a usarnumba para ilustrar o princípio e obter uma implementação muito rápida e independente do tipo:
É definitivamente mais rápido do que a abordagem ingênua que Peque apresentou:
Como esperado, a nova implementação de minmax leva apenas cerca de 3/4 do tempo que a implementação ingênua levou (
2.1 / 2.75 = 0.7636363636363637
)fonte
Apenas para ter algumas idéias sobre os números que você pode esperar, dadas as seguintes abordagens:
(as
extrema_loop_*()
abordagens são semelhantes ao que é proposto aqui , enquanto asextrema_while_*()
abordagens são baseadas no código a partir daqui )Os seguintes horários:
indicam que
extrema_while_*()
são os mais rápidos,extrema_while_nb()
sendo os mais rápidos. Em qualquer caso, também as soluçõesextrema_loop_nb()
eextrema_loop_cy()
superam a abordagem apenas NumPy (usandonp.max()
enp.min()
separadamente).Finalmente, observe que nenhum desses é tão flexível quanto
np.min()
/np.max()
(em termos de suporte n-dim,axis
parâmetro, etc.).(o código completo está disponível aqui )
fonte
extrema_while_nb
Ninguém mencionou numpy.percentile , então pensei que faria. Se você solicitar os
[0, 100]
percentis, receberá uma matriz de dois elementos, o mínimo (0º percentil) e o máximo (100º percentil).No entanto, não satisfaz o propósito do OP: não é mais rápido do que mín e máx separadamente. Isso provavelmente se deve a algum mecanismo que permitiria percentis não extremos (um problema mais difícil, que deve demorar mais).
Uma versão futura do Numpy poderia colocar em um caso especial para pular o cálculo de percentil normal se apenas
[0, 100]
for solicitado. Sem adicionar nada à interface, há uma maneira de pedir a Numpy o mínimo e o máximo em uma chamada (ao contrário do que foi dito na resposta aceita), mas a implementação padrão da biblioteca não aproveita este caso para fazê-lo que vale a pena.fonte
Este é um tópico antigo, mas de qualquer forma, se alguém olhar para isso novamente ...
Ao procurar o mínimo e o máximo simultaneamente, é possível reduzir o número de comparações. Se for floats que você está comparando (o que eu acho que é), isso pode economizar algum tempo, embora não seja uma complexidade computacional.
Em vez de (código Python):
você pode primeiro comparar dois valores adjacentes na matriz e, em seguida, comparar apenas o menor com o mínimo atual e o maior com o máximo atual:
O código aqui é escrito em Python, claramente para velocidade você usaria C ou Fortran ou Cython, mas desta forma você faz 3 comparações por iteração, com len (ar) / 2 iterações, dando 3/2 * len (ar) comparações. Em oposição a isso, fazendo a comparação "da maneira óbvia", você faz duas comparações por iteração, resultando em comparações 2 * len (ar). Economiza 25% do tempo de comparação.
Talvez alguém um dia ache isso útil.
fonte
np.bincount
, veja aqui . Ele não usa o truque que você apontou, porque acabou sendo até 2x mais lento do que a abordagem ingênua. Há um link do PR para alguns benchmarks abrangentes de ambos os métodos.À primeira vista, parece funcionar:
numpy.histogram
... mas se você olhar a origem dessa função, ela simplesmente chama
a.min()
e de formaa.max()
independente e, portanto, falha em evitar as preocupações de desempenho abordadas nesta questão. :-(Da mesma forma,
scipy.ndimage.measurements.extrema
parece uma possibilidade, mas também simplesmente chamaa.min()
e de formaa.max()
independente.fonte
np.histogram
nem sempre funciona para isso porque o retorno(amin, amax)
valores são para os valores mínimo e máximo do bin. Se eu tiver, por exemploa = np.zeros(10)
,np.histogram(a, bins=1)
retornos(array([10]), array([-0.5, 0.5]))
. O usuário está procurando(amin, amax)
= (0, 0) nesse caso.Valeu a pena o esforço para mim de qualquer forma, então vou propor a solução mais difícil e menos elegante aqui para quem estiver interessado. Minha solução é implementar um algoritmo mín-máx multissegmentado em uma passagem em C ++ e usar isso para criar um módulo de extensão Python. Esse esforço requer um pouco de sobrecarga para aprender a usar as APIs Python e NumPy C / C ++, e aqui vou mostrar o código e dar algumas pequenas explicações e referências para quem deseja seguir esse caminho.
Multi-threaded Min / Max
Não há nada muito interessante aqui. A matriz é dividida em pedaços de tamanho
length / workers
. O mín. / Máx. É calculado para cada pedaço em afuture
, que é então verificado para o mín. / Máx. Global.O Módulo de Extensão Python
É aqui que as coisas começam a ficar feias ... Uma maneira de usar o código C ++ em Python é implementar um módulo de extensão. Este módulo pode ser construído e instalado usando o
distutils.core
módulo padrão. Uma descrição completa do que isso implica é coberta na documentação do Python: https://docs.python.org/3/extending/extending.html . NOTA: certamente existem outras maneiras de obter resultados semelhantes, para citar https://docs.python.org/3/extending/index.html#extending-index :Essencialmente, esse caminho é provavelmente mais acadêmico do que prático. Com isso dito, o que fiz a seguir foi, mantendo-me bem próximo ao tutorial, criar um arquivo de módulo. Este é essencialmente um padrão para distutils saber o que fazer com seu código e criar um módulo Python a partir dele. Antes de fazer qualquer coisa, provavelmente é aconselhável criar um ambiente virtual Python para não poluir os pacotes do sistema (consulte https://docs.python.org/3/library/venv.html#module-venv ).
Aqui está o arquivo do módulo:
Neste arquivo há um uso significativo do Python, bem como da API NumPy, para mais informações consulte: https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple e para NumPy : https://docs.scipy.org/doc/numpy/reference/c-api.array.html .
Instalando o Módulo
A próxima coisa a fazer é utilizar distutils para instalar o módulo. Isso requer um arquivo de configuração:
Para finalmente instalar o módulo, execute
python3 setup.py install
partir de seu ambiente virtual.Testando o Módulo
Por fim, podemos testar para ver se a implementação C ++ realmente supera o uso ingênuo de NumPy. Para fazer isso, aqui está um script de teste simples:
Aqui estão os resultados que obtive com tudo isso:
Estes são muito menos encorajadores do que os resultados indicam anteriormente no thread, que indicou algo em torno de 3,5x a aceleração e não incorporou multi-threading. Os resultados que obtive são um tanto razoáveis, eu esperaria que a sobrecarga de threading e dominasse o tempo até que os arrays ficassem muito grandes, ponto em que o aumento de desempenho começaria a se aproximar de
std::thread::hardware_concurrency
aumento x.Conclusão
Certamente há espaço para otimizações específicas de aplicativos para algum código NumPy, ao que parece, em particular com relação a multi-threading. Se vale a pena ou não o esforço não está claro para mim, mas certamente parece um bom exercício (ou algo assim). Acho que talvez aprender algumas dessas "ferramentas de terceiros" como o Cython possa ser um uso melhor do tempo, mas quem sabe.
fonte
v = min_max_it->get();
. Oget
método bloqueia até que o resultado esteja pronto e o retorna. Como o loop passa por cada futuro, ele não terminará até que todos estejam concluídos. future.get ()O caminho mais curto que descobri é este:
Mas, uma vez que classifica o array, não é o mais eficiente.
Outro caminho curto seria:
Isso deve ser mais eficiente, mas o resultado é calculado e um float é retornado.
fonte