在Python数据分析工作中,pandas的DataFrame是最常用的二维表格结构。过滤数据指的是从完整表中按行或列的条件挑出子集,这是数据清洗、特征工程和报表生成的前置步骤。不同过滤方式在可读性、性能和适用场景上有明显区别,理解它们的机制能减少出错并提升开发效率。

布尔索引过滤
布尔索引是DataFrame过滤最基础的手段。它的原理是:用一个与行数等长的布尔序列(每个元素是True或False)作为行索引,pandas会保留值为True的那些行。这个布尔序列通常由某一列的比较运算产生,比如df['age'] > 30会返回一串布尔值。
当条件变复杂时,可以用位运算符连接多个布尔表达式。需要特别注意的是,Python里位运算的优先级高于比较运算,所以每个条件必须用括号包起来,再用&(与)、|(或)、~(非)连接。下面示例展示了筛选年龄大于30且城市为北京的用户:
import pandas as pd
data = {
'name': ['张三', '李四', '王五', '赵六'],
'age': [25, 34, 45, 29],
'city': ['北京', '上海', '北京', '广州']
}
df = pd.DataFrame(data)
# 布尔索引多条件过滤
mask = (df['age'] > 30) & (df['city'] == '北京')
result = df[mask]
print(result)
这种写法的优势是直观、灵活,能和NumPy的向量化运算无缝结合,执行速度也很快。缺点是当条件很长时括号嵌套多,可读性下降;另外如果列名包含空格,就必须用df['列名']形式,不能直接用属性访问。
使用query方法
query方法允许把筛选条件写成类似SQL的查询字符串,pandas会在内部解析并生成布尔索引。它对含有空格的列名更友好,直接用反引号包裹即可,也方便把外部变量嵌进去,通过@变量名引用。
相比布尔索引,query在条件复杂、需要动态拼接时更清晰,也避免了括号优先级陷阱。下面代码完成和上面一样的过滤,但表达更紧凑:
# 使用query方法过滤
min_age = 30
city_name = '北京'
result2 = df.query('age > @min_age and city == @city_name')
print(result2)
query的底层仍会转换成布尔索引,因此性能差异很小。不过它不支持某些Python函数调用,字符串方法也要走str.xxx形式。在交互式分析或条件由用户输入拼接的场景里,query是更安全的方案,因为参数化引用降低了注入式错误的风险。
结合loc按行列同时筛选
如果过滤时不仅要选行,还要指定保留哪些列,用loc是最规范的写法。loc接收两个参数:第一个是行选择器(可以是布尔序列、切片或标签列表),第二个是列选择器。这样一行代码就能完成行列双向裁剪。
以下示例在前面年龄和城市条件基础上,只取出姓名和年龄两列:
# loc同时过滤行和列 subset = df.loc[(df['age'] > 30) & (df['city'] == '北京'), ['name', 'age']] print(subset)
使用loc的好处是语义明确,不会像链式索引(如df[df.x>1]['y'])那样触发pandas的隐式拷贝警告,也能稳定修改原数据。在写数据管道时,推荐统一用loc做带列选择的过滤,代码可维护性更好。
字符串与缺失值处理
真实数据常包含文本列和空值。对字符串过滤要用str.contains,它可以写正则表达式,并用na=False参数把缺失值当作不匹配处理,防止结果里混进NaN行。下面演示筛选姓名中包含“张”的记录:
# 字符串包含过滤,忽略缺失值
name_mask = df['name'].str.contains('张', na=False)
print(df[name_mask])
如果直接对含NaN的列做等于判断,结果里对应行会是False,一般不会报错,但用str访问器时必须显式处理na,否则pandas会抛出错误。过滤完之后,通常接dropna或fillna做后续修补,保证下游计算不被空值打断。
| 过滤方式 | 适用场景 | 主要优点 |
|---|---|---|
| 布尔索引 | 简单或复杂条件行过滤 | 直观、性能好 |
| query | 类SQL动态条件 | 可读高、支持变量 |
| loc | 行列同时选取 | 语义清、易修改 |
综合来看,日常过滤DataFrame并不需要死记所有API,把握“布尔序列选行、loc管行列、query写表达式”三条主线即可。遇到性能瓶颈时,优先检查是否用了向量化过滤而非循环,这往往比换方法更有效。
PythonpandasDataFrame_filter修改时间:2026-08-09 05:51:26