从电工杯赛题到实战:时间序列分析预测BIPV板块指数趋势
2026/8/27 9:12:01 网站建设 项目流程

1. 项目概述:从一道赛题到一套完整的数据分析实战

去年带学生参加电工杯,B题“光伏建筑一体化板块指数发展趋势分析及预测”给我留下了挺深的印象。这题目乍一看是数学建模,但内核其实是一个典型的时间序列分析与行业研究结合的实战项目。它要求参赛者不仅要会建模型、写代码,更要对光伏建筑一体化(BIPV)这个细分领域有基本的认知,能从一堆看似枯燥的指数数据里,挖出行业发展的脉络和未来的可能性。很多新手团队一开始容易懵,要么沉迷于复杂的算法炫技,要么对行业背景一知半解导致分析浮于表面。今天,我就以这道赛题为蓝本,结合我们当时的解题思路和后续的一些思考,拆解一下如何系统性地完成这样一份“发展趋势分析及预测”报告。无论你是为了备战数模竞赛,还是想学习如何对一个新兴行业板块进行量化分析,这篇文章都能提供一套可直接复现的方法论和实操细节。

光伏建筑一体化,简单说就是把太阳能发电产品集成到建筑外墙、屋顶、窗户等部位,让建筑本身成为一个发电站。这道题的核心数据——“板块指数”,就是反映一系列相关上市公司股价整体表现的一个综合指标,其走势隐含了市场对BIPV行业未来预期的共识。因此,我们的任务就非常明确了:第一,读懂历史指数数据,分析其发展趋势(比如,是持续增长、周期性波动还是存在结构性变化);第二,建立可靠的数学模型,对未来一段时间的指数走势进行预测;第三,将数据分析结果与行业实际结合,给出有洞察力的解读。整个过程涉及数据预处理、特征工程、模型选择、参数调优、结果评估和报告撰写等多个环节,是一个完整的数据科学项目流程。

2. 解题核心思路与整体设计

面对这类问题,一个清晰的、结构化的分析框架比急于动手跑模型更重要。我们的整体思路可以概括为“描述-解释-预测-归因”四步走。

2.1 问题拆解与目标定义

首先,必须把赛题笼统的要求转化为具体、可执行的分析目标。题目中的“发展趋势分析”和“预测”是两层意思:

  1. 趋势分析(Descriptive & Diagnostic Analysis):这是“回头看”。需要运用统计方法和可视化工具,刻画历史指数序列的基本特征。这不仅仅是画个折线图说“它涨了”或“它跌了”,而是要深入回答:趋势是线性的还是非线性的?是否存在明显的季节性(例如,受政策年报季、行业展会周期影响)或周期性波动?序列的波动性(方差)是否随时间变化?有没有存在结构性断点(比如某项重大政策出台前后,增长轨迹发生了根本改变)?这些分析构成了预测的基石,也决定了后续该选用哪一类模型。
  2. 指数预测(Predictive Analysis):这是“向前看”。基于历史数据和已识别的模式,构建时间序列预测模型,对未来特定时段(如未来半年、一年)的指数点位进行估计,并最好能给出预测区间(置信区间),以量化预测的不确定性。

2.2 技术路线图设计

