python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Pandas时间序列分析

Pandas中时间序列分析的核心功能和实战指南

作者:小庄-Python办公

本文介绍了Pandas时间序列分析的核心功能,包括日期时间类型转换、日期序列生成、重采样和滑动窗口计算,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下

本节学习目标

为什么学这个?

几乎所有业务数据都和时间相关

在 Pandas 出现之前,处理时间序列数据非常痛苦。你需要手动解析日期字符串、处理闰年、计算工作日……现在,Pandas 提供了一整套时间序列工具,让时间分析变得优雅而强大。

Pandas 的时间序列优势

打个比方:如果说 DataFrame 是一个仓库,那么时间序列工具就是仓库的"时间管理器"——它能告诉你"什么时候进了多少货"、“过去 7 天平均每天出多少货”、“下个月的预期销量”。

核心知识点讲解

datetime 类型与日期解析

1. Python 的 datetime 模块

from datetime import datetime, timedelta

# 创建日期时间对象
dt = datetime(2024, 3, 15, 14, 30, 0)
print(dt)  # 2024-03-15 14:30:00

# 获取当前时间
now = datetime.now()
print(f"当前时间:{now}")

# 日期运算
tomorrow = dt + timedelta(days=1)
print(f"明天:{tomorrow}")

last_week = dt - timedelta(weeks=1)
print(f"上周:{last_week}")

# 提取各个部分
print(f"年:{dt.year}")
print(f"月:{dt.month}")
print(f"日:{dt.day}")
print(f"星期:{dt.weekday()}")  # 0=周一,6=周日
print(f"时分秒:{dt.hour}:{dt.minute}:{dt.second}")

2. Pandas 的 Timestamp 类型

import pandas as pd
import numpy as np

# Pandas 的时间戳
ts = pd.Timestamp('2024-03-15 14:30:00')
print(ts)

# 从字符串自动解析
ts = pd.Timestamp('2024/3/15')
print(ts)

ts = pd.Timestamp('15 March 2024')
print(ts)

# 获取时间属性
print(f"年:{ts.year}")
print(f"月:{ts.month}")
print(f"日:{ts.day}")
print(f"星期几:{ts.day_name()}")    # Friday
print(f"季度:{ts.quarter}")          # 1
print(f"年初至今天数:{ts.dayofyear}")  # 75
print(f"本月天数:{ts.days_in_month}")  # 31

3. to_datetime:批量转换日期列

# 这是处理时间序列数据的第一步:把字符串列转为 datetime 类型
df = pd.DataFrame({
    '日期': ['2024-01-01', '2024-02-15', '2024-03-20', '2024-04-10'],
    '销售额': [1000, 1500, 1200, 1800]
})

print("转换前:")
print(df.dtypes)
# 日期    object(字符串)

# 转换为 datetime
df['日期'] = pd.to_datetime(df['日期'])

print("\n转换后:")
print(df.dtypes)
# 日期    datetime64[ns]

# ===== 处理不同格式的日期 =====
df2 = pd.DataFrame({
    '日期': ['01/15/2024', '02/20/2024', '03/25/2024']
})
df2['日期'] = pd.to_datetime(df2['日期'], format='%m/%d/%Y')
print(df2)

# ===== 处理包含错误的日期 =====
df3 = pd.DataFrame({
    '日期': ['2024-01-01', 'invalid', '2024-03-01']
})
# errors='coerce' 将无法解析的值转为 NaT(Not a Time)
df3['日期'] = pd.to_datetime(df3['日期'], errors='coerce')
print(df3)

date_range:生成日期序列

# ===== 基本用法 =====

# 生成从 2024-01-01 开始的 10 天日期序列
dates = pd.date_range(start='2024-01-01', periods=10)
print("10天序列:")
print(dates)

# 生成到 2024-01-31 结束的所有日期
dates = pd.date_range(end='2024-01-31', periods=5)
print("\n5天序列:")
print(dates)

# 指定起止日期
dates = pd.date_range('2024-01-01', '2024-01-10')
print("\n指定起止:")
print(dates)

# ===== 不同频率 =====

# freq 参数控制频率
print("每日:", pd.date_range('2024-01-01', periods=5, freq='D'))
print("每周:", pd.date_range('2024-01-01', periods=5, freq='W'))
print("每月:", pd.date_range('2024-01-01', periods=5, freq='M'))
print("每季:", pd.date_range('2024-01-01', periods=5, freq='Q'))
print("每年:", pd.date_range('2024-01-01', periods=5, freq='Y'))
print("每小时:", pd.date_range('2024-01-01', periods=5, freq='H'))

# ===== 常用频率代码 =====
# D  —— 日历日
# B  —— 工作日(排除周末)
# W  —— 每周
# M  —— 月末
# MS —— 月初
# Q  —— 季末
# QS —— 季初
# Y  —— 年末
# YS —— 年初
# H  —— 每小时
# T/min —— 每分钟
# S  —— 每秒

