第11章封面

股价、订单、传感器、日志——真实数据大多带着时间戳。pandas 对时间序列的支持深到什么程度?用时间当索引,切片比数组还顺手。这一章是做金融、运营、IoT 分析同学的必修课。

🕐 三种时间概念

from datetime import datetime
now = datetime.now()                 # 时刻:2026-09-20 14:00:00
now.year, now.month, now.day        # 各字段随意访问
now.strftime("%Y-%m-%d")            # 格式化输出
datetime.strptime("2026-09-20", "%Y-%m-%d")  # 字符串解析(较慢)

pandas 用 Timestamp(时刻)和 Period(时期,如「2026 年 9 月」这个整月)扩展了 datetime,而 to_datetime 是万能入口:

dates = pd.to_datetime(["2026-09-01", "2026-09-02"])
ts = pd.Series(np.arange(2), index=dates)

🔍 时间索引:切片爽到飞起

ts = pd.Series(rng.standard_normal(1000),
               index=pd.date_range("2024-01-01", periods=1000))

ts["2024-05"]                # 直接取一整月!
ts["2024"]                   # 取一整年
ts["2024-05-01":"2024-06-15"]  # 切片(含尾)
ts.truncate(after="2024-05-30")  # 截断

重复时间戳用 ts.index.is_unique 检查、ts.groupby(level=0).mean() 聚合去重。

📅 date_range 与频率偏移

pd.date_range("2026-04-01", "2026-06-01")          # 默认按天
pd.date_range("2026-04-01", periods=20, freq="W-MON")  # 每周一
pd.date_range(start="2026-01-01", periods=10, normalize=True)  # 归零到午夜

频率代码速查:D 天、B 工作日、H 小时、M(月末)、Q(季度末)、A(年末)、W-MON 每周一、MS 月初——还可以加倍数 4H 每 4 小时。

⏪ shift:对比的魔法

时间序列分析里「和上一期比」无处不在,shift 就是那个「上一期」:

ts.shift(1)                    # 整体后移一位
df["pct_change"] = ts / ts.shift(1) - 1     # 环比涨跌幅!
ts.shift(2, freq="M")          # 移动的是时间戳不是数据(重对齐用)

🌍 时区:不是玄学,是 tz 参数

ts_utc = ts.tz_localize("UTC")         # 给「naive」时间贴上时区
ts_utc.tz_convert("Asia/Shanghai")     # 转换到另一时区
# 两个不同时区的对象做运算,自动统一到 UTC

⚠️ 经验:内部存储一律 UTC,展示时再转本地时区,能避开夏令时的绝大多数坑。

📆 Period:把「一段时期」当元素

p = pd.Period("2026-09", freq="M")     # 「2026年9月」这个月
p + 5                                  # 2027年2月,直接做时期运算

# Timestamp ↔ Period 互转
ts.to_period("M")                      # 时间戳序列 → 月时期序列

季度数据处理(财年 Q-DEC 等)在会计/财报场景是刚需,书里有完整示例。

⚖️ resample:重采样是时间序列的心脏

把高频数据折叠成低频(降采样)或反向插值(升采样):

ts.resample("M").mean()        # 按月取均值
ts.resample("M", kind="period").sum()
# 降采样的开闭区间语义:closed="left", label="left" 控制边界归哪组
ohlc = ts.resample("5min").ohlc()   # 金融五分钟 K 线:open/high/low/close
upsampled = ts.resample("D").asfreq()   # 升采样只取频点
upsampled = ts.resample("D").ffill()    # 或前向填充

时间序列重采样

📈 rolling:移动窗口与平滑

ts.rolling(30).mean()              # 30 日均线——消除噪声看趋势
ts.rolling(30, min_periods=20).std()  # 窗口内至少 20 个有效值才算
ts.ewm(span=30).mean()             # 指数加权:近期数据权重更大
ts.rolling(60).corr(other)         # 滚动相关系数:相关性随时间变化
ts.expanding().mean()              # 扩展窗口:从起点到当前的累计均值

移动窗口是技术分析(均线、布林带)、运营监控(7 日均值告警)的基础。rolling("30D") 还能按时间长度而非条数开窗——不规则采样的救星。

📌 本章小结:时间当索引,ts["2024-05"] 直取整月;date_range(freq="W-MON") 生成规则序列;ts/ts.shift(1)-1 就是环比;时区先 tz_localizetz_convertresample 折叠时间、rolling 平滑噪声,这俩组合是时间序列分析的主武器。