基于上述目标,我们设计了如下技术路线,这张路线图确保了分析的逻辑性和完整性:

  • 第一阶段:数据基础处理。获取并清洗指数数据(日度、周度或月度),处理缺失值、异常值,进行必要的平稳性检验(如ADF检验)。
  • 第二阶段:深度趋势解析。运用多种方法多角度刻画趋势,包括:① 可视化分析(时序图、滚动统计量图);② 统计分解(使用STL或经典分解法,将序列拆分为趋势项、季节项和残差项);③ 统计检验(检验平稳性、自相关性)。
  • 第三阶段:预测模型构建与比选。这是核心环节。我们计划同时搭建几个不同哲学基础的模型进行对比:
    • 经典时序模型:如ARIMA(自回归积分滑动平均)及其季节性变体SARIMA。这类模型基于序列自身的过去值和误差项进行预测,适用于具有明显自相关性的平稳序列。
    • 机器学习模型:如LightGBM、XGBoost等梯度提升树模型。这类模型不要求序列平稳,可以方便地引入外部特征(如宏观经济指标、行业政策虚拟变量、其他相关板块指数等),捕捉复杂的非线性关系。
    • 深度学习模型:如LSTM(长短期记忆网络)。这类模型特别擅长处理长序列依赖关系,能记忆长期的历史模式,对于波动复杂的时序数据有潜力。
  • 第四阶段:模型评估与综合预测。使用历史数据的一部分作为测试集,用RMSE(均方根误差)、MAE(平均绝对误差)等指标客观评估各模型性能。不迷信单一模型,可采用模型集成(如加权平均)的方式生成最终预测结果,并计算预测区间。
  • 第五阶段:行业洞察与报告撰写。将冰冷的预测数字与行业热点(如“双碳”目标推进、绿色建筑补贴政策、新型光伏材料技术突破等)结合,解释趋势背后的驱动因素,使报告有血有肉。

注意:在实际竞赛中,受限于数据可得性和时间,外部特征的引入可能比较困难。此时,重点应放在对纯指数序列的深度挖掘上,把经典时序模型(ARIMA)和机器学习/深度学习模型(仅用时间特征和滞后特征)的对比做扎实,同样能体现工作量和技术深度。

3. 数据获取、预处理与探索性分析

巧妇难为无米之炊,高质量的数据是分析的起点。

3.1 数据来源与初步处理

对于BIPV板块指数,通常可以从金融数据终端(如Wind、同花顺iFinD)或开源财经API(如akshare,一个基于Python的库)获取。我们当时使用了akshare,因为它免费且方便集成到Python分析流程中。获取的数据字段至少应包含日期和收盘指数。

拿到原始数据后,第一步是“清洗”:

  • 缺失值处理:检查是否存在交易日但指数数据缺失的情况。对于少数缺失,可以用前向填充(用前一天的数据)或插值法补充。如果缺失较多,需要审视数据源可靠性。
  • 异常值检测:由于股市有涨跌停限制,单纯的指数值异常较少,但需警惕因数据错误导致的“毛刺”。可以通过计算滚动均值加减3倍标准差来识别,或观察日涨跌幅是否超过合理范围(如±10%)。对于确认为错误的点,予以修正或剔除。
  • 格式转换:确保日期列被正确解析为datetime格式,并将指数列转换为数值型。
# 示例:使用akshare获取板块指数数据(以同花顺BIPV概念板块为例,代码需根据实际情况调整) import akshare as ak import pandas as pd # 假设板块代码为 ‘885937’ (此处为示例,实际代码需查询) # df = ak.stock_board_concept_hist_em(symbol="BIPV", start_date="20190101", end_date="20211231") # 由于akshare接口可能变动,更通用的做法是获取股票池后自己计算,或使用其他稳定接口 # 这里展示一个数据清洗的框架 def clean_index_data(df): # 确保日期格式 df['date'] = pd.to_datetime(df['date']) df.set_index('date', inplace=True) # 按日期排序 df.sort_index(inplace=True) # 处理缺失值(如果有) df['close'].fillna(method='ffill', inplace=True) # 前向填充 # 重采样(如果需要统一为周线或月线) # df_weekly = df['close'].resample('W').last() # 周线,取每周最后一天 return df # 假设df是已经读取的包含‘date’和‘close’两列的DataFrame # df_clean = clean_index_data(df)

3.2 探索性分析与可视化

清洗后的数据,要通过可视化手段形成初步认知。关键图表包括:

  1. 原始序列时序图:直接观察指数走势,对整体趋势、波动、可能的周期和异常点有个直观感受。
  2. 滚动统计量图:计算滚动均值(如60日移动平均)和滚动标准差。滚动均值线可以平滑短期波动,更清晰地显示长期趋势。滚动标准差图可以观察波动率是否稳定(例如,是否在某个事件后波动急剧放大,即存在“波动率聚集”现象)。
  3. 分布与自相关图:绘制指数收益率的直方图与Q-Q图,检验其是否服从正态分布(通常金融时间序列不服从,具有尖峰厚尾特征)。绘制自相关函数(ACF)和偏自相关函数(PACF)图,这是为后续选择ARIMA模型参数(p, d, q)提供关键依据。
