跳到主要内容

数据选择与过滤

本节定位

很多新人第一次学 Pandas 时,最早会卡住的通常不是清洗,而是:

  • 我到底怎么把想要的那部分数据挑出来?

所以这节最重要的不是记住所有写法,而是先建立一个判断:

我是按标签取、按位置取,还是按条件筛?

学习目标

  • 掌握 loc(标签索引)和 iloc(位置索引)
  • 学会使用布尔索引进行条件过滤
  • 掌握 query() 方法
  • 学会多条件组合筛选

先建立一张地图

数据选择与过滤更适合按“我要选谁”来理解:

所以这节真正想解决的是:

  • 不同场景下到底该先想到 lociloc 还是布尔索引
  • 为什么很多 Pandas 题的第一步都是“先把要看的数据选出来”

准备示例数据

import pandas as pd
import numpy as np

df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六", "钱七", "孙八"],
"年龄": [22, 28, 25, 35, 21, 30],
"部门": ["技术", "市场", "技术", "管理", "技术", "市场"],
"薪资": [15000, 18000, 22000, 35000, 12000, 20000],
"入职年份": [2023, 2020, 2021, 2018, 2024, 2019]
})
print(df)

一个更适合新人的总类比

你可以把这一节理解成:

  • 在一张很大的表里找你真正要看的那几行几列

也就是说,这节最核心的不是“写法多”,而是:

  • 先搞清你是按名字找
  • 还是按位置找
  • 还是按条件筛

loc:标签索引

loc标签(名称) 来定位数据,格式:df.loc[行标签, 列标签]

第一次学 loc,最该先记什么?

最值得先记的是:

loc 是按“名字和标签”在选。

也就是说,它更像:

  • 我知道我要哪一列、哪一段标签范围
# 取单行
print(df.loc[0]) # 第一行(标签为 0 的行)

# 取多行
print(df.loc[0:2]) # 标签 0 到 2(包含 2!)

# 取特定行和列
print(df.loc[0, "姓名"]) # "张三"
print(df.loc[0:2, "姓名"]) # 前 3 行的姓名
print(df.loc[0:2, ["姓名", "薪资"]]) # 前 3 行的姓名和薪资

# 取所有行的某些列
print(df.loc[:, ["姓名", "年龄"]])

# 条件筛选(最常用!)
print(df.loc[df["年龄"] > 25]) # 年龄大于 25 的所有行

iloc:位置索引

iloc位置(整数) 来定位数据,和 Python 列表的切片规则一致:

第一次学 iloc,最该先记什么?

最值得先记的是:

iloc 是按“第几行第几列”在选。

所以它更像:

  • 你拿着坐标去表里取值
# 取单行
print(df.iloc[0]) # 第一行

# 取多行(不包含末尾!和 Python 一致)
print(df.iloc[0:3]) # 第 0、1、2 行

# 取特定位置
print(df.iloc[0, 0]) # 第 0 行第 0 列 → "张三"
print(df.iloc[0:3, 0:2]) # 前 3 行、前 2 列
print(df.iloc[[0, 2, 4]]) # 第 0、2、4 行

# 取最后一行
print(df.iloc[-1])

loc vs iloc 对比

特性lociloc
索引方式标签(名称)位置(整数)
切片末尾包含不包含
示例df.loc[0:2] → 3 行df.iloc[0:2] → 2 行
条件筛选✅ 支持❌ 不支持
最常见的坑

当索引是默认的 0, 1, 2... 时,loc[0:2] 返回 3 行iloc[0:2] 返回 2 行

print(len(df.loc[0:2]))    # 3  (包含标签 2)
print(len(df.iloc[0:2])) # 2 (不包含位置 2)

一个很适合初学者先记的选择表

你的想法更稳的第一反应
我知道列名或标签loc
我只知道第几行第几列iloc
我要按某个条件筛人或筛订单布尔索引
条件很长、想写得更像一句话query()

这个表很适合新人,因为它会把“到底用哪个”直接变成一个可判断的问题。


布尔索引:条件筛选

这是数据分析中使用最频繁的操作:

为什么布尔索引这么重要?

因为真实分析题里你最常做的事情往往就是:

  • 找出金额大于某值的订单
  • 找出某部门的人
  • 找出满足两三个条件的子集

也就是说,很多分析真正开始的第一步,就是:

  • 先筛出你要分析的那部分数据

单条件筛选

# 薪资大于 20000 的员工
high_salary = df[df["薪资"] > 20000]
print(high_salary)

# 部门是"技术"的员工
tech = df[df["部门"] == "技术"]
print(tech)

# 年龄不等于 22 的员工
print(df[df["年龄"] != 22])

