Python pandas怎么根据日期范围筛选DataFrame数据

Cora
Cora
发布时间:2026-11-12| 13人浏览过
原创

详细讲解在Python pandas中根据日期范围筛选DataFrame数据的四种核心方法:布尔索引、Series.between()、DataFrame.query()以及DatetimeIndex切片,并剖析时间戳时分秒截断与边界开闭区间等易错陷阱。

一、核心前置步骤:确保日期列为 datetime64 类型

很多初学者在根据日期筛选数据时遇到的首个问题是:执行了筛选语句,但返回的数据不全或直接为空。其最主要的原因在于目标日期列在加载时被识别为普通文本(object 或 string 类型)。

在普通字符串状态下,比较操作符依据的是字符的 ASCII 字典序。例如字符串 "2023-1-10" 在字典序上会排在 "2023-02-01" 之后,一旦混用非标准化格式就会引发不可预期的逻辑错漏。因此,所有筛选工作的第一步,都必须通过 pd.to_datetime() 完成类型标准化。

Python pandas怎么根据日期范围筛选DataFrame数据
图 1:Pandas 日期筛选的标准四阶段执行链路

我们先构建一个包含不同订单日期的基础 DataFrame,作为全文各方法演示的数据基准:

Python 3 示例代码:准备基准数据集环境:pandas >= 1.4.0
import pandas as pd

# 构建模拟订单销售数据
raw_data = {
    'order_id': ['ORD-101', 'ORD-102', 'ORD-103', 'ORD-104', 'ORD-105', 'ORD-106'],
    'order_date': [
        '2023-10-01', 
        '2023-10-15', 
        '2023-10-31', 
        '2023-11-05', 
        '2023-11-20', 
        '2023-12-01'
    ],
    'amount': [1200.5, 450.0, 890.0, 3100.2, 760.0, 2150.0]
}

df = pd.DataFrame(raw_data)

# 关键步骤:转换为 datetime64 类型
df['order_date'] = pd.to_datetime(df['order_date'])

# 检查转换后的类型
print(df.dtypes)
print(df)
示例运行输出
order_id object order_date datetime64[ns] amount float64 dtype: object order_id order_date amount 0 ORD-101 2023-10-01 1200.5 1 ORD-102 2023-10-15 450.0 2 ORD-103 2023-10-31 890.0 3 ORD-104 2023-11-05 3100.2 4 ORD-105 2023-11-20 760.0 5 ORD-106 2023-12-01 2150.0

确认 order_date 列的 dtype 为 datetime64[ns] 后,即可放心地运用以下四种方法提取任意时间区间的数据。

二、方法一:布尔索引组合(& 运算符)

布尔索引是 Pandas 中最基础也是最通用的过滤机制。通过使用大于等于(>=)和小于等于(<=)两个布尔向量,配合逻辑与(&)运算符,即可圈定目标时间窗口。

Python 3 示例代码:使用布尔逻辑与筛选 10 月 15 日至 11 月 5 日的数据
# 设定日期边界(字符串格式会自动隐式转换为 Timestamp 比对)
start_date = '2023-10-15'
end_date = '2023-11-05'

# 执行布尔切片(注意:每个比较条件必须用圆括号括起来)
filtered_df = df[(df['order_date'] >= start_date) & (df['order_date'] <= end_date)]

print(filtered_df)
示例运行输出
order_id order_date amount 1 ORD-102 2023-10-15 450.0 2 ORD-103 2023-10-31 890.0 3 ORD-104 2023-11-05 3100.2
编写提醒:必须严格添加小括号
在 Python 中,位运算符 & 的执行优先级高于比较运算符 >= 和 <=。如果写成 df[df['order_date'] >= start_date & df['order_date'] <= end_date],Python 会优先计算 start_date & df['order_date'],从而抛出 TypeError: cannot compare a dtyped [datetime64[ns]] array with a scalar of type [bool] 错误。

三、方法二:使用 Series.between() 方法(推荐首选)

针对单一列的连续闭区间或半开区间筛选,pandas 提供了语义更加精炼的内置方法 Series.between(left, right, inclusive='both')。该方法避免了重复书写两次列名,同时自带边界开闭配置。