import matplotlib.pyplot as plt import seaborn as sns from statsmodels.graphics.tsaplots import plot_acf, plot_pacf def exploratory_plots(series, window=60): fig, axes = plt.subplots(3, 2, figsize=(15, 12)) # 1. 原始序列 axes[0, 0].plot(series.index, series.values) axes[0, 0].set_title('Raw Index Series') axes[0, 0].set_xlabel('Date') axes[0, 0].set_ylabel('Index') # 2. 滚动均值与标准差 rolling_mean = series.rolling(window=window).mean() rolling_std = series.rolling(window=window).std() axes[0, 1].plot(series.index, series.values, label='Original', alpha=0.5) axes[0, 1].plot(rolling_mean.index, rolling_mean.values, label=f'{window}-Day Rolling Mean', color='red') axes[0, 1].set_title(f'Rolling Mean & Std (Window={window})') axes[0, 1].legend() ax_twin = axes[0, 1].twinx() ax_twin.plot(rolling_std.index, rolling_std.values, label=f'{window}-Day Rolling Std', color='green', alpha=0.7) ax_twin.set_ylabel('Rolling Std Dev') ax_twin.legend(loc='upper right') # 3. 收益率序列(通常分析对数收益率) returns = np.log(series / series.shift(1)).dropna() axes[1, 0].plot(returns.index, returns.values) axes[1, 0].set_title('Daily Log Returns') axes[1, 0].axhline(y=0, color='r', linestyle='--', alpha=0.5) # 4. 收益率分布 axes[1, 1].hist(returns.values, bins=50, edgecolor='black', alpha=0.7, density=True) sns.kdeplot(returns.values, ax=axes[1, 1], color='red') axes[1, 1].set_title('Distribution of Returns') # 5. ACF and PACF of returns (or original series after differencing if non-stationary) plot_acf(returns.dropna(), lags=40, ax=axes[2, 0]) axes[2, 0].set_title('Autocorrelation Function (ACF)') plot_pacf(returns.dropna(), lags=40, ax=axes[2, 1]) axes[2, 1].set_title('Partial Autocorrelation Function (PACF)') plt.tight_layout() plt.show() # 对清洗后的指数序列进行探索 # exploratory_plots(df_clean['close'])

通过这一步,我们就能回答趋势分析中的很多基础问题。例如,从滚动均值线看,BIPV指数在2020年中之后是否呈现加速上升趋势?滚动标准差是否在某个时期(如政策发布期)明显放大?收益率序列的ACF/PACF图是否显示短期自相关性?这些观察都将直接指导下一步的模型选择。

4. 预测模型构建:从经典统计到机器学习

探索性分析之后,我们对数据特性有了了解,接下来就是构建预测模型。我们采用多模型竞技的策略。

4.1 经典时间序列模型:ARIMA/SARIMA

ARIMA模型是时序预测的基准方法。其建模流程非常标准化:

  1. 平稳性检验与差分:使用ADF检验判断原序列是否平稳。若不平稳,则进行差分运算(d阶),直到序列平稳。对于BIPV指数,通常一阶差分(即计算日收益率)即可平稳。
  2. 确定模型阶数:对平稳化后的序列,观察其ACF和PACF图,初步判断自回归阶数p和移动平均阶数q。更常用的方法是网格搜索配合信息准则(如AIC、BIC),自动寻找最优的(p,d,q)组合。
  3. 模型拟合与诊断:用选定的参数拟合ARIMA模型。然后必须进行残差诊断:残差序列应该是白噪声(均值为0、无自相关、正态分布)。可以通过残差ACF图、Ljung-Box检验和Q-Q图来验证。如果残差不是白噪声,说明模型未能完全捕捉数据中的信息,需要重新调整阶数。
  4. 季节性扩展:如果数据存在季节性(例如,月度数据可能显示年度周期),则使用SARIMA模型,它额外包含了季节性部分的(P, D, Q, S)参数。
