Numpy基础之 广播(broadcasting)

数组很重要,因为它使你不用编写循环即可对数据执行批量运算。这通常就叫做矢量化(vectorization)。

  • 大小相等的数组之间的任何算术运算都会将运算应用到元素级:
In [81]: arr=np.array([[1,2],[3,4.]])

In [82]: arr*arr
Out[82]:
array([[  1.,   4.],
       [  9.,  16.]])

In [83]: arr-arr
Out[83]:
array([[ 0.,  0.],
       [ 0.,  0.]])
  • 不同大小的数组之间的运算叫做广播(broadcasting).如,数组和标量的算术运算会将标量值传播到各个元素:

    In [84]: 1/arr
    Out[84]:
    array([[ 1.        ,  0.5       ],
           [ 0.33333333,  0.25      ]])
    
    In [85]: arr**0.5
    Out[85]:
    array([[ 1.        ,  1.41421356],
           [ 1.73205081,  2.        ]])

广播的的原则:

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

通俗点说就是较小数组的“广播维”必须为1.

我们先来看下在二维数组上的广播。arr的shape为(4,3),arr.mean(0)(ar.mean(index)可以简单理解为对arr进行扁平化处理,如shape为(m,n,l,...)index为1处理完后shape为(m,l,...))的shape为(3,),符合广播原则,运行如下:

>>> import numpy as np
>>> arr=np.arange(12).reshape((4,3))
>>> arr
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
>>> arr.mean(0)
array([4.5, 5.5, 6.5])
>>> arr.mean(0).shape
(3,)
>>> demeaned = arr-arr.mean(0)
>>> demeaned
array([[-4.5, -4.5, -4.5],
       [-1.5, -1.5, -1.5],
       [ 1.5,  1.5,  1.5],
       [ 4.5,  4.5,  4.5]])

还是二维,接上面的例子,我们对1轴进行降维,得到shape为(4,)的数组,后缘维度不为3且不为1,故不符合广播规则,你将会得到如下错误:

>>> arr.mean(1)
array([ 1.,  4.,  7., 10.])
>>> arr.mean(1).shape
(4,)
>>> arr-arr.mean(1)
Traceback (most recent call last):
  File "", line 1, in 
ValueError: operands could not be broadcast together with shapes (4,3) (4,) 
>>> 

shape(4,)转成(4,1)就可以广播了,如下:

>>> arr-arr.mean(1).reshape((4,1))
array([[-1.,  0.,  1.],
       [-1.,  0.,  1.],
       [-1.,  0.,  1.],
       [-1.,  0.,  1.]])

多维数组跟这类似,以三维举例。对于shape为(2,3,4)和shape为(2,4)的两个数组,我们需要把shape(2,4)转成shape(2,1,4)才符合广播规则,这里给出两种方法来转换,一是用reshape方法,二是用通过特殊的np.newaxis属性

>>> arr= np.arange(24).reshape((2,3,4))
>>> arr
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>> arr.mean(1)
array([[ 4.,  5.,  6.,  7.],
       [16., 17., 18., 19.]])
>>> arr1= arr.mean(1).reshape((2,1,4))
>>> arr2= arr.mean(1)[:,np.newaxis,:]
>>> arr-arr1
array([[[-4., -4., -4., -4.],
        [ 0.,  0.,  0.,  0.],
        [ 4.,  4.,  4.,  4.]],

       [[-4., -4., -4., -4.],
        [ 0.,  0.,  0.,  0.],
        [ 4.,  4.,  4.,  4.]]])
>>> arr-arr2
array([[[-4., -4., -4., -4.],
        [ 0.,  0.,  0.,  0.],
        [ 4.,  4.,  4.,  4.]],

       [[-4., -4., -4., -4.],
        [ 0.,  0.,  0.,  0.],
        [ 4.,  4.,  4.,  4.]]])

对于多维数组来讲,有没有一种既通用又不牺牲性能的方法呢?实际上是有的,但是需要一些索引的技巧,这里用到了slice类。slice接受三个参数,分别是开始索引,结束索引,和步长。

>>> def demeaned_axis(arr,axis=0):
...     means=arr.mean(axis)
...     indexer = [slice(None)]*arr.ndim
...     indexer[axis] =np.newaxis
...     return arr-means[indexer]
... 
>>> demeaned_axis(arr,1)
array([[[-4., -4., -4., -4.],
        [ 0.,  0.,  0.,  0.],
        [ 4.,  4.,  4.,  4.]],

       [[-4., -4., -4., -4.],
        [ 0.,  0.,  0.,  0.],
        [ 4.,  4.,  4.,  4.]]])

广播同样适用于设置数组值的操作。arr还是之前的三维数组:

>>> arr[:1] = np.array([0,0,0])[:,np.newaxis]
>>> arr
array([[[ 0,  0,  0,  0],
        [ 0,  0,  0,  0],
        [ 0,  0,  0,  0]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])


你可能感兴趣的:(numpy)