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)?

20
demandé sur Eric Lebigot 2010-05-17 20:13:45

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
13
répondu Olivier Verdier 2013-10-31 17:56:40

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 dtypevectors est suffisant pour emmagasiner le carré des grandeurs avec assez de précision.

24
répondu Geoff 2017-05-23 10:29:36

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!

13
répondu Eric Lebigot 2013-01-04 11:38:06

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:

http://scikit-learn.org/stable/modules/preprocessing.html

9
répondu SenhorSchaefers 2013-11-28 09:56:54

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

2
répondu Fnord 2016-02-18 22:59:04