from statsmodels.tsa.stattools import adfuller from statsmodels.tsa.arima.model import ARIMA import itertools import warnings warnings.filterwarnings('ignore') def arima_modeling(series, test_size=0.2): # 划分训练集和测试集 split_idx = int(len(series) * (1 - test_size)) train, test = series[:split_idx], series[split_idx:] # 1. 平稳性检验 (以原始序列为例) result = adfuller(train.dropna()) print(f'ADF Statistic: {result[0]:.4f}') print(f'p-value: {result[1]:.4f}') if result[1] > 0.05: print("Series is non-stationary. Differencing needed.") # 通常进行一阶差分 train_diff = train.diff().dropna() else: print("Series is stationary.") train_diff = train # 2. 网格搜索寻找最优 (p,d,q) - 这里d取1(假设一阶差分后平稳) p = d = q = range(0, 4) # 搜索范围,可根据ACF/PACF缩小 best_aic = float('inf') best_order = None # 注意:这是一个计算量较大的步骤,实际中可以用更智能的方法(如auto_arima) for order in itertools.product(p, [1], q): # 固定d=1 try: model = ARIMA(train, order=order) results = model.fit() if results.aic < best_aic: best_aic = results.aic best_order = order except: continue print(f'Best ARIMA order: {best_order} with AIC: {best_aic:.2f}') # 3. 用最优阶数拟合最终模型 final_model = ARIMA(train, order=best_order) final_results = final_model.fit() print(final_results.summary()) # 4. 残差诊断(可视化) residuals = final_results.resid fig, axes = plt.subplots(1, 2, figsize=(12,4)) axes[0].plot(residuals) axes[0].set_title('Residuals over Time') plot_acf(residuals.dropna(), lags=40, ax=axes[1]) axes[1].set_title('ACF of Residuals') plt.tight_layout() plt.show() # 5. 预测 forecast_obj = final_results.get_forecast(steps=len(test)) forecast = forecast_obj.predicted_mean conf_int = forecast_obj.conf_int() # 绘制预测结果对比 plt.figure(figsize=(10,6)) plt.plot(train.index, train.values, label='Train') plt.plot(test.index, test.values, label='Test', color='gray') plt.plot(forecast.index, forecast.values, label='ARIMA Forecast', color='red') plt.fill_between(conf_int.index, conf_int.iloc[:,0], conf_int.iloc[:,1], color='pink', alpha=0.3) plt.legend() plt.title('ARIMA Model Forecast vs Actual') plt.show() return final_results, forecast, test # 注意:实际应用中,对于非平稳序列,更常见的做法是直接对序列建模,让ARIMA模型内部的差分参数(d)来处理。 # 可以使用 pmdarima 库的 auto_arima 函数自动完成差分、阶数选择和季节性检测,效率更高。

4.2 机器学习模型:以LightGBM为例

与ARIMA只利用序列自身历史值不同,机器学习模型允许我们构建丰富的特征。即使在没有外部数据的情况下,我们也可以从时间戳中创造特征。

  • 特征工程
    • 滞后特征:这是最重要的特征。创建指数前1天、前5天、前10天、前20天等的滞后值(lag_1,lag_5, ...)。
    • 滚动统计特征:计算过去N个窗口的滚动均值、标准差、最大值、最小值(如rolling_mean_7,rolling_std_30),用来描述近期趋势和波动。
    • 时间特征:从日期中提取年份、月份、季度、星期几、一年中的第几天等,以捕捉可能的季节性。
    • 趋势特征:如距离序列起点的天数,或一个简单的线性趋势项。
  • 模型训练:将问题转化为监督学习问题。每一行数据对应一个目标值(当天的指数)和一系列特征(基于过去信息构造的特征)。使用训练集训练LightGBM回归模型。
  • 预测:进行多步预测时,需要采用递归策略(Recursive Strategy)。即用模型预测出t+1期后,将这个预测值作为已知值,用于构造t+2期的滞后特征,如此循环。
