NumPy: comment normaliser rapidement de nombreux vecteurs?
comment une liste de vecteurs peut-elle être élégamment normalisée, dans NumPy?
Voici un exemple qui n' :
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
la dernière opération donne "l'inadéquation de la forme: les objets ne peuvent pas être diffusés à une seule forme".
comment la normalisation des vecteurs 2D dans vectors
être élégamment fait, avec num Py?
Modifier: pourquoi ce qui précède ne fonctionne pas tout en ajoutant une dimension à norms
fonctionne (selon ma réponse ci-dessous)?
5 réponses
eh Bien, à moins que j'ai raté quelque chose, ceci fonctionne:
vectors / norms
le problème dans votre suggestion est les règles de diffusion.
vectors # shape 2, 10
norms # shape 10
La forme n'ont pas la même longueur! La règle est donc d'abord étendre la petite forme par un sur la gauche:
norms # shape 1,10
Vous pouvez le faire manuellement en appelant:
vectors / norms.reshape(1,-1) # same as vectors/norms
Si vous vouliez calculer vectors.T/norms
, vous auriez à faire la refonte manuellement comme suit:
vectors.T / norms.reshape(-1,1) # this works
calcul de la magnitude
je suis tombé sur cette question et suis devenu curieux au sujet de votre méthode pour normaliser. J'utilise une méthode différente pour calculer les grandeurs. Note: je calcule aussi typiquement des normes à travers le dernier index (lignes dans ce cas, pas colonnes).
magnitudes = np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]
typiquement, cependant, je normalise juste comme ça:
vectors /= np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]
Une comparaison du temps
j'ai fait un test pour comparer les temps, et j'ai trouvé que ma méthode est plus rapide par un peu, mais Freddie Witherdonest encore plus rapide.
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
Attention cependant, comme ce StackOverflow answer notes, il y a quelques contrôles de sécurité ne se passe pas avec einsum
, donc vous devriez être sûr que le dtype
vectors
est suffisant pour emmagasiner le carré des grandeurs avec assez de précision.
Alright: la diffusion de la forme du tableau de NumPy ajoute des dimensions à la gauche de la forme du tableau, pas à sa droite. NumPy peut toutefois être demandé d'ajouter une dimension à la droite de la norms
array:
print vectors.T / norms[:, newaxis]
ne fonctionne!
il y a déjà une fonction dans scikit learn:
import sklearn.preprocessing as preprocessing
norm =preprocessing.normalize(m, norm='l2')*
Plus d'info sur:
ma façon préférée de normaliser les vecteurs est d'utiliser l'inner1d de numpy pour calculer leurs grandeurs. Voici ce qui a été suggéré jusqu'à présent par rapport à 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]
de Test de performance avec 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 calculé les grandeurs d'un cheveu plus rapide que l'einsum. Utiliser inner1d pour normaliser:
n = points/np.sqrt(inner1d(points,points))[:,None]
cProfile.run('points/np.sqrt(inner1d(points,points))[:,None]') # 2 function calls in 0.026 seconds
Essais contre 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
Conclusion: utiliser inner1d semble être la meilleure option