Skip to content
话题
Pandas
Pandas 过滤行:在 Python 中按条件选择数据

Pandas 过滤行:在 Python 中按条件选择数据

Updated on

在 pandas DataFrame 中过滤行是数据分析中最常见的操作之一。无论您是清理数据、探索模式还是为机器学习准备数据集,您都需要根据条件选择特定的行。挑战在于在 pandas 的多种过滤方法中选择正确的方法——每种方法都有不同的语法、性能特征和使用场景。

许多数据科学家在处理大型数据集或复杂条件时都会遇到过滤效率的问题。使用错误的方法可能会使您的分析速度降低几个数量级。了解何时使用布尔索引而不是 .query().loc[] 可能意味着一个在几秒钟内运行的脚本和一个需要几分钟的脚本之间的差异。

本指南涵盖了所有 pandas 过滤方法,包括实用示例、性能比较和最佳实践。您将学习布尔索引、.query() 方法、.loc[] 选择、.where() 函数和 .filter() 方法。最后,您将确切地知道在任何过滤场景中使用哪种方法。

📚

理解 Pandas 行过滤方法

Pandas 提供了五种主要方法来过滤 DataFrame 行,每种方法都适合不同的场景:

方法最适合语法示例性能
布尔索引简单条件、可读性df[df['age'] > 25]小到中型数据快速
.query()复杂条件、基于字符串df.query('age > 25 and city == "NYC"')大型数据更快
.loc[]基于标签的条件df.loc[df['age'] > 25, ['name', 'age']]灵活的列选择
.where()保持结构、替换值df.where(df['age'] > 25, np.nan)保留 DataFrame 形状
.filter()按列/索引名称过滤df.filter(like='total', axis=1)列/索引名称模式

让我们通过详细的示例来探索每种方法。

布尔索引:最常见的方法

布尔索引通过创建布尔掩码(True/False 值)并将其应用于 DataFrame 来过滤行。这是初学者最直观的方法。

import pandas as pd
import numpy as np
 
# 创建示例 DataFrame
df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['NYC', 'LA', 'NYC', 'Chicago', 'LA'],
    'salary': [70000, 80000, 90000, 75000, 85000]
})
 
# 过滤 age 大于 30 的行
filtered = df[df['age'] > 30]
print(filtered)
#       name  age city  salary
# 2  Charlie   35  NYC   90000
# 4      Eve   32   LA   85000
 
# 查看布尔掩码
print(df['age'] > 30)
# 0    False
# 1    False
# 2     True
# 3    False
# 4     True

布尔索引通过评估条件 df['age'] > 30 来工作,它返回一个 True/False 值的 Series。当您使用 df[mask] 将此掩码传递给 DataFrame 时,pandas 只返回掩码为 True 的行。

使用多个条件进行过滤

使用逻辑运算符组合多个条件。重要:使用 &(和)、|(或)、~(非)而不是 Python 的 andornot 关键字。始终用括号包围每个条件。

# 使用 AND 的多个条件
filtered = df[(df['age'] > 25) & (df['city'] == 'NYC')]
print(filtered)
#       name  age city  salary
# 2  Charlie   35  NYC   90000
 
# 使用 OR 的多个条件
filtered = df[(df['age'] > 30) | (df['salary'] > 80000)]
print(filtered)
#       name  age     city  salary
# 2  Charlie   35      NYC   90000
# 4      Eve   32       LA   85000
 
# NOT 运算符
filtered = df[~(df['city'] == 'NYC')]
print(filtered)
#     name  age     city  salary
# 1    Bob   30       LA   80000
# 3  David   28  Chicago   75000
# 4    Eve   32       LA   85000

对多个值使用 .isin()

使用 .isin() 过滤列与列表中任何值匹配的行:

# 过滤 city 为 NYC 或 LA 的行
cities = ['NYC', 'LA']
filtered = df[df['city'].isin(cities)]
print(filtered)
#       name  age city  salary
# 0    Alice   25  NYC   70000
# 1      Bob   30   LA   80000
# 2  Charlie   35  NYC   90000
# 4      Eve   32   LA   85000
 
# 反向:不在列表中的城市
filtered = df[~df['city'].isin(cities)]
print(filtered)
#     name  age     city  salary
# 3  David   28  Chicago   75000

使用 .between() 进行过滤

.between() 方法过滤范围内的值(默认包含边界):

