Comment normaliser un tableau dans NumPy?

205

Je voudrais avoir la norme d'un tableau NumPy. Plus précisément, je recherche une version équivalente de cette fonction

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

Y a-t-il quelque chose comme ça dans skearnou numpy?

Cette fonction fonctionne dans une situation où vest le vecteur 0.

Donbeo
la source
3
Quel est le problème avec ce que vous avez écrit?
ali_m
5
Si c'est vraiment un problème, vous devez vérifier la norme <epsilon, où epsilon est une petite tolérance. De plus, je ne repasserais pas silencieusement un vecteur de norme zéro, je ferais raiseune exception!
accroché le
4
ma fonction fonctionne mais je voudrais savoir s'il y a quelque chose dans la bibliothèque la plus commune du python. J'écris différentes fonctions d'apprentissage automatique et j'aimerais éviter de définir trop de nouvelles fonctions pour rendre le code plus clair et lisible
Donbeo
1
J'ai fait quelques tests rapides et j'ai trouvé que ce x/np.linalg.norm(x)n'était pas beaucoup plus lent (environ 15-20%) que x/np.sqrt((x**2).sum())dans numpy 1.15.1 sur un CPU.
Bill

Réponses:

162

Si vous utilisez scikit-learn, vous pouvez utiliser 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
la source
2
Merci pour la réponse mais êtes-vous sûr que sklearn.preprocessing.normalize fonctionne également avec le vecteur de forme = (n,) ou (n, 1)? J'ai des problèmes avec cette bibliothèque
Donbeo
normalizenécessite une entrée 2D. Vous pouvez passer l' axis=argument pour spécifier si vous souhaitez appliquer la normalisation sur les lignes ou les colonnes de votre tableau d'entrée.
ali_m
9
Notez que l'argument «norm» de la fonction de normalisation peut être «l1» ou «l2» et la valeur par défaut est «l2». Si vous voulez que la somme de votre vecteur soit 1 (par exemple une distribution de probabilité), vous devez utiliser norm = 'l1' dans la fonction de normalisation.
Ash
2
Notez également que np.linalg.norm(x)calcule la norme 'l2' par défaut. Si vous voulez que la somme de votre vecteur soit 1, vous devez utilisernp.linalg.norm(x, ord=1)
Omid
Remarque: x doit être ndarraypour qu'il fonctionne avec la normalize()fonction. Sinon, cela peut être un list.
Ramin Melikov
47

Je conviendrais que c'était bien si une telle fonction faisait partie des piles incluses. Mais ce n'est pas, autant que je sache. Voici une version pour axes arbitraires, et offrant des performances optimales.

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
la source
Je n'ai pas testé en profondeur la solution ali_m mais dans un cas simple, elle semble fonctionner. Y a-t-il des situations où votre fonction est meilleure?
Donbeo
1
Je ne sais pas; mais cela fonctionne sur des axes arbitraires, et nous avons un contrôle explicite sur ce qui se passe pour les vecteurs de longueur 0.
Eelco Hoogendoorn
1
Très agréable! Cela devrait être en nombre - bien que l'ordre devrait probablement venir avant l'axe à mon avis.
Neil G
@EelcoHoogendoorn Curieux de comprendre pourquoi l'ordre = 2 a été choisi parmi les autres?
Henry Thornton
7
Parce que la norme euclidienne / pythagoricienne est la plus utilisée; tu n'es pas d'accord?
Eelco Hoogendoorn
21

Vous pouvez spécifier ord pour obtenir la norme L1. Pour éviter la division zéro, j'utilise eps, mais ce n'est peut-être pas génial.

def normalize(v):
    norm=np.linalg.norm(v, ord=1)
    if norm==0:
        norm=np.finfo(v.dtype).eps
    return v/norm
Eduard Feicho
la source
6
normaliser les [inf, 1, 2]rendements [nan, 0, 0], mais cela ne devrait pas être le cas [1, 0, 0]?
pasbi
12

Cela pourrait également fonctionner pour vous

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

mais échoue quand va la longueur 0.

mrk
la source
10

Si vous avez des données multidimensionnelles et souhaitez que chaque axe soit normalisé à son maximum ou à sa somme:

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

Utilise la fonction crête à crête de numpys .

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
la source
Attention si toutes les valeurs sont les mêmes dans la matrice d'origine, alors ptp serait 0. La division par 0 renverra nan.
Milso
8

Il y a aussi la fonction unit_vector()de normalisation des vecteurs dans le module de transformations populaires de 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))
Joe
la source
7

Vous avez mentionné sci-kit learn, donc je veux partager une autre solution.

sci-kit apprendre MinMaxScaler

Dans sci-kit learn, il existe une API appelée MinMaxScaler qui peut personnaliser la plage de valeurs comme vous le souhaitez.

Il traite également des problèmes de NaN pour nous.

Les NaN sont traités comme des valeurs manquantes: ignorés dans l'ajustement et maintenus dans la transformation. ... voir référence [1]

Exemple de code

Le code est simple, il suffit de taper

# 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)
Référence
WY Hsu
la source
6

Sans sklearnet en utilisant juste numpy. Définissez simplement une fonction :.

En supposant que les lignes sont les variables et les colonnes les échantillons ( 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)

production:

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

stdmtx(X)
array([[-1.,  0.,  1.],
       [-1.,  0.,  1.]])
seralouk
la source
4

Si vous souhaitez normaliser des vecteurs d'entités à n dimensions stockés dans un tenseur 3D, vous pouvez également utiliser 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
la source
4

Si vous travaillez avec des vecteurs 3D, vous pouvez le faire de manière concise à l'aide de la ceinture d'outils vg . C'est une couche légère au-dessus de numpy et elle prend en charge les valeurs uniques et les vecteurs empilés.

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

J'ai créé la bibliothèque lors de mon dernier démarrage, où elle était motivée par des utilisations comme celle-ci: des idées simples qui sont beaucoup trop verbeuses dans NumPy.

paulmelnikow
la source
3

Si vous n'avez pas besoin d'une précision extrême, votre fonction peut être réduite à:

v_norm = v / (np.linalg.norm(v) + 1e-16)
sergio verduzco
la source
3

Si vous travaillez avec un tableau multidimensionnel, une solution rapide est possible.

Disons que nous avons un tableau 2D, que nous voulons normaliser par le dernier axe, tandis que certaines lignes ont une norme nulle.

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
la source