首页 / Pandas 入门教程 / 布尔索引与条件筛选

Pandas 入门教程

布尔索引与条件筛选

本教程共 54 篇 · 第 20 篇 · 更新于 2026-08-11 · 约 8 分钟阅读

PandasPandas 入门教程布尔索引条件筛选isinbetween

本节目标:学会用比较运算和 &、|、~ 组合条件筛选数据,掌握 isin、between 和字符串条件的用法。

比较运算:先造出”筛子”

筛选的核心是布尔索引:用一个跟 DataFrame 等长的 True/False 序列,把 True 的行留下。

先看比较运算怎么造出这个序列:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "年龄": [25, 30, 28, 35, 22],
    "城市": ["北京", "上海", "广州", "北京", "深圳"],
    "薪资": [12000, 15000, 11000, 18000, 9000],
})

mask = df["年龄"] > 28
print(mask)

输出是一串 True/False,每个值对应一行。把它放进 df[...] 里,就完成了筛选:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "年龄": [25, 30, 28, 35, 22],
    "薪资": [12000, 15000, 11000, 18000, 9000],
})

mask = df["年龄"] > 28
print(df[mask])                   # 年龄大于 28 的行
print(df[df["薪资"] >= 12000])    # 直接写也行

等于、不等于、大于、小于都支持:==、!=、>、<、>=、<=。

几个条件的等价写法可以互相替换,看哪种顺眼用哪种:

写法含义等价写法
isin([“北京”, “上海”])属于集合== “北京” | == “上海”
between(25, 30)闭区间>= 25 & <= 30
~(条件)取反!= 或 反向条件

有一个细节要注意:NaN 和任何值比较都是 False,包括它自己。判断缺失要用 isna(),不能写 == np.nan

import pandas as pd
import numpy as np

s = pd.Series([1, np.nan, 3])
print(s == np.nan)   # 全是 False
print(s.isna())      # 这才是判断缺失的正确方式

多条件:& 与 | 与 ~

多个条件组合时,用位运算符:

  • &:并且
  • |:或者
  • ~:取反
import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "年龄": [25, 30, 28, 35, 22],
    "城市": ["北京", "上海", "广州", "北京", "深圳"],
    "薪资": [12000, 15000, 11000, 18000, 9000],
})

# 北京 且 薪资大于 10000
print(df[(df["城市"] == "北京") & (df["薪资"] > 10000)])

# 广州 或 薪资大于 15000
print(df[(df["城市"] == "广州") | (df["薪资"] > 15000)])

# 不是北京(也可以写成 df["城市"] != "北京")
print(df[~(df["城市"] == "北京")])
Warning

必须用 &|,不能用 Python 的 andor。原因:and/or 要求两边是单个布尔值,而这里两边是 Series。另外每个条件都要用括号括起来,否则运算优先级会出错。这是 pandas 新手报错率最高的地方。

isin:值在列表里

判断”是否属于某个集合”,用 isin,比一串 == 加 | 清爽得多:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "城市": ["北京", "上海", "广州", "北京", "深圳"],
})

print(df[df["城市"].isin(["北京", "上海"])])

# 取反:不在列表里
print(df[~df["城市"].isin(["北京", "上海"])])

isin 还能同时查多列:先对多列做 isin 得到全是布尔值的 DataFrame,再按行判断”每一列都命中”:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六"],
    "城市": ["北京", "上海", "广州", "北京"],
    "部门": ["技术", "销售", "技术", "运营"],
})

target = pd.DataFrame({"城市": ["北京", "上海"], "部门": ["技术", "销售"]})
mask = df[["城市", "部门"]].isin(target).all(axis=1)
print(df[mask])

between:数值区间

闭区间判断,两头都包含:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "年龄": [25, 30, 28, 35, 22],
})

print(df[df["年龄"].between(25, 30)])

# 等价写法
print(df[(df["年龄"] >= 25) & (df["年龄"] <= 30)])

字符串条件:.str 系列

对文本列做条件判断,用 .str 访问器:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
})

print(df[df["姓名"].str.contains("三")])     # 包含
print(df[df["姓名"].str.startswith("张")])   # 开头是
print(df[df["姓名"].str.endswith("五")])     # 结尾是

str.contains 支持正则表达式,复杂模式也能写:

import pandas as pd
import numpy as np

