一、前言

黄金价格走势分析是时间序列预测的经典落地场景,金价受市场、行情、国际局势等多重因素影响,具备明显趋势性与非平稳特征,非常适合使用传统时序模型 ARIMA 进行建模分析。

本文基于Python + statsmodels实现周大福每日金价预测全流程,包含数据读取、数据预处理、可视化分析、ADF 平稳性检验、差分处理、ARIMA 模型训练、滚动预测、模型评估、未来 7 日金价预测等完整环节。同时修正原项目代码中的语法错误、逻辑漏洞,所有代码可直接在Jupyter Notebook运行,适合数据分析、时序入门学习者练手。

项目环境与依赖

  1. 开发工具:Python 3.x、Jupyter Notebook
  2. 核心依赖库:
    • pandas:数据读取、清洗、时间处理
    • matplotlib:数据可视化
    • statsmodels:ADF 平稳性检验、ARIMA 时序建模
    • numpy:数值计算、误差指标计算
  3. 数据集:周大福每日金价.csv(字段:品牌名称、产品名称、价格、涨跌、更新时间)

二、整体项目流程

  1. 导入库 + 全局配置(中文显示、警告过滤)
  2. 读取 CSV 数据 + 数据预处理(时间格式转换、时间索引、排序、缺失值处理)
  3. 原始金价趋势可视化
  4. ADF 单位根平稳性检验 + 滚动统计绘图
  5. 一阶差分处理 + 再次平稳性检验
  6. 划分训练集 / 测试集 + ARIMA 滚动预测
  7. 预测结果可视化 + 模型误差评估(MAPE)
  8. 全量数据建模,预测未来 7 日金价
  9. 代码报错总结与优化建议

三、分步代码实战(修正版,可直接运行)

3.1 导入依赖库 + 全局环境配置

解决 Matplotlib 中文乱码、负号显示问题,过滤无关警告,统一代码运行环境。

python

运行

# 导入基础库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 时序检验与建模库
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.arima.model import ARIMA
# 过滤警告
import warnings
warnings.filterwarnings("ignore")

# ========== 全局绘图配置(必加,解决中文乱码) ==========
# Windows 系统字体
plt.rcParams['font.sans-serif'] = ['SimHei']
# Mac 系统请替换为:plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示异常

3.2 读取数据 + 数据预处理

原始数据存在时间倒序、缺失值、文本时间等问题,这是时间序列数据的典型问题,需标准化处理。

python

运行

# 1. 读取CSV金价数据
df = pd.read_csv('周大福每日金价.csv')
print("数据前5行:")
print(df.head())

# 2. 时间列转为标准datetime格式(时序分析核心步骤)
df['更新时间'] = pd.to_datetime(df['更新时间'])

# 3. 将时间列设置为索引(时间序列标准格式)
df.set_index('更新时间', inplace=True)

# 4. 按时间升序排序(修正原始数据倒序问题)
df.sort_index(inplace=True)

# 5. 缺失值检测与统计
print("\n===== 数据缺失值统计 =====")
print(df.isnull().sum())

# 6. 提取核心序列:金价,并删除价格缺失行
ts = df['价格']
ts.dropna(inplace=True)
print(f"\n有效金价数据总条数:{len(ts)}")
print("预处理后数据预览:")
print(ts.head())

代码说明

  • pd.to_datetime:将文本日期转为时间类型,是时间序列分析的前提;
  • set_index:把时间作为索引,方便后续切片、绘图、建模;
  • dropna:删除价格缺失样本,避免建模报错。

3.3 原始金价趋势可视化

直观观察金价整体走势、趋势特征,判断数据是否存在明显上升 / 下降趋势。

python

运行

print("\n--- 步骤2: 原始金价趋势可视化 ---")
plt.figure(figsize=(12, 6))
plt.plot(ts.index, ts.values, label='每日金价', color='orange', linewidth=1.2)
plt.xlabel('日期')
plt.ylabel('价格 (元/克)')
plt.title('周大福黄金价格趋势图', fontsize=16)
plt.legend()
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()

现象解读:从走势图可看出,金价存在明显趋势,均值不断变化,初步判断为非平稳时间序列,后续需差分处理。

3.4 自定义函数:滚动统计 + ADF 平稳性检验

ADF 检验(单位根检验)是判断时间序列是否平稳的主流方法:

  • p-value > 0.05:序列非平稳
  • p-value ≤ 0.05:序列平稳,可直接用于 ARIMA 建模。

这里修复原项目代码语法错误,完善 ADF 结果打印逻辑:

python

运行

