广播机制
numpy两个数组的相加、相减以及相乘都是在两个数组对应位置上的元素进行操作。
数组形状:维度数量和每个维度下的大小
x = np.array([[2,2,3],[1,2,3]])
y = np.array([[1,1,3],[2,2,4]])#这两个数组形状(维度和长度)都一致,可以直接对应操作
x*y
[[ 2 2 9]
[ 2 4 12]]
当两个数组的形状(维度数量和同一维度下的大小)并不相同的时候,我们可以通过扩展某一个数组(也可以是两个数组都广播)的方法来实现相加、相减、相乘等操作,这种机制叫做广播
两个数组都广播的情况a(2,1,4 )和b(2,3,1)会广播为a(2,3,4 )和b(2,3,4)
总的来说这三张图片已经能比较好的解释广播的规则了,后面关于广播的概念可以不用看了。
比如,一个二维数组每一列的元素都减去这列平均值,两个数组的形状显然是不一样的,这里就会经过一个对小数组的广播,广播只会对于一个数组进行,而不会两个数组都进行广播。
arr = np.arange(12).reshape((4,3)) #shape(4,3)
arr_mean = arr.mean(0) #shape(3,),被广播成(4,3)
arr - arr_mean
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]])
注:如果是对行求平均值,再相减,必须要做reshape处理,因为这两个数组形状不符合广播的要求,不能直接广播,形状分别是(4,3)(4,)。
广播的原则
广播的原则:广播是把维数少或者一个维度下长度为1的数组进行广播(也有可能两者都存在)。
- 一种是两个数组的维数不相等,但是b的所有维度都和a的尾巴维度的长度一样。
(一方的所有维度都被另一方兼容),(2,3,4)兼容(3,4)兼容(4),但是不兼容(5,4)因为是要全部兼容。
特别注意(2,3,4)不兼容(5,4)更不兼容(3,5) - 维数少的数组的尾巴维度的长度为1。 有一个特点需要介绍,如果某一维度的长度是1,那么可以不费力气的进行广播,体现在广播中,这种维度长度是1的需要在尾巴上,才能广播。
两个原则不是互相独立的,可以结合,效果更好。
原则一体现的是,将数组向外层维度广播,原则二体现的是将数组的同等维度上的延展
举例:
a是(2,3,4)
可以对b广播的情况
b是(3,4) 、(4,)#原则一
b是(2,3,1) #原则二
(3,1) #先使用原则二变成(3,4),再使用原则一
(1,1) #先使用两遍原则二变成(3,4),再使用原则一
(1,1,1) #使用三遍原则二
也就是说形状(1,)的是万能的,这也是为什么ndarray可以直接和int运算
判断流程:
对于上图①、②、③、④的几种举例:
①、
a是(2,3,4)
b是(2,1,4)(2,3,1)(2,1,1)
对于这个情况,会有两个数组都进行广播的情况。
②、
a是(2,3,5)
b是(2,1,4)(9,3,1)
③、
a是(2,3,4)
b是(1,4)(1,)(3,1)
④
a是(2,3,4)
b是(1,7)
特殊情况a(2,1,4)b(2,3,1)可以同时对a和b进行广播
几个广播的图片示例
布尔索引
布尔索引,就是将元素都是布尔值的数组作为索引在另一个数组中取出元素的做法。
如下例,booling1为布尔数组,下标为0,3,4的位置是True,因此将会取出目标数组arr中对应位置的元素。
In [24]: arr = np.arange(7)
In [25]: booling1 = np.array([True,False,False,True,True,False,False])
In [26]: arr[booling1]
Out[26]: array([0, 3, 4])
如果布尔数组和目标数组的维度不匹配,例如布尔数组booling1是一维的,目标数组arr是二维的,那么二者作用,就会先作用到最外层,也就是列轴,在这个刻度上取,就是取出行了,实际上这是一种广播。
In [27]: arr = np.arange(28).reshape((7,4))
In [28]: arr
Out[28]:
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],
[24, 25, 26, 27]])
----------------------------------------------
In [29]: booling1 = np.array([True,False,False,True,True,False,False])
In [30]: arr[booling1]
Out[30]:
array([[ 0, 1, 2, 3],
[12, 13, 14, 15],
[16, 17, 18, 19]])
上面说到的是如何利用布尔数组,下面将介绍如何生成布尔数组,一般情况就是使用原生数组做判断。
In [35]: names = np.array(['Ben','Tom','Ben','Jeremy','Jason','Michael','Ben'])
In [36]: names == 'Ben' #生成布尔数组
Out[36]: array([ True, False, True, False, False, False, True], dtype=bool)
也可以连起来使用
In [37]: arr[names == 'Ben']
Out[37]:
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11],
[24, 25, 26, 27]])
还可以搭配切片使用,这是当布尔数组的维数少于目标数组的时候,使用切片来限定剩余的维度。
In [38]: arr[names == 'Ben',3]#
Out[38]: array([ 3, 11, 27])
In [39]: arr[names == 'Ben',1:4]#
Out[39]:
array([[ 1, 2, 3],
[ 9, 10, 11],
[25, 26, 27]])
多个筛选条件的使用
一些筛选机巧:
(1)当然上面的例子都是用name == "Ben"也可以使用name != "Ben"来剔除。
(2)使用其他的逻辑运算符
- ==、!=、>、>=
(3)需要注意的一点是,在生成布尔数组的时候,如果使用一维数组做判断操作,生成的布尔数组就是一维的,同理,二维的产生二维的,在上面的例子中,判断的数组就是一个简单的姓名组成一位数组,目标数组是另外的一个大数组,当然也可以使用大数组的某一列或某一行作为判断数组。
(4)使用与、或(&和|)来组合多个条件
arr[(names == 'Jason') | (names == 'Tom')]
arr[(names == 'Jason') & (names == 'Tom')]#当然这在逻辑上没有意义。
切片索引
切片索引是普通索引,形式是n[ :,2:6:-1,: ]在这里逗号之间分割的是切片,如果是整数列表,那就是花式索引了,这二者是截然不同的。
切片索引的逻辑比较普通,不详细介绍,下面通过其与花式索引之间的区别简单介绍。
花式索引
花式索引主要的区别就是解决普通索引的切片无法解决的不规则的制定元素。普通索引取出来的是方方正正的元素,但是花式索引能够以点的形式在多维数组中取出多个元素。
a = np.arange(36).reshape(3,3,4)
print(a)
print("普通索引")
print(a[0:2,0:2,0:2])
print("花式索引")
print(a[[0,1],[0,1],[0,1]])
#花式索引取出的是[0,0,0]和[1,1,1]两个元素
#但是普通索引取出的是一个立方块
结果
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]
[[24 25 26 27]
[28 29 30 31]
[32 33 34 35]]]
普通索引
[[[ 0 1]
[ 4 5]]
[[12 13]
[16 17]]]
花式索引
[ 0 17]
通过上面例子的示范,感觉花式索引取元素的形式就像是map函数构造坐标点,然后取出一样。
花式索引比较适合于要取的元素比较分散(上例是分散在两块中)。
再有一点,是关于多个中括号和一个中括号的普通索引的区别
1、
花式索引是指利用整数数组进行索引,这里的整数数组可以是Numpy数组可以是Python中列表、元组等可迭代类型,一般数组有几个维度,就会有几个整数数组作为参数,如果不过的话就会广播。
2、
花式索引根据索引整型数组的值作为目标数组的某个轴的下标来取值。这里提到的是整数数组,而不是整数,这一点的区别会在后续介绍。
3、
花式索引完全可以被其它的索引方式所替代,但是花式索引擅长一些不规则的索引,虽然这也可以被其他索引来实现。
4、
"""假设a是一个二维数组"""
a = np.array([
[1,10,1],
[2,20,2],
[3,30,3],
[2,40,4]
])
"""取一个值"""
a[1,3] 2
a[1][3] 2
a[[1],[3]] [2]
"""取分散在不同地方的多个值"""
#只能使用花式索引,会简单一些
"""取呈规则矩形的多个值"""
#这种使用切片的普通索引更占优势
"""没有考虑取正行的,其实也是一个道理"""
有关不能直接修改数组,也就是返回拷贝