import lightgbm as lgb from sklearn.metrics import mean_squared_error, mean_absolute_error def create_features(df, target_col, max_lag=30, rolling_windows=[7, 30]): """ 为时序数据创建特征 """ df = df.copy() # 滞后特征 for lag in range(1, max_lag+1): df[f'lag_{lag}'] = df[target_col].shift(lag) # 滚动统计特征 for window in rolling_windows: df[f'rolling_mean_{window}'] = df[target_col].rolling(window=window, min_periods=1).mean().shift(1) df[f'rolling_std_{window}'] = df[target_col].rolling(window=window, min_periods=1).std().shift(1) df[f'rolling_max_{window}'] = df[target_col].rolling(window=window, min_periods=1).max().shift(1) df[f'rolling_min_{window}'] = df[target_col].rolling(window=window, min_periods=1).min().shift(1) # 时间特征 df['year'] = df.index.year df['month'] = df.index.month df['day_of_week'] = df.index.dayofweek df['day_of_year'] = df.index.dayofyear df['quarter'] = df.index.quarter # 趋势特征 df['time_idx'] = range(len(df)) df.dropna(inplace=True) # 由于创建滞后特征,前几行会变成NaN return df def lightgbm_forecast(df, target_col, test_size=0.2, forecast_horizon=30): # 创建特征 df_feat = create_features(df[[target_col]], target_col) # 划分特征X和目标y feature_cols = [col for col in df_feat.columns if col != target_col] X = df_feat[feature_cols] y = df_feat[target_col] # 按时间划分训练集和测试集(不能随机打乱!) split_idx = int(len(X) * (1 - test_size)) X_train, X_test = X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test = y.iloc[:split_idx], y.iloc[split_idx:] # 训练LightGBM模型 model = lgb.LGBMRegressor( n_estimators=500, learning_rate=0.05, num_leaves=31, random_state=42 ) model.fit(X_train, y_train, eval_set=[(X_test, y_test)], callbacks=[lgb.early_stopping(50), lgb.log_evaluation(0)]) # 在测试集上评估 y_pred_test = model.predict(X_test) rmse = np.sqrt(mean_squared_error(y_test, y_pred_test)) mae = mean_absolute_error(y_test, y_pred_test) print(f'Test RMSE: {rmse:.4f}') print(f'Test MAE: {mae:.4f}') # 可视化测试集拟合效果 plt.figure(figsize=(12,6)) plt.plot(y_test.index, y_test.values, label='Actual Test', alpha=0.7) plt.plot(y_test.index, y_pred_test, label='LightGBM Predictions', alpha=0.9) plt.legend() plt.title('LightGBM Model: In-Sample Test Performance') plt.show() # 进行多步预测(递归预测) last_known_data = df_feat.iloc[-1:].copy() future_predictions = [] for i in range(forecast_horizon): # 使用最新数据(包括上一次的预测值)生成特征 if i > 0: # 更新目标值为上一次的预测值 last_known_data[target_col] = future_predictions[-1] # 重新计算所有基于该目标值的特征(这是一个简化示例,实际需要更严谨地更新所有滞后和滚动特征) # 更稳健的做法是每次循环都基于完整的更新后的序列重新调用 create_features,但效率低。 # 这里为演示,我们只更新最关键的滞后1特征。 last_known_data['lag_1'] = last_known_data[target_col].shift(1).fillna(df_feat[target_col].iloc[-1]) # 注意:滚动特征也需要更新,此处省略复杂逻辑,实际项目需仔细处理。 # 预测下一步 next_pred = model.predict(last_known_data[feature_cols])[0] future_predictions.append(next_pred) return model, y_pred_test, future_predictions, rmse, mae

4.3 深度学习模型:LSTM简介

对于更复杂的非线性模式,可以尝试LSTM。其核心是构建一个序列到序列的预测模型。需要将数据整理成样本格式:[样本数, 时间步长, 特征数]。例如,用过去60天的数据(时间步长=60)预测下一天。LSTM对数据标准化非常敏感,通常需要将数据缩放到[0,1]或[-1,1]之间。模型结构通常包括若干LSTM层、Dropout层(防止过拟合)和全连接输出层。训练完成后,同样采用递归方式进行多步预测。