# 过滤 28 到 32 之间的年龄
filtered = df[df['age'].between(28, 32)]
print(filtered)
#     name  age     city  salary
# 1    Bob   30       LA   80000
# 3  David   28  Chicago   75000
# 4    Eve   32       LA   85000
 
# 排他边界
filtered = df[df['age'].between(28, 32, inclusive='neither')]
print(filtered)
#   name  age city  salary
# 1  Bob   30   LA   80000

.query() 方法:基于字符串的过滤

.query() 方法接受字符串表达式,使其对于复杂条件具有可读性。由于它使用 numexpr 进行优化,因此对于大型 DataFrame 特别高效。

# 简单查询
filtered = df.query('age > 30')
print(filtered)
#       name  age city  salary
# 2  Charlie   35  NYC   90000
# 4      Eve   32   LA   85000
 
# 多个条件
filtered = df.query('age > 25 and city == "NYC"')
print(filtered)
#       name  age city  salary
# 2  Charlie   35  NYC   90000
 
# 使用 @ 符号使用变量
min_age = 30
filtered = df.query('age > @min_age')
print(filtered)
#       name  age city  salary
# 2  Charlie   35  NYC   90000
# 4      Eve   32   LA   85000

高级 .query() 表达式

# 在 query 中使用 .isin()
cities = ['NYC', 'LA']
filtered = df.query('city in @cities')
print(filtered)
 
# 范围条件
filtered = df.query('28 <= age <= 32')
print(filtered)
 
# 字符串方法
filtered = df.query('city.str.contains("LA")', engine='python')
print(filtered)

用于基于标签过滤的 .loc[]

.loc[] 索引器结合了行过滤和列选择。当您需要从过滤的行中获取特定列时使用它。

# 过滤行并选择列
filtered = df.loc[df['age'] > 30, ['name', 'age']]
print(filtered)
#       name  age
# 2  Charlie   35
# 4      Eve   32
 
# 多个条件
filtered = df.loc[(df['age'] > 25) & (df['salary'] > 75000), ['name', 'salary']]
print(filtered)
#       name  salary
# 1      Bob   80000
# 2  Charlie   90000
# 4      Eve   85000
 
# 所有列
filtered = df.loc[df['city'] == 'NYC', :]
print(filtered)

.where() 方法:条件值替换

与其他方法不同,.where() 通过将不满足条件的值替换为 NaN(或指定值)来保留 DataFrame 形状。

# 保留 age > 30 的值,将其他值替换为 NaN
result = df.where(df['age'] > 30)
print(result)
#       name   age city   salary
# 0      NaN   NaN  NaN      NaN
# 1      NaN   NaN  NaN      NaN
# 2  Charlie  35.0  NYC  90000.0
# 3      NaN   NaN  NaN      NaN
# 4      Eve  32.0   LA  85000.0
 
# 用自定义值替换
result = df.where(df['age'] > 30, 'FILTERED')
print(result)
 
# .where() 后删除包含 NaN 的行
result = df.where(df['age'] > 30).dropna()
print(result)

.filter() 方法:按列/索引名称过滤

.filter() 方法按标签(名称)而不是值过滤列或行。将其用于基于模式的列选择。

# 创建具有多列的 DataFrame
df_wide = pd.DataFrame({
    'total_sales': [100, 200, 300],
    'total_profit': [20, 40, 60],
    'monthly_sales': [10, 20, 30],
    'yearly_sales': [120, 240, 360],
    'region': ['East', 'West', 'North']
})
 
# 过滤包含 'total' 的列
filtered = df_wide.filter(like='total')
print(filtered)
#    total_sales  total_profit
# 0          100            20
# 1          200            40
# 2          300            60
 
# 使用正则表达式过滤列
filtered = df_wide.filter(regex=r'.*sales$')
print(filtered)
#    total_sales  monthly_sales  yearly_sales
# 0          100             10           120
# 1          200             20           240
# 2          300             30           360
 
# 按确切名称过滤列
filtered = df_wide.filter(items=['total_sales', 'region'])
print(filtered)

过滤字符串数据

Pandas 通过 .str 访问器提供字符串方法来过滤文本列。

# 创建包含文本数据的 DataFrame
df_text = pd.DataFrame({
    'name': ['Alice Smith', 'Bob Johnson', 'Charlie Brown', 'David Lee'],
    'email': ['alice@gmail.com', 'bob@yahoo.com', 'charlie@gmail.com', 'david@outlook.com']
})
 
