草庐IT

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

fakeProgramer 2023-10-17 原文

算数运算

与常规的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】数组的算数运算与广播机制的更多相关文章

  1. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  2. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  3. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  4. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  5. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  6. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  7. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  8. ruby - 在 Ruby 中用键盘诅咒数组浏览 - 2

    我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作

  9. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  10. ruby - 使用多个数组创建计数 - 2

    我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

随机推荐