Python爬虫实战⑳|Pandas时间序列,趋势分析一网打尽


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() 插值填充

七、课后作业

必做题:

  1. 解析日期字段并提取年/月/日属性
  2. 用resample按月统计销量趋势
  3. 计算7日移动平均线

选做题:

  1. 实现完整的价格趋势分析
  2. 用rolling和shift构建简单的交易信号

有问题欢迎评论区留言,大家一起讨论!


标签:Python | Pandas | 时间序列 | 趋势分析 | rolling | resample | shift

相关推荐
金融大 k2 小时前
多市场行情时间戳对齐:UTC 存储的夏令时陷阱与数据库设计方案
python·websocket·行情数据
risc1234562 小时前
python 的字符串前缀
开发语言·python
如竟没有火炬2 小时前
字符串相乘——int数组转字符串
开发语言·数据结构·python·算法·leetcode·深度优先
Pkmer2 小时前
古法编程·新解:Python 类型注解的"一箭三雕"之术
python·ai编程
吃好睡好便好2 小时前
在Matlab中绘制三维等高线图
开发语言·python·学习·算法·matlab·信息可视化
keineahnung23452 小时前
PyTorch symbolic_shapes 模組的 is_contiguous 從哪來?── sizes_strides_user 安裝與實作解析
人工智能·pytorch·python·深度学习
C137的本贾尼3 小时前
别怕异步:`async` 和 `await` 的简单理解
开发语言·python
__log3 小时前
ComfyUI 集成技术方案分析报告
javascript·python·django