# 过滤 name 包含 'Smith' 的行
filtered = df_text[df_text['name'].str.contains('Smith')]
print(filtered)
#          name           email
# 0  Alice Smith  alice@gmail.com
 
# 不区分大小写的搜索
filtered = df_text[df_text['name'].str.contains('smith', case=False)]
print(filtered)
 
# 按电子邮件域过滤
filtered = df_text[df_text['email'].str.endswith('gmail.com')]
print(filtered)
#            name              email
# 0   Alice Smith    alice@gmail.com
# 2  Charlie Brown  charlie@gmail.com
 
# 使用正则表达式过滤
filtered = df_text[df_text['name'].str.match(r'^[A-C]')]
print(filtered)

过滤空值和非空值

使用 .isna().notna().isnull().notnull() 基于缺失数据进行过滤。

# 创建包含缺失值的 DataFrame
df_missing = pd.DataFrame({
    'A': [1, 2, np.nan, 4],
    'B': [5, np.nan, 7, 8],
    'C': [9, 10, 11, 12]
})
 
# 过滤列 A 不为 null 的行
filtered = df_missing[df_missing['A'].notna()]
print(filtered)
#      A    B   C
# 0  1.0  5.0   9
# 1  2.0  NaN  10
# 3  4.0  8.0  12
 
# 过滤任何列为 null 的行
filtered = df_missing[df_missing.isna().any(axis=1)]
print(filtered)
#      A    B   C
# 1  2.0  NaN  10
# 2  NaN  7.0  11
 
# 过滤所有列都不为 null 的行
filtered = df_missing[df_missing.notna().all(axis=1)]
print(filtered)
#      A    B   C
# 0  1.0  5.0   9
# 3  4.0  8.0  12

按日期范围过滤

在处理 datetime 列时,您可以使用标准比较运算符按日期范围进行过滤。

# 创建包含日期的 DataFrame
df_dates = pd.DataFrame({
    'date': pd.date_range('2026-01-01', periods=10, freq='D'),
    'value': range(10)
})
 
# 过滤特定日期之后的日期
filtered = df_dates[df_dates['date'] > '2026-01-05']
print(filtered)
 
# 过滤日期范围
start_date = '2026-01-03'
end_date = '2026-01-07'
filtered = df_dates[(df_dates['date'] >= start_date) & (df_dates['date'] <= end_date)]
print(filtered)
 
# 对日期使用 .between()
filtered = df_dates[df_dates['date'].between('2026-01-03', '2026-01-07')]
print(filtered)

性能比较:大型 DataFrame

不同的过滤方法具有不同的性能特征。以下是对包含 100 万行的 DataFrame 的比较:

import time
 
# 创建大型 DataFrame
np.random.seed(42)
df_large = pd.DataFrame({
    'A': np.random.randint(0, 100, 1000000),
    'B': np.random.randint(0, 100, 1000000),
    'C': np.random.choice(['X', 'Y', 'Z'], 1000000)
})
 
# 布尔索引
start = time.time()
result = df_large[(df_large['A'] > 50) & (df_large['B'] < 30)]
print(f"布尔索引: {time.time() - start:.4f} 秒")
 
# .query() 方法
start = time.time()
result = df_large.query('A > 50 and B < 30')
print(f".query() 方法: {time.time() - start:.4f} 秒")
 
# .loc[] 方法
start = time.time()
result = df_large.loc[(df_large['A'] > 50) & (df_large['B'] < 30)]
print(f".loc[] 方法: {time.time() - start:.4f} 秒")

性能见解:

  • 布尔索引:对于简单条件快速,对于复杂的多条件过滤器较慢
  • .query():对于具有多个条件的大型 DataFrame 最快(使用 numexpr 优化)
  • .loc[]:类似于布尔索引,但在列选择方面更灵活
  • .where():由于需要遍历整个 DataFrame 而较慢,仅在需要保留形状时使用

对于超过 100,000 行且具有多个条件的数据集,.query() 通常比布尔索引快 20-40%。

常见错误及如何避免

错误 1:使用 'and' 而不是 '&'

# 错误 - 引发 ValueError
# filtered = df[df['age'] > 25 and df['city'] == 'NYC']
 
# 正确
filtered = df[(df['age'] > 25) & (df['city'] == 'NYC')]

错误 2:忘记括号

# 错误 - 运算符优先级问题
# filtered = df[df['age'] > 25 & df['city'] == 'NYC']
 