多条件组合

# 技术部门且薪资大于 15000(用 & 表示 AND)
result = df[(df["部门"] == "技术") & (df["薪资"] > 15000)]
print(result)

# 技术部门或管理部门(用 | 表示 OR)
result = df[(df["部门"] == "技术") | (df["部门"] == "管理")]
print(result)

# 取反(用 ~ 表示 NOT)
result = df[~(df["部门"] == "技术")] # 非技术部门
print(result)
多条件必须加括号

和 NumPy 一样,每个条件必须加括号,用 & | ~ 而不是 and or not

# ❌ 错误
df[df["年龄"] > 25 and df["薪资"] > 20000]

# ✅ 正确
df[(df["年龄"] > 25) & (df["薪资"] > 20000)]

isin:匹配多个值

# 部门在 ["技术", "市场"] 中的员工
result = df[df["部门"].isin(["技术", "市场"])]
print(result)

# 反向:不在这些部门中
result = df[~df["部门"].isin(["技术", "市场"])]
print(result)

between:范围筛选

# 年龄在 22~30 之间(包含两端)
result = df[df["年龄"].between(22, 30)]
print(result)

字符串条件

# 姓名包含"三"
result = df[df["姓名"].str.contains("三")]

# 姓名以"张"开头
result = df[df["姓名"].str.startswith("张")]

第一次做筛选题时,最稳的默认顺序

更稳的顺序通常是:

  1. 先问自己按标签选、按位置选,还是按条件筛
  2. 条件简单时先用布尔索引
  3. 条件很长时再考虑 query()
  4. 最后再组合取列和取行

这样会比一上来就把几种写法混着用更不容易乱。


query() 方法

query() 让你用更接近自然语言的方式筛选数据:

# 等价于 df[df["薪资"] > 20000]
result = df.query("薪资 > 20000")
print(result)

# 多条件
result = df.query("部门 == '技术' and 薪资 > 15000")
print(result)

# 用变量
min_salary = 20000
result = df.query("薪资 > @min_salary") # @引用外部变量
print(result)

# 范围查询
result = df.query("22 <= 年龄 <= 30")
print(result)
什么时候用 query()?
  • 条件简单时:布尔索引 df[df["col"] > 5] 更直接
  • 条件复杂时:query() 更可读,尤其是多条件组合
  • 需要引用变量时:query("col > @var") 很方便

选择特定数据的方法总结

一个新人可直接照抄的数据选择检查表

第一次做 Pandas 筛选题时,最稳的检查表通常是:

  1. 我是想选列、选行,还是同时选行和列?
  2. 我是按标签、按位置,还是按条件?
  3. 条件有没有加括号?
  4. 结果是不是我以为的那几行几列?

这 4 个问题答清楚后,很多筛选题都会顺很多。


实战:数据筛选

import pandas as pd
import numpy as np

# 创建一份电商订单数据
np.random.seed(42)
n = 100
orders = pd.DataFrame({
"订单ID": range(1001, 1001 + n),
"客户": np.random.choice(["Alice", "Bob", "Charlie", "Diana", "Eve"], n),
"商品类别": np.random.choice(["电子", "服装", "食品", "图书"], n),
"金额": np.random.randint(10, 500, n),
"数量": np.random.randint(1, 10, n),
"是否退货": np.random.choice([True, False], n, p=[0.1, 0.9])
})

# 查看数据
print(orders.head(10))
print(orders.info())

# 筛选练习
# 1. 金额大于 300 的订单
print(orders[orders["金额"] > 300])

# 2. Alice 购买的电子产品
print(orders.query("客户 == 'Alice' and 商品类别 == '电子'"))

# 3. 未退货且金额前 10 的订单
not_returned = orders[~orders["是否退货"]]
top10 = not_returned.nlargest(10, "金额")
print(top10[["订单ID", "客户", "金额"]])

动手练习

练习 1:基本筛选

# 用上面的 orders 数据
# 1. 找出所有退货的订单
# 2. 找出金额在 100~200 之间的订单数量
# 3. 找出购买"图书"或"食品"类别的订单
# 4. 找出 Bob 的非退货订单的平均金额

练习 2:综合筛选

# 1. 每个客户的最大订单金额是多少?(提示:先筛选再统计)
# 2. 哪些客户有退货记录?
# 3. 金额排名前 5% 的订单有哪些?(提示:用 quantile)

这节最该带走什么

  • loc 按标签,iloc 按位置,布尔索引按条件
  • 很多真实分析题,第一步都不是算,而是先筛
  • 先把“我要选谁”想清楚,再写代码,会比死记写法更稳