Pandas 数据筛选

数据筛选是从数据集中提取满足特定条件子集的核心操作。Pandas 提供了丰富而灵活的筛选方式,从基础的布尔索引到高级的 query() 方法,让你能够以最自然的方式获取所需数据。

📌 准备示例数据

import pandas as pd
import numpy as np

df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五', '赵六', '陈七', '周八'],
    '年龄': [28, 35, 42, 29, 31, 38],
    '工资': [8000, 12000, 15000, 9500, 11000, 13500],
    '部门': ['技术', '销售', '技术', '管理', '销售', '技术'],
    '入职年份': [2020, 2018, 2015, 2021, 2019, 2017]
})
print(df)

🔍 布尔索引

布尔索引是 Pandas 最基本的筛选方式,通过传入一个布尔型 Series(与 DataFrame 长度相同)来选取对应位置为 True 的行。

单条件筛选
df[df['年龄'] > 30]              # 年龄大于30
df[df['部门'] == '技术']           # 部门为技术
多条件筛选(&、|、~)
# 年龄大于30且部门为技术
df[(df['年龄'] > 30) & (df['部门'] == '技术')]

# 工资小于10000或部门为管理
df[(df['工资'] < 10000) | (df['部门'] == '管理')]

# 年龄不大于30(取反)
df[~(df['年龄'] > 30)]

注意:每个条件必须用括号括起来,因为 &| 的优先级高于比较运算符。

🎯 使用 isin() 进行成员筛选

当需要筛选列值属于某个集合时,isin() 非常方便。

# 筛选部门为技术或销售的行
df[df['部门'].isin(['技术', '销售'])]

# 筛选年龄为28、35、38的行
df[df['年龄'].isin([28, 35, 38])]

📏 使用 between() 进行区间筛选

between() 用于筛选数值在某个闭区间内的行,包含左右边界。

# 筛选工资在9000到13000之间
df[df['工资'].between(9000, 13000)]

# 也可用于日期、字符串等可比较类型
df[df['姓名'].between('王', '赵')]  # 按字典序

📝 字符串方法筛选

通过 .str 访问器可以使用字符串方法进行筛选。

# 筛选姓名以“张”开头的行
df[df['姓名'].str.startswith('张')]

# 筛选姓名包含“五”的行
df[df['姓名'].str.contains('五')]

# 筛选部门长度大于等于2的行
df[df['部门'].str.len() >= 2]

⚡ 使用 query() 方法

query() 提供了一种类似 SQL 的字符串表达式语法,使代码更简洁。

# 筛选年龄大于30且部门为技术
df.query('年龄 > 30 and 部门 == "技术"')

# 使用变量
min_wage = 10000
df.query('工资 >= @min_wage and 入职年份 < 2020')

# 复杂条件
df.query('(年龄 > 30 or 工资 > 12000) and 部门 != "管理"')

🔢 对索引进行筛选

有时需要根据行索引或列索引进行筛选。

# 设置姓名列为索引
df_indexed = df.set_index('姓名')

# 根据索引值筛选
df_indexed.loc[['张三', '王五']]

# 根据索引条件筛选(需先重置索引或使用 index 属性)
df[df.index.isin([0, 2, 4])]   # 筛选行索引为0,2,4的行

🧩 同时筛选行和列

结合 .loc[] 可以在筛选行的同时选择特定列。

# 筛选年龄大于30的行,并只保留姓名和工资列
df.loc[df['年龄'] > 30, ['姓名', '工资']]

# 使用 query 配合列选择
df.query('年龄 > 30').loc[:, ['姓名', '工资']]

🧪 综合示例

import pandas as pd

# 员工数据
df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五', '赵六', '陈七', '周八', '吴九'],
    '年龄': [28, 35, 42, 29, 31, 38, 45],
    '工资': [8000, 12000, 15000, 9500, 11000, 13500, 18000],
    '部门': ['技术', '销售', '技术', '管理', '销售', '技术', '管理'],
    '入职年份': [2020, 2018, 2015, 2021, 2019, 2017, 2014]
})

# 需求1:筛选年龄大于30且工资低于15000的员工
df1 = df[(df['年龄'] > 30) & (df['工资'] < 15000)]
print("年龄>30且工资<15000:\n", df1)

# 需求2:筛选部门为技术或管理,且入职年份在2018年之后的员工
df2 = df[df['部门'].isin(['技术', '管理']) & (df['入职年份'] > 2018)]
print("\n技术或管理,且入职年份>2018:\n", df2)

# 需求3:筛选姓名包含“五”或“七”的员工,只保留姓名和部门
df3 = df[df['姓名'].str.contains('五|七')][['姓名', '部门']]
print("\n姓名含五或七:\n", df3)

# 需求4:使用query筛选工资在10000-16000之间且部门不是销售
df4 = df.query('10000 <= 工资 <= 16000 and 部门 != "销售"')
print("\n工资在10000-16000之间且非销售:\n", df4)

⚠️ 注意事项

运算符优先级:多条件时必须用括号包裹每个条件,例如 (df['年龄'] > 30) & (df['工资'] < 15000),不能写成 df['年龄'] > 30 & df['工资'] < 15000
字符串方法性能:str.contains() 对于大数据集可能较慢,考虑使用正则或提前处理。
query 中的变量引用:query() 中使用外部变量需加 @ 前缀,如 @min_wage
缺失值处理:布尔索引会自动排除 NaN,但若需要包含 NaN 需特殊处理,如 df[df['年龄'].isna() | (df['年龄'] > 30)]
最佳实践:
  • 简单条件用布尔索引,复杂条件用 query() 提高可读性。
  • 多条件时使用 &|,不要用 andor(除非在 query 中)。
  • 对于大量筛选操作,考虑先建立索引或使用分类数据类型提升性能。
  • 筛选后如需修改原 DataFrame,务必使用 .loc[] 进行赋值,避免链式索引警告。