在对高维数组进行索引时,常会不知从何而起,想提取的部分和实际的效果会出现偏差。
本文将以尽可能容易懂的方式,来揭开Numpy高维数组之间的运算逻辑,并呈现一些简单运用
目录
情况一:只含一个值的一维数组M和另一个任意长度,元素为任意值的一维数组N:
3、高维数组(只要两个数组里有一个是二维以上,就满足该条件)
一、文章用词阐释
此处先解释一下文章中可能会提到的两个名词
1、轴长度和shape
轴长度:下一级的“元素”的个数,如果一个三维数组里有10个二维数组,那么三维的这个维度(由于Numpy中维度和轴是等效的,也可以说成三维的这个轴)的轴长度就是10
ndarray对象的shape属性(反映数组的维度)——以下面两个例子来说明
如果一个数组的shape是(10,10,2),其中参数从左到右的意义是:
在一个三维数组中:有10(左边第一个10)个二维数组,每一个二维数组中有10个一维数组,每个一维数组里有2个元素
我们进一步推广一下,对于一个shape为(A1,A2,…An)的n维数组
他有A1个 (n-1)维数组,每一个(n-1)维数组里有A2个(n-2)维数组……每一个二维数组里有An-1个一维数组,每个一维数组里有An个元素
2、广播
广播可以看作是两个数组进行二元运算所遵循的一套“对应原则”,它是NumPy的一种向量化操作的支撑。
当你在对两个数组进行运算比较,或者你同时引入两个数组参与花式索引时,广播规则在发生作用
二、剖析广播的规则
1、单一标量A(可认为是零维的数组)与一维数组B
A将会被扩展成与B等长的一维数组A''(但实际在内存中并未发生,这样说是为了便于理解)
然后这个想象出来的、被扩展的、里面全是相同值的 A" 将与B进行元素的一一对应
import numpy as np
A=5
B=np.array([4,8,63,2])
B+A # B中每一个元素与A中相同位置的元素相加 (减乘除)
B>A # B中每一个元素与A中相同位置的元素比较大小(大于小于等于)(返回一个布尔数组)
2、一维数组与一维数组
情况一:只含一个值的一维数组M和另一个任意长度,元素为任意值的一维数组N:
等同于单一标量A与一个一维数组B
情况二:两个一维数组数组,长度均不为1,且长度不相等
无法进行广播!即使某个数组里的值全相同,你也别指望编译器会把它自动填充
import numpy as np
row=np.array([0,1,2])
col=np.array([2,2])
print(row+col)
#这行不通!当然如果你把col改成[2,3]也行不通
#你将会看到 “could not be broadcast together with shapes (3,) (2,)” 这样的字段
情况三:两个一维数组,长度一样
从左到右挨个对应进行各种操作,你的第一个和我的第一个……
3、高维数组(只要两个数组里有一个是二维以上,就满足该条件)
广播最重要的逻辑在这里展开,核心是扩展成shape相同的两个高维数组
温馨提示:仍然会有无法广播的情况!
三条基本规则
1、如果两个数组的维度数不相同,那么小维度数组的形状将会在最左边补1
阐释:shape中逗号间隔开了几个数字,就是几维,所以数字个数少的就是低维
举例:A的shape(3,2,4) ,B的shape(2,1),A三维,B二维,B是小维度
>>>那么为了让shape在“维度数上一致” ,B的形状将变成(1,2,1),在最左边补1即是这个意思
2、在维度数相同了之后,在对应的维度上,如果一个数组在该维度上为1,那它在这个维度上应当扩展成另一个数组在该维度上的值
阐释:对应的维度上:两个数组shape属性中的同一位置的参数
以第一条规则举的例子(我在这里用颜色标记),(3,2,4)与(1,2,1)
在shape中的第一个参数,A是3,B是1,那么B的这个1就会扩展成3
第二个参数,A的2与B的2匹配,无需扩展
第三个参数,A是4,B是1,B的这个1会扩展成4
细心的你一定发现了:shape的值在该维度上为1才能够扩展,不是一的就不动它
那你一定会想:如果在某个维度上A是2,B是3,那广播之后是2,是 3,还是6呢
3、在进行了前两个步骤之后,只要两个数组的shape在某一个维度上,不相同,且都不为一,那这两个数组就无法进行广播!
(2,3)和(2,2)不行;(3,2,1)和(2,2,1)不行————不同且无一,是会引发大异常的
三、花哨索引——用索引数组来一次性获得多个数组元素
1、用前须知
花哨索引(Fancy Indexing)结果的shape,将于索引数组的shape一致
之所以这么说是因为你传入的“筛选标准”——索引数组,有可能也会发生广播。
2、不广播,但与索引数组的形状一致
import numpy as np
x=rand.randint(100,size=10)#随机生成一个长度为10的数组
print(ind=[3,7,4])#一个索引数组
x[ind]#效果相当于x[ind[0]],x[ind[1]],x[ind[2]]
#当然如果抽象化ind,你可以理解成把ind理解成“下标的数组”,会一次性提取完
ind_1=np.array([[3,7],[4,5]])
print(x[ind])#你得到的将会和ind_1长得结构是一致的
#第一行是x中的第4个和第8个,第二行是x中的第5个和第6个
3、紧急插播,广播与否其实取决于索引数组的来源
对一个一维数组,你只能给他一个索引数组,但这个索引数组可以是二维,可以是一维
随便几维(前提是你的索引值需要在下标范围内)
如果这个索引数组是广播而来的——那你自然需要广播(好似废话)
比如两个数组进行二元操作后得到了一个索引操作,二元操作中就极会涉及广播
4、多维度数组的花哨索引
import numpy as np
#In[1]
X=np.arange(12).reshape((3,4))
row=np.array([0,1,2])
col=np.array([2,1,3])
print("X数列的row,col",X[row,col]);
'''注意到,两个一维数组,分别作为索引的两个,是按照广播规则一一对应的'''
#In[2]
print("把row给升维,X[row[:,np.newaxis],col]是\n",X[row[:,np.newaxis],col])
'''
row会变成[[0],[1],[2]],shape为(3,1),而col的shape为(3,),会广播先统一col和row
第一步,把col的shape变成(1,3)>>> col 变为 [[2,1,3]]
第二步,因为两个shape在这两个维度上都存在1,所以可以参与扩展
row从(3,1)到(3,3)>>> [ [0,0,0]
[1,1,1]
[2,2,2] ]沿1这个维度来进行复制
col从(1,3)到(3,3)>>> [[2,1,3]
,[2,1,3]
,[2,1,3]]
挨个对应,你的[0,0]位置和我的[0,0]位置,组成一个索引的坐标对
值得一提的是,扩展其实就是对最原始数组的复制而达成的升维
再挨个对应
还需要注意的是shape里的第一个数字i永远是代表n维里有i个(n-1)维数组
花式索引得到的值反映的是————索引数组广播后的形状!!!
'''