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.


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 .


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.


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]

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

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.


2

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


1

il semble que cela fonctionne aussi

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

1

Vous pouvez également utiliser la transposition matricielle:

(a.T / row_sums).T

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.


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

-2
normed_matrix = normalize(input_data, axis=1, norm='l1')
print(normed_matrix)

où input_data est le nom de votre tableau 2D

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.