文章简介

大家好,本篇博文将基于 Python 完成一套完整的股票时间序列数据分析实战,涵盖数据导入、数据清洗预处理、多维可视化分析(均线图、K 线图、成交量、涨跌幅分布),并分别使用线性回归ARIMA 时间序列模型实现股价预测,最后对比两大模型效果,同时完成未来股价预测。

全文附带完整可运行代码、详细注释、问题排错方案,零基础也能跟着复现,适合数据分析入门、Python 时间序列学习、金融数据分析练手。 工具栈:Pandas、Matplotlib、Pyecharts、Scikit-learn、Statsmodels 数据集:本地 Excel 股票行情数据(包含开盘价、收盘价、最高价、最低价、成交量、涨跌幅、5/10/20 日均线等字段)

一、整体流程概览

本次实战整体分为 6 大模块,循序渐进完成分析与建模:

  1. 环境配置 & 库导入
  2. 数据读取、探索与缺失值检测
  3. 数据预处理(时间格式转换、排序、字段筛选)
  4. 多维数据可视化(4 类核心图表)
  5. 模型构建:线性回归股价预测
  6. 模型构建:ARIMA 时间序列预测
  7. 双模型效果对比 + 未来股价预测
  8. 总结与踩坑排错

二、环境准备与库导入

2.1 依赖库安装

本次用到多个第三方库,若环境缺失,执行以下命令安装:

bash

运行

# 核心数据分析库
pip install pandas numpy
# 绘图库
pip install matplotlib pyecharts
# 机器学习 + 时间序列模型库
pip install scikit-learn statsmodels

2.2 代码初始化(解决中文 / 负号乱码)

Matplotlib 默认不支持中文,开头统一配置,避免图表出现方框乱码:

python

运行

# 引入基础模块
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 解决 Matplotlib 中文、负号显示异常
plt.rcParams["font.family"] = "SimHei"   # 设置中文字体
plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题

三、数据读取与探索分析

3.1 读取 Excel 股票数据

数据集为本地 股价数据.xlsx,包含 611 条股票日行情数据,字段涵盖行情核心指标:

python

运行

# 读取Excel数据
df = pd.read_excel("股价数据.xlsx")
# 查看前5行数据
df.head()

数据字段说明

表格

字段名 含义
date 交易日期
open 开盘价
high 当日最高价
close 当日收盘价(核心预测目标)
low 当日最低价
volume 成交量
p_change 涨跌幅 (%)
ma5/ma10/ma20 5/10/20 日移动平均线

3.2 数据基本信息 & 缺失值检测

数据分析前必须检查数据完整性,判断是否需要清洗缺失值、异常值:

python

运行

# 查看数据整体结构、字段类型、数据量
df.info()

# 统计每一列缺失值数量
df.isnull().sum()

运行结果解读: 数据集共 611 条样本、14 个字段,所有字段均无缺失值,无需做缺失值填充,大幅简化预处理流程。


四、数据预处理(时间序列核心步骤)

股票数据属于时间序列数据时间格式、数据排序是重中之重,直接影响后续绘图与建模效果:

python

运行

# 1. 将date字段从字符串转为标准时间格式(时间序列必备)
df['date'] = pd.to_datetime(df['date'])

# 2. 按交易日期【从小到大】排序(原始数据时间乱序,必须修正)
df.sort_values(by='date', inplace=True)

# 3. 筛选核心分析字段,剔除无用字段,精简数据集
df = df[['date', 'open', 'close', 'high', 'low', 'volume', 'p_change', 'ma5', 'ma10', 'ma20']]

# 4. 重置行索引(排序后索引混乱,重置为连续索引)
df.reset_index(drop=True, inplace=True)

# 查看预处理后数据
df.head()

预处理完成后,数据时间有序、字段精简,正式进入可视化环节。


五、多维数据可视化分析

可视化是理解股票数据规律的关键,本次依次绘制 均线折线图、K 线图、成交量柱状图、涨跌幅直方图 四类经典图表。

