2026/11/5 13:07:31

多因变量无标签小样本:ARIMA、PLS与K-means建模

多因变量无标签小样本:ARIMA、PLS与K-means建模 简介面向美赛参赛者与数据分析学习者的2023年美赛C题M论文聚焦Wordle游戏数据的分析与预测可作为竞赛论文写作、时间序列与多变量建模的学习参考也适合毕业设计中的案例借鉴。全文围绕数据清洗与标准化、基于9个单词属性的Spearman相关性分析、时间序列区间预测、偏最小二乘回归以及K-means聚类展开给出MAE约34、MSE约2224的误差表现并通过肘部法则与轮廓系数确定聚类数结合难度评分和混淆矩阵评估模型准确率达0.941。压缩包仅含1个PDF文件大小约2.18MB便于直接阅读与检索目前已有170人学习。读者可从中获取完整赛题论文框架、问题拆解路径、模型选择依据、结果验证方式及不确定性讨论对复盘美赛C题和迁移到类似数据分析任务具有较高参考价值。1. 一道猜词游戏的数据题为什么同时用上了 ARIMA、PLS 和 K-means2023 年美赛 C 题给了一份 359 天的 Wordle 运营数据最终要求回答四个问题报告数量怎么变、某个词的七类尝试比例是多少、单词难度怎么分级、还有哪些有趣特征。表面看是数据分析预测真正麻烦的地方在于要预测的是一个七维百分比向量每一维都受其他维约束单词本身又没有任何现成难度标签。于是整套解法变成三层串接——ARIMA 管总量区间PLS 管多因变量结构K-means 管无标签分级。搞懂这三层为什么必须这么接比记住任何单个模型都值钱因为这类“多因变量 无标签 小样本”的组合在毕业设计和工作里的用户行为数据上会反复出现。2. 数据清洗与归一化为什么 98% 和 102% 必须拉回 100%2.1 两处断崖和两个四字母单词清洗的第一现场原始表有 359 行有效数据、12 列标签缺失值为零看起来干净得不像真实数据。但一查单词长度就露馅了有两个词只有四个字母对照游戏记录后修正为 clean 和 trash。这种错误不是笔误而是数据从页面抓取或人工录入时被截断的典型痕迹单词长度本身又是后续属性提取的输入不修就会污染整条特征链。真正影响时间序列的是两处断崖。2022/11/30 的报告数只有 2569前后两天分别是 23739 和 226282022/2/13 的 Hard Mode 人数是 3249前后是 9310 和 10343。处理方式是用两侧均值替代报告数取 (2373922628)/2≈23184Hard Mode 取 (931010343)/2≈9827。这里没有选择直接删除这两天原因很实在——ARIMA 要求等间隔时间序列删掉一行会让时间索引错位后面差分和滞后项全部对不上。import pandas as pd import numpy as np df pd.read_csv(wordle.csv) df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) # 定位单点异常与前后两日均值偏离超过 5 倍 for col in [reported_results, hard_mode_count]: prev, nxt df[col].shift(1), df[col].shift(-1) neighbor_mean (prev nxt) / 2 outlier (df[col] neighbor_mean * 0.2) | (df[col] neighbor_mean * 5) # 只替换孤立异常点连续异常不碰 df.loc[outlier, col] neighbor_mean[outlier].round() # 七类百分比归一化到总和 100 pct_cols [try1, try2, try3, try4, try5, try6, tryX] row_sum df[pct_cols].sum(axis1) df[pct_cols] df[pct_cols].div(row_sum, axis0) * 100代码里的关键参数是偏离倍数阈值。取 5 倍是经验值低于 2 倍容易把正常波动当异常高于 10 倍又会漏掉 2569 这种只剩十分之一的塌陷。另一个细节是只替换孤立点连续多天异常通常意味着统计口径变了均值替代会掩盖真实结构。2.2 均值替代的适用边界什么时候不能这么干均值替代在这份数据上成立前提是异常点孤立、前后趋势平稳、且异常不携带信息。如果异常来自游戏改版、节日活动或统计口径调整前后均值反而会把真实的结构变化抹平让模型学到一个不存在的平滑曲线。常见的实现思路是先画时间序列图看异常点是“尖刺”还是“台阶”尖刺可以插值台阶必须引入虚拟变量或分段建模。这份数据的两处异常都是单日尖刺前后没有持续偏移所以均值替代是合理的。但即便合理也应该在论文里记录替换前后的值否则复现时没人知道 23184 这个数是怎么来的。血泪经验是清洗步骤不写日志后面调参时根本分不清误差是模型不行还是数据被改坏了。2.3 百分比归一化的数学动机单纯形约束七类百分比相加原始数据里出现了 98%、99%、100%、101%、102% 五种总和。来源不止一个百分比四舍五入、分母用报告数还是完成数、X 类七次及以上未成功的统计边界。如果不处理PLS 会把“行和不为 100”当成一个可学习的信号回归系数就会去拟合这个人为误差而不是单词属性与尝试分布的真实关系。归一化的做法是每个百分比除以行和再乘 100等价于把每个样本投影到七维空间里 Σp100 的单纯形面上。这个约束很关键七个因变量不是自由的一个涨另一个必跌协方差矩阵天然奇异普通多元线性回归在这里会直接翻车。归一化之后因变量的几何结构才和模型假设对齐。提示凡是遇到“若干比例加起来应该等于 100%”的数据先跑一遍行和分布再决定归一化还是补一个“其他”类不要默认数据是干净的。2.4 先可视化再建模黄块分布说了什么清洗完之后先画图能得到几个建模前必须知道的先验。七类百分比堆叠图里第四次尝试黄色占比最大多数人集中在 3 到 5 次成功未成功比例在 359 天里多数低于 5%只有几十天高于 10%说明出题方对难度的控制相当稳Hard Mode 通关占比随时间逐步上升说明玩家熟练度在积累。这些结论看起来是描述统计实际决定了后续模型的选择。比如未成功率长期低位意味着 X 类百分比分布高度偏斜直接对它做线性回归的残差不会好看Hard Mode 占比有趋势说明做时间序列时不能假设均值恒定。先可视化再建模本质是给自己留一份“模型结果离谱时用来对照”的基准否则黑匣子吐出什么数都只能照单全收。3. ARIMA 预测2023 年 3 月 1 日的报告数量区间是怎么算出来的3.1 差分在消除什么ARMA 与 ARIMA 的分界ARMA(p,q) 的前提是序列平稳均值、方差不随时间漂移。报告数量明显先升后降直接喂 ARMA 会得到虚假回归。ARIMA 在 ARMA 前面加了一个 d 阶差分把非平稳序列变成平稳序列一阶差分是 y_t - y_{t-1}二阶差分再对一阶差分做一次写成算子形式就是 (1-B)^d y_tB 是滞后算子。差分不是越多越好。每差一次就丢一个样本二阶差分还会放大噪声让方差膨胀。判断 d 取多少标准做法是单位根检验ADF 检验在差分前不拒绝单位根一阶差分后拒绝就取 d1一阶差分后仍不拒绝再取 d2。凭感觉定 d 是这类题里最常见的翻车点差分过头会让预测区间虚胖看起来“稳健”其实全是噪声。3.2 p、d、q 定阶ACF、PACF 与信息准则的配合d 定下来之后p 和 q 靠自相关函数和偏自相关函数的截尾、拖尾形态给初值再用 AIC/BIC 在网格里挑。ACF 在 q 阶后截尾、PACF 拖尾提示 MA(q)PACF 在 p 阶后截尾、ACF 拖尾提示 AR(p)两者都拖尾就上 ARMA。实际流程通常是先网格搜索 p、q 从 0 到 5按 AIC 排序再对候选模型的残差做白噪声检验残差还有自相关就继续加阶。定阶阶段最容易被忽略的是残差检验。AIC 最小的模型不一定残差干净如果残差里还留着周期性说明模型没吃干净信息预测区间会偏窄。常见的实现思路是画残差 ACF 图加 Ljung-Box 检验p 值大于 0.05 才算过关。这一步不做后面算出来的区间只是数学上成立统计上站不住。3.3 置信区间与预测区间两个区间别混用题目要的是 2023/3/1 报告数量的区间必须是预测区间不是置信区间。置信区间描述的是“均值响应”的不确定性只包含参数估计误差预测区间还要加上新观测本身的随机误差所以更宽。公式上预测区间标准差约等于 σ√(1 x(XX)⁻¹x)那个额外的 1 就是新观测的方差项。很多人把回归输出的 confidence interval 直接当预测区间写进论文区间偏窄结论显得过于自信。区分方法很简单问一句“这个区间是要覆盖一个未来的真实观测还是覆盖未来观测的均值”前者用预测区间后者用置信区间。这道题显然是前者。3.4 [17363,18994] 的边界条件最终给出的 3 月 1 日预测区间是 [17363, 18994]中值约 18178区间宽度约 1600相对宽度 9% 左右。这个区间成立有前提历史模式延续、没有游戏改版、没有外部事件把流量打散、异常值处理没有改变趋势结构。3 月 1 日处于数据末端外推一步的位置外推越远区间越宽这是 ARIMA 的固有性质不是模型缺陷。读这个区间时要关注两件事。第一区间是否覆盖了历史波动范围如果历史同日波动远超这个宽度说明模型低估了不确定性第二区间是否对异常值敏感把 23184 换成原始 2569 再跑一遍如果区间大幅偏移说明清洗步骤对结论影响过大需要在论文里做敏感性说明。这两条检查比区间本身更有信息量。4. 从 PLS 到 K-means七维因变量回归与无标签难度分级4.1 七维因变量为什么不能拆成七个独立回归第二个问题要为每个词预测 (1,2,3,4,5,6,X) 七个百分比。直觉做法是跑七个线性回归每个因变量一个模型。这个做法在这里有三重问题七个百分比和为 100因变量之间强负相关七个模型的系数会互相矛盾自变量是 9 个单词属性属性之间本身共线比如词频和首字母常见度样本只有 359 天拆成七个模型后每个模型的有效信息更少。PLS 的思路是把自变量和因变量同时投影到低维潜变量空间在投影后的空间里做回归。它一次性处理全部七个因变量潜变量提取时最大化 X 和 Y 的协方差等于让模型自动找到“最能同时解释七个百分比”的方向。这比先 PCA 降维再回归多了一步监督信息比直接多元回归多了一步降维正好卡在这道题的需求上。4.2 PLS 的潜变量提取与 MAE/MSE 的读法PLS 的每次迭代做三件事从 X 里提取一个与 Y 协方差最大的方向 w算得分向量 tXw对 Y 做同样的事得到 u用 t 和 u 做回归并把残差传下去重复提取下一个成分。成分数不能无限加加多了会把噪声也拟合进去通常用交叉验证的预测残差平方和定取曲线拐点前的成分数。论文给出的 MAE 在 3 到 4 之间MSE 在 22 到 24 之间。注意单位是百分点不是比例平均每个百分比类别的预测误差约 3 到 4 个百分点RMSE 约 4.7 到 4.9 个百分点。单类百分比常在 10% 到 30% 之间5 个百分点的误差对应 20% 左右的相对误差属于中等水平不能算“高精度”。把 MAE 3.5 直接说成“误差很小”是过度解读读这两个指标一定要带单位。from sklearn.cross_decomposition import PLSRegression from sklearn.model_selection import cross_val_predict from sklearn.metrics import mean_absolute_error, mean_squared_error X df[word_attr_cols].values # 9 个单词属性 Y df[pct_cols].values # 7 类百分比行和已归一化为 100 best None for n in range(1, 8): # 成分数上限不超过自变量个数 pls PLSRegression(n_componentsn, scaleTrue) pred cross_val_predict(pls, X, Y, cv5) mae mean_absolute_error(Y, pred) if best is None or mae best[1]: best (n, mae) print(最优成分数:, best[0], MAE:, round(best[1], 2))代码里 scaleTrue 是必须的9 个属性量纲不同不标准化会让数值大的属性主导协方差。成分数上限设为 7 而不是 9是因为交叉验证在成分数接近自变量个数时容易过拟合留一点余量更稳。这段代码跑完应该能复现 MAE 3 到 4 的量级如果差很远先检查因变量有没有归一化。4.3 Spearman 相关9 个属性的筛选与共线性在进 PLS 之前论文先对 9 个单词属性和 Hard Mode 得分百分比做了 Spearman 相关分析。选 Spearman 而不是 Pearson是因为属性里有词频这类长尾分布Pearson 会被极端值拉扯Spearman 只看秩次对单调非线性更稳。结论是 9 个属性对 Hard Mode 百分比都有一定影响但“有一定影响”不等于“可以全部直接塞进线性回归”。属性之间的共线性是真实存在的首字母常见度和词频相关字母重复度和单词长度相关。PLS 对共线性有一定容忍度但相关分析的作用是给出解释框架——哪些属性是主要驱动哪些是伴随效应。写结论时如果把 Spearman 显著直接说成因果就会掉进“相关即因果”的坑属性对难度的影响需要通过模型系数或变量投影重要性来交叉验证。4.4 K-means 定 K肘部法则的伪拐点与轮廓系数补位第三个问题要给单词按难度分级但数据里没有难度标签只能无监督。K-means 的输入是单词属性加上 PLS 预测出的七类百分比先标准化再聚类否则量纲大的属性会垄断欧氏距离。K 的选择用了两个指标肘部法则看簇内平方和随 K 下降的拐点轮廓系数看每个点与自己簇的紧密度和与最近邻簇的分离度。肘部法则的问题是拐点经常不唯一K3 和 K5 都像拐点的情况很常见。轮廓系数 s(b-a)/max(a,b)a 是点到同簇其他点的平均距离b 是到最近异簇的平均距离取值在 -1 到 1 之间越接近 1 分离度越好。两个指标一起看再结合“五档难度”的业务可解释性最终定 K5。from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score from sklearn.preprocessing import StandardScaler Xc StandardScaler().fit_transform(cluster_features) for k in range(2, 9): km KMeans(n_clustersk, n_init20, random_state42).fit(Xc) s silhouette_score(Xc, km.labels_) print(fK{k}, SSE{km.inertia_:.1f}, Silhouette{s:.3f})n_init20 是为了避开局部最优K-means 对初始质心敏感只跑一次容易得到不稳定的簇。SSE 用来画肘部曲线Silhouette 用来交叉验证两个都指向 5 附近才定 5。如果 SSE 拐点在 4、轮廓系数峰值在 6说明数据结构本身模糊这时候硬定 5 档就需要在论文里说明这是业务约束而不是数据结论。4.5 难度加权评分与混淆矩阵EERIE 的 4.635 和 0.941 准确率定完簇之后难度分不是簇标签直接给的而是用加权公式算把 PLS 预测的七类百分比按尝试次数赋权尝试次数越多权重越高X 类权重最高加权求和得到连续难度分。EERIE 的得分是 4.635落在第五档说明这个词的预测分布偏向高尝试次数。这个分数比簇标签更有解释力因为它保留了“差一点就进上一档”的信息。模型评估用了混淆矩阵热图和准确率 0.941。这里要留个心眼K-means 本身没有真实标签混淆矩阵的“真值”来自哪里如果是用同一批属性构造的代理标签准确率就有循环论证的成分0.941 衡量的是聚类结果与代理标签的一致性不是与真实难度的吻合度。更稳的验证是留出一批人工标注的词做外部检验或者在论文里明确说明代理标签的构造方式。0.941 可以写但不能写成“模型难度分级准确率 94.1%”。5. 避坑记录五个让模型看起来很好看的陷阱5.1 坑一百分比不归一化就喂进模型现象是 PLS 的 MAE 看起来很低但预测出的七类百分比加起来是 103%单类甚至出现负值。原因是行和不为 100 时模型把总量误差也当成可学习的信号。解决办法是先做行和检查把每个样本除以行和再乘 100然后在预测端加一道非负裁剪和再归一化。这件事必须在建模前做事后补救会让整个误差评估失真。5.2 坑二异常值一删了之现象是删掉两天异常后 ARIMA 拟合得很好但预测区间窄得离谱。原因是时间序列删点会破坏等间隔结构滞后项错位模型低估了真实波动。解决办法是孤立点用前后均值或插值替代连续异常单独建虚拟变量并在论文里记录替换前后的值。359 天的样本经不起删每删一天都是信息损失。5.3 坑三只靠肘部法则定 K现象是肘部曲线在 K3 和 K5 都有拐点换一个随机种子聚类结果就变。原因是 SSE 单调下降拐点判断带主观性。解决办法是肘部法则加轮廓系数交叉验证再看每个簇的样本量和业务可解释性。如果三个指标打架说明数据结构本身不适合硬分五档应该在论文里承认这一点而不是硬凑一个好看的 K。5.4 坑四0.941 的准确率直接写进结论现象是混淆矩阵热图很漂亮但每一类的召回率差距很大小簇几乎全被吞进大簇。原因是准确率在类别不均衡时会被大簇主导。解决办法是看每一类的 precision、recall 和混淆矩阵对角线而不是只看总准确率。如果第五档只有十几个词0.941 里可能一个都没分对这个数字就没有意义。5.5 坑五ARIMA 差分阶数凭感觉现象是 d2 之后残差看起来像白噪声但预测区间比 d1 宽了一倍。原因是对本来只需一阶差分的序列做了二次差分过差分放大方差。解决办法是先跑 ADF 检验确定 d差分后再看 ACF 是否还留趋势残差做 Ljung-Box 检验。差分阶数是 ARIMA 里最容易被拍脑袋决定的参数也是预测区间虚胖的主要来源。6. 设计取舍这套流程真正的价值不在 0.941 的准确率三层模型的分工可以用一张表说清楚层次模型输入输出主要局限总量ARIMA历史报告数序列3/1 报告数预测区间外推越远区间越宽假设历史模式延续结构PLS9 个单词属性七类尝试百分比成分数需交叉验证误差约 5 个百分点分级K-means属性 预测百分比五档难度标签K 值带主观性准确率依赖代理标签这张表暴露的核心取舍是可解释性和预测精度在这里不是一回事。PLS 的 MAE 3 到 4 不算惊艳但它把九个属性对七类百分比的影响拆开了能回答“哪个属性让词变难”K-means 的 0.941 看起来高但它衡量的是聚类一致性不是真实难度准确率。如果只追求预测精度完全可以上梯度提升树但那会丢掉属性层面的解释而这道题第四问恰恰要的是“哪些属性与难度相关”。加权公式里的权重是另一个主观点。尝试次数越多权重越高这个方向没错但具体给 1 次 1 分、X 类 7 分还是指数加权会直接改变 EERIE 的 4.635 落在哪一档。更稳的做法是做权重敏感性分析换两组权重看排名前 10 的难词是否稳定。如果 EERIE 在两套权重下都进前五档结论才站得住如果只在某一套权重下成立那 4.635 就只是一个数字不是结论。这套流程值得复用的地方是它面对“多因变量 无标签 小样本”时的拆解顺序先清洗到几何结构正确再用 PLS 处理因变量之间的约束最后用无监督方法补上缺失的标签层。这个顺序反过来做——先聚类再回归——会因为聚类输入里混进未归一化的百分比而整体跑偏。从那以后我每次拿到带百分比列的数据都会先跑一遍行和检查再决定要不要归一化每次用 K-means 定 K都会强制把轮廓系数和混淆矩阵一起打出来不让单一指标替我做决定。希望帮到你。本文还有配套的精品资源点击获取