Python 3 示例代码:使用 between() 实现区间过滤
# 使用 between 方法提取 2023-10-15 至 2023-11-05 区间(双闭区间)
result_between = df[df['order_date'].between('2023-10-15', '2023-11-05', inclusive='both')]

print(result_between)
示例运行输出
order_id order_date amount 1 ORD-102 2023-10-15 450.0 2 ORD-103 2023-10-31 890.0 3 ORD-104 2023-11-05 3100.2

在默认情况下,inclusive='both' 会同时包含起始日期与截止日期。如果业务场景需要排除端点,可以指定不同的参数取值:

  • inclusive='both':两端皆闭 [left, right](默认值);
  • inclusive='neither':两端皆开 (left, right),不含两端日期;
  • inclusive='left':左闭右开 [left, right);
  • inclusive='right':左开右闭 (left, right]。
Python pandas怎么根据日期范围筛选DataFrame数据
图 2:between() 方法的四种边界模式与端点判定图解

例如,若只需筛选 10 月 15 日之后、但不含 11 月 5 日当天的数据,只需指定 inclusive='left':

Python 3 示例代码:左闭右开区间筛选
left_only = df[df['order_date'].between('2023-10-15', '2023-11-05', inclusive='left')]
print(left_only[['order_id', 'order_date']])
示例运行输出
order_id order_date 1 ORD-102 2023-10-15 2 ORD-103 2023-10-31

四、方法三:使用 DataFrame.query() 表达式筛选

DataFrame.query() 允许使用类似 SQL WHERE 子句的字符串表达式来进行条件过滤。对于日期范围判断,query() 原生支持如同数学表达式一般的连续比较运算符,如 @start <= date <= @end。

Python 3 示例代码:query() 表达式与外部变量引用
start_val = '2023-10-15'
end_val = '2023-11-05'

# 在 query 字符串中,前缀 '@' 符号代表引用当前 Python 环境中的局部或全局变量
query_df = df.query('@start_val <= order_date <= @end_val')

print(query_df)
示例运行输出
order_id order_date amount 1 ORD-102 2023-10-15 450.0 2 ORD-103 2023-10-31 890.0 3 ORD-104 2023-11-05 3100.2

query() 的显著优势在于:当需要进行多步链式处理(Method Chaining)或由外部函数动态组装查询参数时,代码十分整洁直观,避免在长链式语句中嵌套多次 df[...] 引用。

五、方法四:设置 DatetimeIndex 并使用 .loc 切片

当处理专门的时间序列数据(如股票行情、服务器监控指标、传感器日志)时,将日期时间列提升为 DataFrame 的索引(DatetimeIndex)是业界标准范式。通过 .loc[start:end],不仅能够实现闭区间连续切片,还能享受 pandas 强大的时间解析加速与部分字符串匹配功能。

Python 3 示例代码:DatetimeIndex 标签切片与部分字符串匹配
# 1. 复制一份数据并将 order_date 设置为索引
ts_df = df.set_index('order_date')

# 2. 关键前提:按时间顺序排序索引(未排序的切片可能导致报错或低效)
ts_df = ts_df.sort_index()

# 3. 使用 .loc 语法切片(日期字符串两端均包含)
sliced_df = ts_df.loc['2023-10-15':'2023-11-05']
print(sliced_df)

# 4. 支持更高级的部分匹配:直接截取 2023 年 10 月整月
october_df = ts_df.loc['2023-10']
print("\n-- 2023年10月份整月数据 --")
print(october_df)
示例运行输出
order_id amount order_date 2023-10-15 ORD-102 450.0 2023-10-31 ORD-103 890.0 2023-11-05 ORD-104 3100.2 -- 2023年10月份整月数据 -- order_id amount order_date 2023-10-01 ORD-101 1200.5 2023-10-15 ORD-102 450.0 2023-10-31 ORD-103 890.0
Python pandas怎么根据日期范围筛选DataFrame数据
图 3:四种日期筛选方案的语法、特性与适用场景全维度对比
筛选方法 基本语法 边界开闭控制 适用场景与建议
布尔索引组合 df[(df['d'] >= s) & (df['d'] <= e)] 由 >= / > 自行掌控 需要与其他非日期条件自由组合时使用
Series.between() df[df['d'].between(s, e)] 通过 inclusive 参数调节 常规场景首选,语法最紧凑、不易漏写括号
DataFrame.query() df.query('@s <= d <= @e') 支持 <= 与 < 链式流水线数据清洗、外部动态传参
DatetimeIndex .loc df.sort_index().loc[s:e] 切片默认为双闭区间 高频时间序列分析、支持按年/按月模糊切片

