Dada uma matriz NumPy A , qual é a maneira mais rápida / eficiente de aplicar a mesma função, f , a todas as células?
Suponha que vamos atribuir a A (i, j) a f (A (i, j)) .
A função, f , não possui uma saída binária, portanto as operações de máscara (ing) não ajudarão.
A iteração "óbvia" de loop duplo (através de todas as células) é a solução ideal?
Respostas:
Você pode apenas vetorizar a função e aplicá-la diretamente a uma matriz Numpy sempre que precisar:
Provavelmente é melhor especificar diretamente um tipo de saída explícito ao vetorizar:
fonte
vectorize
descrição da função: A função vetorizar é fornecida principalmente por conveniência, não por desempenho. A implementação é essencialmente um loop for. Portanto, é muito provável que isso não acelere o processo.vectorize
determina o tipo de retorno. Isso produziu bugs.frompyfunc
é um pouco mais rápido, mas retorna uma matriz de objetos dtype. Ambos alimentam escalares, não linhas ou colunas.np.vectorize
na minha função (que utiliza RK45) me dá uma velocidade por um fator de ~ 20.Uma pergunta semelhante é: Mapeando uma matriz NumPy no local . Se você encontrar um ufunc para seu f (), use o parâmetro out.
fonte
Se você estiver trabalhando com números e
f(A(i,j)) = f(A(j,i))
, poderá usar scipy.spatial.distance.cdist definindo f como uma distância entreA(i)
eA(j)
.fonte
Acredito ter encontrado uma solução melhor. A ideia de alterar a função para a função universal python (consulte a documentação ), que pode exercer computação paralela sob o capô.
Pode-se escrever seu próprio personalizado
ufunc
em C, que certamente é mais eficiente, ou invocandonp.frompyfunc
, que é o método de fábrica embutido. Após o teste, isso é mais eficiente do quenp.vectorize
:Também testei amostras maiores e a melhoria é proporcional. Para comparação de desempenhos de outros métodos, consulte este post
fonte
Quando a matriz 2d (ou matriz nd) é contígua em C ou F, essa tarefa de mapear uma função em uma matriz 2d é praticamente a mesma que a tarefa de mapear uma função em uma matriz 1d - nós apenas tem que vê-lo dessa maneira, por exemplo, via
np.ravel(A,'K')
.A possível solução para a matriz 1d foi discutida, por exemplo, aqui .
No entanto, quando a memória do 2d-array não é contígua, a situação é um pouco mais complicada, porque se deseja evitar possíveis falhas de cache se o eixo for tratado na ordem errada.
A Numpy já possui um mecanismo para processar os eixos na melhor ordem possível. Uma possibilidade de usar esta maquinaria é
np.vectorize
. No entanto, a documentação da numpynp.vectorize
afirma que ela é "fornecida principalmente por conveniência, não por desempenho" - uma função python lenta permanece uma função python lenta com toda a sobrecarga associada! Outra questão é seu enorme consumo de memória - veja, por exemplo, este SO-post .Quando alguém deseja executar uma função C, mas usar o maquinário de numpy, uma boa solução é usar o numba para criação de ufuncs, por exemplo:
Ele bate facilmente,
np.vectorize
mas também quando a mesma função seria executada como multiplicação / adição de array numpy, ou seja,Veja o apêndice desta resposta para o código de medição do tempo:
A versão do Numba (verde) é cerca de 100 vezes mais rápida que a função python (ou seja
np.vectorize
), o que não é surpreendente. Mas também é 10 vezes mais rápido que a funcionalidade numpy, porque a versão numbas não precisa de matrizes intermediárias e, portanto, usa o cache com mais eficiência.Embora a abordagem não-funcional da numba seja uma boa alternativa entre usabilidade e desempenho, ela ainda não é a melhor que podemos fazer. No entanto, não existe uma bala de prata ou uma abordagem melhor para qualquer tarefa - é preciso entender quais são as limitações e como elas podem ser mitigadas.
Por exemplo, para as funções transcendentes (por exemplo
exp
,sin
,cos
) numba não fornece quaisquer vantagens em relação aos da numpynp.exp
(não há matrizes temporários criados - a principal fonte do aumento de velocidade). No entanto, minha instalação do Anaconda utiliza o VML da Intel para vetores maiores que 8192 - apenas não pode ser feito se a memória não for contígua. Portanto, pode ser melhor copiar os elementos para uma memória contígua para poder usar o VML da Intel:Para a equidade da comparação, desativei a paralelização da VML (consulte o código no apêndice):
Como se pode ver, uma vez que a VML entra em ação, a sobrecarga da cópia é mais do que compensada. No entanto, uma vez que os dados se tornam grandes demais para o cache L3, a vantagem é mínima, pois as tarefas se tornam novamente vinculadas à largura de banda da memória.
Por outro lado, a numba também poderia usar o SVML da Intel, conforme explicado neste post :
e usando VML com paralelização produz:
A versão do numba tem menos sobrecarga, mas, para alguns tamanhos, o VML supera o SVML, apesar da sobrecarga adicional de cópia - o que não é uma surpresa, já que os ufuncs do numba não são paralelos.
Listagens:
A. comparação da função polinomial:
B. comparação de
exp
:fonte
Todas as respostas acima se comparam bem, mas se você precisar usar a função personalizada para mapeamento, e tiver
numpy.ndarray
, e precisar manter a forma da matriz.Comparei apenas dois, mas ele manterá a forma de
ndarray
. Eu usei a matriz com 1 milhão de entradas para comparação. Aqui eu uso a função quadrada. Estou apresentando o caso geral de n array dimensional. Para bidimensional basta criariter
2D.Resultado
aqui você pode ver claramente a
numpy.fromiter
função quadrada do usuário, use qualquer uma de sua escolha. Se a sua função depende dosi, j
índices da matriz, itere no tamanho da matrizfor ind in range(arr.size)
, usenumpy.unravel_index
para obter comi, j, ..
base no seu índice 1D e na forma da matriz numpy.unravel_indexEsta resposta é inspirada na minha resposta em outra pergunta aqui
fonte