df = pd.DataFrame({"城市": ["北京市", "上海市", "广州市", "深圳市", "杭州市"]})
print(df[df["城市"].str.contains("^北|^上")])   # 以"北"或"上"开头

.str 系列常用判断方法:

方法含义
str.contains(“x”)包含子串或正则
str.startswith(“x”)以 x 开头
str.endswith(“x”)以 x 结尾
str.match(“x”)从头匹配正则
Tip

列里有缺失值时,str.contains 的行为要注意:str 类型下缺失值返回 False;object 类型下会得到含 NaN 的掩码,直接当筛选条件会报错。稳妥写法是显式加 na=Falsedf["姓名"].str.contains("三", na=False)

条件 + 列选择:loc 一步到位

筛出符合条件的行,再只取某些列,用 loc 最顺手:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "年龄": [25, 30, 28, 35, 22],
    "城市": ["北京", "上海", "广州", "北京", "深圳"],
})

print(df.loc[df["年龄"] > 28, ["姓名", "城市"]])

给符合条件的行赋值也用它:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "年龄": [25, 30, 28, 35, 22],
    "薪资": [12000, 15000, 11000, 18000, 9000],
})

# 给年龄大于 28 的人加薪 1000
df.loc[df["年龄"] > 28, "薪资"] += 1000
print(df)
Note

pandas 3.0 默认开启写时复制(Copy-on-Write,如 §09 所述),上面的写法才是安全的赋值方式。旧教程里”先筛再改”的链式写法在新版会失效,看到就换成 loc。

筛选结果还能接着用

筛选出来的 DataFrame 可以继续接任何方法,比如筛完再排序、取前三名:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五", "赵六", "钱七"],
    "年龄": [25, 30, 28, 35, 22],
    "薪资": [12000, 15000, 11000, 18000, 9000],
    "部门": ["技术", "销售", "技术", "运营", "技术"],
})

# 技术或销售部门里薪资最高的 3 人
cond = df["部门"].isin(["技术", "销售"])
print(df[cond].sort_values("薪资", ascending=False).head(3))

复杂条件先存成变量,代码可读性会好很多。想用更接近自然语言的方式写条件,下一节介绍的 query 方法更合适(如 §21 所述)。

再补两个实用判断:any 和 all

布尔 Series 上还有两个常用的收尾操作:

  • any():有没有任何一个 True
  • all():是不是全部 True
import pandas as pd
import numpy as np

mask = pd.Series([True, False, True])
print(mask.any())   # True,至少有一个 True
print(mask.all())   # False,不是全部 True

典型场景是配合 isin 检查多列:

import pandas as pd
import numpy as np

df = pd.DataFrame({"姓名": ["张三", "李四"], "城市": ["北京", "上海"]})

# 每行:姓名含"三" 且 城市是北京
check = df["姓名"].str.contains("三", na=False) & df["城市"].isin(["北京"])
print(check.all())   # 检查所有行是否都满足

筛选完行数会变,索引还是原来的。想重新编号,接 reset_index(drop=True)(如 §23 所述)。

多条件的优先级

多个条件混合时,& 的优先级高于 |,和数学里”乘除先于加减”一个道理。不写括号,A & B | C 会被解释成 (A & B) | C,很可能不是你想要的。所以规则很简单:每个条件一对括号,组合符号放外面

import pandas as pd
import numpy as np

df = pd.DataFrame({"年龄": [25, 30, 28, 35], "薪资": [12000, 15000, 11000, 18000]})

# 推荐:每个条件都加括号,优先级一目了然
cond = (df["年龄"] > 28) & (df["薪资"] > 10000) | (df["年龄"] < 25)
print(df[cond])

一眼就能看出顺序是:先算两个括号里的,再 &,最后 |。

筛选和索引是分析的基础动作,练熟之后,后面的数据清洗、分组统计都会顺畅很多。

筛选的本质是”按布尔数组取行”,所以 df[mask]df.loc[mask] 等价,结果都是新对象,原 df 不会变。两者差别在于:df.loc[mask] 还能同时选列(df.loc[mask, "薪资"]),df[mask] 只能选行。

新手最常见的三个错误

  1. and / or 连接条件 → 直接报错,换成 & / |
  2. 忘了括号 → 结果诡异但不报错,检查每个条件有没有括号
  3. 列名拼错 → KeyError,用 df.columns 核对一遍

错误信息看不懂时,先把条件拆开单独跑:df["年龄"] > 28 单独输出看看是不是一串 True/False,再逐步组合。