Come normalizzare un array numpy bidimensionale in python meno prolisso?


87

Dato un array numpy 3 volte 3

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

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

Per normalizzare le righe della matrice bidimensionale a cui ho pensato

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 esserci un modo migliore, no?

Forse per chiarire: normalizzando intendo, la somma delle entrate per riga deve essere uno. Ma penso che sarà chiaro alla maggior parte delle persone.


17
Attenzione, "normalizza" di solito significa che la somma quadrata dei componenti è uno. La tua definizione difficilmente sarà chiara alla maggior parte delle persone;)
coldfix

Risposte:


138

La trasmissione è davvero utile per questo:

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

row_sums[:, numpy.newaxis]rimodella row_sums dall'essere (3,)all'essere (3, 1). Quando lo fai a / b, ae bvengono trasmessi l'uno contro l'altro.

Puoi saperne di più sulla trasmissione qui o anche meglio qui .


29
Questo può essere ulteriormente semplificato utilizzando a.sum(axis=1, keepdims=True)per mantenere la dimensione della colonna singleton, che puoi quindi trasmettere senza doverla usare np.newaxis.
ali_m

6
cosa succede se uno qualsiasi dei row_sums è zero?
asdf

7
Questa è la risposta corretta per la domanda come indicato sopra, ma se si desidera una normalizzazione nel senso comune, utilizzare np.linalg.norminvece di a.sum!
coldfix

1
è questo preferito row_sums.reshape(3,1)?
Paul

1
Non è così robusto poiché la somma delle righe potrebbe essere 0.
nos

103

Scikit-learn ha una funzione di normalizzazione che ti consente di applicare varie normalizzazioni. Il "rendilo somma a 1" è la norma L1, e per prenderlo:

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

Ora le tue righe verranno sommate a 1.


3
Questo ha anche il vantaggio di funzionare su array sparsi che non si adatterebbero alla memoria come array densi.
JEM_Mosig

10

Penso che dovrebbe funzionare,

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

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

2
bene. notare la modifica di dtype in arange, aggiungendo il punto decimale a 27.
wim

4

Nel caso in cui si stia tentando di normalizzare ogni riga in modo che la sua grandezza sia uno (cioè la lunghezza unitaria di una riga è uno o la somma dei quadrati di ogni elemento in una riga è 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]])

Verifica:

np.sum( result**2, axis=-1 )
# array([ 1.,  1.,  1.]) 

Axis non sembra essere un parametro per np.linalg.norm (più?).
Ztyx

in particolare ciò corrisponde alla norma l2 (dove come righe che si sommano a 1 corrisponde alla norma l1)
dpb

3

Penso che si possa normalizzare la somma elementi di riga a 1 da questo: new_matrix = a / a.sum(axis=1, keepdims=1). E la normalizzazione della colonna può essere eseguita new_matrix = a / a.sum(axis=0, keepdims=1). Spero che questo possa succedere.


2

Puoi usare la funzione numpy incorporata: np.linalg.norm(a, axis = 1, keepdims = True)


1

sembra che funzioni anche questo

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

1

Puoi anche usare la trasposizione della matrice:

(a.T / row_sums).T

0

O usando la funzione lambda, come

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

ogni vettore di vec avrà una norma unitaria.


0

Ecco un altro modo possibile utilizzando reshape:

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

O anche usando Noneopere:

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

Uscita :

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)

dove input_data è il nome del tuo array 2D

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.