Pandas 时间类型与 dt 访问器
承接《Pandas 字符串处理 str 访问器》,本文讲解 Pandas 时间日期类型、字符串转时间、时区、dt 访问器全套属性与方法、时间筛选、时间偏移、时间差计算,是报表、时序、订单流水分析高频工具。延续系列教程风格。
前置导入与示例数据
import pandas as pd
import numpy as np
df = pd.DataFrame({
"order_id": [1001,1002,1003,1004,1005,1006],
"order_time": [
"2025-01-05 08:30:22",
"2025-01-05 14:20:10",
"2025-01-06 09:10:33",
"2025-01-07 23:05:18",
"2025-01-08 11:44:55",
None
],
"pay_time": [
"2025-01-05 08:35:10",
"2025-01-05 14:22:01",
"2025-01-06 09:20:40",
"2025-01-07 23:10:22",
"2025-01-08 12:01:30",
"2025-01-09 02:10:00"
]
})
print(df)
一、时间类型基础
Pandas 核心时间类型:
datetime64[ns]:日期时间(最常用)datetime64[ns, tz]:带时区的时间timedelta64[ns]:时间差(间隔)Period:周期(年月、季度)
字符串时间不能直接使用 dt 访问器,必须先转成 datetime64。
缺失值:pd.NaT(Not a Time,时间专属空值,等价于时间版的 pd.NA)
二、字符串转时间 to_datetime
# 基础转换
df["order_time"] = pd.to_datetime(df["order_time"])
df["pay_time"] = pd.to_datetime(df["pay_time"])
# 指定格式(解析速度更快,适合固定格式)
pd.to_datetime(df["order_time"], format="%Y-%m-%d %H:%M:%S")
# 处理异常值:errors="coerce" 无法解析的转为 NaT
pd.to_datetime(df["order_time"], errors="coerce")
常用 format 占位符
%Y4位年,%m月,%d日%H24小时,%M分钟,%S秒%w星期(0=周日,6=周六)
三、dt 访问器:日期时间属性
转换为 datetime 之后,就可以用 .dt 批量提取时间分量
# 年
df["order_time"].dt.year
# 月
df["order_time"].dt.month
# 日
df["order_time"].dt.day
# 星期几(0=周日,1=周一…6=周六)
df["order_time"].dt.dayofweek
# 星期名称
df["order_time"].dt.day_name()
# 一年中第几天
df["order_time"].dt.dayofyear
# 季度
df["order_time"].dt.quarter
# 小时
df["order_time"].dt.hour
# 分钟
df["order_time"].dt.minute
# 秒
df["order_time"].dt.second
# 是否月底
df["order_time"].dt.is_month_end
# 是否月初
df["order_time"].dt.is_month_start
# 是否年末
df["order_time"].dt.is_year_end
四、dt 访问器:时间格式化输出
# 转为指定格式字符串
df["order_time"].dt.strftime("%Y-%m-%d")
df["order_time"].dt.strftime("%Y-%m-%d %H:%M")
五、时间筛选(业务高频)
# 筛选2025-01-05当天订单
res = df.loc[df["order_time"].dt.date == pd.to_datetime("2025-01-05").date()]
# 范围筛选
start = pd.to_datetime("2025-01-05")
end = pd.to_datetime("2025-01-07 23:59:59")
res = df.loc[(df["order_time"] >= start) & (df["order_time"] <= end)]
# 按小时筛选:早8点-晚18点
res = df.loc[(df["order_time"].dt.hour >= 8) & (df["order_time"].dt.hour <= 18)]
六、时间差 Timedelta
两个 datetime 相减得到 timedelta64 类型
# 计算支付耗时
df["pay_cost"] = df["pay_time"] - df["order_time"]
# timedelta 同样支持 dt 访问器
df["pay_cost"].dt.total_seconds() # 总秒数
df["pay_cost"].dt.seconds # 时分秒部分秒数(不含天数)
df["pay_cost"].dt.days # 天数
手动构造时间间隔:
pd.Timedelta(days=1, hours=2)
pd.Timedelta("30min")
七、时间偏移、加减时间
# 所有订单时间 + 30分钟
df["order_time"] + pd.Timedelta(minutes=30)
# 往前推1天
df["order_time"] - pd.Timedelta(days=1)
八、时区处理(dt.tz)
# 本地化:给无时区时间绑定时区
df["order_time_tz"] = df["order_time"].dt.tz_localize("Asia/Shanghai")
# 时区转换:上海 → UTC
df["order_time_utc"] = df["order_time_tz"].dt.tz_convert("UTC")
# 移除时区
df["order_time_local"] = df["order_time_tz"].dt.tz_localize(None)
注意:无时区 datetime 和带时区 datetime 不能直接比较、相减。
九、时间聚合、重采样 resample
resample 是时间序列专属分组
# 先设置时间索引
df_idx = df.set_index("order_time")
# 按天聚合
df_idx.resample("D")["order_id"].count()
# 按小时聚合
df_idx.resample("H")["order_id"].count()
常用频率标识:
D天,H小时,T分钟,S秒M月末,MS月初,Q季度
十、Period 周期类型(年月分组)
# 转为年月周期
df["order_month"] = df["order_time"].dt.to_period("M")
# 按年月分组统计
df.groupby("order_month")["order_id"].count()
十一、高频坑清单
- 字符串直接调用
.dt→ 报错,必须先pd.to_datetime - 空时间是
NaT,不是 np.nan;判断空值依旧用isna() dt.date返回 Python date 对象,失去时间,用于单日筛选;不要用于大小比较- 带时区和不带时区的 datetime 直接运算会报错
dt.seconds只算当天时分秒,跨天要用dt.total_seconds()- strftime 遇到 NaT 会返回 NaN,不会报错
- 原始数据格式杂乱时,不加
errors="coerce"直接解析会抛异常
十二、综合实战模板
def parse_datetime_series(s: pd.Series):
"""时间解析通用函数,异常转为NaT"""
return pd.to_datetime(s, errors="coerce")
df["order_time"] = parse_datetime_series(df["order_time"])
df["pay_time"] = parse_datetime_series(df["pay_time"])
# 衍生字段
df["order_date"] = df["order_time"].dt.date
df["order_hour"] = df["order_time"].dt.hour
df["weekday"] = df["order_time"].dt.dayofweek
df["pay_cost_sec"] = (df["pay_time"] - df["order_time"]).dt.total_seconds()
print(df[["order_time","order_hour","weekday","pay_cost_sec"]])
十三、dt 访问器速查表
| API | 作用 |
|---|---|
| dt.year / month / day | 年、月、日 |
| dt.hour / minute / second | 时、分、秒 |
| dt.dayofweek / day_name() | 星期 |
| dt.quarter | 季度 |
| dt.strftime(fmt) | 格式化字符串 |
| dt.is_month_start / is_month_end | 是否月初/月末 |
| dt.tz_localize / tz_convert | 时区绑定、转换 |
| dt.to_period(“M”) | 转为年月周期 |
| timedelta.dt.total_seconds() | 间隔总秒数 |
0 条笔记