5.1 收盘价 & 移动平均线折线图

移动平均线(MA)是股票技术分析最常用指标,用于判断股价趋势:

python

运行

# 设置画布大小
plt.figure(figsize=(14,6))

# 绘制收盘价实线
plt.plot(df['date'], df['close'], label='收盘价')
# 绘制5/10/20日均线(虚线区分)
plt.plot(df['date'], df['ma5'], label='5日均线', linestyle='--')
plt.plot(df['date'], df['ma10'], label='10日均线', linestyle='--')
plt.plot(df['date'], df['ma20'], label='20日均线', linestyle='--')

# 图表美化
plt.legend()  # 显示图例
plt.title('收盘价与均线走势图')
plt.grid(alpha=0.3)  # 浅色网格,提升可读性

# 展示图表
plt.show()

图表解读

  • 收盘价紧贴均线运行,说明股价走势平稳;
  • 短期均线(5 日)波动大于长期均线(20 日),符合金融数据规律。

5.2 交互式 K 线图(Pyecharts)

K 线图直观展示每日开盘、收盘、最高、最低价格,采用 Pyecharts 实现交互式图表(支持缩放、保存、数据查看)。

常见坑:Pyecharts 不支持 datetime 类型日期,无需额外转换,代码兼容现有数据。

python

运行

# 导入K线图模块与配置工具
from pyecharts.charts import Kline
from pyecharts import options as opts

# 提取K线四要素:开盘价、收盘价、最低价、最高价(顺序固定不可调换)
y = df[['open','close','low','high']].values.tolist()

# 构建K线图
kline = (
    Kline()
    .add_xaxis(df['date'].tolist())  # X轴:日期
    .add_yaxis('股票K线图', y)       # Y轴:K线数据
    .set_global_opts(
        title_opts=opts.TitleOpts(title='股票行情K线图'),
        toolbox_opts=opts.ToolboxOpts(is_show=True)  # 开启右上角工具箱
    )
)

# 在Jupyter Notebook中渲染图表
kline.render_notebook()

功能说明:工具箱支持图片保存、区域缩放、数据视图、重置视图,交互性远优于 Matplotlib 静态图。

5.3 成交量柱状图

成交量反映市场交易活跃度,结合股价可判断行情强弱:

python

运行

plt.figure(figsize=(14,6))
# 绘制成交量柱状图
plt.bar(df['date'], df['volume'])
plt.title('股票成交量柱状图')
plt.show()

5.4 涨跌幅分布直方图

直方图统计每日涨跌幅的分布规律,判断股票波动大小:

python

运行

plt.figure(figsize=(14,6))
# 绘制涨跌幅直方图,bins=40 划分40个区间
plt.hist(df['p_change'], bins=40)
# 绘制红色虚线:涨跌分界线(0轴)
plt.axvline(0, color='red', linestyle='--', label='涨跌分界线')
plt.legend()
plt.title('股票每日涨跌幅分布直方图')
plt.show()

图表解读: 柱子集中在 0 轴附近,说明该股票大部分时间小幅涨跌,大幅暴涨 / 暴跌天数较少,属于低波动稳健型个股。


六、模型一:线性回归预测收盘价

6.1 特征工程(构造时间序列滞后特征)

时间序列不能随机打乱数据,本次使用前一日收盘价作为滞后特征,结合开盘价、均线构建模型特征:

python

运行

# 构造滞后1天特征:前一日收盘价
df["close_lag1"] = df["close"].shift(1)

# shift(1)会产生第一行空值,删除空数据
df = df.dropna()

# 划分训练集(80%)、测试集(20%)(时间序列按顺序划分,禁止随机打乱)
split = int(len(df) * 0.8)
train = df.iloc[:split]   # 前80%训练
test = df.iloc[split:]    # 后20%测试

# 定义特征列 & 预测标签(预测目标:当日收盘价)
feature_cols = ["close_lag1", "open", "ma5"]
X_train = train[feature_cols]
y_train = train["close"]