# ===== 工作日序列 =====
# 只生成工作日(排除周六日)
workdays = pd.date_range('2024-01-01', periods=10, freq='B')
print("\n工作日序列:")
print(workdays)

# 自定义(如每周三)
wednesdays = pd.date_range('2024-01-01', periods=10, freq='W-WED')
print("\n每周三:")
print(wednesdays)

创建带日期索引的 DataFrame

# 最常用的做法:用 date_range 创建时间序列 DataFrame
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=30, freq='D')
df = pd.DataFrame({
    '销售额': np.random.randint(1000, 5000, 30),
    '访客数': np.random.randint(100, 500, 30),
    '转化率': np.random.uniform(0.02, 0.08, 30)
}, index=dates)

df.index.name = '日期'
print(df.head())

日期访问器(dt accessor)

转换后的 datetime 列可以通过 .dt 访问器提取各种时间属性。

df = pd.DataFrame({
    '日期': pd.date_range('2024-01-01', periods=10, freq='D'),
    '销售额': np.random.randint(1000, 5000, 10)
})

# ===== 提取时间属性 =====
df['年'] = df['日期'].dt.year
df['月'] = df['日期'].dt.month
df['日'] = df['日期'].dt.day
df['季度'] = df['日期'].dt.quarter
df['星期'] = df['日期'].dt.day_name()
df['周几'] = df['日期'].dt.dayofweek  # 0=周一
df['一年中第几天'] = df['日期'].dt.dayofyear
df['第几周'] = df['日期'].dt.isocalendar().week
df['是否月初'] = df['日期'].dt.is_month_start
df['是否月末'] = df['日期'].dt.is_month_end

print(df)

resample 重采样

resample 是时间序列分析中最核心的方法。它可以把数据从一个时间粒度转换到另一个时间粒度

类比:就像你把"按天"的数据"放大"为"按月"看,或"缩小"为"按小时"看。

# 创建日粒度数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=90, freq='D')
df = pd.DataFrame({
    '日期': dates,
    '销售额': np.random.randint(1000, 5000, 90),
    '访客数': np.random.randint(100, 500, 90)
}).set_index('日期')

# ===== 降采样(高频 → 低频)=====

# 按月汇总(求和)
monthly = df.resample('M')['销售额'].sum()
print("月度销售额:")
print(monthly)

# 按周求均值
weekly = df.resample('W')['销售额'].mean()
print("\n周均销售额:")
print(weekly)

# 按季度
quarterly = df.resample('Q').agg({
    '销售额': 'sum',
    '访客数': 'mean'
})
print("\n季度汇总:")
print(quarterly)

# ===== 升采样(低频 → 高频)=====

# 创建月度数据
monthly_df = pd.DataFrame({
    '月度目标': [50000, 60000, 55000]
}, index=pd.date_range('2024-01-01', periods=3, freq='MS'))

# 升采样到日粒度(用 NaN 填充)
daily = monthly_df.resample('D').asfreq()
print("\n升采样到日粒度(前5行):")
print(daily.head())

# 用前向填充
daily_ffill = monthly_df.resample('D').ffill()
print("\n升采样 + 前向填充(前5行):")
print(daily_ffill.head())

常用重采样频率

代码含义代码含义
DW
M月末MS月初
Q季末QS季初
Y年末YS年初
H小时T/min分钟
SB工作日

自定义聚合

# 按周重采样,同时做多种聚合
result = df.resample('W').agg({
    '销售额': ['sum', 'mean', 'max'],
    '访客数': ['sum', 'mean']
})
result.columns = ['周销售额', '周均销售额', '最高日销售', '周访客', '日均访客']
print(result.head())

rolling 滑动窗口

rolling 用于滑动窗口计算,比如计算 7 天移动平均线、30 天累计销量等。

# 继续使用上面的 df

# ===== 基本滑动窗口 =====

# 7天移动平均
df['7日移动平均'] = df['销售额'].rolling(window=7).mean()

# 30天移动平均
df['30日移动平均'] = df['销售额'].rolling(window=30).mean()

print("含移动平均:")
print(df.head(15))

# ===== 其他窗口计算 =====

# 滑动窗口求和(7天累计)
df['7天累计销售'] = df['销售额'].rolling(window=7).sum()

# 滑动窗口标准差(波动率)
df['7日波动率'] = df['销售额'].rolling(window=7).std()

# 滑动窗口最大值/最小值
df['7日最高'] = df['销售额'].rolling(window=7).max()
df['7日最低'] = df['销售额'].rolling(window=7).min()

# 滑动窗口内的排名
df['7日排名'] = df['销售额'].rolling(window=7).apply(
    lambda x: pd.Series(x).rank().iloc[-1]
)

print(df.tail(10))

expanding:扩展窗口

# expanding 是"越来越大"的窗口(从开始到当前位置)
# 等价于 cumsum、cummean 等累计函数

df['累计销售额'] = df['销售额'].expanding().sum()
df['累计均值'] = df['销售额'].expanding().mean()
df['累计最大值'] = df['销售额'].expanding().max()

