Wie normalisiere ich ein Array in NumPy?


202

Ich möchte die Norm eines NumPy-Arrays haben. Insbesondere suche ich nach einer äquivalenten Version dieser Funktion

def normalize(v):
    norm = np.linalg.norm(v)
    if norm == 0: 
       return v
    return v / norm

Gibt es so etwas in skearnoder numpy?

Diese Funktion funktioniert in einer Situation, in vder der Vektor 0 ist.


3
Was ist los mit dem, was du geschrieben hast?
Ali_m

5
Wenn dies wirklich ein Problem ist, sollten Sie nach Norm <epsilon suchen, wobei epsilon eine kleine Toleranz ist. Außerdem würde ich einen Norm-Null-Vektor nicht stillschweigend zurückgeben, ich wäre raiseeine Ausnahme!
Hooked

4
Meine Funktion funktioniert, aber ich würde gerne wissen, ob sich etwas in der allgemeineren Bibliothek des Pythons befindet. Ich schreibe verschiedene maschinelle Lernfunktionen und möchte vermeiden, zu viele neue Funktionen zu definieren, um den Code klarer und lesbarer zu machen
Donbeo

1
Ich habe ein paar schnelle Tests durchgeführt und festgestellt, dass dies x/np.linalg.norm(x)nicht viel langsamer war (ca. 15-20%) als x/np.sqrt((x**2).sum())in numpy 1.15.1 auf einer CPU.
Bill

Antworten:


160

Wenn Sie scikit-learn verwenden, können Sie Folgendes verwenden sklearn.preprocessing.normalize:

import numpy as np
from sklearn.preprocessing import normalize

x = np.random.rand(1000)*10
norm1 = x / np.linalg.norm(x)
norm2 = normalize(x[:,np.newaxis], axis=0).ravel()
print np.all(norm1 == norm2)
# True

2
Vielen Dank für die Antwort, aber sind Sie sicher, dass sklearn.preprocessing.normalize auch mit dem Vektor der Form = (n,) oder (n, 1) funktioniert? Ich habe einige Probleme mit dieser Bibliothek
Donbeo

normalizeerfordert eine 2D-Eingabe. Sie können das axis=Argument übergeben, um anzugeben, ob Sie die Normalisierung auf die Zeilen oder Spalten Ihres Eingabearrays anwenden möchten.
Ali_m

9
Beachten Sie, dass das Argument 'norm' der Normalisierungsfunktion entweder 'l1' oder 'l2' sein kann und der Standardwert 'l2' ist. Wenn Sie möchten, dass die Summe Ihres Vektors 1 ist (z. B. eine Wahrscheinlichkeitsverteilung), sollten Sie in der Normalisierungsfunktion norm = 'l1' verwenden.
Ash

2
Beachten Sie auch, dass np.linalg.norm(x)standardmäßig die 'l2'-Norm berechnet wird. Wenn Sie möchten, dass die Summe Ihres Vektors 1 ist, sollten Sienp.linalg.norm(x, ord=1)
Omid

Hinweis: x muss vorhanden sein, ndarraydamit es mit der normalize()Funktion funktioniert. Ansonsten kann es ein sein list.
Ramin Melikov

46

Ich würde zustimmen, dass es schön wäre, wenn eine solche Funktion Teil der mitgelieferten Batterien wäre. Soweit ich weiß, ist es das nicht. Hier ist eine Version für beliebige Achsen, die eine optimale Leistung bietet.

import numpy as np

def normalized(a, axis=-1, order=2):
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2==0] = 1
    return a / np.expand_dims(l2, axis)

A = np.random.randn(3,3,3)
print(normalized(A,0))
print(normalized(A,1))
print(normalized(A,2))

print(normalized(np.arange(3)[:,None]))
print(normalized(np.arange(3)))

Ich habe die ali_m-Lösung nicht gründlich getestet, aber in einem einfachen Fall scheint sie zu funktionieren. Gibt es Situationen, in denen Ihre Funktion besser funktioniert?
Donbeo

1
Ich weiß es nicht; Es funktioniert jedoch über beliebige Achsen, und wir haben explizite Kontrolle darüber, was für Vektoren der Länge 0 geschieht.
Eelco Hoogendoorn

1
Sehr schön! Dies sollte numpy sein - obwohl die Reihenfolge meiner Meinung nach wahrscheinlich vor der Achse kommen sollte.
Neil G

@EelcoHoogendoorn Neugierig zu verstehen, warum order = 2 über andere gewählt wird?
Henry Thornton

7
Weil die euklidische / pythagoranische Norm die am häufigsten verwendete ist; Würden Sie nicht zustimmen?
Eelco Hoogendoorn

21

Sie können ord angeben, um die L1-Norm zu erhalten. Um eine Nullteilung zu vermeiden, verwende ich EPS, aber das ist vielleicht nicht so toll.

def normalize(v):
    norm=np.linalg.norm(v, ord=1)
    if norm==0:
        norm=np.finfo(v.dtype).eps
    return v/norm

6
[inf, 1, 2]Renditen normalisieren [nan, 0, 0], aber sollte es nicht sein [1, 0, 0]?
Pasbi

12

Dies könnte auch für Sie funktionieren

