2010-05-17 2 views
19

Wie kann eine Liste von Vektoren in NumPy elegant normalisiert werden? HierNumPy: Wie schnell viele Vektoren zu normalisieren?

ist ein Beispiel, das nicht funktionierts:

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 

Die letzte Operation Erträge „-Form Mismatch: Objekte können nicht auf eine einzige Form ausgestrahlt werden“.

Wie kann die Normierung der 2D Vektoren in vectors elegant gemacht werden, mit NumPy?

Bearbeiten: Warum funktioniert das obige nicht, während das Hinzufügen einer Dimension zu norms funktioniert (wie meine Antwort unten)?

+0

FYI, ein Kommentator eine schnellere Methode haben kann, bearbeiten ich meine Antwort mit mehr Details. – Geoff

Antwort

12

Nun, wenn ich etwas verpasst, funktionierts:

vectors/norms 

Das Problem in Ihren Vorschlag die Regeln Rundfunk.

vectors # shape 2, 10 
norms # shape 10 

Die Form hat nicht die gleiche Länge! So ist die Regel ist, zunächst durch eine auf die kleine Form verlängern die links:

norms # shape 1,10 

Sie können das tun manuell durch den Aufruf:

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

Wenn Sie vectors.T/norms berechnen wollten, würden Sie haben zu tun, manuell die Umbildung, wie folgt:

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

warum nicht einfach (Vektoren/Normen) .T wenn das OP das transponiert haben will. Es scheint mir einfach und elegant zu sein. –

+0

Ah, ah! Die Dimensionserweiterung erfolgt also auf der linken Seite: Dies erklärt tatsächlich das beobachtete Verhalten. Vielen Dank! – EOL

13

In Ordnung: NumPy Array-Shape-Broadcast fügt Dimensionen zu der links der Array-Form, nicht auf der rechten Seite. NumPy kann jedoch angewiesen werden, eine Dimension rechts von der norms Array hinzuzufügen:

print vectors.T/norms[:, newaxis] 

funktioniert!

+3

Nur eine Anmerkung, ich verwende 'Normen [..., np.newaxis]' für den Fall, dass die Matrix nicht nur 2D ist. Es würde auch mit einem 3D (oder mehr) Tensor funktionieren. – Geoff

23

die Größe der Computer

Ich bin auf diese Frage gestoßen und bin neugierig auf Ihre Methode zur Normalisierung geworden. Ich verwende eine andere Methode, um die Größen zu berechnen. Hinweis: Ich berechne normalerweise auch Normen über den letzten Index (Zeilen in diesem Fall nicht Spalten).

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

jedoch typischerweise ich normalisieren wie so:

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

Ein Zeitvergleich

lief ich einen Test, um die Zeiten zu vergleichen und festgestellt, dass meine Methode schneller durch ganz ist ein bisschen, aber Freddie Witherdon Vorschlag ist noch schneller.

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 

Beachten Sie jedoch, wie diese StackOverflow answer Notizen, gibt es einige Sicherheitsüberprüfungen sind nicht mit einsum geschieht, so sollten Sie sicher sein, dass die dtype von vectors ausreichend ist genau genug, um das Quadrat der Größen zu speichern.

+1

Interessante Timing-Ergebnisse (ich bekomme jeweils 0,8 s und 1,4 s, mit der robusteren% timeit-Funktion von IPython), danke! – EOL

+2

Ich habe 'np.sqrt (np.einsum ('... i, ... i', Vektoren, Vektoren)) gefunden, um ~ 4 mal schneller als das oben angegebene Verfahren 1 zu sein. –

+0

@FreddieWitherden - Danke für den Kommentar, ich wusste nicht über'Einsum'. Es gibt eine interessante verwandte SO-Frage hier: http://stackoverflow.com/questions/18365073/why-is-numpys-einsum-faster-than-numpys-built-in-functions Es wird normalerweise schneller sein, aber möglicherweise nicht sicher (abhängig vom 'dtype' des Vektors). – Geoff

9

gibt es bereits eine Funktion in scikit lernen:

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

Mehr Infos unter:

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

+0

Interessante Informationen, aber die Frage bezieht sich explizit auf NumPy. Es wäre besser, es in einen Kommentar zu der ursprünglichen Frage zu bringen. – EOL

2

Meine bevorzugten Art und Weise Vektoren zu normalisieren wird durch numpy des inner1d mit ihren Größen zu berechnen. Hier ist, was bisher im Vergleich vorgeschlagen worden ist

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] 

Testing Leistung mit cProfile inner1d:

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 die Größen berechnet ein Haar schneller als einsum. Also mit inner1d zu normalisieren:

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

Testing gegen 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 

Fazit: mit inner1d der besten Option zu sein scheint

+0

Als Referenz ruft die Frage tatsächlich auf, die Norm entlang der ersten Dimension zu berechnen, nicht die zweite (siehe den Vorbehalt, der zu Geoffs Antwort hinzugefügt wurde). Wie würde dies die Ergebnisse verändern? Es kann Auswirkungen auf die Art haben, wie auf den Speicher zugegriffen wird, besonders wenn Sie eine größere zweite Dimension haben (statt 3 in Ihrem Beispiel). – EOL