Como normalizar uma matriz numpy bidimensional em python menos prolixo?

87

Dado um array numpy 3 vezes 3

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

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

Para normalizar as linhas da matriz bidimensional, pensei

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

Deve haver uma maneira melhor, não é?

Talvez para esclarecer: por normalização, quero dizer, a soma das entradas por linha deve ser um. Mas acho que isso ficará claro para a maioria das pessoas.

Aufwind
fonte
16
Cuidado, "normalizar" geralmente significa que a soma quadrada dos componentes é um. Sua definição dificilmente será clara para a maioria das pessoas;)
coldfix

Respostas:

135

A transmissão é muito boa para isso:

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

row_sums[:, numpy.newaxis]remodela row_sums de ser (3,)para ser (3, 1). Quando você faz a / b, ae bsão transmitidos uns contra os outros.

Você pode aprender mais sobre transmissão aqui ou ainda melhor aqui .

Bi Rico
fonte
27
Isso pode ser simplificado ainda mais usando a.sum(axis=1, keepdims=True)para manter a dimensão da coluna singleton, que você pode transmitir sem precisar usar np.newaxis.
ali_m
6
e se algum dos row_sums for zero?
asdf
7
Esta é a resposta correta para a pergunta conforme declarado acima - mas se uma normalização no sentido usual for desejada, use em np.linalg.normvez de a.sum!
coldfix de
1
é preferível row_sums.reshape(3,1)?
Paul
1
Não é tão robusto, pois a soma das linhas pode ser 0.
nos
102

O Scikit-learn tem uma função de normalização que permite aplicar várias normalizações. O "faça a soma para 1" é a norma L1, e para isso faça:

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

Agora, suas linhas somarão 1.

rogueleaderr
fonte
3
Isso também tem a vantagem de funcionar em arrays esparsos que não caberiam na memória como arrays densos.
JEM_Mosig
10

Eu acho que isso deve funcionar,

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

a /=  a.sum(axis=1)[:,numpy.newaxis]
tom10
fonte
2
Boa. observe a mudança de tipo d para arange, acrescentando o ponto decimal a 27.
wim
3

Caso você esteja tentando normalizar cada linha de forma que sua magnitude seja um (ou seja, o comprimento da unidade de uma linha é um ou a soma do quadrado de cada elemento em uma linha é um):

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

Verificando:

np.sum( result**2, axis=-1 )
# array([ 1.,  1.,  1.]) 
valsa
fonte
Axis não parece ser um parâmetro para np.linalg.norm (mais?).
Ztyx
notavelmente, isso corresponde à norma l2 (onde as linhas somadas a 1 correspondem à norma l1)
dpb
3

Eu acho que você pode normalizar a soma elementos de linha de 1 por este: new_matrix = a / a.sum(axis=1, keepdims=1). E a normalização da coluna pode ser feita com new_matrix = a / a.sum(axis=0, keepdims=1). Espero que isso possa afetar.

Snoopy
fonte
2

Você pode usar a função numpy integrada: np.linalg.norm(a, axis = 1, keepdims = True)

Saurabh Gupta
fonte
1

parece que isso também funciona

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

Você também pode usar a transposição de matriz:

(a.T / row_sums).T
Maciek
fonte
0

Ou usando a função lambda, como

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

cada vetor de vec terá uma norma unitária.

XY.W
fonte
-2
normed_matrix = normalize(input_data, axis=1, norm='l1')
print(normed_matrix)

onde input_data é o nome do seu array 2D

sonali b
fonte