import numpy as np
normalized_v = v / np.sqrt(np.sum(v**2))

schlägt aber fehl, wenn vdie Länge 0 ist.


10

Wenn Sie mehrdimensionale Daten haben und möchten, dass jede Achse auf ihr Maximum oder ihre Summe normiert wird:

def normalize(_d, to_sum=True, copy=True):
    # d is a (n x dimension) np array
    d = _d if not copy else np.copy(_d)
    d -= np.min(d, axis=0)
    d /= (np.sum(d, axis=0) if to_sum else np.ptp(d, axis=0))
    return d

Verwendet die Funktion von Spitze zu Spitze .

a = np.random.random((5, 3))

b = normalize(a, copy=False)
b.sum(axis=0) # array([1., 1., 1.]), the rows sum to 1

c = normalize(a, to_sum=False, copy=False)
c.max(axis=0) # array([1., 1., 1.]), the max of each row is 1

Achten Sie darauf, wenn alle Werte in der ursprünglichen Matrix gleich sind, dann wäre ptp 0. Die Division durch 0 gibt nan zurück.
Milso

8

Es gibt auch die Funktion unit_vector(), Vektoren im populären Transformationsmodul von Christoph Gohlke zu normalisieren :

import transformations as trafo
import numpy as np

data = np.array([[1.0, 1.0, 0.0],
                 [1.0, 1.0, 1.0],
                 [1.0, 2.0, 3.0]])

print(trafo.unit_vector(data, axis=1))

7

Sie haben Sci-Kit Learn erwähnt, deshalb möchte ich eine andere Lösung vorstellen.

Sci-Kit lernen MinMaxScaler

In Sci-Kit Learn gibt es eine API namens MinMaxScaler, die den Wertebereich nach Ihren Wünschen anpassen kann.

Es befasst sich auch mit NaN-Problemen für uns.

NaNs werden als fehlende Werte behandelt: in der Anpassung nicht berücksichtigt und in der Transformation beibehalten. ... siehe Referenz [1]

Codebeispiel

Der Code ist einfach, geben Sie einfach ein

# Let's say X_train is your input dataframe
from sklearn.preprocessing import MinMaxScaler
# call MinMaxScaler object
min_max_scaler = MinMaxScaler()
# feed in a numpy array
X_train_norm = min_max_scaler.fit_transform(X_train.values)
# wrap it up if you need a dataframe
df = pd.DataFrame(X_train_norm)
Referenz

6

Ohne sklearnund mit nur numpy. Definieren Sie einfach eine Funktion:

Angenommen, die Zeilen sind die Variablen und die Spalten die samples ( axis= 1):

import numpy as np

# Example array
X = np.array([[1,2,3],[4,5,6]])

def stdmtx(X):
    means = X.mean(axis =1)
    stds = X.std(axis= 1, ddof=1)
    X= X - means[:, np.newaxis]
    X= X / stds[:, np.newaxis]
    return np.nan_to_num(X)

Ausgabe:

X
array([[1, 2, 3],
       [4, 5, 6]])

stdmtx(X)
array([[-1.,  0.,  1.],
       [-1.,  0.,  1.]])

4

Wenn Sie in einem 3D-Tensor gespeicherte n-dimensionale Merkmalsvektoren normalisieren möchten, können Sie auch PyTorch verwenden:

import numpy as np
from torch import FloatTensor
from torch.nn.functional import normalize

vecs = np.random.rand(3, 16, 16, 16)
norm_vecs = normalize(FloatTensor(vecs), dim=0, eps=1e-16).numpy()

4

Wenn Sie mit 3D-Vektoren arbeiten, können Sie dies mit dem Toolbelt vg präzise tun . Es ist eine leichte Schicht über Numpy und unterstützt einzelne Werte und gestapelte Vektoren.

import numpy as np
import vg

x = np.random.rand(1000)*10
norm1 = x / np.linalg.norm(x)
norm2 = vg.normalize(x)
print np.all(norm1 == norm2)
# True

Ich habe die Bibliothek bei meinem letzten Start erstellt, wo sie durch solche Verwendungen motiviert war: einfache Ideen, die in NumPy viel zu ausführlich sind.


3

Wenn Sie keine äußerste Präzision benötigen, kann Ihre Funktion auf Folgendes reduziert werden:

v_norm = v / (np.linalg.norm(v) + 1e-16)

3

Wenn Sie mit mehrdimensionalen Arrays arbeiten, ist eine schnelle Lösung möglich.

Angenommen, wir haben ein 2D-Array, das wir nach der letzten Achse normalisieren möchten, während einige Zeilen die Nullnorm haben.

import numpy as np
arr = np.array([
    [1, 2, 3], 
    [0, 0, 0],
    [5, 6, 7]
], dtype=np.float)

lengths = np.linalg.norm(arr, axis=-1)
print(lengths)  # [ 3.74165739  0.         10.48808848]
arr[lengths > 0] = arr[lengths > 0] / lengths[lengths > 0][:, np.newaxis]
print(arr)
# [[0.26726124 0.53452248 0.80178373]
# [0.         0.         0.        ]
# [0.47673129 0.57207755 0.66742381]]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.