美文网首页【完】Numpy学习笔记
【numpy笔记_5】数组的算数运算与广播机制

【numpy笔记_5】数组的算数运算与广播机制

作者: fakeProgramer | 来源:发表于2023-02-25 00:19 被阅读0次

    算数运算

    与常规的list对象不同,numpy支持把整个数组带入算数运算。

    之前提过,array对象往往要求所有元素保持统一的数据类型,因此numpy的运算能以数组为单位,而不用将元素提出来。
    这也是numpy能够胜任高效运算的原因之一。

    来看几个例子:

    import numpy as np
    arr = np.arange(1,10).reshape(3, 3)
    array_1 = arr > 7   # 条件比较,返回bool
    array_2 = arr * 0.3   # 加减乘除运算
    
    arrs = np.arange(11,20).reshape(3,3)
    array_3 = arrs / arr   # 数组间的运算
    
    print(array_1)
    print('* '*20)
    print(array_2)
    print('* '*20)
    print(array_3)
    # 运行结果:
    [[False False False]
     [False False False]
     [False  True  True]]
    * * * * * * * * * * * * * * * * * * * * 
    [[0.3 0.6 0.9]
     [1.2 1.5 1.8]
     [2.1 2.4 2.7]]
    * * * * * * * * * * * * * * * * * * * * 
    [[11.          6.          4.33333333]
     [ 3.5         3.          2.66666667]
     [ 2.42857143  2.25        2.11111111]]
    

    可以看到,numpy甚至支持两个数组对象的算数运算。我们因此得到结论:
    在numpy中,参与算术运算的单位可以是数组(“可以”不是“只能”)。

    数组和数字的计算没什么好说的。
    而数组与数组的运算,我们发现上面的例子中,两个运算的数组结构相同。这很好理解,两组的元素呈一一对应关系,这是映射

    那如果一对结构不同的数组呢?这就是我们要讲的广播机制


    广播机制

    “广播”,顾名思义有传递、向外扩散的意思。

    numpy的广播机制也是如此,指两个不同shape的数组,在它们的shape满足一定条件时,可进行广播运算。这时,两个数组的元素按照一定规则扩散开来运算,并得到一个新的数组。

    借用经典的描述就是,numpy中的广播机制旨在解决不同形状数组之间的算数运算问题
    我们来看数组运算的几个例子,观察广播机制是如何运作的:

    import numpy as np
    arr_2d_3x3 = np.arange(1,10).reshape(3, 3)   # 3*3
    arr_1d_3 = np.arange(1,4)   # 3个元素的一维数组
    arr_1d_4 = np.arange(1,5)   # 4个元素
    
    array_jia = arr_2d_3x3 + arr_1d_3   # 3*3 与 3个元素 运算(拿拼音代替了..)
    array_jian = arr_2d_3x3 - arr_1d_4   # 3*3 与 4个元素 运算
    
    print(arr_2d_3x3)
    print('- '*5)
    print(arr_1d_3)
    print(arr_1d_4)
    print('* '*20)
    print(array_jia)
    print('* '*20)
    print(array_jian)
    # 运行结果:
    [[1 2 3]
     [4 5 6]
     [7 8 9]]   # arr_2d_3x3
    - - - - - 
    [1 2 3]   # arr_1d_3
    [1 2 3 4]   # arr_1d_4
    * * * * * * * * * * * * * * * * * * * * 
    [[ 2  4  6]
     [ 5  7  9]
     [ 8 10 12]]   # array_jia,3*3 与 3个元素 运算
    * * * * * * * * * * * * * * * * * * * * 
    ValueError: operands could not be broadcast together with shapes (3,3) (4,)    # array_jian,3*3 与 4个元素 运算
    

    可以看到,两个数组在列数(个数)相同、行数(长短)不同时,“较短”的数组对“较长”的数组向下做了传播式的运算,这便是"广播机制"叫法的由来。而列数不同的数组就报错了,便不能广播。
    到这还没完,再看一组例子:

    import numpy as np
    arr_2d_3x3 = np.arange(1,10).reshape(3, 3)   # 3*3
    arr_2d_1x3 = np.arange(1,4).reshape(1, 3)   # 1*3
    arr_2d_3x1 = np.arange(1,4).reshape(3, 1)   # 3*1
    array_jia = arr_2d_3x3 + arr_2d_1x3   # 3*3 与 1*3 运算
    array_jian = arr_2d_3x3 - arr_2d_3x1   # 3*3 与 3*1 运算
    
    print(arr_2d_3x3)
    print('- '*5)
    print(arr_2d_1x3)
    print('- '*5)
    print(arr_2d_3x1)
    print('* '*20)
    print(array_jia)
    print('* '*20)
    print(array_jian)
    # 运行结果:
    [[1 2 3]
     [4 5 6]
     [7 8 9]]   # 3*3
    - - - - - 
    [[1 2 3]]   # 1*3
    - - - - - 
    [[1]
     [2]
     [3]]   # 3*1
    * * * * * * * * * * * * * * * * * * * * 
    [[ 2  4  6]
     [ 5  7  9]
     [ 8 10 12]]   # 3*3 与 1*3 运算
    * * * * * * * * * * * * * * * * * * * * 
    [[0 1 2]
     [2 3 4]
     [4 5 6]]   # 3*3 与 3*1 运算
    

    arr_2d_3x3 = np.arange(1,10).reshape(3, 3)   # 3*3
    arr_2d_1x3 = np.arange(1,7).reshape(2, 3)   # 2*3
    array_jia = arr_2d_3x3 + arr_2d_1x3   # 3*3 与 2*3 运算
    print(array_jia)
    # 运行结果:
    ValueError: operands could not be broadcast together with shapes (3,3) (2,3) 
    

    通过上面第一个例子,很明显numpy在对两个方向不一致的数组进行运算时,基于“较短”数组的shape,选择运算规则是向行扩散还是列扩散。
    用大白话说就是,相对于大数组,小数组行、列的饱和度。即:

    • 列饱和、行缺失则广播行补充;
    • 行饱和、列缺失则广播列补充。

    无论如何,只要行/列有一个饱和的情况下,就将不饱和的地方广播至饱和

    而第二个例子可能你也感觉到了,3x3 和 2x3 这种形状好像根本没法广播,因为小数组拿来广播的元素是不唯一的。

    是的,就是这么好理解。

    可能你想问,那3d及以上的数组怎么广播呢?

    其实,我们已经知道高维数组(体)由多个2维数组(面)组成。
    所以你可能知道了,无非就是对多个“面”逐一广播而已。
    就像这样:

    import numpy as np
    arr_2d_3x3 = np.arange(1,13).reshape(2, 3, 2)   # 3*3
    arr_2d_3x1 = np.arange(1,4).reshape(3,1)
    array_jia = arr_2d_3x3 + arr_2d_3x1   # 3*3 与 3*1 运算
    
    print(arr_2d_3x3)
    print('- '*5)
    print(arr_2d_3x1)
    print('* '*20)
    print(array_jia)
    # 运行结果:
    [[[ 1  2]
      [ 3  4]
      [ 5  6]]
    
     [[ 7  8]
      [ 9 10]
      [11 12]]]
    - - - - - 
    [[1]
     [2]
     [3]]   # 这是多行一列,一行多列就不举例了
    * * * * * * * * * * * * * * * * * * * * 
    [[[ 2  3]
      [ 5  6]
      [ 8  9]]   # 先广播第一块
    
     [[ 8  9]
      [11 12]
      [14 15]]]   # 再广播第二块
    

    最后,我们总结一下广播机制:

    • 广播机制,指两个数组间的推广运算;
    • 广播对shape有一定要求。相对于大数组,小数组的行/列必须有一处饱和,且另一处唯一。即shape(1, n)shape(n,1)
    • array对象和数字直接运算(object + 1),就是numpy广播机制的体现。
    • 用于广播的小数组,必须是单个数字或二维(行、列)。
    • 广播的本质是一种算数运算,适用于加减乘除及其他所有的运算方式。


      广播机制.png

    相关文章

      网友评论

        本文标题:【numpy笔记_5】数组的算数运算与广播机制

        本文链接:https://www.haomeiwen.com/subject/fksxldtx.html