Як нормалізувати двовимірний масив numpy в python менш багатослівно?


87

Дано масив у 3 рази по 3

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

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

Для нормалізації рядків двовимірного масиву, про який я думав

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

Має бути кращий спосіб, чи не так?

Можливо, для уточнення: під нормуванням я маю на увазі, що сума записів у рядку повинна бути одна. Але я думаю, що це буде зрозуміло більшості людей.


16
Обережно, "нормалізувати" зазвичай означає, що квадратна сума компонентів одна. Ваше визначення навряд чи буде зрозумілим для більшості людей;)
виправлення

Відповіді:


137

Мовлення справді добре для цього:

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

row_sums[:, numpy.newaxis] перетворює рядок_сум з того, що він є (3,) на буття (3, 1). Коли ви це робите a / b, aі bтранслюються один проти одного.

Ви можете дізнатись більше про мовлення тут або навіть краще тут .


27
Це може бути спрощено ще більше, використовуючи a.sum(axis=1, keepdims=True)збереження одновимірного розміру стовпця, який потім можна транслювати разом, не використовуючи np.newaxis.
ali_m

6
що якщо будь-який з рядків_сум дорівнює нулю?
asdf

7
Це правильна відповідь на питання, як зазначено вище - але якщо бажана нормалізація у звичному розумінні, використовуйте np.linalg.normзамість a.sum!
виправлення

1
це переважно row_sums.reshape(3,1)?
Пол

1
Це не настільки надійно, оскільки сума рядків може бути 0.
nos

103

Scikit-learn має функцію нормалізації, яка дозволяє застосовувати різні норми. "Зробіть його сумою до 1" - це норма L1, і якщо взяти це, зробіть:

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

Тепер ваші рядки складатимуть до 1.


3
Це також має ту перевагу, що він працює на розріджених масивах, які не поміщаються в пам'ять як щільні масиви.
JEM_Mosig

10

Я думаю, це має спрацювати,

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

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

2
добре зверніть увагу на зміну dtype на arange, додавши десяткову крапку до 27.
wim

3

Якщо ви намагаєтесь нормалізувати кожен рядок таким чином, щоб його величина дорівнювала одиниці (тобто довжина одиниці рядка дорівнює одиниці або сума квадрата кожного елемента в рядку дорівнює одиниці):

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

Перевірка:

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

Здається, вісь не є параметром для np.linalg.norm (більше?).
Ztyx

особливо це відповідає нормі l2 (де як рядки, що підсумовують до 1, відповідають нормі l1)
dpb

3

Я думаю , що ви можете нормалізувати суму ряду елементів 1 цим: new_matrix = a / a.sum(axis=1, keepdims=1). І нормалізацію стовпців можна зробити за допомогою new_matrix = a / a.sum(axis=0, keepdims=1). Сподіваюся, це може зіпсувати.



1

здається, це теж працює

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

1

Ви також можете скористатися транспортуванням матриці:

(a.T / row_sums).T

0

Або за допомогою лямбда-функції, наприклад

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

кожен вектор vec матиме одиничну норму.


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

де input_data - це ім'я вашого 2D-масиву

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.