实操心得:在数模竞赛的有限时间内,LSTM的调参(层数、神经元数、dropout率、时间步长)和训练比较耗时。如果ARIMA和LightGBM已经能取得不错的效果,不一定非要上LSTM。但如果数据量足够大(比如日线数据多年),且序列模式非常复杂,LSTM可能有奇效。关键在于构建合理的监督学习数据集和设计有效的网络结构。

5. 模型评估、集成与结果分析

模型建好了,接下来就是“赛马”,选出最靠谱的那个,或者让它们“组团”工作。

5.1 模型评估指标与对比

我们将历史数据按时间顺序划分为训练集和测试集(例如,前80%训练,后20%测试)。在测试集上比较各模型的预测性能。常用指标包括:

  • RMSE(均方根误差):放大较大误差的影响,对异常值敏感,单位与原始数据相同。
  • MAE(平均绝对误差):对所有误差一视同仁,更稳健。
  • MAPE(平均绝对百分比误差):相对误差,便于理解,但当真实值接近0时可能失真。
  • 拟合优度:可以看预测值与实际值的散点图与y=x直线的接近程度。

我们通常以RMSE和MAE作为主要评判标准,并绘制测试集上的预测对比图,直观感受哪个模型的预测曲线更贴近实际走势。

模型RMSE (测试集)MAE (测试集)优点缺点适用场景建议
ARIMA/SARIMA数值1数值1理论完善,解释性强,适合线性关系明显的平稳序列。对非线性关系捕捉能力弱,对长期预测误差累积快。数据平稳,趋势和季节性明显,且外部影响因素较少时作为基准模型。
LightGBM/XGBoost数值2数值2能捕捉复杂非线性关系,对特征类型包容性好,训练预测速度快。模型可解释性相对较差,需要仔细的特征工程。数据模式复杂,可构造有效特征(包括外部特征),追求较高预测精度时首选。
LSTM数值3数值3擅长捕捉长期依赖关系,对序列数据建模能力强。需要大量数据,训练时间长,超参数调优复杂,易过拟合。数据量非常大,且序列中存在复杂的长期记忆模式时考虑使用。

5.2 模型集成策略

在竞赛或实际应用中,为了追求更稳定、更鲁棒的预测,我们常常不依赖单一模型,而是进行模型集成。简单有效的方法有:

  • 简单平均法:将几个表现较好的模型的预测结果直接取算术平均。
  • 加权平均法:根据各模型在验证集上的表现(如RMSE的倒数)分配权重,表现越好权重越高。
  • 堆叠法:将初级模型(如ARIMA、LightGBM)的预测结果作为新特征,训练一个次级模型(如线性回归)进行最终预测。这种方法更复杂,但潜力也更大。

在我们的项目中,假设ARIMA和LightGBM表现相当,我们可以采用加权平均。例如,ARIMA的测试集RMSE为R_a,LightGBM的为R_l,则它们的权重可以设为w_a = (1/R_a) / (1/R_a + 1/R_l),w_l = (1/R_l) / (1/R_a + 1/R_l)。最终的集成预测值为w_a * forecast_arima + w_l * forecast_lgb

5.3 预测结果的可视化与行业解读

得到最终预测值后,需要以专业的方式呈现:

  1. 绘制综合预测图:在同一个坐标系中,绘制历史指数曲线、测试集的实际值、各单一模型的预测曲线以及最终集成模型的预测曲线。用不同颜色和线型区分,并添加图例。
  2. 添加预测区间:对于ARIMA等统计模型,可以计算出预测值的置信区间(如95%置信区间)。在图上用阴影区域表示,这能直观展示预测的不确定性。对于集成模型,可以通过模拟或经验方法估算区间。
  3. 撰写分析结论:这是升华部分。不能只罗列数字和图表,要结合BIPV行业动态进行解读。例如:
    • 趋势归因:历史上涨趋势主要得益于哪几个阶段的政策驱动(如“整县推进”政策)或技术突破?
    • 波动分析:指数在哪些时间段出现大幅回调?可能与大盘环境、行业短期产能过剩预期或某个公司业绩暴雷有关?
    • 预测启示:模型预测未来半年指数呈现“震荡上行”态势。这反映了市场对哪些长期因素的乐观预期?(如“双碳”目标下的长期需求、建筑节能标准的提升)。同时,预测区间较宽,又提示了哪些潜在风险?(如原材料价格波动、行业竞争加剧、政策执行力度不及预期等)。

