2010-05-17 19 views
19

¿Cómo se puede normalizar de forma elegante una lista de vectores en NumPy?NumPy: cómo normalizar rápidamente muchos vectores?

Aquí es un ejemplo que hace no trabajo:

from numpy import * 

vectors = array([arange(10), arange(10)]) # All x's, then all y's 
norms = apply_along_axis(linalg.norm, 0, vectors) 

# Now, what I was expecting would work: 
print vectors.T/norms # vectors.T has 10 elements, as does norms, but this does not work 

los últimos rendimientos de operación "forma desajuste: los objetos no pueden ser transmitidos a una única forma".

¿Cómo se puede hacer la normalización de los vectores 2D en vectors con elegancia, con NumPy?

Editar: ¿Por qué lo anterior no funciona al agregar una dimensión a norms funciona (según mi respuesta a continuación)?

+0

Para su información, un comentarista puede tener un método más rápido, editado mi responde con mas detalles – Geoff

Respuesta

12

Bueno, a menos que me haya perdido algo, esto hace el trabajo:

vectors/norms 

El problema en su sugerencia es las normas de radiodifusión.

vectors # shape 2, 10 
norms # shape 10 

¡La forma no tiene la misma longitud! Así que la regla es extender primero la pequeña forma a uno en la izquierda:

norms # shape 1,10 

puede hacerlo de forma manual, llamando al:

vectors/norms.reshape(1,-1) # same as vectors/norms 

Si quería calcular vectors.T/norms, tendría para hacer la remodelación manualmente, de la siguiente manera:

vectors.T/norms.reshape(-1,1) # this works 
+0

¿por qué no solo hacer (vectores/normas) .T si el OP quiere que se transponga? Me parece simple y elegante. –

+0

Ah, ¡ah! entonces la extensión de dimensión se hace en _left_: esto de hecho explica el comportamiento observado. ¡Gracias! – EOL

13

Bien: la transmisión de forma de matriz de NumPy agrega dimensiones a la izquierda de la forma de matriz, no a la derecha. NumPy sin embargo puede ser instruido para añadir una dimensión a la derecha de la matriz norms:

print vectors.T/norms[:, newaxis] 

funciona!

+3

Solo una nota, uso 'norms [..., np.newaxis]' en caso de que la matriz no sea solo 2D. Funcionaría con un 3D (o más) tensor también. – Geoff

23

Cálculo de la magnitud

Me encontré con esta pregunta y comencé a sentir curiosidad acerca de su método para normalizar. Utilizo un método diferente para calcular las magnitudes. Nota: También normalmente calculo las normas en el último índice (filas en este caso, no columnas).

magnitudes = np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis] 

Típicamente, sin embargo, acabo de normalizar este modo:

vectors /= np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis] 

Una vez la comparación

me encontré con una prueba para comparar los tiempos, y encontré que mi método es más rápido por todo un poco, pero la sugerencia de Freddie Witherdon es aún más rápida.

import numpy as np  
vectors = np.random.rand(100, 25) 

# OP's 
%timeit np.apply_along_axis(np.linalg.norm, 1, vectors) 
# Output: 100 loops, best of 3: 2.39 ms per loop 

# Mine 
%timeit np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis] 
# Output: 10000 loops, best of 3: 13.8 us per loop 

# Freddie's (from comment below) 
%timeit np.sqrt(np.einsum('...i,...i', vectors, vectors)) 
# Output: 10000 loops, best of 3: 6.45 us per loop 

Sin embargo, Aviso, ya que este StackOverflow answer notas, hay algunas comprobaciones de seguridad no está sucediendo con einsum, por lo que debe estar seguro de que el dtype de vectors es suficiente para almacenar el cuadrado de las magnitudes suficientes precisión.

+1

Interesantes resultados de sincronización (obtengo respectivamente 0.8 sy 1.4 s, con la función de timeit más robusta de IPython), ¡gracias! – EOL

+2

He encontrado que 'np.sqrt (np.einsum ('... i, ... i', vectores, vectores))' es ~ 4 veces más rápido que el Método 1 como se indicó anteriormente. –

+0

@FreddieWitherden - Gracias por el comentario, no sabía sobre 'einsum'. Hay aquí una pregunta de SO relacionada interesante: http://stackoverflow.com/questions/18365073/why-is-numpys-einsum-faster-than-numpys-built-in-functions Normalmente será más rápido, pero puede no ser seguro (dependiendo del 'dtype' del vector). – Geoff

2

mi forma preferida para normalizar los vectores está utilizando inner1d de numpy para calcular sus magnitudes. Aquí está lo que se ha sugerido hasta ahora en comparación con inner1d

import numpy as np 
from numpy.core.umath_tests import inner1d 
COUNT = 10**6 # 1 million points 

points = np.random.random_sample((COUNT,3,)) 
A  = np.sqrt(np.einsum('...i,...i', points, points)) 
B  = np.apply_along_axis(np.linalg.norm, 1, points) 
C  = np.sqrt((points ** 2).sum(-1)) 
D  = np.sqrt((points*points).sum(axis=1)) 
E  = np.sqrt(inner1d(points,points)) 

print [np.allclose(E,x) for x in [A,B,C,D]] # [True, True, True, True] 

el rendimiento de pruebas con cprofile:

import cProfile 
cProfile.run("np.sqrt(np.einsum('...i,...i', points, points))**0.5") # 3 function calls in 0.013 seconds 
cProfile.run('np.apply_along_axis(np.linalg.norm, 1, points)')  # 9000018 function calls in 10.977 seconds 
cProfile.run('np.sqrt((points ** 2).sum(-1))')      # 5 function calls in 0.028 seconds 
cProfile.run('np.sqrt((points*points).sum(axis=1))')     # 5 function calls in 0.027 seconds 
cProfile.run('np.sqrt(inner1d(points,points))')      # 2 function calls in 0.009 seconds 

inner1d calcula las magnitudes un cabello más rápido que einsum. Así, utilizando inner1d para normalizar:

n = points/np.sqrt(inner1d(points,points))[:,None] 
cProfile.run('points/np.sqrt(inner1d(points,points))[:,None]') # 2 function calls in 0.026 seconds 

Pruebas basadas en scikit:

import sklearn.preprocessing as preprocessing 
n_ = preprocessing.normalize(points, norm='l2') 
cProfile.run("preprocessing.normalize(points, norm='l2')") # 47 function calls in 0.047 seconds 
np.allclose(n,n_) # True 

Conclusión: el uso de inner1d parece ser la mejor opción

+0

Como referencia, la pregunta realmente requiere calcular la norma a lo largo de la primera dimensión, no la segunda (ver la advertencia añadida a la respuesta de Geoff). ¿Cómo cambiaría esto los resultados? Puede haber un impacto, debido a la forma en que se accede a la memoria, especialmente si tiene una segunda dimensión más grande (en lugar de 3 en su ejemplo). – EOL

Cuestiones relacionadas