¿Cómo normalizar una matriz numpy bidimensional en Python menos detallada?

87

Dada una matriz de números 3 por 3

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

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

Para normalizar las filas de la matriz bidimensional que 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

Debe haber una forma mejor, ¿no es así?

Quizás para aclarar: por normalizar quiero decir, la suma de las entradas por fila debe ser uno. Pero creo que eso quedará claro para la mayoría de la gente.

Aufwind
fuente
17
Cuidado, "normalizar" generalmente significa que la suma al cuadrado de los componentes es uno. Su definición difícilmente será clara para la mayoría de la gente;)
coldfix

Respuestas:

138

La transmisión es realmente buena para esto:

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

row_sums[:, numpy.newaxis]transforma row_sums de ser (3,)a ser (3, 1). Cuando lo hacen a / b, ay bse transmiten entre sí.

Puede obtener más información sobre la transmisión aquí o incluso mejor aquí .

Bi Rico
fuente
29
Esto se puede simplificar aún más utilizando a.sum(axis=1, keepdims=True)para mantener la dimensión de la columna singleton, que luego puede transmitir sin tener que usar np.newaxis.
ali_m
6
¿Qué pasa si alguno de los row_sums es cero?
asdf
7
Esta es la respuesta correcta para la pregunta como se indicó anteriormente, pero si se desea una normalización en el sentido habitual, use en np.linalg.normlugar de a.sum!
Coldfix
1
es esto preferido a row_sums.reshape(3,1)?
Paul
1
No es tan robusto ya que la suma de la fila puede ser 0.
nos
103

Scikit-learn tiene una función de normalización que le permite aplicar varias normalizaciones. El "haz que sume a 1" es la norma L1, y para tomar eso:

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

Ahora sus filas sumarán 1.

pícaro
fuente
3
Esto también tiene la ventaja de que funciona en matrices dispersas que no cabrían en la memoria como matrices densas.
JEM_Mosig
10

Creo que esto debería funcionar,

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

a /=  a.sum(axis=1)[:,numpy.newaxis]
tom10
fuente
2
bueno. tenga en cuenta el cambio de dtype arange, añadiendo el punto decimal a 27.
wim
4

En caso de que esté intentando normalizar cada fila de modo que su magnitud sea uno (es decir, la unidad de longitud de una fila es uno o la suma del cuadrado de cada elemento en una fila es uno):

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.]) 
walt
fuente
Axis no parece ser un parámetro para np.linalg.norm (¿ya?).
Ztyx
en particular, esto corresponde a la norma l2 (donde las filas que suman 1 corresponde a la norma l1)
dpb
3

Creo que se puede normalizar la suma de elementos de fila 1 por el siguiente: new_matrix = a / a.sum(axis=1, keepdims=1). Y la normalización de la columna se puede hacer con new_matrix = a / a.sum(axis=0, keepdims=1). Espero que esto pueda ayudar.

Snoopy
fuente
2

Podrías usar la función numpy incorporada: np.linalg.norm(a, axis = 1, keepdims = True)

Saurabh Gupta
fuente
1

parece que esto también funciona

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

También puede usar la transposición de matriz:

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

O usando la función 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 vector de vec tendrá una norma unitaria.

XY.W
fuente
0

Aquí hay otra forma posible de usar reshape:

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

O usando Noneobras también:

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

Salida :

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

donde input_data es el nombre de su matriz 2D

sonali b
fuente