Pandas: 日期时间
最后更新:2026-08-26
时间序列分析的第一步是把字符串日期变成 Pandas 时间类型。DatetimeIndex 开启了按周/月/季度聚合的大门,dt 访问器让你像拆解字符串一样拆解日期,Timedelta 让日期运算变得简单。本节覆盖时间类型体系、解析、提取和运算。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。
1. 你将学到
- ❶ Timestamp / DatetimeIndex
- ❷ pd.to_datetime 解析
- ❸ .dt 访问器
- ❹ Timedelta 运算
- ❺ date_range 与频率
2. Charlie 的气象站时间困境
(1) 痛点:日期是字符串,无法按月聚合
Charlie 的气象站数据里日期是字符串,无法按月统计:
PYTHON
import pandas as pd
df = pd.DataFrame({
'date_str': ['2024-01-15', '2024-01-20', '2024-02-10', '2024-02-28', '2024-03-15'],
'temperature': [5.2, 3.8, 8.1, 9.5, 12.3]
})
# df.groupby(df['date_str'].str[:7]) — fragile hack!
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 解法:to_datetime + dt
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:to_datetime 解析与提取(难度⭐)
PYTHON
import pandas as pd
df = pd.DataFrame({
'date_str': ['2024-01-15', '2024-01-20', '2024-02-10', '2024-02-28', '2024-03-15'],
'temperature': [5.2, 3.8, 8.1, 9.5, 12.3]
})
# Convert string to datetime
df['date'] = pd.to_datetime(df['date_str'])
# Extract month for grouping
df['month'] = df['date'].dt.month
# Group by month
monthly = df.groupby('month')['temperature'].mean()
print(monthly)
# month
# 1 4.50
# 2 8.80
# 3 12.30
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
3. Pandas 时间类型体系
(1) 4 种时间类型
| 类型 | 用途 | 示例 |
|---|---|---|
| Timestamp | 单个时间点 | pd.Timestamp('2024-01-15') |
| DatetimeIndex | 时间点序列 | pd.date_range('2024-01', periods=3, freq='M') |
| Timedelta | 时间差 | pd.Timedelta(days=7) |
| Period | 时间段 | pd.Period('2024-01', freq='M') |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:时间类型创建(难度⭐)
PYTHON
import pandas as pd
# Timestamp — single point in time
ts = pd.Timestamp('2024-01-15 10:30:00')
print(ts.year, ts.month, ts.day, ts.hour)
# 2024 1 15 10
# DatetimeIndex — sequence of timestamps
idx = pd.date_range('2024-01-01', periods=5, freq='D')
print(idx)
# DatetimeIndex(['2024-01-01', '2024-01-02', ...], dtype='datetime64[ns]')
# Timedelta — duration
delta = pd.Timedelta(days=7, hours=3)
print(delta) # 7 days 03:00:00
print(ts + delta) # 2024-01-22 13:30:00
# Period — time span (month/quarter/year)
p = pd.Period('2024-Q1', freq='Q')
print(p.start_time, p.end_time)
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
4. pd.to_datetime
(1) 解析多种格式
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:to_datetime 多格式解析(难度⭐⭐)
PYTHON
import pandas as pd
# ISO format (auto-detected)
dates1 = pd.to_datetime(['2024-01-15', '2024-02-20', '2024-03-10'])
print(dates1)
# Mixed formats
dates2 = pd.to_datetime(['01/15/2024', 'Feb 20, 2024', '20240310'])
print(dates2)
# Specify format explicitly (faster for large data)
dates3 = pd.to_datetime(['15-01-2024', '20-02-2024'], format='%d-%m-%Y')
print(dates3)
# Handle errors
bad_dates = pd.to_datetime(
['2024-01-15', 'not-a-date', '2024-03-10'],
errors='coerce' # invalid → NaT (not errors='raise')
)
print(bad_dates)
# DatetimeIndex(['2024-01-15', 'NaT', '2024-03-10'])
# Unix timestamp
unix_ts = pd.to_datetime([1705276800, 1707974400], unit='s')
print(unix_ts)
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 常用 format 代码
| 代码 | 含义 | 示例 |
|---|---|---|
| %Y | 4位年 | 2024 |
| %m | 2位月 | 01 |
| %d | 2位日 | 15 |
| %H | 24小时 | 14 |
| %M | 分钟 | 30 |
| %S | 秒 | 00 |
| %b | 月名缩写 | Jan |
| %B | 月名全称 | January |
| %a | 星期缩写 | Mon |
5. .dt 访问器
(1) 提取时间属性
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:dt 访问器属性(难度⭐⭐)
PYTHON
import pandas as pd
df = pd.DataFrame({
'timestamp': pd.date_range('2024-01-15 08:30:00', periods=5, freq='12h')
})
# Extract date components
df['year'] = df['timestamp'].dt.year
df['month'] = df['timestamp'].dt.month
df['day'] = df['timestamp'].dt.day
df['hour'] = df['timestamp'].dt.hour
df['day_of_week'] = df['timestamp'].dt.dayofweek # 0=Mon, 6=Sun
df['day_name'] = df['timestamp'].dt.day_name() # Monday, Tuesday...
df['is_weekend'] = df['timestamp'].dt.dayofweek >= 5
df['quarter'] = df['timestamp'].dt.quarter
print(df[['timestamp', 'month', 'day_name', 'is_weekend', 'quarter']])
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) dt 方法
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:dt 方法操作(难度⭐)
PYTHON
import pandas as pd
dates = pd.Series(pd.date_range('2024-01-15', periods=5, freq='2D'))
# Convert to period
print(dates.dt.to_period('M')) # monthly periods
# Normalize to midnight (remove time component)
print(dates.dt.normalize())
# Round to nearest hour
timestamps = pd.Series(pd.date_range('2024-01-15 08:35:00', periods=3, freq='45min'))
print(timestamps.dt.round('h')) # round to nearest hour
# Floor / Ceil
print(timestamps.dt.floor('h')) # round down
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
6. Timedelta 运算
(1) 日期加减
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:Timedelta 运算(难度⭐⭐)
PYTHON
import pandas as pd
# Create timestamps
start = pd.Timestamp('2024-01-15')
deadline = start + pd.Timedelta(days=30)
print(f"Deadline: {deadline}")
# Difference between dates
delivery = pd.Timestamp('2024-02-10')
delay = delivery - deadline
print(f"Delay: {delay.days} days") # -4 days (early)
# Timedelta from string
td = pd.to_timedelta('5 days 3 hours')
print(td)
# Operations on Series
df = pd.DataFrame({
'order_date': pd.date_range('2024-01-01', periods=5),
'delivery_days': [3, 5, 2, 7, 4]
})
df['delivery_date'] = df['order_date'] + pd.to_timedelta(df['delivery_days'], unit='D')
df['is_late'] = df['delivery_days'] > 5
print(df)
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
7. date_range 与频率
(1) 生成日期序列
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:date_range 生成日期(难度⭐⭐)
PYTHON
import pandas as pd
# Fixed number of periods
print(pd.date_range('2024-01-01', periods=5, freq='D'))
# 2024-01-01 to 2024-01-05
# Start + End
print(pd.date_range('2024-01-01', '2024-01-10', freq='2D'))
# 2024-01-01, 2024-01-03, 2024-01-05, 2024-01-07, 2024-01-09
# Business days only
bdays = pd.bdate_range('2024-01-01', periods=5) # skip weekends
print(bdays)
# Monthly frequency
months = pd.date_range('2024-01', periods=4, freq='MS') # Month Start
print(months)
# Hourly
hours = pd.date_range('2024-01-15 09:00', periods=4, freq='h')
print(hours)
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 频率别名速查
| 别名 | 含义 | 示例 |
|---|---|---|
| D | 日历日 | 每天 |
| B | 工作日 | 跳过周末 |
| W | 周末 | 每周日 |
| MS | 月初 | 每月1号 |
| M | 月末 | 每月最后一天 |
| QS | 季初 | 每季第一天 |
| Q | 季末 | 每季最后一天 |
| H | 小时 | 每小时 |
| T/min | 分钟 | 每分钟 |
8. 完整示例:咖啡店时间序列分析
(5) ▶ 时间类型体系
graph LR
A[Pandas 时间类型] --> B[Timestamp 时间点]
A --> C[DatetimeIndex 时间序列]
A --> D[Timedelta 时间差]
A --> E[Period 时间段]
B --> F[单个时刻]
C --> G[按频率聚合]
D --> H[日期加减运算]
E --> I[月度/季度聚合]
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:时间序列分析全流程(难度⭐⭐⭐)
PYTHON
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: Coffee shop time
# series analysis with datetime operations
# ============================================
# 1. Create daily sales data for 90 days
np.random.seed(42)
df = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=90, freq='D'),
'sales': np.random.poisson(50, 90) * 10,
'customers': np.random.poisson(30, 90)
})
df = df.set_index('date')
# 2. Extract time features
df['month'] = df.index.month
df['day_of_week'] = df.index.dayofweek
df['day_name'] = df.index.day_name()
df['is_weekend'] = df.index.dayofweek >= 5
df['week_number'] = df.index.isocalendar().week.astype(int)
# 3. Monthly summary
monthly = df.groupby('month').agg(
total_sales=('sales', 'sum'),
avg_customers=('customers', 'mean'),
days=('sales', 'count')
).round(0)
print("=== Monthly Summary ===")
print(monthly)
# 4. Weekday vs Weekend
wknd = df.groupby('is_weekend')['sales'].mean().round(0)
print(f"\n=== Weekday vs Weekend ===\n{wknd}")
# 5. Best day of week
best_day = df.groupby('day_name')['sales'].mean().sort_values(ascending=False)
print(f"\n=== Best Day ===\n{best_day.head(3)}")
# 6. Month-over-month growth
monthly_sales = df.resample('M')['sales'].sum()
mom_growth = monthly_sales.pct_change().round(3) * 100
print(f"\n=== MoM Growth % ===\n{mom_growth}")
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
❓ 常见问题
Q to_datetime 格式怎么写?
A ISO 格式(2024-01-15)自动识别,无需指定 format。非标准格式用 format 参数:
pd.to_datetime(s, format='%d/%m/%Y')。Python strftime 格式代码:%Y年 %m月 %d日 %H时 %M分 %S秒。指定 format 比自动检测快 5-10 倍。Q .dt 和 .str 类似吗?
A 类似——.str 是字符串访问器,.dt 是时间访问器。都用 .访问器.方法() 语法,都自动跳过 NaN/NaT,都支持链式调用。.str 操作字符串列,.dt 操作 datetime 列。
Q Timedelta 支持什么运算?
A Timestamp ± Timedelta = Timestamp(日期加减)。Timestamp - Timestamp = Timedelta(日期差)。Timedelta ± Timedelta = Timedelta(时间差加减)。Timedelta × int = Timedelta(时间差倍数)。不支持 Timedelta + Timedelta = Timestamp(无意义)。
Q 频率 D 和 B 区别?
A D=日历日(包含周末),B=工作日(跳过周末)。
date_range(freq='D') 每天,bdate_range() 或 freq='B' 只工作日。月频 MS=月初,M=月末。选错频率会导致 resample 结果差异很大。Q 时区怎么处理?
A 用 tz_localize 设置时区,tz_convert 转换时区:
df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')。Pandas 用 pytz/dateutil 时区库。常见操作:读入 UTC 时间→转本地时区→按本地日期聚合。Q NaT 是什么?
A NaT(Not a Time)是时间类型的缺失值,等价于 NaN。pd.to_datetime(errors='coerce') 把无效日期转为 NaT。NaT 参与运算结果为 NaT(类似 NaN)。用 pd.isna() / pd.notna() 检测。
Q Period 和 Timestamp 区别?
A Timestamp 是时间点(2024-01-15 10:30:00),Period 是时间段(2024年1月)。Period 有 start_time 和 end_time。月度/季度报表用 Period,事件时间戳用 Timestamp。resample 产出 Period,rolling 需要 Timestamp。
📖 小节
- Pandas 4 种时间类型:Timestamp(时间点)/ DatetimeIndex(时间序列)/ Timedelta(时间差)/ Period(时间段)
- pd.to_datetime 解析字符串日期,format 参数指定格式,errors='coerce' 容错
- .dt 访问器提取时间属性(year/month/day/hour/dayofweek/quarter 等)
- Timedelta 支持日期加减运算,pd.to_timedelta 从字符串/数值创建
- date_range 生成日期序列,频率别名 D/B/W/MS/M/QS/Q/H
- DatetimeIndex 作为索引开启时间序列分析(resample/shift/rolling)
- 工作日用 bdate_range 或 freq='B',时区用 tz_localize/tz_convert
📝 作业
- 基础题(难度⭐):创建含日期字符串的 Series,用 to_datetime 解析,提取 month 和 dayofweek,统计每月记录数。
- 进阶题(难度⭐⭐):创建 30 天日销售 DataFrame(DatetimeIndex),用 dt 提取 is_weekend,对比工作日和周末的平均销售额,计算 Timedelta 找出销售最高日距今多久。
- 挑战题(难度⭐⭐⭐):模拟 90 天咖啡店销售数据,完成:to_datetime 解析→set_index→dt 提取月/季度/星期→groupby 月度汇总→计算环比增长率→找出最佳工作日。