# 正确 - 包围每个条件
filtered = df[(df['age'] > 25) & (df['city'] == 'NYC')]

错误 3:修改原始 DataFrame

# 过滤创建视图,不是副本
filtered = df[df['age'] > 30]
 
# 错误 - SettingWithCopyWarning
# filtered['new_col'] = 100
 
# 正确 - 创建显式副本
filtered = df[df['age'] > 30].copy()
filtered['new_col'] = 100

错误 4:字符串过滤未处理 NaN

df_with_nan = pd.DataFrame({
    'name': ['Alice', np.nan, 'Charlie']
})
 
# 错误 - 如果存在 NaN 会引发错误
# filtered = df_with_nan[df_with_nan['name'].str.contains('li')]
 
# 正确 - 使用 na 参数处理 NaN
filtered = df_with_nan[df_with_nan['name'].str.contains('li', na=False)]

使用 PyGWalker 可视化过滤的数据

过滤 pandas DataFrame 后,可视化结果有助于发现模式和见解。PyGWalker (opens in a new tab) 直接在 Python notebook 中将过滤的 DataFrame 转换为类似 Tableau 的交互式界面——无需导出数据或编写复杂的绘图代码。

PyGWalker 在探索过滤数据集时特别有用,因为它允许您:

  • 拖放列以立即创建图表
  • 在不编写代码的情况下直观地应用其他过滤器
  • 在几秒钟内在图表类型(条形图、折线图、散点图、热图)之间切换
  • 导出可视化内容用于报告或演示
import pygwalker as pyg
 
# 过滤 DataFrame
filtered_df = df[(df['age'] > 25) & (df['salary'] > 75000)]
 
# 启动交互式可视化
pyg.walk(filtered_df)

这将打开一个交互式界面,您可以通过将过滤的列拖到图表构建器中来创建可视化。对于使用多个过滤条件的数据分析师,PyGWalker 消除了过滤 → 绘图 → 调整 → 重新绘图的迭代周期。

常见问题

如何按条件过滤 pandas DataFrame 行?

对于简单条件使用 df[df['column'] > value] 进行布尔索引。对于多个条件,使用带括号的 &(和)、|(或)、~(非):df[(df['A'] > 10) & (df['B'] < 20)]。或者,使用 .query() 获得可读语法:df.query('A > 10 and B < 20')

.loc[] 和布尔索引有什么区别?

布尔索引(df[df['col'] > 5])返回过滤行的所有列。.loc[] 允许同时选择列:df.loc[df['col'] > 5, ['col1', 'col2']]。两种方法具有相似的性能,但 .loc[] 更明确并支持基于标签的行选择。

如何使用多个条件过滤 pandas DataFrame?

使用 &(和)、|(或)、~(非)运算符组合条件。始终用括号包围每个条件:df[(df['age'] > 25) & (df['city'] == 'NYC') | (df['salary'] > 80000)]。对于复杂条件,使用 .query()df.query('age > 25 and (city == "NYC" or salary > 80000)')

何时应该使用 .query() 而不是布尔索引?

对于具有多个条件的大型 DataFrame(>100k 行)使用 .query()——由于 numexpr 优化,它快 20-40%。对于复杂条件也更具可读性。对于简单过滤器或需要最大兼容性时使用布尔索引(.query() 需要更难调试的字符串表达式)。

如何过滤列值在列表中的行?

使用 .isin() 方法:df[df['city'].isin(['NYC', 'LA', 'Chicago'])]。对于反向(不在列表中),使用 ~ 运算符:df[~df['city'].isin(['NYC', 'LA'])]。对于大型列表,这比链接多个 | 条件更高效。

结论

过滤 pandas DataFrame 行是 Python 数据分析的基本技能。您现在了解了五种过滤方法:用于简单性的布尔索引、用于性能的 .query()、用于灵活列选择的 .loc[]、用于值替换的 .where() 和用于列名模式的 .filter()

关键要点:

  • 对小到中型数据集的快速、可读过滤器使用布尔索引
  • 对具有复杂条件的大型数据集选择 .query()(快 20-40%)
  • 当您需要行过滤和列选择时应用 .loc[]
  • 记住使用 &|~ 运算符而不是 andornot
  • 始终用括号包围条件以避免运算符优先级问题

掌握这些过滤技术,您将有效地处理任何数据选择场景。对于过滤数据的交互式探索,考虑使用 PyGWalker 来可视化您的结果,而无需编写额外的绘图代码。

📚