一、为什么时间序列分析这么重要
我们每天都会碰到按时间顺序排列的数据,比如你手机里的步数变化、某款奶茶的日销量、股票收盘价、城市气温……这些数据背后都藏着规律。如果能看透这些规律,我们就能预测未来——比如明天该多备点奶茶原料,或者下周股市可能怎么走。这就是时间序列分析干的事。
数据分析师常遇到这样的场景:老板丢过来一张Excel,说“这是过去三年每天的网站访问量,你帮我看看下个月哪天会爆,我好安排服务器扩容”。用Python做时间序列分析,就是帮你从这些历史数据里挖出趋势、季节性和周期性,再用模型推算出未来的走势。
二、准备工作:装好工具包
做时间序列分析,我们主要靠Pandas处理数据,用Statsmodels做统计建模,再用Matplotlib画图。这几个库在数据科学领域就像厨房里的锅碗瓢盆,缺一不可。
首先确保你的Python环境里装好了这些包。打开终端或命令行,运行下面的命令:
pip install pandas numpy matplotlib statsmodels scipy
装完之后,在Python脚本或Jupyter Notebook里导入它们:
import pandas as pd # 数据处理的核心
import numpy as np # 数值计算
import matplotlib.pyplot as plt # 画图
from statsmodels.tsa.stattools import adfuller # 平稳性检验
from statsmodels.tsa.seasonal import seasonal_decompose # 分解
from statsmodels.tsa.arima.model import ARIMA # 预测模型
import warnings
warnings.filterwarnings('ignore') # 忽略一些烦人的警告
好了,工具齐了,开始干活。
三、数据准备:清洗时间序列数据
时间序列数据最常见的格式是两列:一列时间,一列数值。但实际拿到的数据经常不干净:时间格式不对、有空值、有重复日期。我们先拿一个模拟的日销售额数据来演示。
假设我们有一个CSV文件 sales.csv,内容如下(只展示前几行):
date,sales
2023-01-01,120
2023-01-02,135
2023-01-03,128
...
加载并清洗的步骤:
# 技术栈:Python (pandas + statsmodels)
# 加载数据
df = pd.read_csv('sales.csv')
# 把日期列转成真正的datetime类型
df['date'] = pd.to_datetime(df['date'])
# 把日期列设为索引,方便后续操作
df.set_index('date', inplace=True)
# 检查有没有缺失值
print(df.isnull().sum()) # 输出缺失值个数
# 如果有缺失,可以用前一天的数值填充(简单处理)
df.fillna(method='ffill', inplace=True)
# 检查日期是否连续(有没有跳过的日期)
date_range = pd.date_range(start=df.index.min(), end=df.index.max(), freq='D')
missing_dates = date_range.difference(df.index)
if len(missing_dates) > 0:
print(f"缺少日期: {missing_dates}")
# 重新索引,补上缺失日期,值为NaN
df = df.reindex(date_range)
df.fillna(method='ffill', inplace=True) # 再次填充
print(df.head())
这样我们就得到了一个干净、连续的时间序列数据。注意,用 ffill 填充缺失值只是一种简单做法,实际项目中可能需要更谨慎处理,比如用插值法或模型填补。
四、可视化:一眼看出数据趋势
在分析之前,把数据画出来往往能给你最直观的感受。用Matplotlib画个折线图,看看整体走势、有没有周期性波动。
# 技术栈:Python (pandas + matplotlib)
plt.figure(figsize=(12, 5))
plt.plot(df.index, df['sales'], color='b', linewidth=1)
plt.title('每日销售额变化趋势')
plt.xlabel('日期')
plt.ylabel('销售额')
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()
从图中你能立刻看出:销售额是否有上升或下降趋势?每年某个月份是不是特别高?比如看出每年12月销量飙升(节假日效应),或者每周五销量比平时高。这些模式就是后续建模的依据。
五、平稳性检验:数据是不是“疯”的
很多时间序列预测模型(像ARIMA)要求数据是平稳的——也就是说,数据的均值、方差不会随时间剧烈变化。如果数据有趋势(比如一直涨)或者季节性(比如每年夏天高),就需要先做处理。
检验平稳性最常用的方法是ADF检验。原假设是数据非平稳,如果p值小于0.05,就拒绝原假设,认为数据平稳。
# 技术栈:Python (statsmodels)
# 对销售额列做ADF检验
result = adfuller(df['sales'].dropna())
print(f"ADF统计量: {result[0]:.4f}")
print(f"p值: {result[1]:.4f}")
print(f"临界值:")
for key, val in result[4].items():
print(f" {key}: {val:.4f}")
# 判断是否平稳
if result[1] < 0.05:
print("数据平稳,可以继续建模")
else:
print("数据非平稳,需要差分或变换")
如果数据非平稳,常见的处理方法是做差分:用今天的值减去昨天的值,得到一个新的序列。可以反复差分直到平稳。
# 技术栈:Python (pandas)
# 一阶差分
df['sales_diff'] = df['sales'].diff()
# 再检验差分后的平稳性
result_diff = adfuller(df['sales_diff'].dropna())
print(f"差分后p值: {result_diff[1]:.4f}")
六、时间序列分解:拆开看成分
时间序列通常可以分解成三部分:趋势、季节性和残差(随机波动)。用 seasonal_decompose 函数可以轻松拆开,让你看清每个成分的贡献。
# 技术栈:Python (statsmodels)
# 假设数据有年周期(365天),如果数据是日度,且观察到了一年内的季节性,就设period=365
# 但为了演示,我们设period=7(周周期)
decomposition = seasonal_decompose(df['sales'], model='additive', period=7)
# 提取成分
trend = decomposition.trend
seasonal = decomposition.seasonal
residual = decomposition.resid
# 画图看看
plt.figure(figsize=(12, 8))
plt.subplot(4,1,1)
plt.plot(df['sales'], label='原始')
plt.legend()
plt.subplot(4,1,2)
plt.plot(trend, label='趋势', color='r')
plt.legend()
plt.subplot(4,1,3)
plt.plot(seasonal, label='季节性', color='g')
plt.legend()
plt.subplot(4,1,4)
plt.plot(residual, label='残差', color='m')
plt.legend()
plt.tight_layout()
plt.show()
通过分解,你能直观看到:销售额整体在缓慢上升(趋势),每周有固定的高低(季节性),剩下的随机波动不算太大(残差)。这些信息对选择模型很有帮助。
七、预测模型:用历史推测未来
ARIMA模型是经典的时间序列预测方法。它的名字代表:自回归(AR)、差分(I)、移动平均(MA)。我们不用深究数学原理,只要知道它能拟合平稳序列的规律,并预测后续值。
使用ARIMA前,需要确定三个参数:p(自回归阶数)、d(差分次数)、q(移动平均阶数)。通常通过观察自相关图(ACF)和偏自相关图(PACF)来定参,或者用自动搜索工具如 pmdarima。这里为了简化,我们手动设定参数并进行预测。
# 技术栈:Python (statsmodels)
# 假定经过一次差分后数据平稳,所以d=1
# p和q我们先凭经验设为(1,1),实际中需要调优
model = ARIMA(df['sales'], order=(1,1,1))
model_fit = model.fit()
# 查看模型摘要(了解参数显著性)
print(model_fit.summary())
# 预测未来7天的销售额
forecast = model_fit.forecast(steps=7)
print("未来7天预测值:")
print(forecast)
# 画图展示预测结果
plt.figure(figsize=(12,5))
plt.plot(df.index, df['sales'], label='历史实际')
forecast_index = pd.date_range(start=df.index[-1] + pd.Timedelta(days=1), periods=7)
plt.plot(forecast_index, forecast, label='预测', color='r', marker='o')
plt.legend()
plt.show()
八、实际案例:预测某商店的日销售额
我们综合前面所有步骤,做一个完整的案例。假设你拿到一份真实的销售数据(这里我用代码生成模拟数据,但流程一样)。
# 技术栈:Python (pandas + statsmodels + matplotlib)
# 先生成模拟数据(模拟一年365天,有趋势和季节性)
np.random.seed(42)
date_range = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
trend = np.linspace(100, 130, len(date_range)) # 缓慢上升趋势
seasonal = 10 * np.sin(2 * np.pi * np.arange(len(date_range)) / 7) # 周周期
noise = np.random.normal(0, 5, len(date_range))
sales = trend + seasonal + noise
df = pd.DataFrame({'date': date_range, 'sales': sales})
df.set_index('date', inplace=True)
# 1. 数据预览
print("数据行数:", len(df))
print(df.head())
# 2. 平稳性检验
result = adfuller(df['sales'])
print(f"原始p值: {result[1]:.4f}")
# 3. 如果非平稳,做差分
if result[1] >= 0.05:
df['sales_diff'] = df['sales'].diff()
result_diff = adfuller(df['sales_diff'].dropna())
print(f"差分后p值: {result_diff[1]:.4f}")
d = 1 # 差分次数
else:
d = 0
# 4. 分解时间序列
decomp = seasonal_decompose(df['sales'], model='additive', period=7)
# 可以查看趋势和季节性,这里省略画图
# 5. 建立ARIMA模型(用差分后的序列)
# 设置p=2, q=2(随意定的,实际需要调优)
model = ARIMA(df['sales'], order=(2, d, 2))
model_fit = model.fit()
print(model_fit.summary())
# 6. 预测未来14天
forecast_steps = 14
forecast = model_fit.forecast(steps=forecast_steps)
forecast_index = pd.date_range(start=df.index[-1] + pd.Timedelta(days=1), periods=forecast_steps)
# 7. 输出预测值
print("未来14天预测销售额:")
for date, val in zip(forecast_index, forecast):
print(f"{date.date()}: {val:.2f}")
# 8. 绘制历史+预测图
plt.figure(figsize=(14,6))
plt.plot(df.index, df['sales'], label='历史实际')
plt.plot(forecast_index, forecast, label='预测', color='r', marker='o', linestyle='--')
plt.fill_between(forecast_index,
forecast - 1.96*model_fit.resid.std(),
forecast + 1.96*model_fit.resid.std(),
alpha=0.2, color='red', label='95%置信区间')
plt.xlabel('日期')
plt.ylabel('销售额')
plt.title('日销售额预测')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
这个案例完整展示了从数据生成到预测的全过程。实际工作中你只需要把读CSV部分替换成真实数据。
九、技术优缺点
时间序列分析并不是万能的,我们来客观说说它的优缺点。
优点:
- 对历史数据的规律利用充分,尤其是趋势和季节性明显的场景。
- 模型相对简单,计算成本低,适合快速迭代。
- 可解释性强:能说出“因为去年同期的季节效应,所以下个月应该涨”。
- 预测结果能给出置信区间,帮助评估不确定性。
缺点:
- 对数据质量要求高:缺失值、异常值会严重影响结果。
- 难以捕捉突发的外部因素(比如疫情、政策变化)。
- 长期预测能力弱,通常适合短期(几天到几周)预测。
- 要求数据平稳或经过变换,处理过程可能丢失部分信息。
十、注意事项
- 不要过度拟合:模型参数越多,越容易死记硬背历史,对未来预测反而差。尽量保持模型简洁。
- 检查残差:模型拟合后的残差应该是白噪声(无自相关)。如果残差还有规律,说明模型没提取干净信息,需要调整。
- 考虑节假日效应:很多业务数据受节假日影响巨大,简单的ARIMA可能无法处理,可以考虑加入假日哑变量或使用Prophet等工具。
- 季节周期要选对:日数据可能有周周期、年周期,但不要随意假设,可以通过自相关图确认。
- 交叉验证:不要拿全部数据训练再用同一批数据评估,应该像机器学习一样划分训练集和测试集。
十一、文章总结
时间序列分析是数据科学实战中非常频繁用到的技巧,从销售预测到网站流量监控,再到天气预报,它无处不在。用Python做这件事并不复杂:Pandas负责数据处理,Statsmodels负责统计建模,Matplotlib负责可视化。你只需要掌握数据清洗、平稳性检验、分解和ARIMA模型这几个核心技能,就能解决大部分日常问题。
但别忘了,数据科学永远需要你对业务的理解。一个模型输出的数字再漂亮,如果没解释清背后的业务逻辑,那也只是纸上谈兵。希望这篇文章能帮你跨过入门门槛,以后看到带日期的数据时,心里能多一份“我能预测它”的底气。
最后,记得多动手练习,找一份真实数据跑一遍流程,你的收获会比看一百篇文章都大。
Comments