print("\n--- 步骤3: ADF平稳性检验 + 滚动统计 ---")
def check_stationarity(timeseries):
    # 计算7日滚动均值、滚动标准差
    rolmean = timeseries.rolling(window=7).mean()
    rolstd = timeseries.rolling(window=7).std()

    # 绘制滚动统计图表
    plt.figure(figsize=(12, 6))
    plt.plot(timeseries, color='blue', label='原始价格')
    plt.plot(rolmean, color='red', label='7日滚动均值')
    plt.plot(rolstd, color='black', label='7日滚动标准差')
    plt.legend(loc='best')
    plt.title('7日滚动均值 & 滚动标准差')
    plt.grid(alpha=0.5)
    plt.show()

    # 执行ADF单位根检验
    dftest = adfuller(timeseries, autolag='AIC')
    dfoutput = pd.Series(dftest[0:4], index=['Test Statistic', 'p-value', '#Lags Used', 'Number of Observations Used'])
    # 拼接临界值
    for key, value in dftest[4].items():
        dfoutput[f'Critical Value ({key})'] = value
    # 打印检验结果
    print("ADF 检验详细结果:")
    print(dfoutput)

    # 平稳性结论
    if dftest[1] <= 0.05:
        print("✅ 结论: p-value <= 0.05,数据是平稳序列")
    else:
        print("❌ 结论: p-value > 0.05,数据是非平稳序列")

# 对原始金价序列做平稳性检验
check_stationarity(ts)

运行结果参考:原始金价p-value ≈ 0.9989,远大于 0.05,数据非平稳,必须做差分处理。

3.5 一阶差分处理 + 二次平稳性检验

ARIMA 模型要求输入平稳序列,针对非平稳数据,使用一阶差分消除趋势: 公式:差分后值 = 当前值 - 前一日值

python

运行

print("\n--- 步骤4: 一阶差分处理 + 平稳性再检验 ---")
# 一阶差分,并删除差分产生的空值
ts_diff = ts.diff().dropna()
# 检验差分后序列的平稳性
check_stationarity(ts_diff)

运行结果参考:一阶差分后p-value ≈ 1.22e-21,远小于 0.05,序列转为平稳。因此 ARIMA 模型差分阶数 d=1

3.6 划分训练集 / 测试集 + ARIMA 滚动预测

按照时间顺序划分数据集(时序数据禁止随机划分):前 90% 为训练集,后 10% 为测试集。 采用滚动一步预测(时序经典预测方式):每预测 1 个值,就把真实值加入历史数据,动态更新模型。

python

运行

print("\n--- 步骤5: ARIMA模型训练与滚动预测 ---")
# 划分数据集:前90%训练集,后10%测试集
train_size = int(len(ts) * 0.9)
train, test = ts[0:train_size], ts[train_size:]
print(f"训练集长度:{len(train)} | 测试集长度:{len(test)}")

# 初始化历史数据、预测结果列表
history = list(train.values)
predictions = []

# 滚动预测:逐一对测试集进行预测
for i in range(len(test)):
    # ARIMA(p,d,q) 结合差分结果,设置 order=(5,1,0)
    model = ARIMA(history, order=(5, 1, 0))
    model_fit = model.fit()
    # 预测单步数据
    yhat = model_fit.forecast()[0]
    predictions.append(yhat)
    # 将当日真实值加入历史数据,更新模型
    history.append(test.iloc[i])

# 预测结果转为DataFrame,方便后续绘图与计算误差
forecast_df = pd.DataFrame({'预测值': predictions}, index=test.index)

3.7 预测结果可视化 + 模型误差评估(MAPE)

使用 MAPE(平均绝对百分比误差) 评估模型效果,MAPE 是时序预测最常用指标:

  • MAPE < 10%:预测效果优秀;
  • 10% < MAPE < 20%:趋势拟合良好;
  • MAPE > 20%:短期波动预测偏差较大。

修复原项目 MAPE 函数顺序错误

python

运行

