¿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.


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í .


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.


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]

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

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.


2

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


1

parece que esto también funciona

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

1

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

(a.T / row_sums).T

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.


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

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

donde input_data es el nombre de su matriz 2D

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.