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.

Donbeo
quelle
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
ali_m
quelle
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)))
Eelco Hoogendoorn
quelle
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
Eduard Feicho
quelle
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.

mrk
quelle
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
Jaden Travnik
quelle
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))
Joe
quelle
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
WY Hsu
quelle
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.]])
Seralouk
quelle
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()
max0r
quelle
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.

paulmelnikow
quelle
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)
sergio verduzco
quelle
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]]
Stanislav Tsepa
quelle