六、实际开发中的三大易错点与解决方案

1. 时分秒截断问题(最常见的静默逻辑错误)

如果数据中的时间列带有具体的时分秒(如来自业务系统的打卡时间或支付流水:2023-10-31 16:45:00),直接使用 end_date = '2023-10-31' 作为截止点进行比对,会出现数据丢失。

因为纯日期字符串 '2023-10-31' 在转换为时间戳时,默认时刻为当天的零点 00:00:00。因此,任何发生在 10 月 31 日白天或晚上的记录都会因为大于零点而被过滤掉。

解决方式有以下两种:

  • 方式 A(明确截止时分秒):将结束参数明确设定为当天最后一刻 end_date = '2023-10-31 23:59:59.999999',或将判断上限改为次日零点配合小于号 order_date < '2023-11-01'。
  • 方式 B(抹平时分秒):利用 df['order_date'].dt.normalize() 将所有时间统一规整到当天的 00:00:00,再进行日期比对。

2. 时区不匹配错误(Cannot compare tz-naive and tz-aware timestamps)

如果 DataFrame 的日期列包含时区信息(如通过 API 获取的 UTC 时间,dtype 为 datetime64[ns, UTC]),而用来筛选的变量是无时区的字符串或 Timestamp,执行比较时 pandas 会抛出时区异常。

解决方案:对比较变量补充时区声明,如使用 pd.Timestamp('2023-10-15', tz='UTC'),或者先用 df['order_date'].dt.tz_localize(None) 移除数据列中的时区约束,使其处于相同的时区维度。

3. 缺失值(NaT)的自动忽略与显式排查

当日期列中包含 NaT(Not a Time,时间缺失值)时,无论是 between() 还是布尔比较,NaT 与任何日期的比较结果都会判定为 False,即自动被过滤排除。如果业务上需要额外审计含有缺失时间的行,应通过 df[df['order_date'].isna()] 单独检查,切勿遗漏。

Python pandas怎么根据日期范围筛选DataFrame数据
图 4:Pandas 日期范围筛选决策树与异常排障清单

七、可直接复用的综合实战代码模板

在日常脚本或工程代码中,推荐将上述清洗、类型转换与范围筛选封装为鲁棒性良好的通用函数:

Python 3 综合代码模板
import pandas as pd
from typing import Optional

def filter_by_date_range(
    df: pd.DataFrame,
    date_col: str,
    start_date: Optional[str] = None,
    end_date: Optional[str] = None,
    include_end_day: bool = True
) -> pd.DataFrame:
    """
    安全地按日期范围筛选 DataFrame 数据
    """
    # 浅拷贝避免 SettingWithCopyWarning
    res = df.copy()
    
    # 1. 确保目标列为 datetime 类型
    if not pd.api.types.is_datetime64_any_dtype(res[date_col]):
        res[date_col] = pd.to_datetime(res[date_col], errors='coerce')
    
    # 2. 起始边界处理
    if start_date is not None:
        t_start = pd.to_datetime(start_date)
        res = res[res[date_col] >= t_start]
        
    # 3. 结束边界处理(支持覆盖全天)
    if end_date is not None:
        t_end = pd.to_datetime(end_date)
        if include_end_day and t_end.hour == 0 and t_end.minute == 0 and t_end.second == 0:
            # 自动延展至当天 23:59:59.999999
            t_end = t_end + pd.Timedelta(days=1, microseconds=-1)
        res = res[res[date_col] <= t_end]
        
    return res

# 示例调用
cleaned_df = filter_by_date_range(df, date_col='order_date', start_date='2023-10-01', end_date='2023-10-31')
print(cleaned_df)
相关专题:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。