如何在Python中更简洁地对二维NumPy数组进行归一化处理?

115

给定一个 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

一定有更好的方法,不是吗?

或许需要澄清一下:我所说的规范化是指每行条目之和必须为1。但我想大多数人都会明白。


20
注意,“normalize”通常指的是组成部分的平方和为1。你的定义可能不太清晰易懂给大多数人;) - coldfix
5
@coldfix 谈到了 L2 范数并认为它是最常见的(这可能是正确的),而 Aufwind 使用的是 L1 范数,它也确实是一种范数。 - Bálint Sass
12个回答

174

广播对此非常有效:

row_sums = a.sum(axis=1)
new_matrix = a / row_sums[:, numpy.newaxis]
row_sums[:, numpy.newaxis] 将 row_sums 从 (3,) 改变为 (3, 1) 的形状。当你执行 a / b 时,ab 会自动进行 广播运算
你可以在 这里 或者更好的 这里 了解更多关于广播运算的知识。

42
可以使用 a.sum(axis=1, keepdims=True) 更进一步简化,以保持单列维度,然后可以在广播时无需使用 np.newaxis - ali_m
9
如果任何一行的总和为零会怎样? - asdf
12
这是上述问题的正确答案 - 但如果想要通常意义下的归一化,请使用 np.linalg.norm 而不是 a.sum - coldfix
2
这种写法比 row_sums.reshape(3,1) 更好吗? - Paul
1
由于行总和可能为0,因此它不是很健壮。 - nos
显示剩余5条评论

133

Scikit-learn提供了一个名为normalize()的函数,可以应用各种规范化方法。将其总和归一化为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

11

我认为这应该可以工作,

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

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

2
注意将dtype更改为arange,通过在27后添加小数点。 - wim

6

如果您试图将每一行规范化为其大小为1(即,行的单位长度为1或行中每个元素的平方和为1):

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

4

我认为你可以通过以下方式将行元素总和归一化为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),它可以计算矩阵a中每个向量的模长并返回一个列向量。

这个计算范数,但不对矩阵进行归一化。 - qwr

1
看起来这也可以工作。
def normalizeRows(M):
    row_sums = M.sum(axis=1)
    return M / row_sums

0

你也可以使用矩阵转置:

(a.T / row_sums).T

这个回答如果没有解释如何计算row_sums就是不完整的。 - qwr
它在原始问题中:row_sums = a.sum(axis=1) - Maciek

0

使用

a = a / np.linalg.norm(a, ord = 2, axis = 0, keepdims = True)

由于广播,它将按预期工作。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接