Как нормализовать двумерный массив numpy в Python менее подробным?

87

Учитывая 3 раза 3 массива numpy

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

Должен быть способ получше, не так ли?

Возможно, чтобы прояснить: под нормализацией я имею в виду, что сумма записей в строке должна быть равна единице. Но я думаю, это будет понятно большинству людей.

Aufwind
источник
17
Осторожно, «нормализовать» обычно означает, что сумма квадратов компонентов равна единице. Ваше определение вряд ли будет понятно большинству людей;)
coldfix

Ответы:

138

Трансляция для этого действительно хороша:

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

row_sums[:, numpy.newaxis]преобразует row_sums из бытия (3,)в бытие (3, 1). Когда вы это делаете a / b, aи bтранслируются друг против друга.

Вы можете узнать больше о трансляциях здесь или даже лучше здесь .

Би-Рико
источник
29
Это можно еще больше a.sum(axis=1, keepdims=True)упростить, сохранив измерение одноэлементного столбца, которое затем можно транслировать без необходимости использования np.newaxis.
ali_m
6
что делать, если любая из row_sums равна нулю?
asdf
7
Это правильный ответ на поставленный выше вопрос, но если требуется нормализация в обычном смысле, используйте np.linalg.normвместо a.sum!
coldfix
1
это предпочтительнее row_sums.reshape(3,1)?
Пол
1
Это не так надежно, поскольку сумма строки может быть равна 0.
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]
tom10
источник
2
хороший. обратите внимание на изменение dtype на arange, добавив десятичную точку к 27.
wim
4

В случае, если вы пытаетесь нормализовать каждую строку так, чтобы ее величина была равна единице (т.е. единичная длина строки равна единице или сумма квадратов каждого элемента в строке равна единице):

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.]) 
Walt
источник
Axis, похоже, не является параметром np.linalg.norm (больше?).
Ztyx
в частности, это соответствует норме l2 (где
сумма строк, равная
3

Я думаю , что вы можете нормализовать сумму ряда элементов 1 этим: new_matrix = a / a.sum(axis=1, keepdims=1). А нормализацию столбца можно выполнить с помощью new_matrix = a / a.sum(axis=0, keepdims=1). Надеюсь, это может помочь.

Снупи
источник
2

Вы можете использовать встроенную функцию numpy: np.linalg.norm(a, axis = 1, keepdims = True)

Саураб Гупта
источник
1

похоже, что это тоже работает

def normalizeRows(M):
    row_sums = M.sum(axis=1)
    return M / row_sums
Jamesszm
источник
1

Вы также можете использовать транспонирование матрицы:

(a.T / row_sums).T
Maciek
источник
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 будет иметь единичную норму.

XY.W
источник
0

Вот еще один возможный способ использования reshape:

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

Или с помощью Noneтоже работает:

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

Выход :

array([[0.   , 0.333, 0.667],
       [0.25 , 0.333, 0.417],
       [0.286, 0.333, 0.381]])
Grayrigel
источник
-2
normed_matrix = normalize(input_data, axis=1, norm='l1')
print(normed_matrix)

где input_data - это имя вашего 2D-массива

Сонали Б.
источник