您的位置:首页 > 编程语言 > Python开发

numpy广播机制

2017-02-10 17:25 381 查看
通常使用numpy的时候经常会遇到的一些这样的错误,



这是由于numpy数组在进行运算的时候形状不满足广播机制的要求,一般通过转置或
reshape
等方法可以解决问题,但是没有从根本上了解广播机制。

广播(broadcasting)指的是不同形状的数组之间的算数运算的执行方式。

数组与标量值的乘法

import numpy as np
arr = np.arange(5)
arr #-> array([0, 1, 2, 3, 4])
arr * 4 #-> array([ 0,  4,  8, 12, 16])


在这个乘法运算中,标量值4被广播到了其他所有元素上

通过减去列平均值的方式对数组每一列进行距平化处理

arr = np.random.randn(4,3)
arr #-> array([[ 1.83518156,  0.86096695,  0.18681254],
#       [ 1.32276051,  0.97987486,  0.27828887],
#       [ 0.65269467,  0.91924574, -0.71780692],
#       [-0.05431312,  0.58711748, -1.21710134]])
arr.mean(axis=0) #-> array([ 0.93908091,  0.83680126, -0.36745171])


关于
mean
中的
axis
参数,个人是这么理解的:

numpy
中,
axis = 0
为行轴(竖直方向),
axis = 1
为列轴(水平方向),指定
axis
表示该操作沿
axis
进行,得到结果将是一个
shape
为除去该
axis
array


在上例中,
arr.mean(axis=0)
表示对
arr
沿着轴0(竖直方向)求均值,即求列均值。而
arr
含有3列,所以结果含有3个元素,这与上面的结论相符。

demeaned = arr - arr.mean(axis=0)
demeaned
> array([[ 0.89610065,  0.02416569,  0.55426426],
[ 0.3836796 ,  0.1430736 ,  0.64574058],
[-0.28638623,  0.08244448, -0.35035521],
[-0.99339402, -0.24968378, -0.84964963]])
demeaned.mean(axis=0)
> array([ -5.55111512e-17,  -5.55111512e-17,   0.00000000e+00])


广播的原则

如果两个数组的后缘维度(从末尾开始算起的维度)轴长度相符其中一方的长度为1,则认为它们是广播兼容的。广播会在缺失维度和(或)轴长度为1的维度上进行。

在上面的对
arr
每一列减去列平均值的例子中,
arr
的后缘维度为
3
arr.mean(0)
后缘维度也是
3
,满足轴长度相符的条件,广播会在缺失维度进行。

这里有点奇怪的是缺失维度不是
axis=1
,而是
axis=0
,个人理解是缺失维度指的是两个
arr
除了轴长度匹配的维度,在上面的例子中,正好是
axis=0
这块欢迎指正

arr.mean(0)
沿着
axis=0
广播,可以看作是把
arr.mean(0)
沿着竖直方向复制4份,即广播的时候
arr.mean(0)
相当于一个
shape=(4,3)
的数组,数组的每一行均相同,均为
arr.mean(0)


各行减去行均值

row_means = arr.mean(axis=1)
row_means.shape
> (4,)
arr - row_means
> ---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-10-3d1314c7e700> in <module>()
----> 1 arr - row_means

ValueError: operands could not be broadcast together with shapes (4,3) (4,)


直接相减,报错,无法进行广播。回顾上面的原则,要么满足后缘维度轴长度相等,要么满足其中一方长度为1。在这个例子中,两者均不满足,所以报错。根据广播原则,较小数组的广播维必须为1

解决方案是为较小的数组添加一个长度为1的新轴。

numpy
提供了一种通过索引机制插入轴的特殊语法。通过特殊的
np.newaxis
属性以及“全”切片来插入新轴。

上面的例子中,我们希望实现二维数组各行减去行均值,我们需要你将行均值沿着水平方向进行广播,广播轴为
axis=1
,对
arr.mean(1)
添加一个新轴
axis=1


row_means[:,np.newaxis].shape
> (4, 1)
arr - row_means[:,np.newaxis]
> array([[ 0.87419454, -0.10002007, -0.77417447],
[ 0.46245243,  0.11956678, -0.58201921],
[ 0.36798351,  0.63453458, -1.00251808],
[ 0.17378588,  0.81521647, -0.98900235]])


另一个例子

a = np.array([1,2,3])
a.shape # -> (3,)
b = np.array([[1,],[2,],[3]]) # -> (3,1)
b - a # -> array([[ 0, -1, -2],
# [ 1,  0, -1],
# [ 2,  1,  0]])


输出为什么是一个3*3的数组?我们来分析以下,根据广播原则,b满足其中一方轴长度为1,那么广播会沿着长度为1的轴,及
axis=1
进行,对数组b沿着
axis=1
即水平方向进行复制,相当于b变成一个
shape
(3,3)
且各列均为
[1,2,3]
的数组,一个维度为(3,3)的数组减去一个维度为(3,)的数组,满足后缘维度轴长度相等,数组a沿着
axis=0
即竖直方向进行广播,相当远a变成一个
shape
(3,3)
且个行均为
[1,2,3]
的数组。

相减的时候,b被广播成为

⎡⎣⎢123123123⎤⎦⎥

a被广播成为

⎡⎣⎢111222333⎤⎦⎥

结果应该是

⎡⎣⎢012−101−2−10⎤⎦⎥

三维情况

下面的例子中,构造一个
3*4*5
的随机数组
arr_3d
,我们希望实现对
arr_3d
的每个元素减去其深度(axis=2)方向的均值

#构造三维数组
arr_3d = np.random.randn(3,4,5)
#求深度方向的均值,想想结果的shape是什么?原始shape是(3,4,5)
#除去axis=2后还剩(3,4)
depth_means = arr_3d.mean(axis=2)
depth_means.shape
> (3, 4)
#arr(3,4,5)和depth_means(3,4)不能直接广播,后缘维度不相符且不存在轴长度为1的轴
#添加广播轴
arr_3d_new = arr_3d - depth_means[:,:,np.newaxis]
arr_3d_new.mean(axis=2)#结果为0
>  array([[ -5.55111512e-17,   4.44089210e-17,   4.44089210e-17,
4.44089210e-17],
[ -8.88178420e-17,  -1.11022302e-16,  -6.66133815e-17,
0.00000000e+00],
[  0.00000000e+00,  -7.77156117e-17,  -2.22044605e-17,
-2.22044605e-17]])


以上就是关于numpy广播机制的一点理解,如果有不对的地方欢迎指正!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  numpy Python