Jak znormalizować dwuwymiarową tablicę numpy w Pythonie mniej gadatliwym?

87

Biorąc pod uwagę tablicę numpy 3 razy 3

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

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

Aby znormalizować wiersze dwuwymiarowej tablicy, o której myślałem

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

Musi być lepszy sposób, prawda?

Być może dla wyjaśnienia: przez normalizację mam na myśli, że suma wpisów na wiersz musi wynosić jeden. Ale myślę, że dla większości ludzi będzie to jasne.

Aufwind
źródło
17
Ostrożnie, „normalizuj” zwykle oznacza, że suma kwadratowa składników wynosi jeden. Twoja definicja nie będzie jasna dla większości ludzi;)
coldfix

Odpowiedzi:

138

Nadawanie jest naprawdę dobre do tego:

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

row_sums[:, numpy.newaxis]przekształca sumy_wierszów z bycia (3,)w bycie (3, 1). Kiedy to robisz a / b, ai bsą nadawane przeciwko sobie.

Możesz dowiedzieć się więcej o nadawaniu tutaj, a nawet lepiej tutaj .

Bi Rico
źródło
29
Można to jeszcze bardziej uprościć, a.sum(axis=1, keepdims=True)zachowując wymiar pojedynczej kolumny, którą można następnie transmitować bez konieczności używania np.newaxis.
ali_m
6
co się stanie, jeśli którakolwiek z sum_wierszów wynosi zero?
asdf
7
To jest poprawna odpowiedź na powyższe pytanie - ale jeśli pożądana jest normalizacja w zwykłym sensie, użyj np.linalg.normzamiast a.sum!
coldfix
1
czy to jest preferowane row_sums.reshape(3,1)?
Paweł,
1
Nie jest tak solidna, ponieważ suma wierszy może wynosić 0.
nr
103

Scikit-learn ma funkcję normalizacji, która umożliwia stosowanie różnych normalizacji. „Zrób to sumę do 1” to norma L1, i aby to zrobić:

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

Teraz twoje wiersze będą sumowane do 1.

rogueleaderr
źródło
3
Ma to również tę zaletę, że działa na rzadkich tablicach, które nie pasowałyby do pamięci jako tablice gęste.
JEM_Mosig
10

Myślę, że to powinno działać,

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

a /=  a.sum(axis=1)[:,numpy.newaxis]
tom10
źródło
2
dobry. zwróć uwagę na zmianę typu dtype na arange, dodając kropkę dziesiętną do 27.
wim
4

W przypadku, gdy próbujesz znormalizować każdy wiersz w taki sposób, aby jego wielkość wynosiła jeden (tj. Długość jednostki wiersza wynosi jeden lub suma kwadratów każdego elementu w wierszu wynosi jeden):

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

Weryfikacja:

np.sum( result**2, axis=-1 )
# array([ 1.,  1.,  1.]) 
Walt
źródło
Axis nie wydaje się być parametrem np.linalg.norm (już?).
Ztyx
w szczególności odpowiada to normie l2 (gdzie jako wiersze sumujące się do 1 odpowiada normie l1)
dpb
3

Myślę, że można znormalizować sumę elementów wiersz na 1 przez to: new_matrix = a / a.sum(axis=1, keepdims=1). I normalizację kolumny można wykonać za pomocą new_matrix = a / a.sum(axis=0, keepdims=1). Mam nadzieję, że to może pomóc.

wścibski
źródło
2

Możesz użyć wbudowanej funkcji numpy: np.linalg.norm(a, axis = 1, keepdims = True)

Saurabh Gupta
źródło
1

wydaje się, że to też działa

def normalizeRows(M):
    row_sums = M.sum(axis=1)
    return M / row_sums
Jamesszm
źródło
1

Możesz również użyć transpozycji macierzy:

(a.T / row_sums).T
Maciek
źródło
0

Lub używając funkcji lambda, takiej jak

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

każdy wektor vec będzie miał normę jednostkową.

XY.W
źródło
0

Oto jeszcze jeden możliwy sposób użycia reshape:

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

Lub też używanie Nonedziała:

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

Wyjście :

array([[0.   , 0.333, 0.667],
       [0.25 , 0.333, 0.417],
       [0.286, 0.333, 0.381]])
Grayrigel
źródło
-2
normed_matrix = normalize(input_data, axis=1, norm='l1')
print(normed_matrix)

gdzie dane_wejściowe to nazwa tablicy 2D

sonali b
źródło