自学教程 学编程从这里开始

Pandas 时间类型

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 核心时间类型:

  1. datetime64[ns]:日期时间(最常用)
  2. datetime64[ns, tz]:带时区的时间
  3. timedelta64[ns]:时间差(间隔)
  4. 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 占位符

  • %Y 4位年,%m 月,%d 日
  • %H 24小时,%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()

十一、高频坑清单

  1. 字符串直接调用 .dt → 报错,必须先 pd.to_datetime
  2. 空时间是 NaT,不是 np.nan;判断空值依旧用 isna()
  3. dt.date 返回 Python date 对象,失去时间,用于单日筛选;不要用于大小比较
  4. 带时区和不带时区的 datetime 直接运算会报错
  5. dt.seconds 只算当天时分秒,跨天要用 dt.total_seconds()
  6. strftime 遇到 NaT 会返回 NaN,不会报错
  7. 原始数据格式杂乱时,不加 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 条笔记