X_test = test[feature_cols]
y_test = test["close"]

# 输出数据集大小
print(f"训练集:{len(train)} 条 | 测试集:{len(test)} 条")

6.2 模型训练、预测与评估

选用平均绝对误差 (MAE)均方根误差 (RMSE)决定系数 (R²) 三大指标评估模型:

python

运行

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

# 定义模型评估函数
def eval_model(y_true, y_pred):
    mae = mean_absolute_error(y_true, y_pred)
    rmse = np.sqrt(mean_squared_error(y_true, y_pred))
    r2 = r2_score(y_true, y_pred)
    print(f"MAE(平均绝对误差):{mae:.2f}")
    print(f"RMSE(均方根误差):{rmse:.2f}")
    print(f"R²(拟合度):{r2:.2f}")

# 初始化线性回归模型并训练
lr = LinearRegression()
lr.fit(X_train, y_train)

# 测试集预测
y_pred_lr = lr.predict(X_test)

# 输出评估结果
print("==== 线性回归模型评估 ====")
eval_model(y_test, y_pred_lr)

输出结果参考

plaintext

==== 线性回归模型评估 ====
MAE(平均绝对误差):0.16
RMSE(均方根误差):0.21
R²(拟合度):0.98

结果解读:R2=0.98,模型拟合效果极佳,误差很小。

6.3 线性回归预测结果可视化

对比真实收盘价与预测值走势:

python

运行

