如何实现茶叶出口总量及分茶种、重量等级的销量预测?
茶叶出口量预测落地方案
一、先修复分组后的数据结构
你当前的层级索引数据不适合直接建模,先做扁平化和补全处理,这是之前模型失败的核心原因之一:
# 重置层级索引为普通列 df_flat = df.reset_index() # 生成所有可能的 年月-茶种-重量等级 组合,补全缺失项的销量为0 from itertools import product all_combinations = pd.DataFrame( product(df_flat['YearMonth'].unique(), df_flat['Type'].unique(), df_flat['Weight'].unique()), columns=['YearMonth', 'Type', 'Weight'] ) df_full = pd.merge(all_combinations, df_flat, on=['YearMonth','Type','Weight'], how='left').fillna(0) # 将年月转为标准时间格式,方便后续提取时间特征 df_full['YearMonth'] = pd.to_datetime(df_full['YearMonth'], format='%Y%m')
这一步解决了「部分年月缺少特定茶种/重量等级数据」的问题,保证每个类别组合的时间序列连续完整。
二、构建时间序列专属特征
线性回归和XGBoost无法直接理解原始时间值,必须提取能体现趋势、周期的特征:
# 提取基础时间特征 df_full['Year'] = df_full['YearMonth'].dt.year df_full['Month'] = df_full['YearMonth'].dt.month df_full['Quarter'] = df_full['YearMonth'].dt.quarter # 生成连续时间索引(从数据集第一个月开始的累计月数),捕捉长期趋势 df_full['TimeIndex'] = (df_full['YearMonth'] - df_full['YearMonth'].min()).dt.days // 30 # 对茶种、重量等级做独热编码,转为模型可识别的数值特征 df_full = pd.get_dummies(df_full, columns=['Type', 'Weight'], drop_first=True)
三、分维度预测实现
1. 细粒度预测(先预测每个茶种+重量组合,再汇总)
适合需要精准到细分品类的场景:
数据拆分(必须按时间顺序,禁止随机拆分)
df_full = df_full.sort_values('YearMonth') train_size = int(len(df_full) * 0.8) train = df_full.iloc[:train_size] val = df_full.iloc[train_size:] X_train = train.drop(['YearMonth', 'Quantity'], axis=1) y_train = train['Quantity'] X_val = val.drop(['YearMonth', 'Quantity'], axis=1) y_val = val['Quantity']
XGBoost模型训练(针对回归任务优化)
import xgboost as xgb model = xgb.XGBRegressor( objective='reg:squarederror', n_estimators=1000, learning_rate=0.05, max_depth=5, random_state=42 ) # 用验证集做早停,避免过拟合 model.fit( X_train, y_train, eval_set=[(X_val, y_val)], early_stopping_rounds=50, verbose=False )
生成未来年月的预测数据
# 生成未来3年的时间序列 last_month = df_full['YearMonth'].max() future_months = pd.date_range(start=last_month + pd.DateOffset(months=1), periods=36, freq='MS') # 生成未来所有茶种+重量组合 future_combinations = pd.DataFrame( product(future_months, df_flat['Type'].unique(), df_flat['Weight'].unique()), columns=['YearMonth', 'Type', 'Weight'] ) # 同步提取时间特征和编码(和训练集逻辑完全一致) future_combinations['Year'] = future_combinations['YearMonth'].dt.year future_combinations['Month'] = future_combinations['YearMonth'].dt.month future_combinations['Quarter'] = future_combinations['YearMonth'].dt.quarter future_combinations['TimeIndex'] = (future_combinations['YearMonth'] - df_full['YearMonth'].min()).dt.days // 30 future_combinations = pd.get_dummies(future_combinations, columns=['Type', 'Weight'], drop_first=True) # 对齐训练集特征列(防止缺失) missing_cols = set(X_train.columns) - set(future_combinations.columns) for col in missing_cols: future_combinations[col] = 0 future_combinations = future_combinations[X_train.columns] # 预测销量 future_combinations['Predicted_Quantity'] = model.predict(future_combinations)
汇总各维度预测结果
# 总出口量(按月汇总) total_pred = future_combinations.groupby('YearMonth')['Predicted_Quantity'].sum().reset_index() # 各茶种销量(按月+茶种汇总) type_pred = future_combinations.groupby(['YearMonth', 'Type'])['Predicted_Quantity'].sum().reset_index() # 各重量等级销量(按月+重量等级汇总) weight_pred = future_combinations.groupby(['YearMonth', 'Weight'])['Predicted_Quantity'].sum().reset_index()
2. 直接预测汇总维度(快速得到总销量/茶种销量)
如果不需要细分到重量等级,可以先汇总数据,用更简单的时间序列模型(比如Facebook Prophet):
from prophet import Prophet # 总出口量时间序列 total_df = df_flat.groupby('YearMonth')['Quantity'].sum().reset_index() total_df = total_df.rename(columns={'YearMonth':'ds', 'Quantity':'y'}) total_df['ds'] = pd.to_datetime(total_df['ds'], format='%Y%m') # 训练并预测未来3年 model = Prophet(seasonality_mode='multiplicative') model.fit(total_df) future = model.make_future_dataframe(periods=36, freq='MS') forecast = model.predict(future) # 预测结果在forecast的yhat列
四、之前模型失败的核心原因
- 层级索引数据未扁平化,模型无法识别类别组合的时间序列规律
- 缺失的年月-类别组合未补全,导致模型学习不完整
- 直接用
201601这类数值作为时间特征,模型无法理解时间的周期性和趋势 - 分类变量未做编码,线性回归和XGBoost无法处理字符串输入
- 可能采用了随机拆分数据集的方式,导致时间序列数据泄露
五、模型效果验证
必须用时间序列专属的验证方式:
- 滚动验证:比如用2016-2020年数据预测2021年,再用2016-2021年数据预测2022年,重复计算平均误差
- 评估指标:优先用MAPE(平均绝对百分比误差),适合销量类业务场景,也可搭配MAE、RMSE使用
内容的提问来源于stack exchange,提问作者John Snape
相关产品推荐
相关产品推荐

