我们了解了如何使用索引进行切片以及选择 ndarray 元素。当我们知道要选择的元素的确切索引时,这些方法很有用。但是,在很多情况下,我们不知道要选择的元素的索引。例如,假设有一个 10,000 x 10,000 ndarray,其中包含从 1 到 15,000 的随机整数,我们只想选择小于 20 的整数。这时候就要用到布尔型索引。
来看这样一个例子,假设我们有一个用于存储数据的数组以及一个存储姓名的数组(含有重复项)。在这里,我将使用numpy.random中的randn函数生成一些正态分布的随机数据:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data =np.random.randn(7,4)
data
out
array([[-0.53447574, 0.44543995, 0.20598584, 1.08783591],
[-0.77863291, 0.19012144, -0.57628278, 2.21230602],
[-1.94785822, 1.75829325, 0.76416317, -0.26749686],
[ 1.55128542, -0.48229173, -0.10775036, 1.82210628],
[-0.75344013, 1.49328631, 0.19576227, -1.37773471],
[-0.28998065, -0.54219643, 0.44587161, -1.17333163],
[ 2.28372601, 0.9102604 , -0.15811983, 1.16763758]])
假设每个名字都对应data数组中的一行,而我们想要选出对应于名字"Bob"的所有行。跟算术运算一样,数组的比较运算(如==)也是矢量化的。因此,对names和字符串"Bob"的比较运算将会产生一个布尔型数组:
names == "Bob"
out
array([ True, False, False, True, False, False, False])
********************************************************
data[names =='Bob'] #True为选择,False为不选择
out
array([[7., 7., 7., 7.],
[7., 7., 7., 7.]])
布尔型数组的长度必须跟被索引的轴长度一致。此外,还可以将布尔型数组跟切片、整数(或整数序列,稍后将对此进行详细讲解)混合使用:
data[names == 'Bob']
out
array([[-1.33599022, 0.04700321, 0.98537189, 0.33183721],
[-0.2225323 , 0.10320653, 1.24636372, -0.38496333]])
注意:如果布尔型数组的长度不对,布尔型选择就会出错,因此一定要小心。
下面的例子,我选取了names == 'Bob'
的行,并索引了列:
data[names == 'Bob', 2:]
out
array([[ 0.98537189, 0.33183721],
[ 1.24636372, -0.38496333]])
********************************************************
data[names == 'Bob', 3]
out
要选择除"Bob"以外的其他值,既可以使用不等于符号(!=),也可以通过~对条件进行否定:
names != "Bob"
out
array([False, True, True, False, True, True, True])
********************************************************
data[~(names == 'Bob')]
out
array([[ 0.04813526, 0.481971 , -0.23318465, -0.08770203],
[ 2.39572745, -0.03834795, -0.49294046, -0.76124253],
[-0.30962763, 0.3454903 , 0.30120547, 0.82999187],
[-1.46354427, -1.00491798, 0.4574028 , 1.26923957],
[ 0.46142634, 1.55466835, -0.54772161, -0.65769753]])
~操作符用来反转条件很好用
cond = names == 'Bob'
data[~cond]
out
array([[ 0.04813526, 0.481971 , -0.23318465, -0.08770203],
[ 2.39572745, -0.03834795, -0.49294046, -0.76124253],
[-0.30962763, 0.3454903 , 0.30120547, 0.82999187],
[-1.46354427, -1.00491798, 0.4574028 , 1.26923957],
[ 0.46142634, 1.55466835, -0.54772161, -0.65769753]])
选取这三个名字中的两个需要组合应用多个布尔条件,使用&(和)、|(或)之类的布尔算术运算符即可:
mask = (names == "Bob") | (names == "Will" )
mask
out
array([ True, False, True, True, True, False, False])
********************************************************
data[mask]
out
array([[-1.33599022, 0.04700321, 0.98537189, 0.33183721],
[ 2.39572745, -0.03834795, -0.49294046, -0.76124253],
[-0.2225323 , 0.10320653, 1.24636372, -0.38496333],
[-0.30962763, 0.3454903 , 0.30120547, 0.82999187]])
通过布尔型索引选取数组中的数据,将总是创建数据的副本,即使返回一模一样的数组也是如此。
注意:Python关键字and和or在布尔型数组中无效。要使用&与|。
通过布尔型数组设置值是一种经常用到的手段。为了将data中的所有负值都设置为0,我们只需:
data[data < 0] = 0
data
out
array([[0. , 0.04700321, 0.98537189, 0.33183721],
[0.04813526, 0.481971 , 0. , 0. ],
[2.39572745, 0. , 0. , 0. ],
[0. , 0.10320653, 1.24636372, 0. ],
[0. , 0.3454903 , 0.30120547, 0.82999187],
[0. , 0. , 0.4574028 , 1.26923957],
[0.46142634, 1.55466835, 0. , 0. ]])
通过一维布尔数组设置整行或列的值也很简单:
data[names != 'Joe'] = 7
data
out
array([[7. , 7. , 7. , 7. ],
[0.04813526, 0.481971 , 0. , 0. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[0. , 0. , 0.4574028 , 1.26923957],
[0.46142634, 1.55466835, 0. , 0. ]])
后面会看到,这类二维数据的操作也可以用pandas方便的来做。