plt.figure(figsize=(14, 6))
plt.plot(test["date"], y_test, label="真实收盘价", linewidth=2)
plt.plot(test["date"], y_pred_lr, label="线性回归预测", linestyle="--")
plt.title("线性回归股价预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

七、模型二:ARIMA 时间序列预测

ARIMA 是经典时间序列模型,专门用于时序数据预测,本次采用滚动单步预测(金融时序主流用法):

python

运行

from statsmodels.tsa.arima.model import ARIMA

# 1. 初始化历史数据与预测列表
history = list(train["close"])
predictions = []

# 2. 滚动预测:逐行预测,每预测1条就把真实值加入历史数据
for i in range(len(test)):
    # 模型阶数 order=(p,d,q):(10,1,0)
    model = ARIMA(history, order=(10,1,0))
    model_fit = model.fit()
    # 预测下一步数据
    yhat = model_fit.forecast()[0]
    predictions.append(yhat)
    # 追加真实值,更新历史数据
    history.append(test["close"].iloc[i])

# 转为数组
y_pred_arima = np.array(predictions)

# 模型评估
print("\n==== ARIMA模型评估 ====")
eval_model(y_test, y_pred_arima)

输出结果参考

plaintext

==== ARIMA模型评估 ====
MAE(平均绝对误差):0.18
RMSE(均方根误差):0.25
R²(拟合度):0.97

7.1 ARIMA 预测可视化

python

运行

plt.figure(figsize=(14, 6))
plt.plot(test["date"], y_test, label="真实收盘价", linewidth=2)
plt.plot(test["date"], y_pred_arima, label="ARIMA预测", linestyle="--", color="orange")
plt.title("ARIMA时间序列预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

八、双模型对比 & 未来股价预测

8.1 线性回归 VS ARIMA 同图对比

直观对比两个模型的预测效果:

python

运行

plt.figure(figsize=(15, 6))
# 真实值
plt.plot(test["date"], y_test, label="真实收盘价", linewidth=2)
# 线性回归预测
plt.plot(test["date"], y_pred_lr, label="线性回归", linestyle="--")
# ARIMA预测
plt.plot(test["date"], y_pred_arima, label="ARIMA", linestyle="--", color="red")

plt.title("线性回归 & ARIMA 双模型预测效果对比")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

模型对比总结

  1. 线性回归:误差更小、拟合度更高,依赖人工构造特征
  2. ARIMA:纯时序模型,无需额外特征,泛化能力强,误差略高于线性回归;
  3. 两个模型整体趋势均与真实股价高度一致。

8.2 ARIMA 预测未来 10 个交易日股价

使用全量数据训练模型,预测未来 10 天收盘价:

python

运行

# 取全量收盘价序列
full_series = df["close"]

# 训练ARIMA模型,order=(5,1,0)
final_arima = ARIMA(full_series, order=(5,1,0)).fit()

# 预测未来10步(10个交易日)
future_pred = final_arima.get_forecast(steps=10).predicted_mean

# 打印预测结果
print("\n==== 未来10个交易日预测收盘价 ====")
for i, val in enumerate(future_pred, 1):
    print(f"第{i}天预测股价:{val:.2f} 元")

补充说明:运行时出现 ValueWarning正常提示(时间索引兼容警告),不影响预测结果,可忽略。


九、常见报错 & 踩坑排错(重点)

结合实战中高频问题,整理排错方案,解决 “老师代码能跑,自己运行报错” 的问题:

9.1 ModuleNotFoundError: No module named 'xxx'

原因:缺少对应第三方库 解决:执行 pip install 库名,Jupyter 中可使用 !pip install 库名 快速安装。

9.2 Matplotlib 中文方框乱码

原因:未配置中文字体 解决:开头添加两行配置:

python

运行

plt.rcParams["font.family"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False

9.3 Pyecharts 日期显示异常

原因:传入 datetime 类型数据 解决:日期转为字符串格式:

python

运行

df['date'] = df['date'].dt.strftime('%Y-%m-%d')

9.4 时间序列划分数据集使用随机打乱

致命错误:时序数据不能使用 train_test_split 随机划分,会造成数据泄露正确做法:按时间顺序切分(本文 8:2 划分方式)。

9.5 shift() 产生空值报错

原因:构造滞后特征后首行出现 NaN 解决:使用 df = df.dropna() 删除空行。


十、总结与拓展方向

10.1 实战总结

  1. 数据层面:股票时间序列数据优先处理时间格式、排序、缺失值,这是所有分析的基础;
  2. 可视化层面:均线图、K 线图、成交量、涨跌幅直方图构成股票分析标准可视化体系;
  3. 模型层面
    • 线性回归:简单高效,适合特征丰富的场景;
    • ARIMA:经典时序模型,无需人工特征,适合纯趋势预测;
  4. 本次两个模型对该股票短期预测效果优异,可用于初步行情研判。

10.2 拓展学习方向(进阶玩法)

  1. 特征优化:新增换手率、MACD、RSI 等金融技术指标,提升模型精度;
  2. 模型升级:尝试 LSTM、Prophet、XGBoost 等模型,对比更多算法效果;
  3. 数据扩充:使用 Tushare、Akshare 接口爬取实时股票数据,脱离本地 Excel;
  4. 策略回测:基于预测结果构建简单买卖策略,回测收益率。

十一、完整整合代码(一键运行版)

将所有代码整合,复制到 Jupyter Notebook 即可直接运行:

python

运行

# ===================== 1. 库导入 & 中文配置 =====================
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pyecharts.charts import Kline
from pyecharts import options as opts
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from statsmodels.tsa.arima.model import ARIMA

# 解决中文乱码
plt.rcParams["font.family"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False

# ===================== 2. 数据读取与探索 =====================
df = pd.read_excel("股价数据.xlsx")
df.info()
df.isnull().sum()

# ===================== 3. 数据预处理 =====================
df['date'] = pd.to_datetime(df['date'])
df.sort_values(by='date', inplace=True)
df = df[['date', 'open', 'close', 'high', 'low', 'volume', 'p_change', 'ma5', 'ma10', 'ma20']]
df.reset_index(drop=True, inplace=True)

# ===================== 4. 可视化1:均线图 =====================
plt.figure(figsize=(14,6))
plt.plot(df['date'], df['close'], label='收盘价')
plt.plot(df['date'], df['ma5'], label='5日均线', linestyle='--')
plt.plot(df['date'], df['ma10'], label='10日均线', linestyle='--')
plt.plot(df['date'], df['ma20'], label='20日均线', linestyle='--')
plt.legend()
plt.title('收盘价与均线走势图')
plt.grid(alpha=0.3)
plt.show()

# ===================== 5. 可视化2:K线图 =====================
y = df[['open','close','low','high']].values.tolist()
kline = (
    Kline()
    .add_xaxis(df['date'].tolist())
    .add_yaxis('股票K线图', y)
    .set_global_opts(
        title_opts=opts.TitleOpts(title='股票行情K线图'),
        toolbox_opts=opts.ToolboxOpts(is_show=True)
    )
)
kline.render_notebook()

# ===================== 6. 可视化3:成交量柱状图 =====================
plt.figure(figsize=(14,6))
plt.bar(df['date'], df['volume'])
plt.title('股票成交量柱状图')
plt.show()

# ===================== 7. 可视化4:涨跌幅直方图 =====================
plt.figure(figsize=(14,6))
plt.hist(df['p_change'], bins=40)
plt.axvline(0, color='red', linestyle='--', label='涨跌分界线')
plt.legend()
plt.title('股票每日涨跌幅分布直方图')
plt.show()

# ===================== 8. 特征工程 & 数据集划分 =====================
df["close_lag1"] = df["close"].shift(1)
df = df.dropna()
split = int(len(df) * 0.8)
train = df.iloc[:split]
test = df.iloc[split:]
feature_cols = ["close_lag1", "open", "ma5"]
X_train = train[feature_cols]
y_train = train["close"]
X_test = test[feature_cols]
y_test = test["close"]

# 评估函数
def eval_model(y_true, y_pred):
    mae = mean_absolute_error(y_true, y_pred)
    rmse = np.sqrt(mean_squared_error(y_true, y_pred))
    r2 = r2_score(y_true, y_pred)
    print(f"MAE:{mae:.2f} | RMSE:{rmse:.2f} | R²:{r2:.2f}")

# ===================== 9. 线性回归模型 =====================
lr = LinearRegression()
lr.fit(X_train, y_train)
y_pred_lr = lr.predict(X_test)
print("==== 线性回归模型评估 ====")
eval_model(y_test, y_pred_lr)

# 线性回归可视化
plt.figure(figsize=(14, 6))
plt.plot(test["date"], y_test, label="真实收盘价", linewidth=2)
plt.plot(test["date"], y_pred_lr, label="线性回归预测", linestyle="--")
plt.title("线性回归股价预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

# ===================== 10. ARIMA 模型 =====================
history = list(train["close"])
predictions = []
for i in range(len(test)):
    model = ARIMA(history, order=(10,1,0))
    model_fit = model.fit()
    yhat = model_fit.forecast()[0]
    predictions.append(yhat)
    history.append(test["close"].iloc[i])
y_pred_arima = np.array(predictions)
print("\n==== ARIMA模型评估 ====")
eval_model(y_test, y_pred_arima)

# ARIMA可视化
plt.figure(figsize=(14, 6))
plt.plot(test["date"], y_test, label="真实收盘价", linewidth=2)
plt.plot(test["date"], y_pred_arima, label="ARIMA预测", linestyle="--", color="orange")
plt.title("ARIMA时间序列预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

# ===================== 11. 双模型对比 =====================
plt.figure(figsize=(15, 6))
plt.plot(test["date"], y_test, label="真实收盘价", linewidth=2)
plt.plot(test["date"], y_pred_lr, label="线性回归", linestyle="--")
plt.plot(test["date"], y_pred_arima, label="ARIMA", linestyle="--", color="red")
plt.title("双模型预测效果对比")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

# ===================== 12. 预测未来10个交易日 =====================
full_series = df["close"]
final_arima = ARIMA(full_series, order=(5,1,0)).fit()
future_pred = final_arima.get_forecast(steps=10).predicted_mean
print("\n==== 未来10个交易日预测收盘价 ====")
for i, val in enumerate(future_pred, 1):
    print(f"第{i}天预测股价:{val:.2f} 元")

更多推荐