Jak znormalizować tablicę w NumPy?

203

Chciałbym mieć normę jednej tablicy NumPy. Mówiąc dokładniej, szukam równoważnej wersji tej funkcji

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

Czy jest coś takiego w skearnlub numpy?

Ta funkcja działa w sytuacji, gdy vjest to wektor 0.

Donbeo
źródło
3
Co jest nie tak z tym, co napisałeś?
ali_m
5
Jeśli jest to naprawdę problem, powinieneś sprawdzić normę <epsilon, gdzie epsilon jest małą tolerancją. Ponadto nie przekazałbym po cichu wektora normalnego zera, byłbym raisewyjątkiem!
Hooked
4
moja funkcja działa, ale chciałbym wiedzieć, czy coś jest w bardziej popularnej bibliotece pytona. Piszę różne funkcje uczenia maszynowego i chciałbym unikać definiowania zbyt wielu nowych funkcji, aby kod był bardziej przejrzysty i czytelny
Donbeo
1
Zrobiłem kilka szybkich testów i okazało się, że x/np.linalg.norm(x)nie było to dużo wolniejsze (około 15-20%) niż x/np.sqrt((x**2).sum())w numpy 1.15.1 na CPU.
Bill

Odpowiedzi:

160

Jeśli używasz scikit-learn, możesz użyć 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
źródło
2
Dzięki za odpowiedź, ale czy jesteś pewien, że sklearn.preprocessing.normalize działa również z wektorem kształtu = (n,) lub (n, 1)? Mam pewne problemy z tą biblioteką
Donbeo
normalizewymaga wejścia 2D. Możesz przekazać axis=argument, aby określić, czy chcesz zastosować normalizację w wierszach lub kolumnach tablicy wejściowej.
ali_m
9
Zauważ, że argumentem „norm” funkcji normalizacji może być „l1” lub „l2”, a domyślna wartość to „l2”. Jeśli chcesz, aby suma wektora wynosiła 1 (np. Rozkład prawdopodobieństwa), powinieneś użyć norm = 'l1' w funkcji normalizacji.
Ash
2
Zauważ też, że np.linalg.norm(x)domyślnie oblicza normę „l2”. Jeśli chcesz, aby suma wektora wynosiła 1, powinieneś użyćnp.linalg.norm(x, ord=1)
Omid
Uwaga: musi być x ndarray, aby działał z tą normalize()funkcją. W przeciwnym razie może to być list.
Ramin Melikov
46

Zgodziłbym się, że byłoby miło, gdyby taka funkcja była częścią dołączonych baterii. Ale o ile mi wiadomo, nie jest. Oto wersja dla dowolnych osi i zapewniająca optymalną wydajność.

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
źródło
Nie przetestowałem dogłębnie rozwiązania ali_m, ale w prostym przypadku wydaje się, że działa. Czy są sytuacje, w których Twoja funkcja działa lepiej?
Donbeo
1
Nie wiem; ale działa na dowolnych osiach i mamy wyraźną kontrolę nad tym, co dzieje się dla wektorów o długości 0.
Eelco Hoogendoorn
1
Bardzo dobrze! Powinno to być nieparzyste - chociaż moim zdaniem kolejność powinna prawdopodobnie nastąpić przed osią.
Neil G
@EelcoHoogendoorn Ciekawe, dlaczego rozumiesz, dlaczego zamówienie = 2 wybrano nad innymi?
Henry Thornton,
7
Ponieważ zdarza się, że najczęściej stosowana jest norma euklidesowa / pitagorańska; nie zgodziłbyś się?
Eelco Hoogendoorn,
21

Możesz określić ord, aby uzyskać normę L1. Aby uniknąć podziału zerowego, używam eps, ale to może nie jest świetne.

def normalize(v):
    norm=np.linalg.norm(v, ord=1)
    if norm==0:
        norm=np.finfo(v.dtype).eps
    return v/norm
Eduard Feicho
źródło
6
normalizowanie [inf, 1, 2]zbiorów [nan, 0, 0], ale czy nie powinno tak być [1, 0, 0]?
pasbi
12

To może również działać dla Ciebie

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

ale kończy się niepowodzeniem, gdy vma długość 0.

mrk
źródło
10

Jeśli masz dane wielowymiarowe i chcesz, aby każda oś została znormalizowana do wartości maksymalnej lub sumy:

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

Używa funkcji numpys od szczytu do szczytu .

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
źródło
Uważaj, jeśli wszystkie wartości są takie same w oryginalnej matrycy, to ptp będzie wynosić 0. Dzielenie przez 0 zwróci nan.
Milso
8

Istnieje również funkcja unit_vector()normalizacji wektorów w popularnym module transformacji Christopha Gohlke:

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
źródło
7

Wspomniałeś o sci-kit learn, więc chcę podzielić się innym rozwiązaniem.

nauka sci-kit MinMaxScaler

W uczeniu się sci-kit istnieje interfejs API, MinMaxScalerktóry może dostosowywać zakres wartości, jak chcesz.

Zajmuje się także dla nas kwestiami NaN.

NaN są traktowane jako brakujące wartości: pomijane w dopasowaniu i utrzymywane w transformacji. ... patrz odniesienie [1]

Próbka kodu

Kod jest prosty, wystarczy wpisać

# 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)
Odniesienie
WY Hsu
źródło
6

Bez sklearni tylko za pomocą numpy. Wystarczy zdefiniować funkcję :.

Zakładając, że wiersze są zmiennymi, a kolumny sample ( 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)

wynik:

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

stdmtx(X)
array([[-1.,  0.,  1.],
       [-1.,  0.,  1.]])
seralouk
źródło
4

Jeśli chcesz znormalizować n wektorów cech wymiarowych przechowywanych w tensorze 3D, możesz również użyć PyTorch:

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
źródło
4

Jeśli pracujesz z wektorami 3D, możesz to zrobić zwięźle za pomocą paska narzędzi vg . Jest to lekka warstwa na górze numpy i obsługuje pojedyncze wartości i ułożone wektory.

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

Bibliotekę utworzyłem przy moim ostatnim uruchomieniu, gdzie była motywowana takimi zastosowaniami: proste pomysły, które są zbyt szczegółowe w NumPy.

Paulmelnikow
źródło
3

Jeśli nie potrzebujesz najwyższej precyzji, możesz zredukować swoją funkcję do:

v_norm = v / (np.linalg.norm(v) + 1e-16)
sergio verduzco
źródło
3

Jeśli pracujesz z tablicą wielowymiarową, możliwe jest szybkie rozwiązanie.

Powiedzmy, że mamy tablicę 2D, którą chcemy znormalizować według ostatniej osi, podczas gdy niektóre wiersze mają zerową normę.

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]]
Stanisław Tsepa
źródło