目录
- Numpy数组中数据的抽取
- 1.比较操作
- 2.操作布尔数组
- 统计个数
- 记录True的个数numpy.count_nonzero函数
- 记录True的个数numpy.sum函数
- 快速查阅
- 3.将布尔数组作为掩码
- 4.花哨的索引
- 单纯的花哨索引
- 组合索引
- 花哨索引与普通索引
- 花哨索引与切片
- 花哨索引与掩码结合使用
- 花哨索引举例_随机取点
- 花哨索引举例_修改值
Numpy数组中数据的抽取
前面讲解了Numpy中数组的创建,操作,运算等内容
有些时候我们还想要抽取和查看Numpy数组中符合某些条件的值
还有的时候,我们想要统计数组中有多少值大于某一个给定的值,或则删除所有超过设定的阈值的异常点
因此,本章将讲解如下内容:
- 比较,掩码和布尔逻辑
- 高级索引
1.比较操作
前面讲解的通用函数,主要关注于加减乘除等算数运算符和其他一些运算符实现了数组的逐元素操作
Numpy中还实现了如<(小于)和>(大于)等逐元素比较的通用函数.
比较运算的结果是一个布尔数据类型的数组.
主要的比较操作有以下几种:
运算符 | 对应通用函数 | 作用 |
== | np.equal | 比较是否相等 |
!= | np.not_equal | 比较是否不相等 |
< | np.less | 比较是否小于 |
<= | np.less_equal | 比较是否小于等于 |
> | np.greater | 比较是否大于 |
>= | np.greater_equal | 比较是否大于等于 |
下面讲举几个例子:
array_1=np.arange(6)
print(array_1<3)
print(array_1>3)
print(array_1<=3)
print(array_1>=3)
print(array_1!=3)
print(array_1==3)
>>>
[ True True True False False False]
[False False False False True True]
[ True True True True False False]
[False False False True True True]
[ True True True False True True]
[False False False True False False]
此外,我们还可以将比较运算符和普通运算符进行复合运算
print(array_1**2==2**array_1)
>>>
[False False True False True False]
其实使用这些比较运算符(封装器)的时候,可以直接调用函数
print(np.equal(array_1**2,2**array_1))
>>>
[False False True False True False]
这些比较通用函数还能用于更高维度数组
array_1=np.random.randint(0,10(10,3))
print(array_1<=5)
>>>
[[False True True]
[ True True True]
[False False True]
[ True True False]
[ True True False]
[ True False True]
[ True True True]
[ True False True]
[ True True True]
[False False True]]
2.操作布尔数组
向上面一样,如果我们得到了布尔数组,我们就可以实现很多有用的操作.
下面就将来讲解这些操作:
统计个数
记录True的个数numpy.count_nonzero函数
Numpy.count_nonzero函数主要用于统计一个数组中有多少个True
np.count_nonzero(布尔数组名)
例如:
array_1=np.arange(1,10).reshape(3,3)
print(np.count_nonzero(array_1<3))
>>>
2
记录True的个数numpy.sum函数
由于在Python中,逻辑值False具有数值0,True具有逻辑值1,均可以参与运算.
因此我们可以用numpy.sum函数来计算有多少个True
np.sum(布尔数组名)
此外,如果我们想要求出False的个数,直接用元素个数减去True的个数即可
快速查阅
我们在前面的通用函数中,提到了两个函数np.all和np.any
这两个函数用于查询数组中是否全部的值都是某一个值,或者数组是否具有某一个值
这两个函数的参数都要放入布尔数组,然后根据调用的函数,判断数组中是否有True或者是否全是True
np.any(布尔数组)
np.all(布尔数组)
其实一般我们不会放入布尔数组,而是结合判断运算符,来使用
例如:
array_1=np.arange(10)
print(np.any(array_1==6))
print(np.all(array_1<3))
>>>
True
False
此外,我们还能指定沿特定的坐标轴方向,利用axis参数
array_1=np.random.randint((10,3))
print(array_1<5,axis=0)
print(array_1<5,axis=1)
>>>
[ True True True]
[ True True True True True True True True True True]
其中:
- axis=0表示沿列查询,axis=1表示沿行查询,最后都会得到一个行向量
3.将布尔数组作为掩码
上面我们讲解了如何直接对布尔数组进行聚合计算.
除此以外,我们还能够使用布尔数组作为掩码,通过掩码选择数据的子数据集
掩码用于从数组中抽出特定的数据.
例如:
有一个数组,我们想要从中选出小于5的数字
array_1=np.arange(11)
>>>
[0,1,2,3,4,5,6,7,8,9,10]
我们想要小于5的数字,那么我们可以用另外一个"数组"来结合
[0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10]
[要 ,要 ,要 ,要 ,要 ,不要 ,不要 ,不要 ,不要 ,不要 ,不要]
这样结合之后,我们就能够得到我们需要的数字.
这样,第二个数组就是掩码,用于掩盖我们不需要的数字
Numpy中的布尔数组可以直接用做索引,用做掩码操作
数组[布尔数组]
例如:
array_1=np.arange(10)
print(array_1[array_1<5])
>>>
[0 1 2 3 4]
通过这种方法,最后返回的是所有符合要求的值的一个向量
array_1=np.arange(9).reshape(3,3)
print(array_1[array_1<6])
>>>
[0 1 2 3 4 5]
4.花哨的索引
前面我们曾经讲解过用偏移量来获取单个值或者用切片来获取多个值.
其实Numpy中还支持更加强大的索引操作,称为花哨的索引(fancy indexing)
它支持传入索引数组,因此花哨的索引能够实现更加强大的功能.下面就将来讲解花哨的索引
单纯的花哨索引
花哨的索引其实和普通的索引非常类似,但是他传递的是索引数组而非单个偏移量.
花哨的索引能够让我们快速获得并修改复杂的数组值得子数据集
一般我们如果想要索取一个数组的多个值,可能会用如下的方法:
array_1=np.arange(10)
array_1_sub=[array_1[0],array_1[3],array_1[5]]
print(array_1)
print(array_1_sub)
>>>
[0 1 2 3 4 5 6 7 8 9]
[0, 3, 5]
但是其实我们可以直接向索引中传递数组
array_1=np.arange(10)
index=[0,3,5]
print(array_1)
print(array_1[index])
>>>
[0 1 2 3 4 5 6 7 8 9]
[0, 3, 5]
或者直接传递数组,而非传递数组变量
像这样用传递数组索引来获取值的方法,就叫做花哨的索引.
并且,利用花哨的索引得到的数组形状和索引数组形状一致,而非与被索引数组形状一致
array_1=np.arange(10,20)
index=np.array([[3,7],[4,5]])
print(array_1)
print(array_1[index])
>>>
[10 11 12 13 14 15 16 17 18 19]
[[13 17]
[14 15]]
我们会发现尽管值就是按照索引数组中的偏移量所指定的,但是得到的却是和索引数组形状维度一样的数组
此外,花哨的数组对于多个维度也同样适用
但是这个时候每一个元素都需要多个维度的索引.
例如:
array_1=np.arange(12).reshape((3,4))
index_row=np.array([0,1,2])
index_col=np.array([2,1,3])
print(array_1)
print(array_1[index_row,index_col])
>>>
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[ 2 5 11]
我们会发现,索取到的元素索引分别是:[0,2],[1,1],[2,3]
即,当我们使用两个同形状数组的时候,就会按照对应位置来组合,得到索引
但是我们需要注意的是,通过这种方法来进行组合,必须是一维数组.
例如我们想要索取三维数组的值的话,就必须要三个一维数组来进行组合.
此外,如果数组形状不同的话,还会进行广播
array_1=np.arange(12).reshape((3,4))
index_row=np.array([0,1,2])
index_col=np.array([2,1,3])
print(array_1)
print(array_1[index_row,index_col])
>>>
array_1=np.arange(12).reshape((3,4))
index_row=np.array([0,1,2])
index_col=np.array([2,1,3])
print(array_1)
print(array_1[index_row[:,np.newaxis],index_col])
>>>
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 2 1 3]
[ 6 5 7]
[10 9 11]]
具体发生的事情如下:
#index_col和index_row[:,np.newaxis]发生广播
print(index_col)
print(index_row[:,np.newaxis])
>>>
[2 1 3]
[[0]
[1]
[2]]
#广播之后得到结果是
[[0,2],[0,1],[0,3]
[1,2],[1,1],[1,3]
[2,2],[2,1],[2,3]]
#然后按照位置来进行取值
需要注意的是,最后得到的数组形状和广播之后的数组形状一样
组合索引
花哨的索引可以和其他任何索引方式结合起来,形成更加强大的索引操作
花哨索引与普通索引
我们可以把花哨索引和普通索引结合起来,来获取某一行或者某一列上的元素
其实就是普通索引的广播,注意,形状还是与广播后相同
array_1=np.arange(12).reshape((3,4))
print(array_1)
print(array_1[2,[2,0,1]])
>>>
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[10 8 9]
花哨索引与切片
花哨索引与切片结合起来.可以获取指定行列的全部或者部分
array_1=np.arange(12).reshape((3,4))
print(array_1)
print(array_1[1:,[2,0,1]])
>>>
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 6 4 5]
[10 8 9]]
可以看到,切片操作指定了索引的行,花哨的索引指定选取的列.
花哨索引与掩码结合使用
在创建数组的时候,我们可以指定数据类型为布尔型,这样我们就可以用于掩码
array_1=np.arange(12).reshape((3,4))
mask=np.array([1,0,1,0],dtype=bool)
row=np.array([0,1,2])
print(array_1)
print(array_1[[:,newaxis],mask])
>>>
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 0 2]
[ 4 6]
[ 8 10]]
进行了广播之后得到的数组如下:
>>>
[[0,1],[0,0],[0,1],[0,0]
[1,1],[1,0],[1,1],[1,0]
[2,1],[2,0],[2,1],[2,0]]
其中,每个元素第二个坐标是bool值.
花哨索引举例_随机取点
我们生成正态分布的点列,然后使用matplotlib绘制出图像(在Matplotlib专题里将会讲解matplotlib)
然后使用花哨的索引选取二十个随机的,不重复的点,在图中圈出来
首先观察正态分布的点(这里使用了seaborn库来调整样式)
mean=[0,0]
cov=[[1,2],[2,5]]
x=np.random.multivariate_normal(mean,cov,100)
print(x.shape)
seaborn.set()
plt.scatter(x[:,0],x[:,1])
plt.show()
得到图像如下:
然后使用花哨的索引来取值
indices=np.random.choice(x.shape[0],20,replace=False)
print(indices)
selection=x[indices]
print(selection.shape)
plt.scatter(x[:,0],x[:,1],alpha=0.3)
plt.scatter(selection[:,0],selection[:,1],facecolor='none',edgecolors='b',s=200)
plt.show()
得到图像如下:
花哨索引举例_修改值
我们前面说过,通过索引得到的都是原数组的一个视图,花哨的索引得到的也不例外
我们可以使用花哨的索引得到原数组的视图,然后进行修改
array_1=np.arange(10)
i=np.array([2,1,8,4])
print(array_1)
array_1[i]=99
print(array_1)
>>>
[0 1 2 3 4 5 6 7 8 9]
[ 0 99 99 3 99 5 6 7 99 9]
我们也可以使用其他任何赋值符号,包括自增自减等