author: 专注Python实战,分享爬虫与数据分析干货
title: Python爬虫实战⑳|Pandas时间序列,趋势分析一网打尽
update: 2026-04-26
tags: Python,Pandas,时间序列,时间处理,趋势分析,rolling,shift,resample
作者:专注Python实战,分享爬虫与数据分析干货
更新时间:2026年4月
适合人群:有Pandas基础、想分析时间趋势的开发者
前言:时间序列 = 数据分析的"时间维度"
爬虫数据通常都有时间字段:
- 每天的价格波动
- 每月销量趋势
- 每小时的访问量
掌握时间序列,就能回答"趋势如何?未来怎样?"
一、时间数据处理
1.1 日期解析与创建
python
import pandas as pd
import numpy as np
# 解析日期字符串
df = pd.DataFrame({
"日期": ["2026-01-01", "2026-01-02", "2026-01-03", "2026-01-04"],
"销量": [100, 120, 95, 130],
})
df["日期"] = pd.to_datetime(df["日期"])
df = df.set_index("日期") # 设为索引
print(df)
# 创建日期范围
dates = pd.date_range("2026-01-01", periods=30, freq="D") # 30天
dates = pd.date_range("2026-01-01", "2026-12-31", freq="M") # 每月末
dates = pd.date_range("2026-01-01", "2026-12-31", freq="W") # 每周日
1.2 日期属性提取
python
df = pd.DataFrame({
"日期": pd.date_range("2026-01-01", periods=365, freq="D"),
"销量": np.random.randint(50, 200, 365),
})
df["年份"] = df["日期"].dt.year
df["月份"] = df["日期"].dt.month
df["日"] = df["日期"].dt.day
df["星期"] = df["日期"].dt.dayofweek # 0=周一, 6=周日
df["周名"] = df["日期"].dt.day_name()
df["季度"] = df["日期"].dt.quarter
df["是否周末"] = df["日期"].dt.dayofweek >= 5
df["周数"] = df["日期"].dt.isocalendar().week.astype(int)
print(df.head(10))
二、时间重采样(resample)
2.1 降采样(高频→低频)
python
# 生成日级数据
df = pd.DataFrame({
"日期": pd.date_range("2026-01-01", periods=90, freq="D"),
"销量": np.random.randint(50, 200, 90),
"金额": np.random.uniform(1000, 5000, 90).round(2),
})
df = df.set_index("日期")
# 日 → 周
weekly = df.resample("W").agg({"销量": "sum", "金额": "sum"})
print("周数据:")
print(weekly.head())
# 日 → 月
monthly = df.resample("M").agg({"销量": "sum", "金额": "mean"})
print("\n月数据:")
print(monthly)
# 日 → 季
quarterly = df.resample("Q").agg({"销量": "sum", "金额": "mean"})
print("\n季数据:")
print(quarterly)
2.2 升采样(低频→高频)
python
# 月 → 日(插值填充)
monthly_df = pd.DataFrame({
"日期": pd.date_range("2026-01-01", periods=3, freq="MS"),
"价格": [3999, 3799, 3599],
}).set_index("日期")
daily = monthly_df.resample("D").interpolate(method="linear")
print(daily.head(10))
三、滑动窗口(rolling)
3.1 移动平均
python
df = pd.DataFrame({
"日期": pd.date_range("2026-01-01", periods=30, freq="D"),
"销量": np.random.randint(50, 200, 30),
}).set_index("日期")
# 7日移动平均
df["MA7"] = df["销量"].rolling(window=7).mean()
# 30日移动平均
df["MA30"] = df["销量"].rolling(window=30).mean()
# 移动标准差
df["STD7"] = df["销量"].rolling(window=7).std()
print(df.head(15))
3.2 滑动窗口统计
python
# 多种统计
df_rolling = df["销量"].rolling(window=7).agg(["mean", "std", "min", "max"])
print(df_rolling.tail())
# 指数加权移动平均(EWMA)
df["EWMA7"] = df["销量"].ewm(span=7).mean()
四、滞后与差分(shift/diff)
python
df = pd.DataFrame({
"日期": pd.date_range("2026-01-01", periods=10, freq="D"),
"价格": [3999, 4050, 3980, 4100, 4200, 4150, 4300, 4250, 4400, 4350],
}).set_index("日期")
# 滞后(上一天的价格)
df["昨日价格"] = df["价格"].shift(1)
# 涨跌额
df["涨跌额"] = df["价格"].diff(1)
# 涨跌幅
df["涨跌幅"] = df["价格"].pct_change(1) * 100
# 累计涨跌幅
df["累计涨跌幅"] = (1 + df["价格"].pct_change(1)).cumprod() - 1
print(df.round(2))
五、实战:价格趋势分析
python
import pandas as pd
import numpy as np
# 模拟一年价格数据
np.random.seed(42)
dates = pd.date_range("2025-01-01", "2025-12-31", freq="D")
prices = 4000 + np.cumsum(np.random.randn(len(dates)) * 20)
df = pd.DataFrame({"日期": dates, "价格": prices.round(2)})
df = df.set_index("日期")
# 1. 基本统计
print("=== 年度价格统计 ===")
print(f"最高价: {df['价格'].max():.2f}")
print(f"最低价: {df['价格'].min():.2f}")
print(f"平均价: {df['价格'].mean():.2f}")
print(f"年度涨跌: {((df['价格'].iloc[-1] / df['价格'].iloc[0]) - 1) * 100:.1f}%")
# 2. 月度趋势
monthly = df.resample("M").agg({"价格": ["first", "last", "mean", "max", "min"]})
monthly.columns = ["月初", "月末", "月均价", "月最高", "月最低"]
monthly["月涨跌幅"] = ((monthly["月末"] / monthly["月初"]) - 1) * 100
print("\n=== 月度趋势 ===")
print(monthly.round(2))
# 3. 移动平均线
df["MA5"] = df["价格"].rolling(5).mean()
df["MA20"] = df["价格"].rolling(20).mean()
df["MA60"] = df["价格"].rolling(60).mean()
# 金叉/死叉信号
df["信号"] = np.where(df["MA5"] > df["MA20"], "金叉", "死叉")
cross = df[df["信号"] != df["信号"].shift(1)]
print(f"\n=== 交叉信号(共{len(cross)}次) ===")
print(cross[["价格", "MA5", "MA20", "信号"]].head(10))
# 4. 波动率
df["日波动"] = df["价格"].pct_change().rolling(20).std() * np.sqrt(252) * 100
print(f"\n平均年化波动率: {df['日波动'].mean():.1f}%")
六、知识卡
| 方法 | 说明 |
|---|---|
| pd.to_datetime() | 解析日期 |
| dt.year/month/day | 提取日期属性 |
| resample("M") | 月度重采样 |
| resample("W") | 周度重采样 |
| rolling(n) | n期滑动窗口 |
| ewm(span=n) | 指数加权移动平均 |
| shift(n) | 滞后n期 |
| diff(n) | 差分n期 |
| pct_change() | 变化率 |
| cumprod() | 累计乘积 |
| interpolate() | 插值填充 |
七、课后作业
必做题:
- 解析日期字段并提取年/月/日属性
- 用resample按月统计销量趋势
- 计算7日移动平均线
选做题:
- 实现完整的价格趋势分析
- 用rolling和shift构建简单的交易信号
有问题欢迎评论区留言,大家一起讨论!
标签:Python | Pandas | 时间序列 | 趋势分析 | rolling | resample | shift