Python 股票时间序列全流程分析与预测(可视化 + 线性回归 + ARIMA)
文章简介
大家好,本篇博文将基于 Python 完成一套完整的股票时间序列数据分析实战,涵盖数据导入、数据清洗预处理、多维可视化分析(均线图、K 线图、成交量、涨跌幅分布),并分别使用线性回归与 ARIMA 时间序列模型实现股价预测,最后对比两大模型效果,同时完成未来股价预测。
全文附带完整可运行代码、详细注释、问题排错方案,零基础也能跟着复现,适合数据分析入门、Python 时间序列学习、金融数据分析练手。 工具栈:Pandas、Matplotlib、Pyecharts、Scikit-learn、Statsmodels 数据集:本地 Excel 股票行情数据(包含开盘价、收盘价、最高价、最低价、成交量、涨跌幅、5/10/20 日均线等字段)
一、整体流程概览
本次实战整体分为 6 大模块,循序渐进完成分析与建模:
- 环境配置 & 库导入
- 数据读取、探索与缺失值检测
- 数据预处理(时间格式转换、排序、字段筛选)
- 多维数据可视化(4 类核心图表)
- 模型构建:线性回归股价预测
- 模型构建:ARIMA 时间序列预测
- 双模型效果对比 + 未来股价预测
- 总结与踩坑排错
二、环境准备与库导入
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()
模型对比总结:
- 线性回归:误差更小、拟合度更高,依赖人工构造特征;
- ARIMA:纯时序模型,无需额外特征,泛化能力强,误差略高于线性回归;
- 两个模型整体趋势均与真实股价高度一致。
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 实战总结
- 数据层面:股票时间序列数据优先处理时间格式、排序、缺失值,这是所有分析的基础;
- 可视化层面:均线图、K 线图、成交量、涨跌幅直方图构成股票分析标准可视化体系;
- 模型层面:
- 线性回归:简单高效,适合特征丰富的场景;
- ARIMA:经典时序模型,无需人工特征,适合纯趋势预测;
- 本次两个模型对该股票短期预测效果优异,可用于初步行情研判。
10.2 拓展学习方向(进阶玩法)
- 特征优化:新增换手率、MACD、RSI 等金融技术指标,提升模型精度;
- 模型升级:尝试 LSTM、Prophet、XGBoost 等模型,对比更多算法效果;
- 数据扩充:使用 Tushare、Akshare 接口爬取实时股票数据,脱离本地 Excel;
- 策略回测:基于预测结果构建简单买卖策略,回测收益率。
十一、完整整合代码(一键运行版)
将所有代码整合,复制到 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} 元")更多推荐

所有评论(0)