本节介绍通过表达式、条件替换与抽样完成数据选择与过滤的常用工具。
1. query()
query() 使用字符串表达式过滤 DataFrame 行,语法接近自然语言,底层经 NumExpr 加速,性能优秀。
基本语法
df.query('表达式')常用操作
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 40],
'城市': ['北京', '上海', '北京', '广州']
})
# 单条件
df.query('年龄 > 30')
df.query('城市 == "北京"') # 字符串用引号
# 多条件(用 and / or / not)
df.query('年龄 > 30 and 城市 == "北京"')
df.query('年龄 > 30 or 城市 == "上海"')
df.query('not 城市 == "北京"')
# 比较运算符
df.query('年龄 >= 35')
df.query('年龄 != 25')
# in / not in
df.query('城市 in ["北京", "上海"]')
df.query('城市 not in ["北京"]')
# 表达式运算
df.query('年龄 * 2 > 60')
df.query('年龄 + 5 < 40')
# 列名含空格或非标识符:使用反引号
df2 = df.rename(columns={'城市': '所在 城市'})
df2.query('`所在 城市` == "北京"')外部变量引用
使用 @ 前缀引用 Python 变量:
min_age = 28
target_cities = ['北京', '上海']
df.query('年龄 > @min_age')
df.query('城市 in @target_cities')query() 与布尔索引对比
| 方式 | 写法 | 可读性 | 性能 |
|---|---|---|---|
| 布尔索引 | df[(df['年龄'] > 30) & (df['城市'] == '北京')] | 冗长 | 一般 |
| query | df.query('年龄 > 30 and 城市 == "北京"') | 简洁 | 更快(NumExpr) |
query 优点
- 列名直接在表达式中引用,无需重复写
df['col']- 底层使用 NumExpr 加速,大数据集上优势明显
- 特别适合交互式探索
query 限制
- 不能直接调用 Python 内置函数(需通过
@传入)- 普通索引名不能直接引用,MultiIndex 层级名可用
- 表达式中的字符串必须用引号包裹
2. eval()
eval() 使用字符串表达式对 DataFrame 进行运算和列赋值。
基础用法
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
# 计算新列(返回新对象)
df.eval('C = A + B')
# A B C
# 0 1 4 5
# 1 2 5 7
# 2 3 6 9
# 原地修改
df.eval('C = A + B', inplace=True)
# 多表达式(用分号分隔)
df.eval('D = A - B; E = A * B', inplace=True)表达式语法
# 算术运算
df.eval('C = A ** 2 + B / 2')
# 比较运算返回布尔列
df.eval('F = A > B')
# 引用外部变量
x = 10
df.eval('G = A + @x') # 使用 @ 前缀
# 调用自定义函数
def my_func(v): return v * 2
df.eval('H = A.apply(@my_func)')eval() 与 query() 的关系
| 函数 | 用途 |
|---|---|
query() | 过滤行:df.query('A > B') |
eval() | 表达式求值与新列计算:df.eval('C = A + B') |
eval 注意
eval()默认返回新对象(除非inplace=True)- 对 MultiIndex 可先
reset_index()再 evalnumexpr可选安装(pip install numexpr),可进一步提升性能
3. where()
where() 保留满足条件的值,不满足条件的位置替换为 other(默认 NaN)。
Series.where()
s = pd.Series([1, 2, 3, 4, 5])
s.where(s > 3)
# 0 NaN
# 1 NaN
# 2 NaN
# 3 4.0
# 4 5.0
s.where(s > 3, other=0) # 替换为 0
# 0 0
# 1 0
# 2 0
# 3 4
# 4 5
s.where(s > 3, -1, inplace=True) # 原地修改DataFrame.where()
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
# 条件为 False 的单元格替换为 NaN
df.where(df > 2)
# A B
# 0 NaN 4.0
# 1 NaN 5.0
# 2 3.0 6.0
# 替换为标量
df.where(df > 2, 0)
# 替换为另一个 DataFrame/Series(按位置对齐)
other = pd.DataFrame({'A': [10, 20, 30], 'B': [40, 50, 60]})
df.where(df > 2, other) # 不满足时取 other 的值where() 参数详解
| 参数 | 说明 |
|---|---|
cond | 布尔条件(Series、DataFrame 或可调用函数) |
other | 替换值:标量、Series、DataFrame,或可调用函数 |
axis | 对齐轴,当 other 为 Series 时使用 |
level | MultiIndex 对齐层级 |
inplace | 是否原地修改 |
可调用函数
df.where(lambda x: x > 2, other=0) df.where(df > 2, lambda x: x * 10)
where 与布尔索引的对比
| 需求 | 推荐方式 |
|---|---|
| 筛选行(删除不满足的行) | 布尔索引 df[cond] / df.query() |
| 保留原形状、替换不合格值 | where() / mask() |
| 按值替换 | replace() |
4. mask()
mask() 是 where() 的反操作:满足 cond 的位置被替换为 other(默认 NaN),不满足的位置保留。
s = pd.Series([1, 2, 3, 4, 5])
s.mask(s > 3) # 大于 3 的替换为 NaN
# 0 1.0
# 1 2.0
# 2 3.0
# 3 NaN
# 4 NaN
# 条件为 True 的位置替换
df.mask(df['A'] > 2, 0)| 函数 | 条件 True 时 | 条件 False 时 |
|---|---|---|
where() | 保留原值 | 替换为 other |
mask() | 替换为 other | 保留原值 |
逻辑区分
df.where(cond, other)等价于df.mask(~cond, other),二者互为逆操作。
5. take()
take() 按整数位置从对象中取行或列(不依赖索引标签)。
df = pd.DataFrame({'A': [10, 20, 30, 40], 'B': [50, 60, 70, 80]})
df.take([0, 2]) # 取第 0、2 行
df.take([-1]) # 最后一行
df.take([0, 1], axis=1) # 取第 0、1 列
df.take([0, 2, 2, 3]) # 允许重复索引take() 参数
| 参数 | 说明 |
|---|---|
indices | 整数位置列表或数组 |
axis | 轴:0 = 行,1 = 列 |
allow_fill | 是否允许填充 fill_value(索引含 -1 时) |
fill_value | 替换 -1 的值(allow_fill=True 时) |
# 使用 -1 表示缺失位置并填充
df.take([0, -1], axis=0, allow_fill=True, fill_value=-999)take 与 iloc 的区别
take()不支持切片和布尔数组- 主要用于对一维数组(如
np.argsort结果)按位置取值的场景
6. sample()
sample() 进行随机抽样,支持按行(默认)或按列。
df = pd.DataFrame({'A': range(100), 'B': range(100, 200)})
# 随机抽取 5 行
df.sample(n=5)
# 按比例抽取
df.sample(frac=0.1)
# 设置随机种子(结果可复现)
df.sample(n=5, random_state=42)
# 有放回抽样(可重复)
df.sample(n=200, replace=True)
# 按权重抽样
weights = [0.1] * 99 + [0.9] # 最后一行权重更大
df.sample(n=10, weights=weights)
# 抽样列
df.sample(n=2, axis=1)
# 重置索引
df.sample(frac=0.1, ignore_index=True)sample() 参数
| 参数 | 说明 |
|---|---|
n | 抽样数量(与 frac 二选一) |
frac | 抽样比例(0~1) |
replace | 是否允许重复抽样(有放回) |
weights | 行权重(数组或列名) |
axis | 抽样轴(0 = 行,1 = 列) |
random_state | 随机种子,保证结果可复现 |
ignore_index | 是否重置输出索引 |
用途
- 训练/测试集划分(配合
frac)- 大数据集快速探索(抽样查看)
- Bootstrap 重抽样(
replace=True)