简介:这份资源面向具备Python基础、希望入门数据挖掘与机器学习实战的学习者,聚焦线性回归在股票预测中的完整落地流程。内容围绕数据获取与清洗、特征工程、模型训练与预测评估展开,涉及pandas数据处理、NumPy特征计算,以及移动平均、MACD、RSI等技术指标的构建思路,并借助sklearn的线性回归完成拟合,通过MSE、RMSE与R²等指标衡量预测效果,同时讨论了线性模型在非线性市场中的局限与改进方向。压缩包共2个文件,包含1个py源码与1个pdf讲解文档,整体约2.34MB,源码可直接运行对照,文档便于梳理原理与流程。目前已有4741人学习下载,适合作为课程设计、项目练手或金融预测入门的参考案例,帮助读者打通从数据预处理到模型评估的完整链路。
1. 从一份股票预测源码说起:线性回归到底能挖出什么
很多人第一次接触数据挖掘和机器学习,都是从一份股票预测的源码包开始的。打开一看,无非是几个 CSV 文件、一个 Python 脚本,跑完输出一条拟合直线和几个误差指标。但真正动手跑过的人会发现一个反直觉的现象:同样的代码、同样的数据,每次运行出来的预测结果居然不一样。这不是代码写错了,而是机器学习流程里数据划分、随机种子、特征顺序这些环节在起作用。这份以线性回归为核心的股票预测案例,本质上是一个完整的监督学习闭环:数据获取、特征工程、模型训练、预测评估。它适合刚学完 Python 语法、想找一个能跑通的项目来理解机器学习全流程的人,也适合已经会调库、但没认真想过每一步为什么这么做的开发者。线性回归虽然简单,但它是理解更复杂模型的基石,股票预测虽然噪声大,但它是练习时间序列特征构造的绝佳场景。
2. 数据挖掘与机器学习在股票预测里的分工:先搞清楚谁干什么
2.1 数据挖掘负责找关系,机器学习负责学映射
很多人把数据挖掘和机器学习混着说,但在一个股票预测项目里,两者的分工其实很清晰。数据挖掘阶段要做的是从原始行情数据里构造出有意义的特征,比如把收盘价转成收益率、计算移动平均线、提取成交量变化率。这些操作不涉及模型,纯粹是对数据的加工和探索。机器学习阶段则是拿这些特征去拟合一个目标变量,比如明天的收盘价或者涨跌方向。线性回归在这里的角色是建立一个可解释的线性映射:y = w1x1 + w2x2 + ... + b。权重 wi 表示每个特征对预测目标的影响方向和大小,截距 b 是基准值。这个公式看起来简单,但背后有几个关键假设:特征与目标之间存在线性关系、误差项独立同分布、特征之间不能有严重共线性。股票数据往往不满足这些假设,所以线性回归在股票预测里的定位不是追求最高精度,而是作为一个可解释的基线模型。你先用它跑通流程,知道哪些特征有正向贡献、哪些有负向贡献,再考虑换更复杂的模型。
2.2 从原始行情到特征矩阵:一个可复现的构造流程
假设你手里有一份包含日期、开盘价、最高价、最低价、收盘价、成交量的 CSV 文件。第一步是加载数据并检查缺失值。常见做法是用 pandas 读取,然后按日期排序,确保时间顺序正确。第二步是构造特征。对于股票预测,最基础的特征包括:前一日收盘价、前五日收盘价均值、前一日成交量、前一日涨跌幅。目标变量可以设为当日收盘价或次日收盘价。这里有一个容易翻车的地方:如果你用当日收盘价去预测当日收盘价,那就是数据泄露,模型会给出完美结果但毫无意义。正确的做法是用 t 时刻及之前的数据预测 t+1 时刻的值。第三步是划分训练集和测试集。时间序列不能随机打乱,必须按时间顺序切分,比如前 80% 作为训练集,后 20% 作为测试集。下面是一个可复现的特征构造代码片段:
import pandas as pd import numpy as np # 读取数据,假设文件名为 stock.csv df = pd.read_csv('stock.csv', parse_dates=['date']) df = df.sort_values('date').reset_index(drop=True) # 构造特征 df['ret_1'] = df['close'].pct_change(1) # 前一日涨跌幅 df['ma_5'] = df['close'].rolling(5).mean() # 前五日收盘价均值 df['vol_ratio'] = df['volume'] / df['volume'].rolling(5).mean() # 成交量比值 df['target'] = df['close'].shift(-1) # 次日收盘价作为目标 # 去掉缺失值 df = df.dropna().reset_index(drop=True) # 按时间顺序划分 split = int(len(df) * 0.8) train = df.iloc[:split] test = df.iloc[split:] feature_cols = ['ret_1', 'ma_5', 'vol_ratio'] X_train = train[feature_cols].values y_train = train['target'].values X_test = test[feature_cols].values y_test = test['target'].values这段代码里,pct_change(1)计算的是相邻两日的收益率,rolling(5).mean()是滚动窗口均值,shift(-1)把下一行数据上移一行作为目标。参数方面,窗口大小 5 是一个经验值,你可以改成 10 或 20,但要注意窗口越大,丢失的早期数据越多。dropna()会删掉因为滚动和移位产生的空值行,这是必须的,否则模型训练会报错。划分比例 0.8 是常见选择,时间序列里不建议用随机划分,因为那会引入未来信息。
2.3 线性回归模型的训练与评估:三个必调参数和两个评估指标
训练线性回归模型在 scikit-learn 里只需要三行代码,但要让结果稳定可复现,有几个参数必须注意。第一个是fit_intercept,默认是 True,表示模型会计算截距项。如果你的特征已经做过标准化,保留截距通常没问题;但如果你的数据已经中心化到零均值,可以设为 False 来减少一个参数。第二个是normalize,这个参数在新版本里已经废弃,正确的做法是在训练前用StandardScaler对特征做标准化。第三个是random_state,线性回归本身没有随机性,但如果你用了train_test_split或者后续要做交叉验证,必须固定这个种子,否则每次划分不同,结果自然不一样。评估指标方面,回归任务常用均方误差 MSE 和决定系数 R²。MSE 衡量预测值与真实值的平均平方偏差,单位是目标变量的平方,不好直接解释;R² 表示模型解释了目标变量多少比例的方差,越接近 1 越好,但股票数据上通常只有 0.01 到 0.1 之间,甚至为负。下面是一个完整的训练和评估代码:
from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_squared_error, r2_score # 标准化特征 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 训练模型 model = LinearRegression(fit_intercept=True) model.fit(X_train_scaled, y_train) # 预测 y_pred = model.predict(X_test_scaled) # 评估 mse = mean_squared_error(y_test, y_pred) r2 = r2_score(y_test, y_pred) print(f'MSE: {mse:.4f}, R2: {r2:.4f}') print('系数:', model.coef_) print('截距:', model.intercept_)这里StandardScaler做了两件事:减去均值、除以标准差。注意训练集用fit_transform,测试集只能用transform,否则测试集的统计量会泄露到训练过程中。model.coef_输出每个特征的权重,正数表示该特征增加时预测值增加,负数相反。如果某个特征的权重绝对值特别大,说明它对预测影响很大,但也可能是共线性导致的数值不稳定。R² 为负说明模型还不如直接拿均值去预测,这在股票数据里很常见,不必惊慌,重点是理解流程。
3. 把源码跑起来:环境配置、数据替换和结果解读的完整路径
3.1 Python 环境与依赖安装:避开版本冲突的坑
拿到一份源码包,第一步不是急着运行,而是先看它依赖哪些库。常见的组合是 pandas、numpy、scikit-learn、matplotlib。如果你用的是 Anaconda,很多库已经预装了,但版本可能和源码要求的不一致。我一般会创建一个独立的虚拟环境,避免污染全局环境。命令如下:
python -m venv stock_env source stock_env/bin/activate # Linux/Mac # stock_env\Scripts\activate # Windows pip install pandas numpy scikit-learn matplotlib如果你在 Windows 上,激活命令是stock_env\Scripts\activate。安装完成后,用pip list检查版本。pandas 建议 1.3 以上,scikit-learn 建议 1.0 以上。如果源码里用了normalize参数而你的 scikit-learn 是 1.2 以上,会报错,因为该参数已被移除。解决办法是把LinearRegression(normalize=True)改成先用StandardScaler标准化,再训练LinearRegression()。另一个常见问题是 matplotlib 中文显示乱码,需要在代码里加两行配置:
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = FalseSimHei是黑体,Windows 上一般都有;Linux 上可能需要换成WenQuanYi Micro Hei或其他已安装的中文字体。axes.unicode_minus设为 False 是为了正常显示负号。
3.2 替换自己的数据:CSV 列名映射和日期格式处理
源码包里的数据通常是示例数据,你想用自己的股票数据跑一遍,就需要做列名映射。假设你的 CSV 里列名是中文的“日期、开盘、最高、最低、收盘、成交量”,而代码里用的是英文date, open, high, low, close, volume。有两种改法:一是改代码里的列名,二是读入后重命名列。我一般用后者,不动源码逻辑:
df = pd.read_csv('my_stock.csv') df = df.rename(columns={ '日期': 'date', '开盘': 'open', '最高': 'high', '最低': 'low', '收盘': 'close', '成交量': 'volume' }) df['date'] = pd.to_datetime(df['date']) df = df.sort_values('date').reset_index(drop=True)pd.to_datetime会把字符串转成时间戳,如果日期格式不标准,可以加format参数,比如format='%Y/%m/%d'。排序和重置索引是必须的,因为后面的滚动计算依赖顺序。如果你的数据里有停牌日或者缺失交易日,pct_change会算出异常值,建议先检查缺失情况,必要时用前值填充或直接删除。
3.3 预测结果怎么看:残差图、系数表和滚动预测
模型跑完输出 MSE 和 R² 只是第一步,真正要看的是残差分布和系数含义。残差是真实值减去预测值,如果残差围绕零随机分布,说明模型没有系统性偏差;如果残差呈现明显的趋势或周期性,说明还有信息没被提取。画残差图的代码如下:
import matplotlib.pyplot as plt residuals = y_test - y_pred plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.plot(residuals) plt.title('残差序列') plt.subplot(1, 2, 2) plt.hist(residuals, bins=30) plt.title('残差分布') plt.show()系数表可以帮你理解每个特征的影响。比如ret_1的系数是正的,说明前一日涨跌幅对次日收盘价有正向影响;vol_ratio的系数是负的,说明成交量放大时次日价格可能下跌。但要注意,这些系数是在标准化之后得到的,比较的是相对重要性,不是原始单位下的变化量。滚动预测是更贴近实盘的验证方式:每次用过去一段窗口训练,预测下一天,然后窗口向前滑动。这样能看出模型在不同时间段的表现是否稳定。如果某段时间 R² 突然变负,说明市场结构可能发生了变化,模型需要重新训练。
4. 避坑与排查:股票预测里那些让你白忙一天的细节
4.1 现象:每次运行结果都不一样,R² 忽高忽低
原因通常有三个:一是没有固定随机种子,如果你用了train_test_split或者任何带随机性的步骤,必须设random_state;二是数据划分方式不对,时间序列如果随机打乱,每次划分不同,结果自然不同;三是特征标准化时用了全部数据,导致测试集信息泄露。解决方法是:固定random_state=42,按时间顺序切分,标准化只在训练集上fit。
4.2 现象:模型在训练集上 R² 很高,测试集上为负
这是典型的过拟合。线性回归本身不容易过拟合,但如果特征数量接近甚至超过样本数量,或者特征之间存在严重共线性,就会出现这种情况。解决方法是减少特征数量、使用正则化(Ridge 或 Lasso)、或者增加样本量。股票数据里,如果你用了太多技术指标,很容易陷入过拟合。我一般会先只保留三到五个核心特征,跑通后再逐步增加。
4.3 现象:预测值是一条直线,完全不波动
原因可能是目标变量选错了。如果你用当日收盘价预测当日收盘价,或者特征里包含了目标变量的直接衍生项,模型会直接学到恒等映射。另一个可能是特征没有标准化,导致某些大数值特征主导了权重。解决方法是检查特征和目标的时间对齐关系,确保用 t 时刻预测 t+1 时刻,并对特征做标准化。
4.4 现象:CSV 读取报错,提示列不存在或类型错误
常见原因是列名有空格、编码不对、或者日期列被当成了字符串。解决方法是先用df.columns打印列名,确认没有多余空格;用df.info()查看每列类型;如果编码是 GBK,读取时加encoding='gbk'。日期列用pd.to_datetime转换,转换失败的行可以用errors='coerce'变成 NaT,然后删掉。
4.5 现象:画图时中文显示成方框
这是 matplotlib 字体配置问题。Windows 上设置plt.rcParams['font.sans-serif'] = ['SimHei'],Mac 上可以用['Arial Unicode MS'],Linux 上需要先确认系统安装了中文字体,然后用fc-list :lang=zh查看可用字体名称,再配置到font.sans-serif里。改完配置后需要重启 Python 内核才能生效。
5. 从线性回归到滚动回测:一个更接近实盘的验证习惯
线性回归跑通之后,很多人会直接看测试集上的 R² 就下结论。但股票预测里,单次划分的测试结果偶然性很大。我后来养成了一个习惯:做滚动回测。具体做法是设定一个训练窗口,比如 250 个交易日,然后每次用窗口内的数据训练,预测窗口后一天,窗口向前滑动一天,重复这个过程。这样能得到一条完整的预测序列,再计算整体的 MSE 和 R²。代码实现如下:
def rolling_backtest(df, feature_cols, window=250): preds = [] trues = [] for i in range(window, len(df) - 1): train = df.iloc[i-window:i] test = df.iloc[i:i+1] X_train = train[feature_cols].values y_train = train['target'].values X_test = test[feature_cols].values y_test = test['target'].values scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) model = LinearRegression() model.fit(X_train_scaled, y_train) pred = model.predict(X_test_scaled) preds.append(pred[0]) trues.append(y_test[0]) mse = mean_squared_error(trues, preds) r2 = r2_score(trues, preds) return mse, r2, preds, trues这个函数里,window=250大约是一年的交易日数量,你可以根据数据长度调整。每次循环都重新fit标准化器,确保只用历史信息。跑完滚动回测后,把预测值和真实值画在一起,能直观看到模型在哪些时间段表现好、哪些时间段失效。如果整体 R² 仍然很低,不要灰心,股票预测本身就是高噪声任务,线性回归的价值在于提供一个可解释的基线。你可以在这个基础上尝试加入更多特征、换用 Ridge 回归做正则化、或者把目标变量从收盘价改成涨跌方向做分类。但无论怎么改,滚动回测这个验证习惯都值得保留,它比单次划分更能反映模型的真实泛化能力。希望帮到你。
本文还有配套的精品资源,点击获取