Comment normaliser un tableau numpy à 2 dimensions en python moins verbeux?

87

Étant donné un tableau 3 fois 3 numpy

a = numpy.arange(0,27,3).reshape(3,3)

# array([[ 0,  3,  6],
#        [ 9, 12, 15],
#        [18, 21, 24]])

Pour normaliser les lignes du tableau à 2 dimensions auquel j'ai pensé

row_sums = a.sum(axis=1) # array([ 9, 36, 63])
new_matrix = numpy.zeros((3,3))
for i, (row, row_sum) in enumerate(zip(a, row_sums)):
    new_matrix[i,:] = row / row_sum

Il doit y avoir un meilleur moyen, n'est-ce pas?

Peut-être pour clarifier: en normalisant je veux dire, la somme des entrées par ligne doit être un. Mais je pense que ce sera clair pour la plupart des gens.

Aufwind
la source
17
Attention, «normaliser» signifie généralement que la somme carrée des composants est un. Votre définition sera à peine claire pour la plupart des gens;)
coldfix

Réponses:

138

La diffusion est vraiment bonne pour cela:

row_sums = a.sum(axis=1)
new_matrix = a / row_sums[:, numpy.newaxis]

row_sums[:, numpy.newaxis]remodèle row_sums d'être (3,)en être (3, 1). Lorsque vous faites a / b, aet bsont diffusés les uns contre les autres.

Vous pouvez en savoir plus sur la diffusion ici ou encore mieux ici .

Bi Rico
la source
29
Cela peut être encore simplifié en utilisant a.sum(axis=1, keepdims=True)pour conserver la dimension de colonne singleton, que vous pouvez ensuite diffuser sans avoir à utiliser np.newaxis.
ali_m
6
et si l'un des row_sums est nul?
asdf
7
C'est la bonne réponse à la question indiquée ci-dessus - mais si une normalisation au sens habituel est souhaitée, utilisez à la np.linalg.normplace de a.sum!
coldfix
1
est-ce préféré row_sums.reshape(3,1)?
Paul
1
Ce n'est pas aussi robuste puisque la somme des lignes peut être de 0.
nos
103

Scikit-learn a une fonction de normalisation qui vous permet d'appliquer diverses normalisations. Le "make it sum to 1" est la norme L1, et pour prendre cela, faites:

from sklearn.preprocessing import normalize
matrix = numpy.arange(0,27,3).reshape(3,3).astype(numpy.float64)

#array([[  0.,   3.,   6.],
#   [  9.,  12.,  15.],
#   [ 18.,  21.,  24.]])

normed_matrix = normalize(matrix, axis=1, norm='l1')

#[[ 0.          0.33333333  0.66666667]
#[ 0.25        0.33333333  0.41666667]
#[ 0.28571429  0.33333333  0.38095238]]

Maintenant, vos lignes totaliseront 1.

rogueleaderr
la source
3
Cela présente également l'avantage de fonctionner sur des tableaux clairsemés qui ne rentreraient pas dans la mémoire en tant que tableaux denses.
JEM_Mosig
10

Je pense que cela devrait fonctionner,

a = numpy.arange(0,27.,3).reshape(3,3)

a /=  a.sum(axis=1)[:,numpy.newaxis]
tom10
la source
2
bien. notez le changement de dtype à arange, en ajoutant un point décimal à 27.
wim
4

Dans le cas où vous essayez de normaliser chaque ligne de manière à ce que sa magnitude soit un (c'est-à-dire que la longueur unitaire d'une ligne est un ou la somme du carré de chaque élément d'une ligne est un):

import numpy as np

a = np.arange(0,27,3).reshape(3,3)

result = a / np.linalg.norm(a, axis=-1)[:, np.newaxis]
# array([[ 0.        ,  0.4472136 ,  0.89442719],
#        [ 0.42426407,  0.56568542,  0.70710678],
#        [ 0.49153915,  0.57346234,  0.65538554]])

Vérification:

np.sum( result**2, axis=-1 )
# array([ 1.,  1.,  1.]) 
Walt
la source
Axis ne semble pas être un paramètre de np.linalg.norm (plus?).
Ztyx
notamment cela correspond à la norme l2 (où la somme des lignes à 1 correspond à la norme l1)
dpb
3

Je pense que vous pouvez normaliser les éléments de ligne somme 1 par ceci: new_matrix = a / a.sum(axis=1, keepdims=1). Et la normalisation de la colonne peut être effectuée avec new_matrix = a / a.sum(axis=0, keepdims=1). J'espère que cela peut aider.

Snoopy
la source
2

Vous pouvez utiliser la fonction numpy intégrée: np.linalg.norm(a, axis = 1, keepdims = True)

Saurabh Gupta
la source
1

il semble que cela fonctionne aussi

def normalizeRows(M):
    row_sums = M.sum(axis=1)
    return M / row_sums
Jamesszm
la source
1

Vous pouvez également utiliser la transposition matricielle:

(a.T / row_sums).T
Maciek
la source
0

Ou en utilisant la fonction lambda, comme

>>> vec = np.arange(0,27,3).reshape(3,3)
>>> import numpy as np
>>> norm_vec = map(lambda row: row/np.linalg.norm(row), vec)

chaque vecteur de vec aura une norme unitaire.

XY.W
la source
0

Voici une autre façon possible d'utiliser reshape:

a_norm = (a/a.sum(axis=1).reshape(-1,1)).round(3)
print(a_norm)

Ou en utilisant des Noneœuvres aussi:

a_norm = (a/a.sum(axis=1)[:,None]).round(3)
print(a_norm)

Sortie :

array([[0.   , 0.333, 0.667],
       [0.25 , 0.333, 0.417],
       [0.286, 0.333, 0.381]])
Grayrigel
la source
-2
normed_matrix = normalize(input_data, axis=1, norm='l1')
print(normed_matrix)

où input_data est le nom de votre tableau 2D

sonali b
la source