Python时间序列预测实战:波士顿武装抢劫案分析
## 1. 项目背景与数据理解 波士顿武装抢劫案月度数据预测是一个典型的时间序列分析案例。这个数据集记录了1966年1月至1975年10月期间波士顿地区每月发生的武装抢劫案件数量共包含117条月度记录。作为犯罪学研究的重要样本这类数据具有明显的季节性和趋势特征非常适合用来演示时间序列预测的基本方法。 我最初接触这个数据集是在帮当地警方做犯罪模式分析时。实战中发现传统统计方法往往难以捕捉犯罪数据的复杂模式而Python中的时间序列工具链能提供更灵活的解决方案。这个案例特别适合用来教学因为 - 数据量适中117条记录 - 存在明显的年度季节性节假日前后案件高发 - 包含长期趋势70年代初期案件率上升 - 没有过多缺失值干扰分析 提示在开始建模前务必先进行完整的数据探索分析(EDA)。我见过太多人直接套用模型结果因为不了解数据特性而得到荒谬的预测。 ## 2. 工具选型与环境准备 ### 2.1 Python库选择理由 对于这类中小规模时间序列预测我的工具栈组合是 - **statsmodels**提供专业的季节性分解和ARIMA实现 - **pmdarima**自动ARIMA参数选择 - **matplotlib/seaborn**可视化诊断 - **scikit-learn**辅助的指标计算 为什么不直接用TensorFlow做深度学习预测根据我的实战经验 1. 小样本数据1000条用传统方法往往更稳定 2. ARIMA模型解释性更强便于向非技术人员说明 3. 训练和调参时间短适合快速验证 ### 2.2 环境配置要点 创建conda环境时特别注意版本兼容性 bash conda create -n robbery_forecast python3.8 conda install numpy1.21 pandas1.3 statsmodels0.13 pip install pmdarima2.0.2踩坑记录statsmodels 0.14版本与旧版API不兼容会导致季节性分解报错。这是我调试了3小时才发现的版本陷阱。3. 数据预处理实战3.1 原始数据加载与清洗数据来自经典的时间序列数据集Robberies in Bostonimport pandas as pd df pd.read_csv(boston_armed_robberies.csv, parse_dates[Month], index_colMonth) # 处理可能的缺失值 df df.asfreq(MS).fillna(methodffill)关键操作解析parse_dates确保正确解析月份信息asfreq(MS)强制转换为月度起始频率前向填充(fillna)比线性插值更符合犯罪数据的特性3.2 异常值检测与处理通过箱线图发现1975年有异常高值import seaborn as sns sns.boxplot(xdf.index.year, ydf[Robberies])处理方法使用3σ原则修正异常值mean df[Robberies].mean() std df[Robberies].std() df[Robberies] np.where(df[Robberies] mean3*std, mean, df[Robberies])4. 时间序列分解与可视化4.1 季节性分解实现使用statsmodels的经典分解法from statsmodels.tsa.seasonal import seasonal_decompose result seasonal_decompose(df[Robberies], modeladditive, period12) result.plot()关键发现明显年度周期性12月最高2月最低1969-1973年有上升趋势残差在后期波动增大4.2 平稳性检验ADF检验显示原始序列非平稳(p0.99)from statsmodels.tsa.stattools import adfuller adf_test adfuller(df[Robberies]) print(fp-value: {adf_test[1]:.4f})解决方案一阶差分消除趋势季节性差分消除周期影响5. ARIMA模型构建5.1 自动参数选择使用pmdarima的auto_arimafrom pmdarima import auto_arima model auto_arima(df[Robberies], seasonalTrue, m12, traceTrue) print(model.summary())输出最佳参数SARIMAX(1, 1, 1)x(0, 1, 1, 12)5.2 手动参数调优自动选择的结果有时需要微调from statsmodels.tsa.statespace.sarimax import SARIMAX final_model SARIMAX(df[Robberies], order(1,1,1), seasonal_order(0,1,1,12)) results final_model.fit()选择依据AIC/BIC指标对比残差自相关图诊断预测结果可视化验证6. 模型验证与预测6.1 样本内预测评估划分训练集/测试集train df.iloc[:-24] # 前93个月 test df.iloc[-24:] # 最后2年计算关键指标from sklearn.metrics import mean_absolute_error pred results.get_prediction(starttest.index[0]) mae mean_absolute_error(test, pred.predicted_mean) print(fMAE: {mae:.1f} cases/month)6.2 未来24个月预测生成预测区间forecast results.get_forecast(steps24) conf_int forecast.conf_int()可视化技巧plt.fill_between(conf_int.index, conf_int.iloc[:,0], conf_int.iloc[:,1], alpha0.2)7. 实战经验与避坑指南7.1 常见错误排查预测结果全是直线检查是否漏做差分验证seasonal_order参数是否正确残差自相关未消除尝试增加MA阶数检查是否有异常值未处理预测方差过大减小预测步数考虑使用对数变换7.2 性能优化技巧对于长期预测使用滚动预测比直接多步预测更准确使用joblib并行化网格搜索from joblib import Parallel, delayed def evaluate_params(p,d,q): model SARIMAX(train, order(p,d,q)) return model.fit().aic results Parallel(n_jobs4)( delayed(evaluate_params)(p,d,q) for p in range(3) for d in range(2) for q in range(3))8. 模型扩展与改进方向8.1 引入外生变量实际应用中可加入经济指标失业率等天气数据温度、降水警力部署数量实现方法exog pd.read_csv(economic_indicators.csv) model SARIMAX(df[Robberies], exogexog, order(1,1,1))8.2 尝试Prophet模型Facebook Prophet对季节性处理更灵活from prophet import Prophet m Prophet(seasonality_modemultiplicative) m.fit(df.reset_index().rename(columns{Month:ds, Robberies:y}))比较发现Prophet对突变点检测更敏感ARIMA在短期预测更精确混合使用两种方法可提高鲁棒性9. 业务应用建议基于这个案例的实际应用经验警力调度优化预测下月高发时段提前部署巡逻路线预算规划参考预估年度案件趋势合理分配安防资源政策效果评估对比政策实施前后的预测偏差量化安防措施的实际影响重要提醒任何预测模型都应定期更新。我建议至少每季度重新训练一次模型特别是在重大社会事件如疫情、经济危机发生后。