6. 实战避坑指南与常见问题

在实操中,我们踩过不少坑,也总结了一些让分析更出彩的技巧。

6.1 数据层面的陷阱

  • 幸存者偏差:板块指数的成分股会定期调整。现在的指数包含的股票,在过去可能并不在板块内。如果直接使用当前成分股回测历史,会产生偏差。解决方法:尽可能获取到历史存续的、准确的板块指数数据,而不是自己用当前成分股回测。
  • 异常值与结构性断点:重大政策发布、行业黑天鹅事件会导致指数跳空或趋势改变。如果简单地将这些点视为异常值剔除,会丢失关键信息。正确的做法是:在模型中引入虚拟变量。例如,在某个政策出台日期之后,设置一个哑变量为1,之前为0,将这个变量作为特征加入机器学习模型,让模型去学习这个结构性变化的影响。
  • 数据频率选择:使用日线数据噪音大,但信息丰富;使用周线或月线数据更平滑,趋势更明显,但可能丢失短期动态。根据预测目标(短期交易还是中长期趋势判断)选择合适频率。对于趋势分析,周线或月线可能更合适。

6.2 模型选择与调优的误区

  • 过度追求复杂模型:不要一上来就搞LSTM、Transformer。ARIMA永远是一个优秀的基线模型。先跑通一个简单的ARIMA,看看效果,再用更复杂的模型去挑战它。如果ARIMA已经能做到MAE很小,那么复杂模型的提升空间可能有限,需要权衡其带来的复杂度。
  • 忽略模型假设:ARIMA要求序列平稳或差分后平稳,且残差为白噪声。如果这些诊断检验没通过,预测结果就不可靠。务必做好平稳性检验和残差诊断。
  • 特征工程中的“数据泄露”:在构造机器学习特征时,绝对不能使用未来的信息。例如,计算“当日滚动均值”时,必须使用到昨日为止的数据(.shift(1))。这是一个极易出错的地方,会导致模型在测试集上表现虚高,而实际预测能力很差。
  • 评估方式错误:时间序列数据绝对不能随机划分训练集和测试集,必须按时间顺序划分。要用过去的数据训练,预测未来的数据。交叉验证也需要使用“时间序列交叉验证”,如TimeSeriesSplit。

6.3 让报告脱颖而出的关键点

  • 敏感性分析:展示模型的稳健性。例如,改变训练集的时间窗口长度(用最近1年、2年、3年数据分别训练),看预测结果是否发生剧烈变化。如果变化很大,说明模型稳定性欠佳,结论需要谨慎。
  • 多情景预测:不要只给出一条预测线。可以结合行业分析,给出“乐观”、“中性”、“悲观”三种情景下的预测。例如,乐观情景对应政策超预期落地,悲观情景对应宏观经济下行压力加大。这能体现思考的全面性。
  • 将量化结果与定性分析深度结合:这是区分优秀报告和普通报告的关键。不能只说“模型预测未来上涨”,而要解释“为什么模型会预测上涨?——可能是因为我们在特征中引入了‘绿色贷款余额同比增长率’这个宏观指标,而该指标与指数在训练集上表现出较强的正相关性,且当前该指标处于上行通道。” 这样,你的预测就不再是黑箱,而是有逻辑支撑的推断。

最后想说的是,这道电工杯的B题是一个非常好的数据分析实战模板。它训练的不是某个孤立的算法,而是一套从问题理解、数据获取、探索分析、模型构建、评估优化到结果解读的完整工作流。掌握这套流程,不仅能在数模竞赛中应对自如,更能应用到实际的行业研究、市场分析乃至量化投资工作中去。真正的价值不在于预测得百分之百准确,而在于通过系统性的分析,建立起对行业动态的量化认知框架,并用数据驱动的方式去验证或修正自己的判断。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询