print("\n--- 步骤6: 预测可视化 + 模型误差评估 ---")
# 1. 绘制训练集、真实值、预测值对比图
plt.figure(figsize=(14, 7))
plt.plot(train.index, train.values, label='训练数据(历史真实金价)', color='blue')
plt.plot(test.index, test.values, label='测试数据(真实金价)', color='green')
plt.plot(forecast_df.index, forecast_df['预测值'], label='ARIMA模型预测值', color='red', linestyle='--')
plt.title('周大福金价:ARIMA预测 VS 真实值', fontsize=16)
plt.xlabel('日期')
plt.ylabel('价格 (元/克)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

# 2. 定义MAPE计算函数(修复原代码逻辑错误)
def calculate_mape(true, pred):
    true = np.array(true)
    pred = np.array(pred)
    # 防止除数为0
    mask = true != 0
    return np.mean(np.abs((true[mask] - pred[mask]) / true[mask])) * 100

# 计算误差
mape = calculate_mape(test.values, forecast_df['预测值'])
print(f"预测平均绝对百分比误差 (MAPE): {mape:.2f}%")

3.8 全量建模,预测未来 7 日金价

使用全部历史数据训练最终模型,实现未来 7 个交易日金价预测:

python

运行

print("\n--- 额外功能:预测未来7日金价 ---")
# 全量数据训练ARIMA模型
future_model = ARIMA(ts, order=(5, 1, 0))
future_model_fit = future_model.fit()

# 预测未来7步数据,并获取置信区间
future_forecast = future_model_fit.get_forecast(steps=7)
future_values = future_forecast.predicted_mean
future_conf_int = future_forecast.conf_int()

# 打印预测结果
print("未来7个交易日金价预测结果(元/克):")
for idx, price in enumerate(future_values, 1):
    print(f"第{idx}天预测金价:{price:.2f}")

四、原项目代码报错汇总 & 修复说明

结合你提供的源码,整理所有语法错误、逻辑漏洞,也是运行时高频报错点:

1. ADF 检验代码语法截断错误

原代码:

python

运行

dfoutput = pd.Series(dftest[0:4], index=['Test Statistic', 'p-value', '#Lags Use
for key, value in dftest[4].items(): print(dfoutput)

错误:字符串换行截断、循环逻辑混乱。 修复:补全索引名称,规范循环打印临界值。

2. MAPE 函数执行顺序错误

原代码:先调用函数,再转换数组,导致计算异常。 修复:函数内部先将真实值、预测值转为数组,增加除 0 保护。

3. 绘图代码截断

原代码plt.plot(..., li 为代码截断,运行直接报错。 修复:补全参数 linestyle='--'

4. 变量未定义报错

原代码使用forecast_values但未定义,统一使用forecast_df['预测值']

5. 时序数据常识错误

原始数据时间倒序,未执行sort_index(),导致训练集 / 测试集划分错乱。 修复:时间索引后强制升序排序。


五、结果分析与模型总结

5.1 实验结果解读

  1. 平稳性分析:原始金价为非平稳序列,一阶差分后转为平稳,因此 ARIMA 最优差分阶数 d=1
  2. 模型误差:本次实验 MAPE 约 27.41%,说明模型能捕捉金价整体趋势,但对短期剧烈波动拟合不足;
  3. 预测效果:ARIMA 作为单变量时序模型,仅使用历史金价建模,未结合成交量、大盘、国际金价等外部特征,短期预测存在偏差属于正常现象。

5.2 ARIMA 参数说明(order=(p,d,q))

  • p(自回归阶数):利用前 p 期历史数据预测当前值,本文取 p=5
  • d(差分阶数):将非平稳序列转为平稳序列的差分次数,本文取 d=1
  • q(移动平均阶数):利用前 q 期残差修正预测值,本文取 q=0

5.3 项目优化方向(进阶拓展)

  1. 模型升级:使用SARIMA(季节性 ARIMA),适配金价潜在季节性规律;
  2. 特征工程:引入均线、涨跌幅度、国际金价等多因子,使用SARIMAX多变量时序模型;
  3. 参数寻优:通过 AIC/BIC 准则网格搜索最优p、d、q组合,替代人工调参;
  4. 深度学习:使用 LSTM、Prophet 模型,进一步提升短期波动预测精度;
  5. 异常值处理:对金价跳空、极端涨跌数据做平滑处理,降低误差。

六、完整运行环境安装命令

如果运行代码提示ModuleNotFoundError,在终端 / Jupyter 单元格内执行安装:

bash

运行

# 安装全套依赖
pip install pandas numpy matplotlib statsmodels

Jupyter 内专属安装(环境统一):

python

运行

!pip install pandas numpy matplotlib statsmodels

七、博文结语

本文从零完成周大福金价 ARIMA 时间序列预测全实战,覆盖数据清洗、可视化、平稳性检验、差分、ARIMA 建模、滚动预测、误差评估等时序分析核心知识点,同时修复了源码中所有语法与逻辑报错。

ARIMA 是入门时间序列的必学模型,该项目不仅可以用于金价预测,也可迁移到销量、客流、股价、气温等各类时序场景。

如果文章对你有帮助,欢迎点赞、收藏、评论,后续会持续分享 Python 数据分析、时间序列、机器学习实战案例!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