print(df.head(10))

ewm:指数加权移动平均

# 指数加权移动平均(最近的数据权重更大)
df['EMA_7'] = df['销售额'].ewm(span=7).mean()
df['EMA_30'] = df['销售额'].ewm(span=30).mean()

print("含 EMA:")
print(df[['销售额', 'EMA_7', 'EMA_30']].head(15))

时区处理

# ===== 设置时区 =====
dates = pd.date_range('2024-01-01', periods=5, freq='D')
print("无时区:")
print(dates)

# 设置为 UTC
dates_utc = pd.date_range('2024-01-01', periods=5, freq='D', tz='UTC')
print("\nUTC时区:")
print(dates_utc)

# ===== 时区转换 =====
dates_utc = pd.date_range('2024-01-01', periods=5, freq='H', tz='UTC')
dates_shanghai = dates_utc.tz_convert('Asia/Shanghai')
print("\n转为上海时间:")
print(dates_shanghai)

# ===== 本地化(无时区 → 有时区)=====
dates_naive = pd.date_range('2024-01-01', periods=5, freq='H')
dates_local = dates_naive.tz_localize('Asia/Shanghai')
print("\n本地化:")
print(dates_local)

日期偏移(DateOffset)

# ===== 日期偏移计算 =====
from pandas.tseries.offsets import Day, Week, MonthEnd, BusinessDay

date = pd.Timestamp('2024-03-15')

# 加 5 天
print(date + Day(5))  # 2024-03-20

# 加 2 周
print(date + Week(2))  # 2024-03-29

# 月末
print(date + MonthEnd(0))  # 2024-03-31(已经在3月,所以回到3月末)
print(date + MonthEnd(1))  # 2024-04-30

# 加 3 个工作日
print(date + BusinessDay(3))  # 2024-03-20(排除周末)

# ===== 在 DataFrame 中使用 =====
df = pd.DataFrame({
    '下单日期': pd.date_range('2024-01-01', periods=5)
})
df['预计发货'] = df['下单日期'] + BusinessDay(2)  # 2个工作日后
df['预计送达'] = df['下单日期'] + BusinessDay(5)  # 5个工作日后
print(df)

实战练习

练习 1:股票数据移动平均分析

题目:模拟股票数据,计算多种技术指标。

# 参考答案
import pandas as pd
import numpy as np

# 模拟 60 天的股价数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=60, freq='B')  # 工作日
df = pd.DataFrame({
    '日期': dates,
    '收盘价': 100 + np.cumsum(np.random.randn(60) * 2)  # 随机游走
}).set_index('日期')

# 1. 计算 5日、10日、20日移动平均线
df['MA5'] = df['收盘价'].rolling(5).mean()
df['MA10'] = df['收盘价'].rolling(10).mean()
df['MA20'] = df['收盘价'].rolling(20).mean()

# 2. 计算日收益率
df['日收益率'] = df['收盘价'].pct_change() * 100

# 3. 计算 20日波动率(收益率的标准差)
df['20日波动率'] = df['日收益率'].rolling(20).std()

# 4. 计算月度统计
monthly = df.resample('M')['收盘价'].agg(['first', 'last', 'max', 'min'])
monthly['月涨跌'] = (monthly['last'] - monthly['first']) / monthly['first'] * 100
print("月度统计:")
print(monthly.round(2))

print("\n技术指标(后10行):")
print(df.tail(10))

练习 2:网站流量时间分析

题目:分析网站的小时级流量数据。

# 参考答案
import pandas as pd
import numpy as np

# 模拟 7 天的小时级流量
np.random.seed(42)
hours = pd.date_range('2024-03-01', periods=7*24, freq='H')
df = pd.DataFrame({
    '时间': hours,
    '访客数': np.random.poisson(lam=100, size=len(hours))
}).set_index('时间')

# 模拟高峰期(9-18点)
df.loc[df.index.hour.between(9, 18), '访客数'] *= 2

# 1. 每日总访客
daily = df.resample('D')['访客数'].sum()
print("每日访客:")
print(daily)

# 2. 每小时平均访客(找出高峰时段)
df['小时'] = df.index.hour
hourly_avg = df.groupby('小时')['访客数'].mean()
print("\n每小时平均访客:")
print(hourly_avg.round(1))

# 3. 24小时移动平均
df['MA24'] = df['访客数'].rolling(24).mean()

# 4. 工作日与周末对比
df['类型'] = np.where(df.index.dayofweek < 5, '工作日', '周末')
weekday_avg = df.groupby('类型')['访客数'].mean()
print("\n工作日vs周末平均:")
print(weekday_avg.round(1))

本节总结

本节我们学习了 Pandas 强大的时间序列处理能力:

日期处理

重采样(resample)

滑动窗口(rolling)

时区与偏移

以上就是Pandas中时间序列分析的核心功能和实战指南的详细内容,更多关于Pandas时间序列分析的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文