本系列是《NumPy 速通》的配套章节。
数据处理的日常:把满足条件的元素挑出来。NumPy 用布尔数组做索引,非常优雅,而且返回副本(不会污染原数据)。本章讲透:基本筛选、组合条件、二维行筛选、np.where、布尔统计。
基本筛选
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
arr[arr > 3] # [4 5] 取出所有大于 3 的元素
拆开看,它分两步:
mask = arr > 3 # [False False False True True] 布尔数组
arr[mask] # [4 5] 只取 True 位置的元素
💡 返回的是副本:修改筛选结果不会影响原数组(和第 3 章花式索引一致)。比切片安全得多。
组合条件:& | ~
用 &(与)、|(或)、~(非),每个条件都要加括号:
arr[(arr > 2) & (arr < 5)] # [3 4] 大于 2 且小于 5
arr[(arr < 2) | (arr > 4)] # [1 5] 小于 2 或大于 4
arr[~(arr == 3)] # [1 2 4 5] 不等于 3
⚠️ 灵魂警告:
- 不能用 Python 的
and/or(它们只认单个布尔值,不认数组)——必须用&/|/~(逐元素位运算) - 每个条件必须加括号:
(arr > 2) & (arr < 5)。写成arr > 2 & arr < 5会报错或算错(运算符优先级问题) - 常见报错
The truth value of an array with more than one element is ambiguous就是忘了这些导致的
二维数组:筛选行 / 列
data = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 按某列条件筛行:第一列大于 3 的行
rows = data[data[:, 0] > 3]
# [[4 5 6]
# [7 8 9]]
# 按行条件筛列:每行第一个元素大于 2 的列?不,是取列
data[:, data[0] > 2] # 第一行大于 2 的列 → 第 2、3 列
# [[2 3]
# [5 6]
# [8 9]]
口诀:data[行条件, :] 筛行;data[:, 列条件] 筛列。条件是布尔数组时,它充当对应维度的"开关列表"。
实战:沿用成绩表
scores = np.array([[90, 80, 85], # 语 数 英
[60, 70, 65],
[95, 88, 92],
[72, 65, 78],
[55, 60, 70]])
# 语文 >= 90 的同学
scores[scores[:, 0] >= 90]
# [[90 80 85]
# [95 88 92]]
# 平均分 >= 70 的同学
means = scores.mean(axis=1) # [85 65 91.67 71.67 61.67]
scores[means >= 70]
# [[90 80 85]
# [95 88 92]
# [72 65 78]]
先算每行统计量,再用布尔索引筛行——两步组合是数据分析最常用的套路。
np.where —— 条件替换
想"满足条件的地方换成 A,否则换成 B":
arr = np.array([1, 2, 3, 4, 5])
np.where(arr > 3, 100, 0) # [0 0 0 100 100] 大于 3 的换成 100
np.where(arr > 3, arr, -1) # [-1 -1 -1 4 5] 满足的保留原值
np.where(条件, 满足时, 不满足时),三个参数。两个分支都可以是数组(广播),因此极灵活:
# 把不及格的成绩替换成 0
np.where(scores < 60, 0, scores)
# [[90 80 85]
# [60 70 65]
# [95 88 92]
# [72 65 78]
# [55 60 70]] → 55 变 0(其实这里没有 <60 的)
# 布尔数组当"开关"
np.where(np.isnan(data), 0, data) # 把缺失值替换成 0
💡
np.where只传一个参数np.where(cond)时,等价于cond.nonzero(),返回满足条件的位置索引。二维时返回(行索引数组, 列索引数组)。
布尔统计:True 当 1 算
arr = np.array([1, 2, 3, 4, 5])
(arr > 3).sum() # 2 有多少个元素大于 3
np.mean(arr > 3) # 0.4 占比 40%
这是统计"符合条件比例"的经典写法,不用写任何循环:
# 及格率
np.mean(scores >= 60) # 总及格率 100%?不对,仔细看全是 >=60
np.mean(scores >= 90) # 优秀率:90 分以上占比
# 每列及格率
np.mean(scores >= 60, axis=0) # [1. 1. 1.]
其它实用筛选函数
np.nonzero(arr > 3) # (array([3, 4]),) 满足条件的位置
np.extract(arr > 3, arr) # [4 5] 等价 arr[arr > 3]
arr[arr > 3] # 最常用,直接布尔索引
小结
arr[mask]返回副本,安全- 组合条件用
&|~,每个条件加括号,别用and/or data[行条件]筛行、data[:, 列条件]筛列np.where(条件, A, B)条件替换/处理缺失值(cond).sum()/np.mean(cond)统计数量与占比
下一章:把多个数组合并、或把一个数组拆开。
上一篇:第 8 章 广播机制 | 下一篇:第 10 章 数组拼接与分割
上一篇:第 8 章 广播机制 | 下一篇:第 10 章 数组拼接与分割