Come normalizzare un array in NumPy?


205

Vorrei avere la norma di un array NumPy. Più specificamente, sto cercando una versione equivalente di questa funzione

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

C'è qualcosa del genere in skearno numpy?

Questa funzione è attiva in una situazione in cui vè il vettore 0.


3
Cosa c'è che non va in quello che hai scritto?
ali_m

5
Se questo è veramente un problema, dovresti controllare la norma <epsilon, dove epsilon è una piccola tolleranza. Inoltre, non restituirei in silenzio un vettore zero normale, farei raiseun'eccezione!
Agganciato il

4
la mia funzione funziona ma vorrei sapere se c'è qualcosa nella libreria più comune di Python. Sto scrivendo diverse funzioni di apprendimento automatico e vorrei evitare di definire troppe nuove funzioni per rendere il codice più chiaro e leggibile
Donbeo,

1
Ho fatto alcuni test rapidi e ho scoperto che x/np.linalg.norm(x)non era molto più lento (circa il 15-20%) rispetto x/np.sqrt((x**2).sum())al numpy 1.15.1 su una CPU.
Bill

Risposte:


162

Se stai usando scikit-learn puoi usare 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
Grazie per la risposta, ma sei sicuro che sklearn.preprocessing.normalize funziona anche con il vettore di forma = (n,) o (n, 1)? Sto avendo dei problemi con questa biblioteca
Donbeo,

normalizerichiede un input 2D. È possibile passare l' axis=argomento per specificare se si desidera applicare la normalizzazione tra le righe o le colonne dell'array di input.
ali_m

9
Si noti che l'argomento 'norm' della funzione normalize può essere 'l1' o 'l2' e il valore predefinito è 'l2'. Se vuoi che la somma del tuo vettore sia 1 (ad es. Una distribuzione di probabilità) dovresti usare norm = 'l1' nella funzione di normalizzazione.
Ash,

2
Si noti inoltre che np.linalg.norm(x)calcola la norma 'l2' per impostazione predefinita. Se vuoi che la somma del tuo vettore sia 1 dovresti usarenp.linalg.norm(x, ord=1)
Omid,

Nota: x deve essere ndarraycompatibile con la normalize()funzione. Altrimenti può essere un list.
Ramin Melikov,

47

Concordo sul fatto che sarebbe stato bello se una tale funzione fosse parte delle batterie incluse. Ma non lo è, per quanto ne so. Ecco una versione per assi arbitrari e che offre prestazioni ottimali.

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)))

Non ho testato a fondo la soluzione ali_m ma in alcuni casi sembra funzionare. Ci sono situtions in cui la tua funzione è migliore?
Donbeo,

1
Non lo so; ma funziona su assi arbitrari e abbiamo un controllo esplicito su ciò che accade per i vettori di lunghezza 0.
Eelco Hoogendoorn,

1
Molto bella! Questo dovrebbe essere insensibile, anche se secondo me probabilmente l'ordine dovrebbe precedere l'asse.
Neil G,

@EelcoHoogendoorn Curioso di capire perché order = 2 scelto rispetto agli altri?
Henry Thornton,

7
Perché la norma euclidea / pitagorica sembra essere la più usata; non sei d'accordo?
Eelco Hoogendoorn,

21

È possibile specificare ord per ottenere la norma L1. Per evitare la divisione zero uso eps, ma forse non è eccezionale.

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

6
normalizzare i [inf, 1, 2]rendimenti [nan, 0, 0], ma non dovrebbe essere [1, 0, 0]?
pasbi

12

Questo potrebbe funzionare anche per te

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

ma fallisce quando vha lunghezza 0.


10

Se disponi di dati multidimensionali e desideri che ciascun asse sia normalizzato al massimo o alla sua somma:

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

Utilizza numpys da picco a picco funzione.

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

Fai attenzione se tutti i valori sono uguali nella matrice originale, quindi ptp sarebbe 0. La divisione per 0 restituirà nan.
Milso

8

C'è anche la funzione unit_vector()di normalizzare i vettori nel popolare modulo di trasformazioni di Christoph 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))

7

Hai menzionato l'apprendimento di sci-kit, quindi voglio condividere un'altra soluzione.

sci-kit impara MinMaxScaler

In sci-kit learn, esiste un'API chiamata MinMaxScaler che può personalizzare l'intervallo di valori come preferisci.

Ci occupiamo anche dei problemi della NaN.

I NaN vengono trattati come valori mancanti: ignorati in forma e mantenuti in trasformazione. ... vedi riferimento [1]

Esempio di codice

Il codice è semplice, basta digitare

# 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)
Riferimento

6

Senza sklearne usando solo numpy. Basta definire una funzione :.

Supponendo che le righe siano le variabili e le colonne i campioni ( 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)

produzione:

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

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

4

Se si desidera normalizzare i vettori di caratteristiche n dimensionali memorizzati in un tensore 3D, è possibile utilizzare anche 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()

4

Se lavori con vettori 3D, puoi farlo in modo conciso utilizzando la cintura degli attrezzi vg . È uno strato leggero sopra intorpidito e supporta valori singoli e vettori impilati.

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

Ho creato la libreria al mio ultimo avvio, dove è stata motivata da usi come questo: idee semplici che sono troppo dettagliate in NumPy.


3

Se non hai bisogno della massima precisione, la tua funzione può essere ridotta a:

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

3

Se si lavora con un array multidimensionale è possibile seguire una soluzione rapida.

Supponiamo di avere un array 2D, che vogliamo normalizzare dall'ultimo asse, mentre alcune righe hanno zero norme.

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]]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.