NumPy: jak szybko znormalizować wiele wektorów?

Jak można elegancko znormalizować listę wektorów w NumPy?

Oto przykład, który działa a nie :

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

Ostatnia operacja daje "niedopasowanie kształtu: obiekty nie mogą być transmitowane do jednego kształtu".

Jak normalizacja wektorów 2D w vectors może być elegancko wykonana, z NumPy?

Edit : dlaczego powyższe nie działa podczas dodawania wymiaru do norms działa (jak na moją odpowiedź poniżej)?

Author: Eric Lebigot, 2010-05-17

5 answers

No chyba, że coś przeoczyłem, to działa: {]}

vectors / norms
Problem w Twojej sugestii To zasady nadawania.
vectors  # shape 2, 10
norms  # shape 10

Kształt nie ma tej samej długości! Zasada jest więc taka, aby najpierw rozszerzyć mały kształt o jeden po lewej :

norms  # shape 1,10
Możesz to zrobić ręcznie przez wywołanie:
vectors / norms.reshape(1,-1)  # same as vectors/norms

Jeśli chcesz obliczyć vectors.T/norms, musisz ręcznie zmienić kształt, w następujący sposób:

vectors.T / norms.reshape(-1,1)  # this works
 13
Author: Olivier Verdier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-31 17:56:40

Obliczanie wielkości

Natknąłem się na to pytanie i zainteresowałem się Twoją metodą normalizacji. Używam innej metody do obliczania wielkości. Uwaga: zwykle obliczam również normy w ostatnim indeksie(wiersze w tym przypadku, nie kolumny).
magnitudes = np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]

Zazwyczaj jednak normuję tak:

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

Porównanie czasu

Przeprowadziłem test, aby porównać czasy, i okazało się, że moja metoda jest szybsza o sporo, ale Freddie Witherdon sugestia jest jeszcze szybsza.

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

Uważaj jednak, ponieważ ta odpowiedź StackOverflow zauważa, że istnieją pewne kontrole bezpieczeństwa nie dzieje się z einsum, więc powinieneś być pewien, że dtype z vectors jest wystarczająca do przechowywania kwadratu wielkości wystarczająco dokładnie.

 24
Author: Geoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:36

W porządku: NumPy ' s array shape broadcast dodaje wymiary do lewej kształtu tablicy, a nie do jej prawej. NumPy może być jednak poinstruowany, aby dodać wymiar po prawej stronie tablicy norms:

print vectors.T / norms[:, newaxis]
Działa!
 13
Author: Eric Lebigot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-04 11:38:06

Istnieje już funkcja w scikit learn:

import sklearn.preprocessing as preprocessing
norm =preprocessing.normalize(m, norm='l2')*

Więcej informacji na:

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

 9
Author: SenhorSchaefers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-28 09:56:54

Moim preferowanym sposobem normalizacji wektorów jest użycie inner1d numpy ' ego do obliczenia ich wielkości. Oto, co zostało zasugerowane do tej pory w porównaniu do 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]

Testowanie wydajności z 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 obliczył wielkość włosów szybciej niż einsum. Więc użycie inner1d do normalizacji:

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

Testowanie przeciwko 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

Wniosek: korzystanie z inner1d wydaje się być najlepszą opcją

 2
Author: Fnord,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 22:59:04