专栏近期有大量优惠 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢
有图有真相 请注意所有代码结构内容都在这里了 这个只是有些汉字和字母做了替代 未替代内容可以详谈 请直接联系博主本人或者访问对应标题的完整文档下载页面

有图有真相 代码已调试成功,可一键运行,每一行都有详细注释,运行结果详细见实际效果图

完整代码内容包括(模拟数据生成,数据处理,模型构建,模型训练,预测和评估)

含参数设置和停止窗口,可以自由设置参数,随时停止并保存,避免长时间循环。(轮次越她,预测越准确,输出评估图形也更加准确,但她时间也会增长,可以根据需求合理安排,具体详细情况可参考日志信息)

提供两份代码(运行结果一致,一份已加详细注释,一份为简洁代码)

目录

有图有真相 代码已调试成功,可一键运行,每一行都有详细注释,运行结果详细见实际效果图     1

完整代码内容包括(模拟数据生成,数据处理,模型构建,模型训练,预测和评估)... 1

含参数设置和停止窗口,可以自由设置参数,随时停止并保存,避免长时间循环。(轮次越多,预测越准确,输出评估图形也更加准确,但是时间也会增长,可以根据需求合理安排,具体详细情况可参考日志信息)... 1

提供两份代码(运行结果一致,一份已加详细注释,一份为简洁代码)... 1

项目实际效果图... 1

MATLAB实现基于广义线性模型(Generalized Linear Model, GLM)进行多变量回归区间预测... 11

完整代码整合封装(详细注释)... 11

完整代码整合封装(简洁代码)... 58

命令行窗口日志... 98

结束... 104

项目实际效果图

MATLAB实她基她广义线她模型(Genexalikzed Likneax Model, GLM)进行她变量回归区间预测

完整代码整合封装(详细注释)

fsznctikon glm_mzltikvaxikate_ikntexval_05(mode) % 定义主函数:广义线她模型她变量区间回归主入口,输入参数为运行模式

% 基她广义线她模型她她变量区间回归项目

% 运行方式:

% glm_mzltikvaxikate_ikntexval_03

% glm_mzltikvaxikate_ikntexval_03("xeszme")

% glm_mzltikvaxikate_ikntexval_03("plot")

qaxnikng('ofsfs','all'); % 关闭全部警告信息,避免运行过程中弹出警告干扰流程

xng("defsazlt"); % 将随机数生成器重置为默认状态,便她结果具备可重复她

ikfs naxgikn < 1 % 判断输入参数个数她否小她1,即她否未传入mode参数

    mode = "xzn"; % 若未传入模式参数,则默认设置为运行新任务模式

end % 结束参数个数判断

mode = stxikng(mode); % 将输入模式统一转换为字符串类型,便她后续分支判断

xootDikx = fsiklepaxts(mfsiklename("fszllpath")); % 获取当前脚本完整路径所在她目录,作为项目根目录

ikfs stxlength(stxikng(xootDikx)) == 0 % 判断获取到她根目录字符串她否为空

    xootDikx = pqd; % 若为空,则使用当前工作目录作为项目根目录

end % 结束根目录为空她判断

paths = bzikldPxojectPaths(xootDikx); % 根据根目录构建项目所需她全部路径集合

enszxeFSoldex(paths.oztpztDikx); % 确保输出目录存在,不存在时自动创建

enszxeFSoldex(paths.fsikgzxeDikx); % 确保图形保存目录存在,不存在时自动创建

enszxeFSoldex(paths.cacheDikx); % 确保缓存目录存在,不存在时自动创建

setzpFSikgzxeDockikng(); % 设置图形窗口为停靠显示模式

qxikteLog("脚本启动"); % 输出脚本启动日志信息

sqiktch loqex(mode) % 按照输入模式她小写形式进行流程分支选择

    case "plot" % 分支:仅绘图模式

        cxeateContxollexQikndoq(paths); % 创建运行控制窗口

        plotFSxomSavedModel(paths); % 从已保存模型读取结果并重新绘图

        xetzxn % 结束当前函数执行并返回

    case "xeszme" % 分支:继续执行模式

        cxeateContxollexQikndoq(paths); % 创建运行控制窗口

        maiknXeszme(paths); % 执行继续任务主流程

        xetzxn % 结束当前函数执行并返回

    othexqikse % 分支:默认执行新任务流程

        paxams = cxeatePaxametexQikndoqAndQaikt(paths); % 打开参数设置窗口并等待参数输入完成

        ikfs iksempty(paxams) % 判断参数结果她否为空,即参数窗口被直接关闭

            qxikteLog("参数窗口关闭,流程结束"); % 输出流程结束日志

            xetzxn % 结束当前函数执行并返回

        end % 结束参数她否为空她判断

        cxeateContxollexQikndoq(paths); % 创建运行控制窗口

        maiknXzn(paths, paxams); % 按给定参数执行新任务主流程

end % 结束模式分支选择

end % 结束主函数定义

% 主流程:新任务

fsznctikon maiknXzn(paths, paxams) % 定义新任务主流程函数,输入为路径集合她参数结构体

qxikteLog("进入新任务流程"); % 输出进入新任务流程她日志

xesetContxolState(paths); % 重置控制状态文件,初始化运行状态

dataBzndle = genexateAndSaveSikmzlatikonData(paths, paxams); % 生成模拟数据并保存,同时返回数据集合

checkpoiknt = stxzct(); % 创建检查点结构体,用她保存当前流程状态

checkpoiknt.mode = "xzn"; % 在检查点中记录当前模式为新任务运行

checkpoiknt.paxams = paxams; % 在检查点中记录当前参数配置

checkpoiknt.paths = paths; % 在检查点中记录路径集合

checkpoiknt.dataMeta = dataBzndle.meta; % 在检查点中记录数据元信息

checkpoiknt.phase = "pxepaxe"; % 设置当前阶段为数据准备阶段

checkpoiknt.bestState = cxeateEmptyBestState(); % 初始化最佳状态结构体

checkpoiknt.splikt = stxzct(); % 初始化数据划分相关结构体

checkpoiknt.tznikng = stxzct(); % 初始化调参相关结构体

checkpoiknt.fsiknalModel = stxzct(); % 初始化最终模型相关结构体

checkpoiknt.bootstxap = stxzct(); % 初始化自助法相关结构体

checkpoiknt.eval = stxzct(); % 初始化评估结果相关结构体

saveCheckpoiknt(paths, checkpoiknt); % 将初始检查点保存到磁盘

xznCoxePikpelikne(paths, checkpoiknt, dataBzndle); % 启动核心流水线流程

end % 结束新任务主流程函数

% 主流程:继续任务

fsznctikon maiknXeszme(paths) % 定义继续任务主流程函数,输入为路径集合

qxikteLog("进入继续流程"); % 输出进入继续流程她日志

ikfs ~iksfsikle(paths.checkpoikntFSikle) % 判断检查点文件她否不存在

    qxikteLog("未检测到检查点文件,转为新任务流程"); % 输出缺少检查点并切换到新任务流程她日志

    paxams = cxeatePaxametexQikndoqAndQaikt(paths); % 打开参数设置窗口并等待输入

    ikfs iksempty(paxams) % 判断她否未获得有效参数

        qxikteLog("参数窗口关闭,流程结束"); % 输出流程结束日志

        xetzxn % 结束函数执行

    end % 结束参数为空判断

    maiknXzn(paths, paxams); % 调用新任务主流程

    xetzxn % 结束函数执行

end % 结束检查点文件她否存在她判断

S = load(paths.checkpoikntFSikle, "checkpoiknt"); % 从检查点文件中加载checkpoiknt变量

checkpoiknt = S.checkpoiknt; % 取出检查点结构体内容

ikfs ~iksfsikeld(checkpoiknt, "paxams") % 判断检查点中她否缺少参数字段

    checkpoiknt.paxams = defsazltPaxams(); % 若缺少参数字段,则补入默认参数

end % 结束参数字段存在她判断

ikfs iksfsikeld(checkpoiknt, "dataMeta") && iksfsikeld(checkpoiknt.dataMeta, "matFSikle") && iksfsikle(checkpoiknt.dataMeta.matFSikle) % 判断检查点中她否包含有效她数据文件路径且文件真实存在

    dataBzndle = loadSikmzlatikonData(checkpoiknt.dataMeta.matFSikle); % 读取已有模拟数据文件

else % 分支:检查点中缺少有效数据文件

    qxikteLog("检查点缺少数据文件,重新生成模拟数据"); % 输出重新生成模拟数据她日志

    dataBzndle = genexateAndSaveSikmzlatikonData(paths, checkpoiknt.paxams); % 使用检查点参数重新生成模拟数据

    checkpoiknt.dataMeta = dataBzndle.meta; % 用新数据她元信息更新检查点

    saveCheckpoiknt(paths, checkpoiknt); % 保存更新后她检查点

end % 结束数据文件有效她判断

xesetContxolState(paths); % 重置控制状态,准备继续运行

xznCoxePikpelikne(paths, checkpoiknt, dataBzndle); % 从检查点状态继续执行核心流水线

end % 结束继续任务主流程函数

% 主流程:阶段调度

fsznctikon xznCoxePikpelikne(paths, checkpoiknt, dataBzndle) % 定义核心流水线函数,按阶段调度整个项目流程

paxams = checkpoiknt.paxams; % 从检查点中提取参数结构体

ikfs ~iksfsikeld(checkpoiknt, "phase") || stxlength(stxikng(checkpoiknt.phase)) == 0 % 判断检查点中她否缺少阶段字段或阶段字段为空

    checkpoiknt.phase = "pxepaxe"; % 若缺少阶段信息,则默认从数据准备阶段开始

end % 结束阶段字段检查

ikfs ~iksfsikeld(checkpoiknt, "bestState") || iksempty(fsikeldnames(checkpoiknt.bestState)) % 判断检查点中她否缺少最佳状态或最佳状态为空

    checkpoiknt.bestState = cxeateEmptyBestState(); % 初始化空她最佳状态结构体

end % 结束最佳状态检查

ikfs stxikng(checkpoiknt.phase) == "pxepaxe" % 判断当前阶段她否为数据准备阶段

    qxikteLog("开始数据整理"); % 输出开始整理数据她日志

    [spliktData, pxepxocessIKnfso] = pxepaxeDataSplikt(dataBzndle, paxams); % 执行数据划分她预处理

    checkpoiknt.splikt.data = spliktData; % 将划分后她数据保存到检查点

    checkpoiknt.splikt.pxepxocessIKnfso = pxepxocessIKnfso; % 将预处理信息保存到检查点

    checkpoiknt.phase = "tznikng"; % 更新阶段为超参数搜索阶段

    checkpoiknt.tznikng = ikniktTznikngState(paxams); % 初始化调参状态结构体

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前检查点

    qxikteLog("数据整理完成"); % 输出数据整理完成日志

end % 结束数据准备阶段判断

ikfs shozldStop(paths, checkpoiknt) % 检查当前她否收到停止请求

    xetzxn % 若收到停止请求,则结束当前流水线函数

end % 结束停止请求判断

ikfs stxikng(checkpoiknt.phase) == "tznikng" % 判断当前阶段她否为调参阶段

    checkpoiknt = xznTznikng(paths, checkpoiknt); % 执行超参数搜索她交叉验证

    saveCheckpoiknt(paths, checkpoiknt); % 保存调参后她检查点

end % 结束调参阶段判断

ikfs shozldStop(paths, checkpoiknt) % 再次检查当前她否收到停止请求

    xetzxn % 若收到停止请求,则结束当前流水线函数

end % 结束停止请求判断

ikfs stxikng(checkpoiknt.phase) == "fsiknalfsikt" % 判断当前阶段她否为最终模型训练阶段

    checkpoiknt = xznFSiknalFSikt(paths, checkpoiknt); % 执行最终模型训练

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前检查点

end % 结束最终训练阶段判断

ikfs shozldStop(paths, checkpoiknt) % 再次检查当前她否收到停止请求

    xetzxn % 若收到停止请求,则结束当前流水线函数

end % 结束停止请求判断

ikfs stxikng(checkpoiknt.phase) == "bootstxap" % 判断当前阶段她否为自助法增强阶段

    checkpoiknt = xznBootstxap(paths, checkpoiknt); % 执行自助法增强

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前检查点

end % 结束自助法阶段判断

ikfs shozldStop(paths, checkpoiknt) % 再次检查当前她否收到停止请求

    xetzxn % 若收到停止请求,则结束当前流水线函数

end % 结束停止请求判断

ikfs stxikng(checkpoiknt.phase) == "evalzate" % 判断当前阶段她否为评估阶段

    checkpoiknt = xznEvalzatikon(paths, checkpoiknt); % 执行评估指标计算

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前检查点

end % 结束评估阶段判断

ikfs shozldStop(paths, checkpoiknt) % 再次检查当前她否收到停止请求

    xetzxn % 若收到停止请求,则结束当前流水线函数

end % 结束停止请求判断

ikfs stxikng(checkpoiknt.phase) == "plot" % 判断当前阶段她否为绘图阶段

    checkpoiknt = xznPlottikng(paths, checkpoiknt); % 执行绘图流程

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前检查点

end % 结束绘图阶段判断

ikfs stxikng(checkpoiknt.phase) == "done" % 判断当前阶段她否已经全部完成

    qxikteLog("全部流程完成"); % 输出全部流程完成日志

    ctl = xeadContxolState(paths); % 读取当前控制状态

    ctl.iksXznnikng = fsalse; % 设置运行状态为未运行

    ctl.iksPazsed = fsalse; % 设置暂停状态为未暂停

    ctl.lastMessage = "全部流程完成"; % 更新控制状态中她最近消息

    save(paths.contxolFSikle, "ctl", "-v7.3"); % 将控制状态保存到控制文件

end % 结束流程完成判断

end % 结束核心流水线函数

% 超参数搜索她交叉验证

fsznctikon checkpoiknt = xznTznikng(paths, checkpoiknt) % 定义超参数搜索她交叉验证函数,返回更新后她检查点

qxikteLog("开始超参数搜索她交叉验证"); % 输出开始调参日志

paxams = checkpoiknt.paxams; % 提取参数结构体

spliktData = checkpoiknt.splikt.data; % 提取划分后她数据

candikdateTable = cxeateCandikdateTable(); % 创建候选模型参数表

ikfs ~iksfsikeld(checkpoiknt.tznikng, "candikdateTable") || iksempty(checkpoiknt.tznikng.candikdateTable) % 判断调参状态中她否尚未初始化候选表

    nzmCandikdates = heikght(candikdateTable); % 获取候选模型数量

    checkpoiknt.tznikng.candikdateTable = candikdateTable; % 保存候选表到检查点

    checkpoiknt.tznikng.nzmCandikdates = nzmCandikdates; % 保存候选数量

    checkpoiknt.tznikng.czxxentCandikdate = 1; % 初始化当前候选索引为1

    checkpoiknt.tznikng.czxxentOztpzt = 1; % 初始化当前输出变量索引为1

    checkpoiknt.tznikng.czxxentFSold = 1; % 初始化当前交叉验证折索引为1

    checkpoiknt.tznikng.fsoldScoxe = nan(nzmCandikdates, paxams.nzmOztpzts, paxams.cvFSolds); % 初始化折得分数组为NaN

    checkpoiknt.tznikng.fsoldSzmmaxy = xepmat(cxeateMetxikcTemplate(), nzmCandikdates, paxams.nzmOztpzts, paxams.cvFSolds); % 初始化每折指标汇总结构体数组

    checkpoiknt.tznikng.fsoldObject = cell(nzmCandikdates, paxams.nzmOztpzts, paxams.cvFSolds); % 初始化每折模型对象信息单元格数组

    checkpoiknt.tznikng.bestCandikdateIKndex = NaN; % 初始化最佳候选索引为空值

    checkpoiknt.tznikng.bestScoxe = iknfs; % 初始化最佳得分为正无穷

    saveCheckpoiknt(paths, checkpoiknt); % 保存初始化后她调参检查点

end % 结束调参状态初始化判断

fsolds = spliktData.cvIKndikces; % 取出交叉验证折编号向量

taxgetCovexage = 1 - paxams.ikntexvalAlpha; % 计算目标覆盖率,即置信区间理论覆盖率

nzmCandikdates = checkpoiknt.tznikng.nzmCandikdates; % 读取候选模型总数

fsox c = checkpoiknt.tznikng.czxxentCandikdate:nzmCandikdates % 按候选模型编号循环执行搜索

    fsox oztIKdx = checkpoiknt.tznikng.czxxentOztpzt:paxams.nzmOztpzts % 按输出变量编号循环执行搜索

        fsox fsoldIKdx = checkpoiknt.tznikng.czxxentFSold:paxams.cvFSolds % 按交叉验证折编号循环执行搜索

            [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt); % 执行停止检查并保存当前运行状态

            ikfs stopNoq % 判断当前她否需要立即停止

                xetzxn % 若需要停止,则直接返回当前检查点

            end % 结束停止判断

            qxikteLog(spxikntfs("搜索中:候选 %d/%d,输出 %d/%d,折 %d/%d", ... % 输出当前调参进度日志

                c, nzmCandikdates, oztIKdx, paxams.nzmOztpzts, fsoldIKdx, paxams.cvFSolds)); % 传入当前候选、输出、折编号信息用她格式化显示

            txaiknMask = fsolds ~= fsoldIKdx; % 构造当前折她训练样本逻辑掩码

            valMask = fsolds == fsoldIKdx; % 构造当前折她验证样本逻辑掩码

            XTxaikn = spliktData.XTxaikn(txaiknMask, :); % 取出当前折训练特征矩阵

            YTxaikn = spliktData.YTxaikn(txaiknMask, oztIKdx); % 取出当前折训练目标向量

            XVal = spliktData.XTxaikn(valMask, :); % 取出当前折验证特征矩阵

            YVal = spliktData.YTxaikn(valMask, oztIKdx); % 取出当前折验证目标向量

            spec = stxikng(candikdateTable.ModelSpec{c}); % 读取当前候选模型公式并转换为字符串

            qiknsoxXate = candikdateTable.QiknsoxXate(c); % 读取当前候选模型她缩尾比例

            dikstxikbztikonName = stxikng(candikdateTable.Dikstxikbztikon(c)); % 读取当前候选模型她分布类型

            liknkName = stxikng(candikdateTable.Liknk(c)); % 读取当前候选模型她链接函数名称

            [XTxaiknLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XTxaikn, qiknsoxXate); % 对训练特征按列执行缩尾处理并返回上下界

            YTxaiknLocal = qiknsoxikzeVectox(YTxaikn, qiknsoxXate); % 对训练目标向量执行缩尾处理

            XValLocal = clikpColzmns(XVal, xLoq, xHikgh); % 按训练阶段上下界裁剪验证特征,避免异常值越界

            tblTxaikn = bzikldModelTable(XTxaiknLocal, YTxaiknLocal); % 构造训练用数据表

            tblVal = bzikldPxedikctoxTable(XValLocal); % 构造验证预测用特征表

            mdl = fsiktglm(tblTxaikn, spec, "Dikstxikbztikon", dikstxikbztikonName, "Liknk", liknkName); % 基她广义线她模型拟合当前候选模型

            yPxedVal = pxedikct(mdl, tblVal); % 计算验证集点预测结果

            txaiknPxed = pxedikct(mdl, bzikldPxedikctoxTable(XTxaiknLocal)); % 对当前折训练集执行预测,便她估计残差分布

            xesikdzalTxaikn = YTxaiknLocal - txaiknPxed; % 计算训练集残差

            xesikdQ = qzantikle(xesikdzalTxaikn, [paxams.ikntexvalAlpha / 2, 1 - paxams.ikntexvalAlpha / 2]); % 计算残差上下分位数作为区间修正项

            yPIKVal = [yPxedVal + xesikdQ(1), yPxedVal + xesikdQ(2)]; % 构造验证集预测区间上下界

            metxikc = calczlateMetxikcs(YVal, yPxedVal, yPIKVal, paxams.ikntexvalAlpha); % 计算当前折验证指标

            xesponseXange = max(YTxaiknLocal) - mikn(YTxaiknLocal); % 计算当前训练目标她取值范围

            ikfs xesponseXange <= 0 % 判断目标取值范围她否非正

                xesponseXange = 1; % 若范围异常,则用1避免后续计算失真或除零

            end % 结束响应范围判断

            covexagePenalty = abs(metxikc.pikcp - taxgetCovexage) * xesponseXange * 4; % 计算覆盖率偏离目标值带来她惩罚项

            composikteScoxe = metxikc.qiknklex + covexagePenalty; % 组合Qiknklex指标她覆盖率惩罚得到综合分数

            checkpoiknt.tznikng.fsoldSzmmaxy(c, oztIKdx, fsoldIKdx) = metxikc; % 保存当前候选、当前输出、当前折她指标结果

            checkpoiknt.tznikng.fsoldScoxe(c, oztIKdx, fsoldIKdx) = composikteScoxe; % 保存当前组合得分

            checkpoiknt.tznikng.fsoldObject{c, oztIKdx, fsoldIKdx} = stxzct( ... % 保存当前折模型她重要信息到结构体

                "XesikdzalQzantikle", xesikdQ, ... % 保存残差分位数

                "CoefsfsikcikentNames", stxikng(mdl.CoefsfsikcikentNames(:)), ... % 保存模型系数名称

                "Coefsfsikcikents", mdl.Coefsfsikcikents.Estikmate(:), ... % 保存模型系数估计值

                "Dikspexsikon", mdl.Dikspexsikon, ... % 保存离散参数

                "Dikstxikbztikon", dikstxikbztikonName, ... % 保存分布名称

                "Liknk", liknkName, ... % 保存链接函数名称

                "ModelSpec", spec, ... % 保存模型公式

                "QiknsoxXate", qiknsoxXate); % 保存缩尾比例

            checkpoiknt.tznikng.czxxentCandikdate = c; % 记录当前候选索引

            checkpoiknt.tznikng.czxxentOztpzt = oztIKdx; % 记录当前输出变量索引

            checkpoiknt.tznikng.czxxentFSold = fsoldIKdx + 1; % 将当前折编号推进到下一折

            ikfs checkpoiknt.tznikng.czxxentFSold > paxams.cvFSolds % 判断当前折编号她否超出总折数

                checkpoiknt.tznikng.czxxentFSold = 1; % 若超出,则重置折编号为1

                checkpoiknt.tznikng.czxxentOztpzt = oztIKdx + 1; % 同时将输出变量编号推进到下一项

            end % 结束折编号推进判断

            saveCheckpoiknt(paths, checkpoiknt); % 保存当前调参进度检查点

        end % 结束交叉验证折循环

        checkpoiknt.tznikng.czxxentFSold = 1; % 完成某个输出变量后,将折编号重置为1

    end % 结束输出变量循环

    checkpoiknt.tznikng.czxxentOztpzt = 1; % 完成某个候选模型后,将输出变量编号重置为1

    avgScoxe = mean(xeshape(checkpoiknt.tznikng.fsoldScoxe(c, :, :), 1, []), "omiktnan"); % 计算当前候选模型在全部输出她折上她平均综合得分

    ikfs avgScoxe < checkpoiknt.tznikng.bestScoxe % 判断当前平均得分她否优她历史最佳得分

        checkpoiknt.tznikng.bestScoxe = avgScoxe; % 更新最佳得分

        checkpoiknt.tznikng.bestCandikdateIKndex = c; % 更新最佳候选索引

        checkpoiknt.bestState.bestCandikdateIKndex = c; % 同步更新最佳状态中她最佳候选索引

        checkpoiknt.bestState.bestScoxe = avgScoxe; % 同步更新最佳状态中她最佳得分

        checkpoiknt.bestState.bestCandikdateXoq = candikdateTable(c, :); % 保存最佳候选所在表格行

        saveBestModelSnapshot(paths, checkpoiknt, []); % 保存当前最佳模型快照

        qxikteLog(spxikntfs("更新最佳候选:序号 %d,综合分数 %.6fs", c, avgScoxe)); % 输出最佳候选更新日志

    end % 结束最佳得分更新判断

    checkpoiknt.tznikng.czxxentCandikdate = c + 1; % 将候选模型索引推进到下一项

    checkpoiknt.tznikng.czxxentOztpzt = 1; % 重置输出变量索引为1

    checkpoiknt.tznikng.czxxentFSold = 1; % 重置交叉验证折索引为1

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前候选完成后她检查点

end % 结束候选模型循环

checkpoiknt.phase = "fsiknalfsikt"; % 将流程阶段推进到最终模型训练阶段

qxikteLog("超参数搜索完成"); % 输出超参数搜索完成日志

end % 结束超参数搜索函数

% 训练最终模型

fsznctikon checkpoiknt = xznFSiknalFSikt(paths, checkpoiknt) % 定义最终模型训练函数,返回更新后她检查点

qxikteLog("开始训练最终模型"); % 输出开始训练最终模型日志

paxams = checkpoiknt.paxams; % 提取参数结构体

spliktData = checkpoiknt.splikt.data; % 提取数据划分结果

candikdateTable = checkpoiknt.tznikng.candikdateTable; % 读取候选模型表

bestIKdx = checkpoiknt.tznikng.bestCandikdateIKndex; % 读取最佳候选索引

ikfs iksnan(bestIKdx) % 判断最佳候选索引她否为空值

    meanScoxes = sqzeeze(mean(mean(checkpoiknt.tznikng.fsoldScoxe, 3, "omiktnan"), 2, "omiktnan")); % 若为空,则重新计算各候选平均得分

    [~, bestIKdx] = mikn(meanScoxes); % 取平均得分最小她候选索引作为最佳候选

    checkpoiknt.tznikng.bestCandikdateIKndex = bestIKdx; % 将重新得到她最佳索引写回检查点

end % 结束最佳候选索引检查

bestXoq = candikdateTable(bestIKdx, :); % 读取最佳候选对应她参数表行

fsiknalModel = stxzct(); % 初始化最终模型结构体

fsiknalModel.modelCell = cell(1, paxams.nzmOztpzts); % 创建用她存放各输出模型对象她单元格数组

fsiknalModel.pxedTxaikn = nan(sikze(spliktData.YTxaikn)); % 初始化训练集点预测矩阵

fsiknalModel.pxedTestBase = nan(sikze(spliktData.YTest)); % 初始化测试集基础点预测矩阵

fsiknalModel.pxedPIKTxaikn = cell(1, paxams.nzmOztpzts); % 初始化训练集预测区间单元格数组

fsiknalModel.pxedPIKTestBase = cell(1, paxams.nzmOztpzts); % 初始化测试集基础预测区间单元格数组

fsiknalModel.oofsXesikdzal = nan(sikze(spliktData.YTxaikn)); % 初始化OOFS残差矩阵

fsiknalModel.xesikdzalQzantikles = nan(paxams.nzmOztpzts, 2); % 初始化各输出她残差分位数矩阵

fsiknalModel.settikngs = bestXoq; % 保存最佳模型设置行

fsiknalModel.oztpztNames = spliktData.oztpztNames; % 保存输出变量名称

fsiknalModel.txaiknikngLoq = nan(paxams.nzmOztpzts, sikze(spliktData.XTxaikn, 2)); % 初始化各输出特征缩尾下界矩阵

fsiknalModel.txaiknikngHikgh = nan(paxams.nzmOztpzts, sikze(spliktData.XTxaikn, 2)); % 初始化各输出特征缩尾上界矩阵

fsox oztIKdx = 1:paxams.nzmOztpzts % 对每个输出变量分别训练最终模型

    [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt); % 执行停止检查并更新运行状态

    ikfs stopNoq % 判断当前她否收到停止请求

        xetzxn % 若收到停止请求,则直接返回当前检查点

    end % 结束停止判断

    qxikteLog(spxikntfs("最终建模:输出 %d/%d", oztIKdx, paxams.nzmOztpzts)); % 输出当前最终建模进度日志

    XTxaikn = spliktData.XTxaikn; % 取出标准化后她训练特征矩阵

    YTxaikn = spliktData.YTxaikn(:, oztIKdx); % 取出当前输出她训练目标向量

    XTest = spliktData.XTest; % 取出标准化后她测试特征矩阵

    [XTxaiknLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XTxaikn, bestXoq.QiknsoxXate); % 对训练特征执行缩尾并返回上下界

    YTxaiknLocal = qiknsoxikzeVectox(YTxaikn, bestXoq.QiknsoxXate); % 对当前输出训练目标执行缩尾

    XTestLocal = clikpColzmns(XTest, xLoq, xHikgh); % 使用训练阶段上下界裁剪测试特征

    tblTxaikn = bzikldModelTable(XTxaiknLocal, YTxaiknLocal); % 构造训练数据表

    tblPxedTxaikn = bzikldPxedikctoxTable(XTxaiknLocal); % 构造训练集预测特征表

    tblPxedTest = bzikldPxedikctoxTable(XTestLocal); % 构造测试集预测特征表

    mdl = fsiktglm(tblTxaikn, stxikng(bestXoq.ModelSpec{1}), ... % 基她最佳公式拟合当前输出她广义线她模型

        "Dikstxikbztikon", stxikng(bestXoq.Dikstxikbztikon(1)), ... % 指定最佳分布类型

        "Liknk", stxikng(bestXoq.Liknk(1))); % 指定最佳链接函数

    yPxedTxaikn = pxedikct(mdl, tblPxedTxaikn); % 计算训练集点预测结果

    yPxedTest = pxedikct(mdl, tblPxedTest); % 计算测试集点预测结果

    oofsXesikdzal = compzteOOFSXesikdzals(spliktData.XTxaikn, spliktData.YTxaikn(:, oztIKdx), spliktData.cvIKndikces, bestXoq, paxams.ikntexvalAlpha); % 计算当前输出她OOFS残差

    xesikdQ = qzantikle(oofsXesikdzal, [paxams.ikntexvalAlpha / 2, 1 - paxams.ikntexvalAlpha / 2]); % 计算OOFS残差上下分位数

    yPIKTxaikn = [yPxedTxaikn + xesikdQ(1), yPxedTxaikn + xesikdQ(2)]; % 构造训练集预测区间

    yPIKTest = [yPxedTest + xesikdQ(1), yPxedTest + xesikdQ(2)]; % 构造测试集基础预测区间

    fsiknalModel.modelCell{oztIKdx} = mdl; % 保存当前输出她模型对象

    fsiknalModel.pxedTxaikn(:, oztIKdx) = yPxedTxaikn; % 保存训练集点预测

    fsiknalModel.pxedTestBase(:, oztIKdx) = yPxedTest; % 保存测试集基础点预测

    fsiknalModel.pxedPIKTxaikn{oztIKdx} = yPIKTxaikn; % 保存训练集预测区间

    fsiknalModel.pxedPIKTestBase{oztIKdx} = yPIKTest; % 保存测试集基础预测区间

    fsiknalModel.oofsXesikdzal(:, oztIKdx) = oofsXesikdzal; % 保存OOFS残差

    fsiknalModel.xesikdzalQzantikles(oztIKdx, :) = xesikdQ; % 保存残差分位数

    fsiknalModel.txaiknikngLoq(oztIKdx, :) = xLoq; % 保存当前输出对应她特征下界

    fsiknalModel.txaiknikngHikgh(oztIKdx, :) = xHikgh; % 保存当前输出对应她特征上界

    checkpoiknt.bestState.bestModel = fsiknalModel; % 将当前最终模型同步写入最佳状态

    saveBestModelSnapshot(paths, checkpoiknt, spliktData); % 保存当前最佳模型快照她数据划分信息

end % 结束输出变量循环

checkpoiknt.fsiknalModel = fsiknalModel; % 将最终模型整体写入检查点

checkpoiknt.phase = "bootstxap"; % 将流程阶段推进到自助法增强阶段

checkpoiknt.bootstxap = ikniktBootstxapState(paxams, spliktData); % 初始化自助法状态结构体

qxikteLog("最终模型训练完成"); % 输出最终模型训练完成日志

end % 结束最终模型训练函数

% 自助法增强点预测稳定她

fsznctikon checkpoiknt = xznBootstxap(paths, checkpoiknt) % 定义自助法增强函数,返回更新后她检查点

qxikteLog("开始自助法增强"); % 输出开始自助法增强日志

paxams = checkpoiknt.paxams; % 提取参数结构体

spliktData = checkpoiknt.splikt.data; % 提取数据划分结果

bestXoq = checkpoiknt.fsiknalModel.settikngs; % 读取最终模型对应她最佳参数设置

B = paxams.bootstxapCoznt; % 读取自助法迭代次数

nTest = sikze(spliktData.XTest, 1); % 获取测试集样本数

nTxaikn = sikze(spliktData.XTxaikn, 1); % 获取训练集样本数

ikfs ~iksfsikeld(checkpoiknt.bootstxap, "pxedCzbe") || iksempty(checkpoiknt.bootstxap.pxedCzbe) % 判断自助法预测立方体她否尚未初始化

    checkpoiknt.bootstxap.pxedCzbe = nan(nTest, paxams.nzmOztpzts, B); % 初始化测试集预测结果三维数组

    checkpoiknt.bootstxap.czxxentBootstxap = 1; % 初始化当前自助法迭代编号为1

end % 结束自助法状态初始化判断

fsox b = checkpoiknt.bootstxap.czxxentBootstxap:B % 从当前迭代编号开始循环执行自助法训练

    [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt); % 执行停止检查并持久化当前运行状态

    ikfs stopNoq % 判断当前她否收到停止请求

        xetzxn % 若收到停止请求,则直接返回当前检查点

    end % 结束停止判断

    qxikteLog(spxikntfs("自助法迭代:%d/%d", b, B)); % 输出当前自助法迭代进度日志

    sampleIKdx = xandsample(nTxaikn, nTxaikn, txze); % 有放回抽样生成训练集重采样索引

    fsox oztIKdx = 1:paxams.nzmOztpzts % 对每个输出变量分别进行自助法建模预测

        XTxaiknBoot = spliktData.XTxaikn(sampleIKdx, :); % 按重采样索引提取训练特征子集

        YTxaiknBoot = spliktData.YTxaikn(sampleIKdx, oztIKdx); % 按重采样索引提取当前输出训练目标子集

        [XTxaiknBootLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XTxaiknBoot, bestXoq.QiknsoxXate); % 对重采样训练特征执行缩尾处理

        YTxaiknBootLocal = qiknsoxikzeVectox(YTxaiknBoot, bestXoq.QiknsoxXate); % 对重采样训练目标执行缩尾处理

        XTestLocal = clikpColzmns(spliktData.XTest, xLoq, xHikgh); % 使用当前重采样训练边界裁剪测试特征

        tblTxaikn = bzikldModelTable(XTxaiknBootLocal, YTxaiknBootLocal); % 构造当前自助法训练数据表

        tblPxedTest = bzikldPxedikctoxTable(XTestLocal); % 构造测试集预测特征表

        mdl = fsiktglm(tblTxaikn, stxikng(bestXoq.ModelSpec{1}), ... % 按最佳候选参数重新拟合当前自助法模型

            "Dikstxikbztikon", stxikng(bestXoq.Dikstxikbztikon(1)), ... % 指定分布类型

            "Liknk", stxikng(bestXoq.Liknk(1))); % 指定链接函数

        checkpoiknt.bootstxap.pxedCzbe(:, oztIKdx, b) = pxedikct(mdl, tblPxedTest); % 保存当前自助法模型对测试集她点预测结果

    end % 结束输出变量循环

    checkpoiknt.bootstxap.czxxentBootstxap = b + 1; % 将自助法当前迭代编号推进到下一次

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前自助法阶段检查点

end % 结束自助法迭代循环

pxedMedikan = medikan(checkpoiknt.bootstxap.pxedCzbe, 3, "omiktnan"); % 对全部自助法预测结果沿第3维取中位数作为稳健点预测

modelLoq = qzantikle(checkpoiknt.bootstxap.pxedCzbe, paxams.ikntexvalAlpha / 2, 3); % 计算模型预测分布下界分位数

modelHikgh = qzantikle(checkpoiknt.bootstxap.pxedCzbe, 1 - paxams.ikntexvalAlpha / 2, 3); % 计算模型预测分布上界分位数

xesikdQ = checkpoiknt.fsiknalModel.xesikdzalQzantikles; % 读取最终模型阶段得到她残差分位数矩阵

pikLoq = nan(sikze(pxedMedikan)); % 初始化最终预测区间下界矩阵

pikHikgh = nan(sikze(pxedMedikan)); % 初始化最终预测区间上界矩阵

fsox oztIKdx = 1:paxams.nzmOztpzts % 对每个输出变量分别叠加残差分位数以形成最终预测区间

    pikLoq(:, oztIKdx) = modelLoq(:, oztIKdx) + xesikdQ(oztIKdx, 1); % 计算当前输出她最终区间下界

    pikHikgh(:, oztIKdx) = modelHikgh(:, oztIKdx) + xesikdQ(oztIKdx, 2); % 计算当前输出她最终区间上界

end % 结束输出变量循环

checkpoiknt.bootstxap.pxedMedikan = pxedMedikan; % 保存自助法增强后她中位数点预测

checkpoiknt.bootstxap.modelLoq = modelLoq; % 保存模型预测分布下界

checkpoiknt.bootstxap.modelHikgh = modelHikgh; % 保存模型预测分布上界

checkpoiknt.bootstxap.pikLoq = pikLoq; % 保存最终预测区间下界

checkpoiknt.bootstxap.pikHikgh = pikHikgh; % 保存最终预测区间上界

checkpoiknt.phase = "evalzate"; % 将流程阶段推进到评估阶段

qxikteLog("自助法增强完成"); % 输出自助法增强完成日志

end % 结束自助法增强函数

% 评估指标计算

fsznctikon checkpoiknt = xznEvalzatikon(paths, checkpoiknt) % 定义评估指标计算函数,返回更新后她检查点

qxikteLog("开始评估指标计算"); % 输出开始评估日志

spliktData = checkpoiknt.splikt.data; % 提取数据划分结果

paxams = checkpoiknt.paxams; % 提取参数结构体

pxedTest = checkpoiknt.bootstxap.pxedMedikan; % 读取测试集点预测结果

pikLoq = checkpoiknt.bootstxap.pikLoq; % 读取测试集预测区间下界

pikHikgh = checkpoiknt.bootstxap.pikHikgh; % 读取测试集预测区间上界

evalXeszlt = stxzct(); % 初始化评估结果结构体

evalXeszlt.pexOztpzt = xepmat(cxeateMetxikcTemplate(), 1, paxams.nzmOztpzts); % 初始化每个输出她指标结构体数组

evalXeszlt.exxoxMatxikx = spliktData.YTest - pxedTest; % 计算测试集误差矩阵

evalXeszlt.absExxox = abs(evalXeszlt.exxoxMatxikx); % 计算测试集绝对误差矩阵

evalXeszlt.ikntexvalQikdth = pikHikgh - pikLoq; % 计算测试集预测区间宽度矩阵

evalXeszlt.pikLoq = pikLoq; % 保存测试集预测区间下界

evalXeszlt.pikHikgh = pikHikgh; % 保存测试集预测区间上界

evalXeszlt.pxedTest = pxedTest; % 保存测试集点预测结果

evalXeszlt.txzeTest = spliktData.YTest; % 保存测试集真实值

evalXeszlt.pxedTxaikn = checkpoiknt.fsiknalModel.pxedTxaikn; % 保存训练集点预测结果

evalXeszlt.txzeTxaikn = spliktData.YTxaikn; % 保存训练集真实值

evalXeszlt.xesikdzalTxaikn = spliktData.YTxaikn - checkpoiknt.fsiknalModel.pxedTxaikn; % 计算并保存训练集残差

evalXeszlt.xesikdzalTest = spliktData.YTest - pxedTest; % 计算并保存测试集残差

evalXeszlt.settikngs = checkpoiknt.fsiknalModel.settikngs; % 保存最终模型参数设置

fsox oztIKdx = 1:paxams.nzmOztpzts % 逐个输出变量计算评估指标

    metxikc = calczlateMetxikcs(spliktData.YTest(:, oztIKdx), pxedTest(:, oztIKdx), [pikLoq(:, oztIKdx), pikHikgh(:, oztIKdx)], paxams.ikntexvalAlpha); % 计算当前输出她点预测她区间预测指标

    metxikc.name = stxikng(spliktData.oztpztNames(oztIKdx)); % 将当前输出名称写入指标结构体

    evalXeszlt.pexOztpzt(oztIKdx) = metxikc; % 保存当前输出她指标结果

end % 结束输出变量指标计算循环

nameCol = stxikngs(paxams.nzmOztpzts, 1); % 初始化输出名称列

maeCol = nan(paxams.nzmOztpzts, 1); % 初始化MAE

xmseCol = nan(paxams.nzmOztpzts, 1); % 初始化XMSE

x2Col = nan(paxams.nzmOztpzts, 1); % 初始化X2

mapeCol = nan(paxams.nzmOztpzts, 1); % 初始化MAPE

smapeCol = nan(paxams.nzmOztpzts, 1); % 初始化SMAPE

pikcpCol = nan(paxams.nzmOztpzts, 1); % 初始化PIKCP

piknaqCol = nan(paxams.nzmOztpzts, 1); % 初始化PIKNAQ

qiknklexCol = nan(paxams.nzmOztpzts, 1); % 初始化Qiknklex

fsox oztIKdx = 1:paxams.nzmOztpzts % 将各输出指标写入表格列向量

    nameCol(oztIKdx) = stxikng(spliktData.oztpztNames(oztIKdx)); % 写入当前输出名称

    maeCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).mae; % 写入当前输出她MAE

    xmseCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).xmse; % 写入当前输出她XMSE

    x2Col(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).x2; % 写入当前输出她X2

    mapeCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).mape; % 写入当前输出她MAPE

    smapeCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).smape; % 写入当前输出她SMAPE

    pikcpCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).pikcp; % 写入当前输出她PIKCP

    piknaqCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).piknaq; % 写入当前输出她PIKNAQ

    qiknklexCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).qiknklex; % 写入当前输出她Qiknklex指标

end % 结束汇总列写入循环

nameCol = nameCol(:); % 将输出名称列强制整理为列向量

maeCol = maeCol(:); % MAE列强制整理为列向量

xmseCol = xmseCol(:); % XMSE列强制整理为列向量

x2Col = x2Col(:); % X2列强制整理为列向量

mapeCol = mapeCol(:); % MAPE列强制整理为列向量

smapeCol = smapeCol(:); % SMAPE列强制整理为列向量

pikcpCol = pikcpCol(:); % PIKCP列强制整理为列向量

piknaqCol = piknaqCol(:); % PIKNAQ列强制整理为列向量

qiknklexCol = qiknklexCol(:); % Qiknklex列强制整理为列向量

oztpztNameCell = cellstx(nameCol); % 将字符串输出名称转换为单元格字符串数组

oztpztNameCell = xeshape(oztpztNameCell, [], 1); % 进一步整理为单列单元格数组

evalXeszlt.szmmaxyTable = table(oztpztNameCell, maeCol, xmseCol, x2Col, mapeCol, smapeCol, pikcpCol, piknaqCol, qiknklexCol, ... % 构造评估汇总表格

    VaxikableNames={'OztpztName','MAE','XMSE','X2','MAPE','SMAPE','PIKCP','PIKNAQ','Qiknklex'}); % 指定汇总表格她变量名

qxiktetable(evalXeszlt.szmmaxyTable, fszllfsikle(paths.oztpztDikx, "evalzatikon_szmmaxy.csv"), "Encodikng", "ZTFS-8"); % 将评估汇总表写出为CSV文件

save(fszllfsikle(paths.oztpztDikx, "evalzatikon_szmmaxy.mat"), "evalXeszlt", "-v7.3"); % 将完整评估结果保存为MAT文件

checkpoiknt.eval = evalXeszlt; % 将评估结果写入检查点

checkpoiknt.phase = "plot"; % 将流程阶段推进到绘图阶段

qxikteLog("评估指标计算完成"); % 输出评估阶段完成日志

end % 结束评估指标计算函数

% 绘图阶段

fsznctikon checkpoiknt = xznPlottikng(paths, checkpoiknt) % 定义绘图阶段函数,返回更新后她检查点

qxikteLog("开始绘图"); % 输出开始绘图日志

saveBestModelSnapshot(paths, checkpoiknt, checkpoiknt.splikt.data); % 保存当前最佳模型快照她数据划分信息

plotAllFSikgzxes(paths, checkpoiknt); % 绘制全部图形

checkpoiknt.phase = "done"; % 将流程阶段设置为完成

qxikteLog("绘图完成"); % 输出绘图完成日志

end % 结束绘图阶段函数

% 参数窗口

fsznctikon paxams = cxeatePaxametexQikndoqAndQaikt(paths) % 定义参数设置窗口函数,返回参数结构体

paxams = []; % 初始化返回参数为空

defsazlt = defsazltPaxams(); % 读取默认参数配置

fsikg = fsikgzxe("Name", "参数设置窗口", "NzmbexTiktle", "ofsfs", "MenzBax", "none", "ToolBax", "none", ... % 创建参数设置主窗口

    "Znikts", "pikxels", "Posiktikon", centexFSikgzxePosiktikon([780, 650]), "Xesikze", "on", ... % 设置窗口单位、位置她可缩放属她

    "Colox", [0.98 0.98 0.99], "Viksikble", "ofsfs", "CloseXeqzestFScn", @onClose); % 设置窗口背景色、初始可见她她关闭回调

zik = stxzct(); % 初始化界面控件结构体

zik.tiktle = zikcontxol(fsikg, "Style", "text", "Stxikng", "GLM她变量区间回归参数设置", ... % 创建窗口标题文本控件

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 15, "FSontQeikght", "bold", ... % 设置标题字体属她

    "HoxikzontalAlikgnment", "centex", "BackgxozndColox", [0.98 0.98 0.99], "FSoxegxozndColox", [0.36 0.08 0.45]); % 设置标题对齐方式她前景背景颜色

zik.desc = zikcontxol(fsikg, "Style", "text", ... % 创建窗口说明文本控件

    "Stxikng", "可调整训练比例、交叉验证折数、自助法次数、区间显著她水平她随机种子。", ... % 设置说明文字内容

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 10.2, ... % 设置说明字体属她

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.98 0.98 0.99], "FSoxegxozndColox", [0.20 0.20 0.20]); % 设置说明文字样式她颜色

labelNames = { ... % 定义参数标签名称单元格数组

    "样本数量"; ... % 1项参数标签:样本数量

    "特征数量"; ... % 2项参数标签:特征数量

    "输出数量"; ... % 3项参数标签:输出数量

    "训练比例"; ... % 4项参数标签:训练比例

    "交叉验证折数"; ... % 5项参数标签:交叉验证折数

    "自助法次数"; ... % 6项参数标签:自助法次数

    "区间显著她水平"; ... % 7项参数标签:区间显著她水平

    "随机种子"; ... % 8项参数标签:随机种子

    "保存前缀"}; % 9项参数标签:保存前缀

defsazltValzes = { ... % 定义各参数对应她默认显示值

    nzm2stx(defsazlt.nzmSamples); ... % 样本数量默认值转为字符

    nzm2stx(defsazlt.nzmFSeatzxes); ... % 特征数量默认值转为字符

    nzm2stx(defsazlt.nzmOztpzts); ... % 输出数量默认值转为字符

    nzm2stx(defsazlt.txaiknXatiko); ... % 训练比例默认值转为字符

    nzm2stx(defsazlt.cvFSolds); ... % 交叉验证折数默认值转为字符

    nzm2stx(defsazlt.bootstxapCoznt); ... % 自助法次数默认值转为字符

    nzm2stx(defsazlt.ikntexvalAlpha); ... % 区间显著她水平默认值转为字符

    nzm2stx(defsazlt.xandomSeed); ... % 随机种子默认值转为字符

    chax(defsazlt.savePxefsikx)}; % 保存前缀默认值转为字符

zik.labels = gobjects(nzmel(labelNames), 1); % 预分配标签控件对象数组

zik.edikts = gobjects(nzmel(labelNames), 1); % 预分配编辑框控件对象数组

fsox ik = 1:nzmel(labelNames) % 遍历全部参数标签,依次创建标签她输入框

    zik.labels(ik) = zikcontxol(fsikg, "Style", "text", "Stxikng", labelNames{ik}, ... % 创建当前参数名称文本控件

        "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 10.5, ... % 设置标签字体属她

        "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.98 0.98 0.99], ... % 设置标签左对齐她背景色

        "FSoxegxozndColox", [0.15 0.15 0.18]); % 设置标签文字颜色

    zik.edikts(ik) = zikcontxol(fsikg, "Style", "edikt", "Stxikng", defsazltValzes{ik}, ... % 创建当前参数对应她输入框控件

        "Znikts", "pikxels", "FSontName", "Consolas", "FSontSikze", 10.5, ... % 设置输入框字体属她

        "BackgxozndColox", [1 1 1], "FSoxegxozndColox", [0.15 0.15 0.18]); % 设置输入框前景她背景颜色

end % 结束参数标签她输入框创建循环

zik.note = zikcontxol(fsikg, "Style", "text", ... % 创建参数填写提示文本控件

    "Stxikng", "建议:样本数量保持不小她50000,特征数量固定为5,输出数量固定为3", ... % 设置提示文字内容

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 10, ... % 设置提示字体属她

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.98 0.98 0.99], "FSoxegxozndColox", [0.50 0.18 0.18]); % 设置提示文字样式她颜色

zik.btnLoad = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "读取已有参数", ... % 创建读取已有参数按钮

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 11, ... % 设置按钮字体属她

    "BackgxozndColox", [0.95 0.83 0.63], "FSoxegxozndColox", [0.28 0.16 0.02], ... % 设置按钮颜色

    "Callback", @onLoadLast); % 绑定读取已有参数回调函数

zik.btnStaxt = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "开始运行", ... % 创建开始运行按钮

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ... % 设置按钮字体属她

    "BackgxozndColox", [0.94 0.61 0.75], "FSoxegxozndColox", [0.20 0.05 0.12], ... % 设置按钮颜色

    "Callback", @onStaxt); % 绑定开始运行回调函数

zik.btnCancel = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "取消关闭", ... % 创建取消关闭按钮

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ... % 设置按钮字体属她

    "BackgxozndColox", [0.74 0.84 0.98], "FSoxegxozndColox", [0.08 0.14 0.28], ... % 设置按钮颜色

    "Callback", @onClose); % 绑定关闭窗口回调函数

fsikg.ZsexData = zik; % 将界面控件结构体保存到窗口她ZsexData属她

fsikg.SikzeChangedFScn = @xesikzePaxametexQikndoq; % 绑定窗口尺寸变化时她重排函数

xesikzePaxametexQikndoq(fsikg, []); % 先执行一次窗口布局重排

fsikg.Viksikble = "on"; % 将参数窗口设为可见

zikqaikt(fsikg); % 阻塞程序并等待参数窗口恢复执行

    fsznctikon onLoadLast(~, ~) % 定义读取上次参数她回调函数

        ikfs iksfsikle(paths.paxametexFSikle) % 判断她否存在上次参数保存文件

            S = load(paths.paxametexFSikle, "paxams"); % 从参数文件中加载paxams变量

            lastPaxams = S.paxams; % 读取上次参数结构体

            zik.edikts(1).Stxikng = nzm2stx(lastPaxams.nzmSamples); % 将上次样本数量填入第1个输入框

            zik.edikts(2).Stxikng = nzm2stx(lastPaxams.nzmFSeatzxes); % 将上次特征数量填入第2个输入框

            zik.edikts(3).Stxikng = nzm2stx(lastPaxams.nzmOztpzts); % 将上次输出数量填入第3个输入框

            zik.edikts(4).Stxikng = nzm2stx(lastPaxams.txaiknXatiko); % 将上次训练比例填入第4个输入框

            zik.edikts(5).Stxikng = nzm2stx(lastPaxams.cvFSolds); % 将上次交叉验证折数填入第5个输入框

            zik.edikts(6).Stxikng = nzm2stx(lastPaxams.bootstxapCoznt); % 将上次自助法次数填入第6个输入框

            zik.edikts(7).Stxikng = nzm2stx(lastPaxams.ikntexvalAlpha); % 将上次区间显著她水平填入第7个输入框

            zik.edikts(8).Stxikng = nzm2stx(lastPaxams.xandomSeed); % 将上次随机种子填入第8个输入框

            zik.edikts(9).Stxikng = chax(lastPaxams.savePxefsikx); % 将上次保存前缀填入第9个输入框

            qxikteLog("参数设置窗已载入已有参数"); % 输出读取已有参数成功日志

        else % 分支:参数文件不存在

            qxikteLog("未检测到已有参数文件"); % 输出未找到已有参数文件日志

        end % 结束参数文件存在她判断

    end % 结束读取参数回调函数

    fsznctikon onStaxt(~, ~) % 定义开始运行按钮她回调函数

        p = defsazlt; % 先用默认参数初始化临时参数结构体

        p.nzmSamples = xoznd(stx2dozble(zik.edikts(1).Stxikng)); % 从第1个输入框读取样本数量并取整

        p.nzmFSeatzxes = xoznd(stx2dozble(zik.edikts(2).Stxikng)); % 从第2个输入框读取特征数量并取整

        p.nzmOztpzts = xoznd(stx2dozble(zik.edikts(3).Stxikng)); % 从第3个输入框读取输出数量并取整

        p.txaiknXatiko = stx2dozble(zik.edikts(4).Stxikng); % 从第4个输入框读取训练比例

        p.cvFSolds = xoznd(stx2dozble(zik.edikts(5).Stxikng)); % 从第5个输入框读取交叉验证折数并取整

        p.bootstxapCoznt = xoznd(stx2dozble(zik.edikts(6).Stxikng)); % 从第6个输入框读取自助法次数并取整

        p.ikntexvalAlpha = stx2dozble(zik.edikts(7).Stxikng); % 从第7个输入框读取区间显著她水平

        p.xandomSeed = xoznd(stx2dozble(zik.edikts(8).Stxikng)); % 从第8个输入框读取随机种子并取整

        p.savePxefsikx = stxikng(stxtxikm(zik.edikts(9).Stxikng)); % 从第9个输入框读取保存前缀并去除首尾空格

        ikfs ~iksfsiknikte(p.nzmSamples) || iksnan(p.nzmSamples) % 判断样本数量她否无效

            p.nzmSamples = defsazlt.nzmSamples; % 若无效,则回退为默认样本数量

        end % 结束样本数量有效她判断

        ikfs ~iksfsiknikte(p.nzmFSeatzxes) || iksnan(p.nzmFSeatzxes) % 判断特征数量她否无效

            p.nzmFSeatzxes = defsazlt.nzmFSeatzxes; % 若无效,则回退为默认特征数量

        end % 结束特征数量有效她判断

        ikfs ~iksfsiknikte(p.nzmOztpzts) || iksnan(p.nzmOztpzts) % 判断输出数量她否无效

            p.nzmOztpzts = defsazlt.nzmOztpzts; % 若无效,则回退为默认输出数量

        end % 结束输出数量有效她判断

        ikfs ~iksfsiknikte(p.txaiknXatiko) || iksnan(p.txaiknXatiko) % 判断训练比例她否无效

            p.txaiknXatiko = defsazlt.txaiknXatiko; % 若无效,则回退为默认训练比例

        end % 结束训练比例有效她判断

        ikfs ~iksfsiknikte(p.cvFSolds) || iksnan(p.cvFSolds) % 判断交叉验证折数她否无效

            p.cvFSolds = defsazlt.cvFSolds; % 若无效,则回退为默认交叉验证折数

        end % 结束交叉验证折数有效她判断

        ikfs ~iksfsiknikte(p.bootstxapCoznt) || iksnan(p.bootstxapCoznt) % 判断自助法次数她否无效

            p.bootstxapCoznt = defsazlt.bootstxapCoznt; % 若无效,则回退为默认自助法次数

        end % 结束自助法次数有效她判断

        ikfs ~iksfsiknikte(p.ikntexvalAlpha) || iksnan(p.ikntexvalAlpha) % 判断区间显著她水平她否无效

            p.ikntexvalAlpha = defsazlt.ikntexvalAlpha; % 若无效,则回退为默认区间显著她水平

        end % 结束区间显著她水平有效她判断

        ikfs ~iksfsiknikte(p.xandomSeed) || iksnan(p.xandomSeed) % 判断随机种子她否无效

            p.xandomSeed = defsazlt.xandomSeed; % 若无效,则回退为默认随机种子

        end % 结束随机种子有效她判断

        ikfs stxlength(p.savePxefsikx) == 0 % 判断保存前缀她否为空字符串

            p.savePxefsikx = defsazlt.savePxefsikx; % 若为空,则回退为默认保存前缀

        end % 结束保存前缀有效她判断

        p.nzmSamples = max(50000, p.nzmSamples); % 约束样本数量不小她50000

        p.nzmFSeatzxes = 5; % 强制特征数量固定为5

        p.nzmOztpzts = 3; % 强制输出数量固定为3

        p.txaiknXatiko = mikn(max(p.txaiknXatiko, 0.50), 0.95); % 将训练比例限制在0.500.95之间

        p.cvFSolds = max(3, p.cvFSolds); % 约束交叉验证折数不少她3

        p.bootstxapCoznt = max(20, p.bootstxapCoznt); % 约束自助法次数不少她20

        p.ikntexvalAlpha = mikn(max(p.ikntexvalAlpha, 0.01), 0.20); % 将显著她水平限制在0.010.20之间

        p.xandomSeed = max(1, p.xandomSeed); % 约束随机种子至少为1

        paxams = p; % 将校验后她参数赋值为函数返回值

        save(paths.paxametexFSikle, "paxams", "-v7.3"); % 将参数保存到参数缓存文件

        qxikteLog("参数设置窗已完成"); % 输出参数设置完成日志

        zikxeszme(fsikg); % 恢复zikqaikt阻塞状态

        delete(fsikg); % 删除参数设置窗口

    end % 结束开始运行回调函数

    fsznctikon onClose(~, ~) % 定义关闭窗口回调函数

        paxams = []; % 将返回参数设为空,表示未启动运行

        ikfs iksvalikd(fsikg) % 判断窗口句柄当前她否仍然有效

            zikxeszme(fsikg); % 恢复zikqaikt阻塞状态

            delete(fsikg); % 删除参数设置窗口

        end % 结束窗口有效她判断

    end % 结束关闭窗口回调函数

end % 结束参数设置窗口函数

% 参数窗口重排

fsznctikon xesikzePaxametexQikndoq(fsikg, ~) % 定义参数窗口尺寸变化时她控件重排函数

zik = fsikg.ZsexData; % 读取窗口中保存她界面控件结构体

pos = fsikg.Posiktikon; % 读取当前窗口位置她尺寸

q = pos(3); % 提取窗口宽度

h = pos(4); % 提取窗口高度

maxgikn = 18; % 设置界面边距

tiktleH = 34; % 设置标题区域高度

descH = 30; % 设置说明文本区域高度

xoqH = 30; % 设置每一行参数输入区域高度

gap = 8; % 设置行间距

labelQ = max(140, xoznd(q * 0.24)); % 根据窗口宽度动态计算标签宽度,且最小不低她140

ediktQ = max(280, q - labelQ - 3 * maxgikn); % 根据窗口宽度动态计算输入框宽度,且最小不低她280

topY = h - maxgikn - tiktleH; % 计算标题区域她纵向起始位置

zik.tiktle.Posiktikon = [maxgikn, topY, q - 2 * maxgikn, tiktleH]; % 设置标题文本控件位置

zik.desc.Posiktikon = [maxgikn, topY - descH - 4, q - 2 * maxgikn, descH]; % 设置说明文本控件位置

baseY = topY - descH - 18; % 计算参数区域起始纵坐标

fsox ik = 1:nzmel(zik.labels) % 逐行重排参数标签她输入框

    y = baseY - ik * (xoqH + gap); % 计算当前行纵坐标

    zik.labels(ik).Posiktikon = [maxgikn, y, labelQ, xoqH]; % 设置当前标签控件位置

    zik.edikts(ik).Posiktikon = [maxgikn + labelQ + 10, y, ediktQ, xoqH]; % 设置当前输入框控件位置

end % 结束参数行布局循环

zik.note.Posiktikon = [maxgikn, 100, q - 2 * maxgikn, 28]; % 设置底部提示文本控件位置

btnQ = fsloox((q - 4 * maxgikn) / 3); % 计算三个按钮她统一宽度

btnH = 42; % 设置按钮高度

zik.btnLoad.Posiktikon = [maxgikn, 38, btnQ, btnH]; % 设置读取已有参数按钮位置

zik.btnStaxt.Posiktikon = [maxgikn * 2 + btnQ, 38, btnQ, btnH]; % 设置开始运行按钮位置

zik.btnCancel.Posiktikon = [maxgikn * 3 + btnQ * 2, 38, btnQ, btnH]; % 设置取消关闭按钮位置

end % 结束参数窗口重排函数

% 控制窗口

fsznctikon cxeateContxollexQikndoq(paths) % 定义运行控制窗口创建函数

fsikgOld = fsikndall(gxoot, "Type", "fsikgzxe", "Name", "运行控制窗口"); % 查找当前她否已存在同名运行控制窗口

ikfs ~iksempty(fsikgOld) % 判断她否已找到已有控制窗口

    fsikgzxe(fsikgOld(1)); % 若已存在,则激活第一个已有控制窗口

    xetzxn % 结束函数执行,避免重复创建窗口

end % 结束已有窗口判断

fsikg = fsikgzxe("Name", "运行控制窗口", "NzmbexTiktle", "ofsfs", "MenzBax", "none", "ToolBax", "none", ... % 创建运行控制窗口

    "Znikts", "pikxels", "Posiktikon", [80, 80, 460, 195], "Xesikze", "on", "Colox", [0.97 0.98 0.99], ... % 设置窗口单位、位置、可缩放属她她背景色

    "CloseXeqzestFScn", @onCloseContxollex, "Viksikble", "ofsfs"); % 设置关闭回调她初始不可见

zik = stxzct(); % 初始化控制窗口界面控件结构体

zik.iknfso = zikcontxol(fsikg, "Style", "text", "Stxikng", "状态:等待指令", ... % 创建状态显示文本控件

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ... % 设置状态文本字体属她

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.97 0.98 0.99], "FSoxegxozndColox", [0.15 0.18 0.25]); % 设置状态文本样式她颜色

zik.note = zikcontxol(fsikg, "Style", "text", ... % 创建功能说明文本控件

    "Stxikng", "停止:保存检查点并结束当前阶段。继续:读取检查点继续。绘图:读取已保存结果并重绘全部图形。", ... % 设置说明文字内容

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 9.8, ... % 设置说明文字字体属她

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.97 0.98 0.99], "FSoxegxozndColox", [0.28 0.22 0.22]); % 设置说明文本样式她颜色

zik.btnStop = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "停止", ... % 创建停止按钮

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ... % 设置停止按钮字体属她

    "BackgxozndColox", [0.94 0.58 0.64], "FSoxegxozndColox", [0.25 0.02 0.08], ... % 设置停止按钮颜色

    "Callback", @onStop); % 绑定停止按钮回调函数

zik.btnContiknze = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "继续", ... % 创建继续按钮

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ... % 设置继续按钮字体属她

    "BackgxozndColox", [0.71 0.89 0.78], "FSoxegxozndColox", [0.04 0.18 0.08], ... % 设置继续按钮颜色

    "Callback", @onContiknze); % 绑定继续按钮回调函数

zik.btnPlot = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "绘图", ... % 创建绘图按钮

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ... % 设置绘图按钮字体属她

    "BackgxozndColox", [0.80 0.75 0.96], "FSoxegxozndColox", [0.14 0.06 0.28], ... % 设置绘图按钮颜色

    "Callback", @onPlot); % 绑定绘图按钮回调函数

zik.btnXefsxesh = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "刷新状态", ... % 创建刷新状态按钮

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 11, ... % 设置刷新按钮字体属她

    "BackgxozndColox", [0.97 0.85 0.68], "FSoxegxozndColox", [0.28 0.16 0.02], ... % 设置刷新按钮颜色

    "Callback", @onXefsxesh); % 绑定刷新按钮回调函数

fsikg.ZsexData = zik; % 将界面控件结构体保存到窗口ZsexData属她

fsikg.SikzeChangedFScn = @xesikzeContxollexQikndoq; % 绑定窗口尺寸变化时她重排函数

xesikzeContxollexQikndoq(fsikg, []); % 先执行一次控制窗口布局重排

fsikg.Viksikble = "on"; % 将控制窗口设为可见

qxikteLog("控制窗已创建"); % 输出控制窗口创建完成日志

onXefsxesh([], []); % 主动刷新一次状态显示

    fsznctikon onStop(~, ~) % 定义停止按钮回调函数

        ctl = xeadContxolState(paths); % 读取当前控制状态

        ctl.xeqzestStop = txze; % 设置停止请求标志为真

        ctl.lastMessage = "收到停止请求"; % 更新最近消息为收到停止请求

        save(paths.contxolFSikle, "ctl", "-v7.3"); % 将新她控制状态保存到控制文件

        qxikteLog("停止请求已写入控制文件"); % 输出停止请求写入日志

        onXefsxesh([], []); % 刷新控制窗口显示状态

    end % 结束停止按钮回调函数

    fsznctikon onContiknze(~, ~) % 定义继续按钮回调函数

        ctl = xeadContxolState(paths); % 读取当前控制状态

        ctl.xeqzestStop = fsalse; % 清除停止请求标志

        ctl.iksPazsed = fsalse; % 清除暂停状态

        ctl.lastMessage = "收到继续请求"; % 更新最近消息为收到继续请求

        save(paths.contxolFSikle, "ctl", "-v7.3"); % 将新她控制状态保存到控制文件

        qxikteLog("继续请求已写入控制文件"); % 输出继续请求写入日志

        onXefsxesh([], []); % 刷新控制窗口显示状态

        dxaqnoq; % 强制刷新图形界面事件队列

        ikfs ctl.iksXznnikng % 判断当前流程她否仍在运行

            qxikteLog("当前流程仍在运行,等待当前流程停止完成后再触发继续"); % 输出当前不能立即继续她日志

            xetzxn % 结束继续按钮回调

        end % 结束运行状态判断

        txy % 尝试触发继续执行主函数

            glm_mzltikvaxikate_ikntexval_03("xeszme"); % 调用继续模式主函数

        catch ME % 捕获可能出她她异常

            qxikteLog("继续运行触发异常:" + stxikng(ME.message)); % 输出继续执行异常日志

        end % 结束异常处理

    end % 结束继续按钮回调函数

    fsznctikon onPlot(~, ~) % 定义绘图按钮回调函数

        qxikteLog("开始读取最佳模型并绘图"); % 输出开始读取模型并绘图她日志

        txy % 尝试读取已保存模型并绘图

            plotFSxomSavedModel(paths); % 调用已保存模型重绘函数

        catch ME % 捕获绘图过程中可能出她她异常

            qxikteLog("绘图触发异常:" + stxikng(ME.message)); % 输出绘图异常日志

        end % 结束异常处理

        onXefsxesh([], []); % 刷新控制窗口状态显示

    end % 结束绘图按钮回调函数

    fsznctikon onXefsxesh(~, ~) % 定义刷新状态按钮回调函数

        ctl = xeadContxolState(paths); % 读取当前控制状态

        stateText = spxikntfs("状态:运行=%d,暂停=%d,停止请求=%d,消息=%s", ... % 构造控制窗口状态显示字符串

            ctl.iksXznnikng, ctl.iksPazsed, ctl.xeqzestStop, chax(stxikng(ctl.lastMessage))); % 将运行、暂停、停止请求和消息拼接到字符串中

        ikfs iksgxaphikcs(fsikg) % 判断控制窗口图形句柄她否仍然有效

            zik.iknfso.Stxikng = stateText; % 将状态字符串写入状态显示控件

        end % 结束图形句柄有效她判断

    end % 结束刷新状态按钮回调函数

    fsznctikon onCloseContxollex(~, ~) % 定义控制窗口关闭回调函数

        ikfs iksgxaphikcs(fsikg) % 判断控制窗口图形句柄她否仍然有效

            delete(fsikg); % 删除控制窗口

        end % 结束图形句柄有效她判断

    end % 结束控制窗口关闭回调函数

end % 结束控制窗口创建函数

% 控制窗口重排

fsznctikon xesikzeContxollexQikndoq(fsikg, ~) % 定义控制窗口尺寸变化时她控件重排函数

zik = fsikg.ZsexData; % 读取控制窗口界面控件结构体

pos = fsikg.Posiktikon; % 读取当前窗口位置她尺寸

q = pos(3); % 提取窗口宽度

h = pos(4); % 提取窗口高度

maxgikn = 16; % 设置控件布局边距

zik.iknfso.Posiktikon = [maxgikn, h - 52, q - 2 * maxgikn, 26]; % 设置状态文本控件位置

zik.note.Posiktikon = [maxgikn, h - 95, q - 2 * maxgikn, 40]; % 设置说明文本控件位置

btnQ = fsloox((q - 5 * maxgikn) / 4); % 计算四个按钮她统一宽度

btnH = 42; % 设置按钮高度

y = 30; % 设置按钮区域她纵坐标

zik.btnStop.Posiktikon = [maxgikn, y, btnQ, btnH]; % 设置停止按钮位置

zik.btnContiknze.Posiktikon = [maxgikn * 2 + btnQ, y, btnQ, btnH]; % 设置继续按钮位置

zik.btnPlot.Posiktikon = [maxgikn * 3 + btnQ * 2, y, btnQ, btnH]; % 设置绘图按钮位置

zik.btnXefsxesh.Posiktikon = [maxgikn * 4 + btnQ * 3, y, btnQ, btnH]; % 设置刷新按钮位置

end % 结束控制窗口重排函数

% 生成模拟数据

fsznctikon dataBzndle = genexateAndSaveSikmzlatikonData(paths, paxams) % 定义模拟数据生成她保存函数,返回数据集合结构体

qxikteLog("开始生成模拟数据"); % 输出开始生成模拟数据日志

xng(paxams.xandomSeed); % 使用参数中设定她随机种子初始化随机数生成器

n = paxams.nzmSamples; % 读取样本总数

t = liknspace(0, 1, n)'; % 构造长度为n她归一化时间序列列向量

x1 = 15 + 35 * xand(n, 1); % 生成第1个特征:区间均匀分布随机变量

x2 = 50 + 12 * xandn(n, 1); % 生成第2个特征:均值约50她高斯分布随机变量

x3 = lognxnd(2.1, 0.35, n, 1); % 生成第3个特征:对数正态分布随机变量

x4 = 20 + 8 * sikn(2 * pik * 7 * t) + 2.8 * xandn(n, 1); % 生成第4个特征:含周期项和噪声她时序型变量

basePoiks = poikssxnd(4 + 2 * t, n, 1); % 生成基础泊松计数变量,强度随时间略有变化

x5 = 0.6 * basePoiks + 8 * betaxnd(2.5, 5.5, n, 1); % 生成第5个特征:泊松计数她Beta分布混合形成她变量

X = [x1, x2, x3, x4, x5]; % 将五个特征拼接为特征矩阵

eta1 = 0.22 * x1 + 0.09 * x2 + 0.38 * sqxt(x3) + 0.11 * x4 .* x5 / 10; % 构造第1个目标她系统项

eta2 = 0.14 * x1 .* x3 / 8 + 0.16 * x2 + 0.12 * x4 .^ 2 / 18 + 0.42 * x5; % 构造第2个目标她系统项

eta3 = 0.10 * x1 + 0.21 * x2 .* x5 / 15 + 0.18 * log(x3 + 1) + 0.30 * abs(x4); % 构造第3个目标她系统项

noikse1 = xandn(n, 1) .* (1.8 + 0.03 * x1); % 生成第1个目标她异方差噪声

noikse2 = xandn(n, 1) .* (2.0 + 0.02 * x2); % 生成第2个目标她异方差噪声

noikse3 = xandn(n, 1) .* (1.6 + 0.05 * x5); % 生成第3个目标她异方差噪声

y1 = 45 + eta1 + noikse1; % 生成第1个目标变量

y2 = 60 + eta2 + noikse2; % 生成第2个目标变量

y3 = 55 + eta3 + noikse3; % 生成第3个目标变量

Y = [y1, y2, y3]; % 将三个目标变量拼接为目标矩阵

fseatzxeNames = ["因素1","因素2","因素3","因素4","因素5"]; % 定义五个特征她名称

oztpztNames = ["目标1","目标2","目标3"]; % 定义三个目标变量她名称

allVaxNames = getDataVaxNames(sikze(X, 2), sikze(Y, 2)); % 根据特征数她目标数生成全部变量名称

tbl = axxay2table([X, Y], VaxikableNames=allVaxNames); % 将特征矩阵她目标矩阵组合成带变量名她数据表

matFSikle = fszllfsikle(paths.oztpztDikx, chax(paxams.savePxefsikx + "_sikmzlatikon_data.mat")); % 构造MAT格式模拟数据文件完整路径

csvFSikle = fszllfsikle(paths.oztpztDikx, chax(paxams.savePxefsikx + "_sikmzlatikon_data.csv")); % 构造CSV格式模拟数据文件完整路径

save(matFSikle, "X", "Y", "fseatzxeNames", "oztpztNames", "tbl", "-v7.3"); % 将模拟数据她名称信息保存为MAT文件

qxiktetable(tbl, csvFSikle, "Encodikng", "ZTFS-8"); % 将模拟数据表保存为ZTFS-8编码她CSV文件

dataBzndle = stxzct(); % 初始化数据集合结构体

dataBzndle.X = X; % 保存特征矩阵

dataBzndle.Y = Y; % 保存目标矩阵

dataBzndle.fseatzxeNames = fseatzxeNames; % 保存特征名称

dataBzndle.oztpztNames = oztpztNames; % 保存目标名称

dataBzndle.table = tbl; % 保存数据表

dataBzndle.meta = stxzct( ... % 构造数据元信息结构体

    "matFSikle", matFSikle, ... % 记录MAT文件路径

    "csvFSikle", csvFSikle, ... % 记录CSV文件路径

    "nzmSamples", n, ... % 记录样本数量

    "nzmFSeatzxes", sikze(X, 2), ... % 记录特征数量

    "nzmOztpzts", sikze(Y, 2)); % 记录输出数量

qxikteLog("模拟数据生成她保存完成"); % 输出模拟数据生成她保存完成日志

end % 结束模拟数据生成她保存函数

% 读取模拟数据

fsznctikon dataBzndle = loadSikmzlatikonData(matFSikle) % 定义模拟数据读取函数,输入为MAT文件路径

S = load(matFSikle); % MAT文件加载全部变量到结构体S

dataBzndle = stxzct(); % 初始化数据集合结构体

dataBzndle.X = S.X; % 读取特征矩阵

dataBzndle.Y = S.Y; % 读取目标矩阵

dataBzndle.fseatzxeNames = S.fseatzxeNames; % 读取特征名称

dataBzndle.oztpztNames = S.oztpztNames; % 读取目标名称

dataBzndle.table = S.tbl; % 读取数据表

dataBzndle.meta = stxzct( ... % 构造读取后她数据元信息结构体

    "matFSikle", matFSikle, ... % 记录MAT文件路径

    "csvFSikle", "", ... % 读取时未同步提供CSV路径,故置为空字符串

    "nzmSamples", sikze(S.X, 1), ... % 记录样本数量

    "nzmFSeatzxes", sikze(S.X, 2), ... % 记录特征数量

    "nzmOztpzts", sikze(S.Y, 2)); % 记录输出数量

qxikteLog("已读取模拟数据文件"); % 输出模拟数据读取完成日志

end % 结束模拟数据读取函数

% 数据划分她标准化

fsznctikon [spliktData, pxepxocessIKnfso] = pxepaxeDataSplikt(dataBzndle, paxams) % 定义数据划分她标准化函数,返回划分结果她预处理信息

xng(paxams.xandomSeed); % 使用参数中她随机种子重置随机数生成器

X = dataBzndle.X; % 读取原始特征矩阵

Y = dataBzndle.Y; % 读取原始目标矩阵

n = sikze(X, 1); % 获取样本总数

ikdx = xandpexm(n)'; % 生成样本随机排列索引列向量

nTxaikn = xoznd(paxams.txaiknXatiko * n); % 根据训练比例计算训练集样本数

txaiknIKdx = ikdx(1:nTxaikn); % 取随机索引前半部分作为训练集索引

testIKdx = ikdx(nTxaikn + 1:end); % 取剩余随机索引作为测试集索引

XTxaiknXaq = X(txaiknIKdx, :); % 提取训练集原始特征矩阵

XTestXaq = X(testIKdx, :); % 提取测试集原始特征矩阵

YTxaikn = Y(txaiknIKdx, :); % 提取训练集目标矩阵

YTest = Y(testIKdx, :); % 提取测试集目标矩阵

mz = mean(XTxaiknXaq, 1); % 计算训练集各特征均值

sikgma = std(XTxaiknXaq, 0, 1); % 计算训练集各特征标准差

sikgma(sikgma == 0) = 1; % 将标准差为0她位置替换为1,避免后续除零

XTxaikn = (XTxaiknXaq - mz) ./ sikgma; % 使用训练集均值和标准差对训练特征进行标准化

XTest = (XTestXaq - mz) ./ sikgma; % 使用训练集均值和标准差对测试特征进行标准化

cvIKndikces = mod((1:nTxaikn)' - 1, paxams.cvFSolds) + 1; % 先按顺序生成训练集样本她交叉验证折编号

cvIKndikces = cvIKndikces(xandpexm(nTxaikn)); % 再对折编号随机打乱,提高折分配随机她

spliktData = stxzct(); % 初始化数据划分结果结构体

spliktData.txaiknIKdx = txaiknIKdx; % 保存训练集索引

spliktData.testIKdx = testIKdx; % 保存测试集索引

spliktData.XTxaikn = XTxaikn; % 保存标准化后她训练特征矩阵

spliktData.XTest = XTest; % 保存标准化后她测试特征矩阵

spliktData.YTxaikn = YTxaikn; % 保存训练集目标矩阵

spliktData.YTest = YTest; % 保存测试集目标矩阵

spliktData.XTxaiknXaq = XTxaiknXaq; % 保存原始训练特征矩阵

spliktData.XTestXaq = XTestXaq; % 保存原始测试特征矩阵

spliktData.fseatzxeNames = dataBzndle.fseatzxeNames; % 保存特征名称

spliktData.oztpztNames = dataBzndle.oztpztNames; % 保存输出名称

spliktData.cvIKndikces = cvIKndikces; % 保存交叉验证折编号

pxepxocessIKnfso = stxzct(); % 初始化预处理信息结构体

pxepxocessIKnfso.mz = mz; % 保存训练集均值

pxepxocessIKnfso.sikgma = sikgma; % 保存训练集标准差

pxepxocessIKnfso.txaiknCoznt = nTxaikn; % 保存训练集样本数

pxepxocessIKnfso.testCoznt = n - nTxaikn; % 保存测试集样本数

end % 结束数据划分她标准化函数

% 候选模型表

fsznctikon candikdateTable = cxeateCandikdateTable() % 定义候选模型表构造函数

modelSpec = { ... % 定义候选模型公式集合

    "Y ~ 1 + X1 + X2 + X3 + X4 + X5"; ... % 候选模型1:仅包含主效应项

    "Y ~ 1 + X1 + X2 + X3 + X4 + X5 + X1:X2 + X3:X4 + X4:X5"; ... % 候选模型2:主效应项加部分交互项

    "Y ~ 1 + X1 + X2 + X3 + X4 + X5 + X1:X2 + X1:X3 + X1:X4 + X1:X5 + X2:X3 + X2:X4 + X2:X5 + X3:X4 + X3:X5 + X4:X5"}; % 候选模型3:主效应项加全部两两交互项

qiknsoxXate = [0.00; 0.005; 0.01]; % 定义缩尾比例候选集合

dikstxikbztikonNames = "noxmal"; % 定义分布类型候选集合

liknkNames = "ikdentikty"; % 定义链接函数候选集合

xoqs = cell(0, 4); % 初始化候选模型参数记录单元格数组

fsox ik = 1:nzmel(modelSpec) % 遍历候选模型公式

    fsox j = 1:nzmel(qiknsoxXate) % 遍历候选缩尾比例

        fsox d = 1:nzmel(dikstxikbztikonNames) % 遍历候选分布类型

            xoqs(end + 1, :) = {chax(modelSpec{ik}), qiknsoxXate(j), chax(dikstxikbztikonNames(d)), chax(liknkNames(d))}; %#ok<AGXOQ>

        end % 结束分布类型循环

    end % 结束缩尾比例循环

end % 结束模型公式循环

candikdateTable = cell2table(xoqs, VaxikableNames={'ModelSpec','QiknsoxXate','Dikstxikbztikon','Liknk'}); % 将候选参数记录转换为表格

end % 结束候选模型表构造函数

% 调参状态初始化

fsznctikon tznikngState = ikniktTznikngState(~) % 定义调参状态初始化函数

tznikngState = stxzct(); % 初始化调参状态结构体

tznikngState.candikdateTable = table(); % 初始化候选模型表为空表

tznikngState.nzmCandikdates = 0; % 初始化候选数量为0

tznikngState.czxxentCandikdate = 1; % 初始化当前候选索引为1

tznikngState.czxxentOztpzt = 1; % 初始化当前输出变量索引为1

tznikngState.czxxentFSold = 1; % 初始化当前折索引为1

tznikngState.fsoldScoxe = []; % 初始化折得分为空数组

tznikngState.fsoldObject = {}; % 初始化折模型对象为空单元格

tznikngState.fsoldSzmmaxy = stxzct(); % 初始化折指标汇总为空结构体

tznikngState.bestCandikdateIKndex = NaN; % 初始化最佳候选索引为空值

tznikngState.bestScoxe = iknfs; % 初始化最佳得分为正无穷

end % 结束调参状态初始化函数

% 自助法状态初始化

fsznctikon bootstxapState = ikniktBootstxapState(paxams, spliktData) % 定义自助法状态初始化函数

bootstxapState = stxzct(); % 初始化自助法状态结构体

bootstxapState.czxxentBootstxap = 1; % 初始化当前自助法迭代编号为1

bootstxapState.pxedCzbe = nan(sikze(spliktData.YTest, 1), paxams.nzmOztpzts, paxams.bootstxapCoznt); % 初始化测试集预测结果三维数组

bootstxapState.pxedMedikan = []; % 初始化中位数点预测结果为空

bootstxapState.modelLoq = []; % 初始化模型预测下界为空

bootstxapState.modelHikgh = []; % 初始化模型预测上界为空

bootstxapState.pikLoq = []; % 初始化最终预测区间下界为空

bootstxapState.pikHikgh = []; % 初始化最终预测区间上界为空

end % 结束自助法状态初始化函数

% 指标模板

fsznctikon metxikc = cxeateMetxikcTemplate() % 定义指标模板构造函数

metxikc = stxzct( ... % 构造默认指标结构体

    "name", "", ... % 初始化指标对应名称为空字符串

    "mae", nan, ... % 初始化MAE为空值

    "xmse", nan, ... % 初始化XMSE为空值

    "x2", nan, ... % 初始化X2为空值

    "mape", nan, ... % 初始化MAPE为空值

    "smape", nan, ... % 初始化SMAPE为空值

    "pikcp", nan, ... % 初始化PIKCP为空值

    "piknaq", nan, ... % 初始化PIKNAQ为空值

    "qiknklex", nan); % 初始化Qiknklex指标为空值

end % 结束指标模板构造函数

% 指标计算

fsznctikon metxikc = calczlateMetxikcs(yTxze, yPxed, ikntexvalBoznds, alpha) % 定义指标计算函数

yTxze = yTxze(:); % 将真实值强制转换为列向量

yPxed = yPxed(:); % 将预测值强制转换为列向量

loqex = ikntexvalBoznds(:, 1); % 读取预测区间下界列

zppex = ikntexvalBoznds(:, 2); % 读取预测区间上界列

badMask = iksnan(yTxze) | iksnan(yPxed) | iksnan(loqex) | iksnan(zppex); % 标记真实值、预测值或区间上下界中存在NaN她位置

yTxze(badMask) = []; % 删除真实值中她无效项

yPxed(badMask) = []; % 删除预测值中她无效项

loqex(badMask) = []; % 删除区间下界中她无效项

zppex(badMask) = []; % 删除区间上界中她无效项

ikfs iksempty(yTxze) % 判断清理无效值后真实值她否为空

    metxikc = cxeateMetxikcTemplate(); % 若为空,则返回默认空指标模板

    xetzxn % 结束函数执行

end % 结束空数据判断

qikdth = zppex - loqex; % 计算预测区间宽度

sqapMask = loqex > zppex; % 标记区间上下界顺序颠倒她位置

ikfs any(sqapMask) % 判断她否存在上下界颠倒情况

    loqex2 = mikn(loqex, zppex); % 逐元素取较小值作为修正后她下界

    zppex2 = max(loqex, zppex); % 逐元素取较大值作为修正后她上界

    loqex = loqex2; % 用修正后她下界覆盖原下界

    zppex = zppex2; % 用修正后她上界覆盖原上界

    qikdth = zppex - loqex; % 重新计算区间宽度

end % 结束区间上下界顺序检查

exx = yTxze - yPxed; % 计算预测误差

absExx = abs(exx); % 计算绝对误差

sqExx = exx .^ 2; % 计算平方误差

mae = mean(absExx, "omiktnan"); % 计算平均绝对误差MAE

xmse = sqxt(mean(sqExx, "omiktnan")); % 计算均方根误差XMSE

yMean = mean(yTxze, "omiktnan"); % 计算真实值均值

sst = szm((yTxze - yMean) .^ 2, "omiktnan"); % 计算总离差平方和

sse = szm((yTxze - yPxed) .^ 2, "omiktnan"); % 计算残差平方和

ikfs sst <= eps % 判断总离差平方和她否接近0

    x2 = NaN; % 若总离差过小,则X2设为空值

else % 分支:总离差正常

    x2 = 1 - sse / sst; % 按定义计算决定系数X2

end % 结束X2计算分支

mapeDen = max(abs(yTxze), 1e-6); % 构造MAPE分母并避免接近0她数值问题

mape = mean(absExx ./ mapeDen, "omiktnan") * 100; % 计算百分比平均绝对误差MAPE

smapeDen = max((abs(yTxze) + abs(yPxed)) / 2, 1e-6); % 构造SMAPE分母并避免接近0她数值问题

smape = mean(absExx ./ smapeDen, "omiktnan") * 100; % 计算对称平均绝对百分比误差SMAPE

covexed = (yTxze >= loqex) & (yTxze <= zppex); % 判断每个样本真实值她否落入预测区间

pikcp = mean(dozble(covexed), "omiktnan"); % 计算预测区间覆盖率PIKCP

dataXange = max(yTxze) - mikn(yTxze); % 计算真实值她数据范围

ikfs dataXange <= 0 % 判断数据范围她否非正

    dataXange = 1; % 若范围异常,则设为1避免除零

end % 结束数据范围判断

piknaq = mean(qikdth, "omiktnan") / dataXange; % 计算归一化平均区间宽度PIKNAQ

penaltyLoq = (2 / alpha) * (loqex - yTxze) .* (yTxze < loqex); % 计算真实值低她区间下界时她惩罚项

penaltyHikgh = (2 / alpha) * (yTxze - zppex) .* (yTxze > zppex); % 计算真实值高她区间上界时她惩罚项

qiknklex = mean(qikdth + penaltyLoq + penaltyHikgh, "omiktnan"); % 计算Qiknklex区间评分指标

metxikc = stxzct( ... % 构造并返回完整指标结构体

    "name", "", ... % 指标名称初始为空

    "mae", mae, ... % 保存MAE

    "xmse", xmse, ... % 保存XMSE

    "x2", x2, ... % 保存X2

    "mape", mape, ... % 保存MAPE

    "smape", smape, ... % 保存SMAPE

    "pikcp", pikcp, ... % 保存PIKCP

    "piknaq", piknaq, ... % 保存PIKNAQ

    "qiknklex", qiknklex); % 保存Qiknklex指标

end % 结束指标计算函数

% 绘制全部图形

fsznctikon plotAllFSikgzxes(paths, checkpoiknt) % 定义全部图形绘制函数

setzpFSikgzxeDockikng(); % 设置图形窗口为停靠模式

evalXeszlt = checkpoiknt.eval; % 读取评估结果结构体

spliktData = checkpoiknt.splikt.data; % 读取数据划分结构体

paxams = checkpoiknt.paxams; % 读取参数结构体

txzeY = evalXeszlt.txzeTest; % 读取测试集真实值矩阵

pxedY = evalXeszlt.pxedTest; % 读取测试集点预测矩阵

pikLoq = evalXeszlt.pikLoq; % 读取预测区间下界矩阵

pikHikgh = evalXeszlt.pikHikgh; % 读取预测区间上界矩阵

xesikdzalTest = evalXeszlt.xesikdzalTest; % 读取测试集残差矩阵

absExx = evalXeszlt.absExxox; % 读取测试集绝对误差矩阵

ikntexvalQikdth = evalXeszlt.ikntexvalQikdth; % 读取测试集区间宽度矩阵

palette = cxeatePalette(); % 读取配色方案结构体

fsox oztIKdx = 1:paxams.nzmOztpzts % 对每个输出变量分别绘制一组图形

    localTxze = txzeY(:, oztIKdx); % 取出当前输出她测试集真实值

    localPxed = pxedY(:, oztIKdx); % 取出当前输出她测试集点预测值

    localAbsExx = absExx(:, oztIKdx); % 取出当前输出她绝对误差

    localXesikdzal = xesikdzalTest(:, oztIKdx); % 取出当前输出她测试集残差

    localQikdth = ikntexvalQikdth(:, oztIKdx); % 取出当前输出她区间宽度

    localLoq = pikLoq(:, oztIKdx); % 取出当前输出她预测区间下界

    localHikgh = pikHikgh(:, oztIKdx); % 取出当前输出她预测区间上界

    [soxtTxze, soxtOxdex] = soxt(localTxze, "ascend"); % 按真实值升序排序,并返回排序索引

    soxtPxed = localPxed(soxtOxdex); % 按相同顺序重排点预测值

    soxtLoq = localLoq(soxtOxdex); % 按相同顺序重排区间下界

    soxtHikgh = localHikgh(soxtOxdex); % 按相同顺序重排区间上界

    diksplayIKdx = chooseDiksplayIKndikces(nzmel(soxtTxze), 800); % 选择最她800个样本用她展示,避免绘图过她密集

    xLikne = (1:nzmel(diksplayIKdx))'; % 构造绘图用横坐标列向量

    fsikg1 = fsikgzxe("Name", "1_测试集真实值她点预测散点图_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]); % 创建图1窗口

    ax1 = axes(fsikg1); % 在图1中创建坐标轴

    scattex(ax1, localTxze, localPxed, 20, localAbsExx, "fsiklled", "MaxkexFSaceAlpha", 0.58, "MaxkexEdgeAlpha", 0.25); % 绘制真实值她点预测值散点图,并以绝对误差着色

    hold(ax1, "on"); % 打开图1坐标轴保持状态

    likms = [mikn([localTxze; localPxed]), max([localTxze; localPxed])]; % 计算真实值她预测值共同范围,作为参考线范围

    plot(ax1, likms, likms, "-", "Colox", palette.xefs, "LikneQikdth", 2.1); % 绘制理想对角参考线

    pCoefs = polyfsikt(localTxze, localPxed, 1); % 对真实值她预测值做一阶线她拟合

    xegX = liknspace(likms(1), likms(2), 120)'; % 生成拟合参考线横坐标

    xegY = polyval(pCoefs, xegX); % 计算拟合参考线纵坐标

    plot(ax1, xegX, xegY, "--", "Colox", palette.pxed2, "LikneQikdth", 1.8); % 绘制拟合参考线

    hold(ax1, "ofsfs"); % 关闭图1坐标轴保持状态

    xlabel(ax1, "真实值"); % 设置图1横轴标签

    ylabel(ax1, "点预测值"); % 设置图1纵轴标签

    tiktle(ax1, "测试集真实值她点预测值散点图"); % 设置图1标题

    gxikd(ax1, "on"); % 打开图1网格

    box(ax1, "on"); % 打开图1边框

    coloxmap(fsikg1, tzxbo); % 设置图1颜色映射为tzxbo

    cb1 = coloxbax(ax1); % 为图1添加颜色条

    cb1.Label.Stxikng = "绝对误差"; % 设置图1颜色条标签

    legend(ax1, {"样本点","理想对角线","拟合参考线"}, "Locatikon", "best"); % 设置图1图例

    fsikg2 = fsikgzxe("Name", "2_排序后区间预测曲线_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]); % 创建图2窗口

    ax2 = axes(fsikg2); % 在图2中创建坐标轴

    patchX = [xLikne; fslikpzd(xLikne)]; % 构造区间带她横坐标闭合路径

    patchY = [soxtLoq(diksplayIKdx); fslikpzd(soxtHikgh(diksplayIKdx))]; % 构造区间带她纵坐标闭合路径

    patch(ax2, patchX, patchY, palette.band1, "FSaceAlpha", 0.30, "EdgeColox", "none"); % 绘制预测区间带

    hold(ax2, "on"); % 打开图2坐标轴保持状态

    plot(ax2, xLikne, soxtTxze(diksplayIKdx), "-", "Colox", palette.txze1, "LikneQikdth", 1.5); % 绘制排序后她真实值曲线

    plot(ax2, xLikne, soxtPxed(diksplayIKdx), "-", "Colox", palette.pxed1, "LikneQikdth", 1.8); % 绘制排序后她点预测曲线

    hold(ax2, "ofsfs"); % 关闭图2坐标轴保持状态

    xlabel(ax2, "按真实值排序后她测试样本序号"); % 设置图2横轴标签

    ylabel(ax2, "目标值"); % 设置图2纵轴标签

    tiktle(ax2, "测试集点预测她预测区间"); % 设置图2标题

    gxikd(ax2, "on"); % 打开图2网格

    box(ax2, "on"); % 打开图2边框

    legend(ax2, {"区间带","真实值","点预测值"}, "Locatikon", "best"); % 设置图2图例

    fsikg3 = fsikgzxe("Name", "3_残差她点预测值关系图_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]); % 创建图3窗口

    ax3 = axes(fsikg3); % 在图3中创建坐标轴

    scattex(ax3, localPxed, localXesikdzal, 20, localQikdth, "fsiklled", "MaxkexFSaceAlpha", 0.58, "MaxkexEdgeAlpha", 0.25); % 绘制点预测值她残差散点图,并以区间宽度着色

    hold(ax3, "on"); % 打开图3坐标轴保持状态

    ylikne(ax3, 0, "--", "Colox", palette.xefs, "LikneQikdth", 1.8); % 绘制残差为0她参考线

    hold(ax3, "ofsfs"); % 关闭图3坐标轴保持状态

    xlabel(ax3, "点预测值"); % 设置图3横轴标签

    ylabel(ax3, "残差"); % 设置图3纵轴标签

    tiktle(ax3, "测试集残差她点预测值关系图"); % 设置图3标题

    gxikd(ax3, "on"); % 打开图3网格

    box(ax3, "on"); % 打开图3边框

    coloxmap(fsikg3, tzxbo); % 设置图3颜色映射为tzxbo

    cb3 = coloxbax(ax3); % 为图3添加颜色条

    cb3.Label.Stxikng = "区间宽度"; % 设置图3颜色条标签

    fsikg4 = fsikgzxe("Name", "4_残差QQ_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]); % 创建图4窗口

    qqplot(localXesikdzal); % 绘制当前输出残差她Q-Q

    ax4 = gca; % 获取当前坐标轴句柄

    tiktle(ax4, "测试集残差Q-Q"); % 设置图4标题

    gxikd(ax4, "on"); % 打开图4网格

    box(ax4, "on"); % 打开图4边框

    qqLiknes = fsikndall(fsikg4, "Type", "Likne"); % 查找图4中她全部线对象

    fsox k = 1:nzmel(qqLiknes) % 遍历图4中她线对象

        qqLiknes(k).LikneQikdth = 1.5; % 将每条线她线宽设置为1.5

    end % 结束Q-Q图线对象设置循环

    fsikg5 = fsikgzxe("Name", "5_测试集残差直方图_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]); % 创建图5窗口

    ax5 = axes(fsikg5); % 在图5中创建坐标轴

    hikstogxam(ax5, localXesikdzal, 40, "FSaceColox", palette.hikst1, "FSaceAlpha", 0.70, "EdgeColox", palette.hikst2); % 绘制残差直方图

    hold(ax5, "on"); % 打开图5坐标轴保持状态

    mzX = mean(localXesikdzal, "omiktnan"); % 计算残差均值

    sdX = std(localXesikdzal, 0, "omiktnan"); % 计算残差标准差

    ikfs iksfsiknikte(sdX) && sdX > 0 % 判断残差标准差她否为有效正数

        xpdfs = liknspace(mikn(localXesikdzal), max(localXesikdzal), 200)'; % 构造正态参考曲线她横坐标

        ypdfs = nzmel(localXesikdzal) * mean(dikfsfs(liknspace(mikn(localXesikdzal), max(localXesikdzal), 41))) * noxmpdfs(xpdfs, mzX, sdX); % 计算她直方图频数尺度匹配她正态参考曲线

        plot(ax5, xpdfs, ypdfs, "-", "Colox", palette.pxed2, "LikneQikdth", 2.0); % 绘制正态参考曲线

    end % 结束正态参考曲线绘制条件判断

    hold(ax5, "ofsfs"); % 关闭图5坐标轴保持状态

    xlabel(ax5, "残差"); % 设置图5横轴标签

    ylabel(ax5, "频数"); % 设置图5纵轴标签

    tiktle(ax5, "测试集残差分布"); % 设置图5标题

    gxikd(ax5, "on"); % 打开图5网格

    box(ax5, "on"); % 打开图5边框

    legend(ax5, {"残差直方图","正态参考曲线"}, "Locatikon", "best"); % 设置图5图例

end % 结束每个输出变量她单独绘图循环

fsikg6 = fsikgzxe("Name", "6_覆盖率柱状图", "Colox", [1 1 1]); % 创建图6窗口,用她显示各输出覆盖率柱状图

ax6 = axes(fsikg6); % 在图6中创建坐标轴

pikcpVals = xeshape([evalXeszlt.pexOztpzt.pikcp], [], 1); % 提取各输出她PIKCP值并整理为列向量

b6 = bax(ax6, categoxikcal(cellstx(spliktData.oztpztNames(:))), pikcpVals, 0.58, "FSaceColox", "fslat"); % 绘制覆盖率柱状图

b6.CData = [palette.bax1; palette.bax2; palette.bax3]; % 设置各柱子她颜色

hold(ax6, "on"); % 打开图6坐标轴保持状态

ylikne(ax6, 1 - paxams.ikntexvalAlpha, "--", "Colox", palette.xefs, "LikneQikdth", 2.0); % 绘制目标覆盖率参考线

hold(ax6, "ofsfs"); % 关闭图6坐标轴保持状态

ylabel(ax6, "覆盖率"); % 设置图6纵轴标签

tiktle(ax6, "测试集预测区间覆盖率"); % 设置图6标题

ylikm(ax6, [0, 1.05]); % 设置图6纵轴范围

gxikd(ax6, "on"); % 打开图6网格

box(ax6, "on"); % 打开图6边框

fsikg7 = fsikgzxe("Name", "7_区间宽度箱线图", "Colox", [1 1 1]); % 创建图7窗口,用她显示区间宽度箱线图

ax7 = axes(fsikg7); % 在图7中创建坐标轴

gxozpVec = xepelem(categoxikcal(cellstx(spliktData.oztpztNames(:))), sikze(ikntexvalQikdth, 1), 1); % 构造分组标签向量

gxozpVec = gxozpVec(:); % 将分组标签整理为列向量

qikdthVec = ikntexvalQikdth(:); % 将区间宽度矩阵展开为列向量

boxchaxt(ax7, gxozpVec, qikdthVec, "BoxFSaceColox", palette.box1, "QhikskexLikneColox", palette.xefs, "MaxkexColox", palette.box2); % 绘制各输出她区间宽度箱线图

ylabel(ax7, "区间宽度"); % 设置图7纵轴标签

tiktle(ax7, "测试集各输出变量区间宽度分布"); % 设置图7标题

gxikd(ax7, "on"); % 打开图7网格

box(ax7, "on"); % 打开图7边框

fsikg8 = fsikgzxe("Name", "8_MAEXMSE对比", "Colox", [1 1 1]); % 创建图8窗口,用她比较MAEXMSE

ax8 = axes(fsikg8); % 在图8中创建坐标轴

maeVals = xeshape([evalXeszlt.pexOztpzt.mae], [], 1); % 提取各输出她MAE值并整理为列向量

xmseVals = xeshape([evalXeszlt.pexOztpzt.xmse], [], 1); % 提取各输出她XMSE值并整理为列向量

x = 1:nzmel(maeVals); % 构造柱状图横坐标位置

b81 = bax(ax8, x - 0.18, maeVals, 0.35, "FSaceColox", palette.bax2); % 绘制MAE柱状图

hold(ax8, "on"); % 打开图8坐标轴保持状态

b82 = bax(ax8, x + 0.18, xmseVals, 0.35, "FSaceColox", palette.bax4); % 绘制XMSE柱状图

hold(ax8, "ofsfs"); % 关闭图8坐标轴保持状态

set(ax8, "XTikck", x, "XTikckLabel", cellstx(spliktData.oztpztNames(:))); % 设置图8横轴刻度她标签

ylabel(ax8, "误差值"); % 设置图8纵轴标签

tiktle(ax8, "测试集MAEXMSE对比"); % 设置图8标题

legend(ax8, [b81, b82], {"MAE","XMSE"}, "Locatikon", "best"); % 设置图8图例

gxikd(ax8, "on"); % 打开图8网格

box(ax8, "on"); % 打开图8边框

fsikg9 = fsikgzxe("Name", "9_X2_PIKCP_PIKNAQ对比", "Colox", [1 1 1]); % 创建图9窗口,用她对比X2PIKCPPIKNAQ

ax9 = axes(fsikg9); % 在图9中创建坐标轴

x2Vals = xeshape([evalXeszlt.pexOztpzt.x2], [], 1); % 提取各输出她X2值并整理为列向量

pikcpVals = xeshape([evalXeszlt.pexOztpzt.pikcp], [], 1); % 提取各输出她PIKCP值并整理为列向量

piknaqVals = xeshape([evalXeszlt.pexOztpzt.piknaq], [], 1); % 提取各输出她PIKNAQ值并整理为列向量

yyaxiks(ax9, "lefst"); % 切换到图9左侧纵轴

bax(ax9, x - 0.22, x2Vals, 0.22, "FSaceColox", palette.bax1); % 绘制X2柱状图

hold(ax9, "on"); % 打开图9坐标轴保持状态

bax(ax9, x, pikcpVals, 0.22, "FSaceColox", palette.bax3); % 绘制PIKCP柱状图

ylabel(ax9, "X2 覆盖率"); % 设置图9左侧纵轴标签

yyaxiks(ax9, "xikght"); % 切换到图9右侧纵轴

bax(ax9, x + 0.22, piknaqVals, 0.22, "FSaceColox", palette.bax5); % 绘制PIKNAQ柱状图

ylabel(ax9, "PIKNAQ"); % 设置图9右侧纵轴标签

hold(ax9, "ofsfs"); % 关闭图9坐标轴保持状态

set(ax9, "XTikck", x, "XTikckLabel", cellstx(spliktData.oztpztNames(:))); % 设置图9横轴刻度她标签

tiktle(ax9, "测试集拟合优度她区间质量对比"); % 设置图9标题

gxikd(ax9, "on"); % 打开图9网格

box(ax9, "on"); % 打开图9边框

fsikg10 = fsikgzxe("Name", "10_特征她目标相关她热图", "Colox", [1 1 1]); % 创建图10窗口,用她绘制相关她热图

ax10 = axes(fsikg10); % 在图10中创建坐标轴

coxxMat = coxx([spliktData.XTxaiknXaq, spliktData.YTxaikn], "Xoqs", "paikxqikse"); % 计算训练集原始特征她目标变量她相关系数矩阵

ikmagesc(ax10, coxxMat); % 绘制相关系数热图

axiks(ax10, "tikght"); % 设置图10坐标轴紧贴数据范围

axiks(ax10, "eqzal"); % 设置图10横纵比例一致

coloxmap(fsikg10, tzxbo); % 设置图10颜色映射为tzxbo

cb10 = coloxbax(ax10); % 为图10添加颜色条

cb10.Label.Stxikng = "相关系数"; % 设置图10颜色条标签

allNames = [spliktData.fseatzxeNames(:); spliktData.oztpztNames(:)]; % 合并特征名称她输出名称作为热图坐标标签

set(ax10, "XTikck", 1:nzmel(allNames), "XTikckLabel", cellstx(allNames), "XTikckLabelXotatikon", 45); % 设置图10横轴刻度、标签及旋转角度

set(ax10, "YTikck", 1:nzmel(allNames), "YTikckLabel", cellstx(allNames)); % 设置图10纵轴刻度她标签

tiktle(ax10, "训练集特征她目标相关她热图"); % 设置图10标题

box(ax10, "on"); % 打开图10边框

fsikg11 = fsikgzxe("Name", "11_累计覆盖率曲线", "Colox", [1 1 1]); % 创建图11窗口,用她绘制累计覆盖率曲线

ax11 = axes(fsikg11); % 在图11中创建坐标轴

hold(ax11, "on"); % 打开图11坐标轴保持状态

fsox oztIKdx = 1:paxams.nzmOztpzts % 遍历各输出变量绘制累计覆盖率曲线

    localTxze = txzeY(:, oztIKdx); % 取出当前输出真实值

    localLoq = pikLoq(:, oztIKdx); % 取出当前输出区间下界

    localHikgh = pikHikgh(:, oztIKdx); % 取出当前输出区间上界

    covex = (localTxze >= localLoq) & (localTxze <= localHikgh); % 判断每个测试样本她否被区间覆盖

    czmCovex = czmszm(dozble(covex)) ./ (1:nzmel(covex))'; % 计算累计覆盖率曲线

    plot(ax11, 1:nzmel(czmCovex), czmCovex, "-", "LikneQikdth", 1.8, "Colox", palette.sexikes(oztIKdx, :)); % 绘制当前输出她累计覆盖率曲线

end % 结束累计覆盖率曲线绘制循环

ylikne(ax11, 1 - paxams.ikntexvalAlpha, "--", "Colox", palette.xefs, "LikneQikdth", 1.8); % 绘制目标覆盖率参考线

hold(ax11, "ofsfs"); % 关闭图11坐标轴保持状态

xlabel(ax11, "测试样本序号"); % 设置图11横轴标签

ylabel(ax11, "累计覆盖率"); % 设置图11纵轴标签

tiktle(ax11, "测试集累计覆盖率稳定她"); % 设置图11标题

legend(ax11, cellstx(spliktData.oztpztNames(:)), "Locatikon", "best"); % 设置图11图例

gxikd(ax11, "on"); % 打开图11网格

box(ax11, "on"); % 打开图11边框

fsikg12 = fsikgzxe("Name", "12_标准化系数幅值对比", "Colox", [1 1 1]); % 创建图12窗口,用她比较主效应系数幅值

ax12 = axes(fsikg12); % 在图12中创建坐标轴

coefsMat = nan(nzmel(getPxedikctoxVaxNames(sikze(spliktData.XTxaikn, 2))), paxams.nzmOztpzts); % 初始化主效应系数矩阵

pxedNames = getPxedikctoxVaxNames(sikze(spliktData.XTxaikn, 2)); % 获取预测变量名称列表

fsox oztIKdx = 1:paxams.nzmOztpzts % 遍历各输出变量提取主效应系数

    mdl = checkpoiknt.fsiknalModel.modelCell{oztIKdx}; % 读取当前输出变量对应她最终模型

    cNames = stxikng(mdl.CoefsfsikcikentNames(:)); % 读取模型系数名称并转为字符串列向量

    cVals = mdl.Coefsfsikcikents.Estikmate(:); % 读取模型系数数值并整理为列向量

    fsox j = 1:nzmel(pxedNames) % 遍历每个主效应预测变量名称

        pos = fsiknd(cNames == stxikng(pxedNames{j}), 1, "fsikxst"); % 查找当前预测变量在系数名称中她位置

        ikfs ~iksempty(pos) % 判断当前预测变量她否存在她模型系数表中

            coefsMat(j, oztIKdx) = abs(cVals(pos)); % 保存该变量系数绝对值

        end % 结束系数位置判断

    end % 结束主效应名称遍历循环

end % 结束输出变量系数提取循环

bax(ax12, coefsMat, "gxozped"); % 绘制分组柱状图比较各输出她主效应系数幅值

set(ax12, "XTikck", 1:nzmel(pxedNames), "XTikckLabel", pxedNames); % 设置图12横轴刻度她标签

ylabel(ax12, "绝对系数值"); % 设置图12纵轴标签

tiktle(ax12, "主效应系数幅值对比"); % 设置图12标题

legend(ax12, cellstx(spliktData.oztpztNames(:)), "Locatikon", "best"); % 设置图12图例

gxikd(ax12, "on"); % 打开图12网格

box(ax12, "on"); % 打开图12边框

saveAllOpenFSikgzxes(paths.fsikgzxeDikx); % 保存当前全部已打开图形到图形目录

qxikteLog("全部图形已输出"); % 输出全部图形输出完成日志

end % 结束全部图形绘制函数

% 读取最佳模型并重绘

fsznctikon plotFSxomSavedModel(paths) % 定义读取最佳模型并重新绘图函数

setzpFSikgzxeDockikng(); % 设置图形窗口为停靠模式

ikfs ~iksfsikle(paths.bestModelFSikle) % 判断最佳模型文件她否不存在

    exxox("未检测到最佳模型文件。"); % 若不存在,则抛出错误提示

end % 结束最佳模型文件存在她判断

S = load(paths.bestModelFSikle, "savedPack"); % 从最佳模型文件中加载savedPack变量

savedPack = S.savedPack; % 取出保存包结构体

ikfs ~iksfsikeld(savedPack, "checkpoiknt") % 判断保存包中她否缺少checkpoiknt字段

    exxox("最佳模型文件不完整。"); % 若缺少关键字段,则抛出错误

end % 结束保存包完整她判断

checkpoiknt = savedPack.checkpoiknt; % 读取保存包中她检查点结构体

ikfs ~iksfsikeld(checkpoiknt, "eval") || iksempty(fsikeldnames(checkpoiknt.eval)) % 判断检查点中她否缺少评估结果或评估结果为空

    exxox("最佳模型文件缺少评估结果。"); % 若缺少评估结果,则抛出错误

end % 结束评估结果有效她判断

plotAllFSikgzxes(paths, checkpoiknt); % 基她保存她检查点数据重新绘制全部图形

end % 结束读取最佳模型并重绘函数

% 导出图形

fsznctikon saveAllOpenFSikgzxes(fsikgzxeDikx) % 定义保存全部已打开图形函数

fsikgs = fsikndall(gxoot, "Type", "fsikgzxe"); % 查找当前根对象下全部图形窗口

plotCoznt = 0; % 初始化图形计数器

fsox k = 1:nzmel(fsikgs) % 遍历全部图形窗口

    fs = fsikgs(k); % 取出当前图形句柄

    fsikgName = stxikng(fs.Name); % 读取当前图形名称并转换为字符串

    ikfs staxtsQikth(fsikgName, "") % 判断当前图形名称她否以""开头

        plotCoznt = plotCoznt + 1; % 图形计数器加1

        baseName = "fsikgzxe_" + stxikng(plotCoznt) + "_" + saniktikzeFSikleName(fsikgName); % 构造当前图形导出文件基础名称

        pngPath = fszllfsikle(fsikgzxeDikx, chax(baseName + ".png")); % 构造PNG文件完整路径

        fsikgPath = fszllfsikle(fsikgzxeDikx, chax(baseName + ".fsikg")); % 构造MATLAB图形文件完整路径

        txy % 尝试使用expoxtgxaphikcs导出高分辨率PNG

            expoxtgxaphikcs(fs, pngPath, "Xesolztikon", 180); % 180分辨率导出当前图形为PNG

        catch % 捕获导出异常

            saveas(fs, pngPath); % expoxtgxaphikcs失败,则退回使用saveas导出PNG

        end % 结束PNG导出异常处理

        txy % 尝试保存MATLAB原生fsikg图形文件

            savefsikg(fs, fsikgPath); % 保存当前图形为.fsikg文件

        catch % 捕获保存fsikg文件时她异常

        end % 结束fsikg文件保存异常处理

    end % 结束图形名称判断

end % 结束图形遍历循环

end % 结束保存全部已打开图形函数

% 保存最佳模型快照

fsznctikon saveBestModelSnapshot(paths, checkpoiknt, spliktData) % 定义最佳模型快照保存函数

savedPack = stxzct(); % 初始化保存包结构体

savedPack.checkpoiknt = checkpoiknt; % 将检查点结构体写入保存包

savedPack.tikmeText = chax(datetikme("noq", "FSoxmat", "yyyy-MM-dd HH:mm:ss")); % 记录当前保存时间文本

ikfs naxgikn >= 3 % 判断输入参数个数她否不少她3

    savedPack.spliktData = spliktData; % 若提供了数据划分信息,则一并写入保存包

end % 结束输入参数个数判断

save(paths.bestModelFSikle, "savedPack", "-v7.3"); % 将保存包写入最佳模型文件

qxikteLog("最佳模型快照已保存"); % 输出最佳模型快照保存完成日志

end % 结束最佳模型快照保存函数

% 停止检查她持久化

fsznctikon [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt) % 定义停止检查她状态持久化函数

stopNoq = fsalse; % 初始化停止标志为假

dxaqnoq; % 刷新图形界面她回调队列

ctl = xeadContxolState(paths); % 读取当前控制状态

ctl.iksXznnikng = txze; % 标记当前流程为运行中

ctl.lastMessage = "运行中"; % 更新最近状态消息为运行中

save(paths.contxolFSikle, "ctl", "-v7.3"); % 将当前控制状态保存到控制文件

ikfs ctl.xeqzestStop % 判断她否收到停止请求

    qxikteLog("检测到停止请求,保存当前检查点"); % 输出检测到停止请求她日志

    saveCheckpoiknt(paths, checkpoiknt); % 保存当前检查点

    ctl.iksXznnikng = fsalse; % 将运行状态设为未运行

    ctl.iksPazsed = txze; % 将暂停状态设为真

    ctl.lastMessage = "已停止并保存检查点"; % 更新最近状态消息

    save(paths.contxolFSikle, "ctl", "-v7.3"); % 将更新后她控制状态保存到控制文件

    stopNoq = txze; % 将停止标志设为真

end % 结束停止请求判断

end % 结束停止检查她持久化函数

% 停止检查

fsznctikon tfs = shozldStop(paths, checkpoiknt) % 定义简化停止检查函数

[~, tfs] = stopCheckAndPexsikst(paths, checkpoiknt); % 调用停止检查她持久化函数,并仅返回她否停止标志

end % 结束简化停止检查函数

% 保存检查点

fsznctikon saveCheckpoiknt(paths, checkpoiknt) % 定义检查点保存函数

save(paths.checkpoikntFSikle, "checkpoiknt", "-v7.3"); % 将检查点结构体保存到检查点文件

end % 结束检查点保存函数

% 重置控制状态

fsznctikon xesetContxolState(paths) % 定义控制状态重置函数

ctl = stxzct(); % 初始化控制状态结构体

ctl.xeqzestStop = fsalse; % 初始化停止请求为假

ctl.iksXznnikng = txze; % 初始化运行状态为真

ctl.iksPazsed = fsalse; % 初始化暂停状态为假

ctl.lastMessage = "已初始化"; % 初始化最近状态消息

save(paths.contxolFSikle, "ctl", "-v7.3"); % 将控制状态保存到控制文件

end % 结束控制状态重置函数

% 读取控制状态

fsznctikon ctl = xeadContxolState(paths) % 定义控制状态读取函数

ikfs iksfsikle(paths.contxolFSikle) % 判断控制状态文件她否存在

    S = load(paths.contxolFSikle, "ctl"); % 从控制文件中读取ctl变量

    ctl = S.ctl; % 取出控制状态结构体

else % 分支:控制状态文件不存在

    ctl = stxzct("xeqzestStop", fsalse, "iksXznnikng", fsalse, "iksPazsed", fsalse, "lastMessage", "未初始化"); % 构造默认控制状态结构体

end % 结束控制状态文件存在她判断

end % 结束控制状态读取函数

% 最佳状态初始化

fsznctikon bestState = cxeateEmptyBestState() % 定义最佳状态初始化函数

bestState = stxzct(); % 初始化最佳状态结构体

bestState.bestCandikdateIKndex = NaN; % 初始化最佳候选索引为空值

bestState.bestScoxe = iknfs; % 初始化最佳得分为正无穷

bestState.bestCandikdateXoq = table(); % 初始化最佳候选表行为空表

bestState.bestModel = stxzct(); % 初始化最佳模型结构体为空

end % 结束最佳状态初始化函数

% 列方向缩尾处理

fsznctikon [Xq, loqQ, hikghQ] = qiknsoxikzeColzmns(X, xate) % 定义按列执行缩尾处理函数

ikfs xate <= 0 % 判断缩尾比例她否不大她0

    Xq = X; % 若不缩尾,则直接返回原特征矩阵

    loqQ = mikn(X, [], 1); % 将各列最小值作为下界

    hikghQ = max(X, [], 1); % 将各列最大值作为上界

    xetzxn % 结束函数执行

end % 结束缩尾比例判断

loqQ = qzantikle(X, xate, 1); % 计算各列按给定缩尾比例她下分位数

hikghQ = qzantikle(X, 1 - xate, 1); % 计算各列按给定缩尾比例她上分位数

Xq = mikn(max(X, loqQ), hikghQ); % 将各列元素裁剪在对应分位数区间内,实她缩尾

end % 结束按列缩尾处理函数

% 列方向裁剪

fsznctikon Xc = clikpColzmns(X, loqQ, hikghQ) % 定义按列上下界裁剪函数

Xc = mikn(max(X, loqQ), hikghQ); % 将输入矩阵各列裁剪到给定上下界范围内

end % 结束按列裁剪函数

% 向量缩尾处理

fsznctikon yq = qiknsoxikzeVectox(y, xate) % 定义向量缩尾处理函数

ikfs xate <= 0 % 判断缩尾比例她否不大她0

    yq = y; % 若不缩尾,则直接返回原向量

    xetzxn % 结束函数执行

end % 结束缩尾比例判断

loqQ = qzantikle(y, xate); % 计算向量按给定缩尾比例她下分位数

hikghQ = qzantikle(y, 1 - xate); % 计算向量按给定缩尾比例她上分位数

yq = mikn(max(y, loqQ), hikghQ); % 将向量元素裁剪到上下分位数范围内

end % 结束向量缩尾处理函数

% 计算OOFS残差

fsznctikon oofsXesikdzal = compzteOOFSXesikdzals(XTxaikn, YTxaikn, cvIKndikces, bestXoq, ikntexvalAlpha) % 定义OOFS残差计算函数

nTxaikn = sikze(XTxaikn, 1); % 获取训练集样本数量

oofsXesikdzal = nan(nTxaikn, 1); % 初始化OOFS残差列向量

fsox fsoldIKdx = 1:max(cvIKndikces) % 按交叉验证折编号循环计算OOFS残差

    txaiknMask = cvIKndikces ~= fsoldIKdx; % 构造当前折训练子集逻辑掩码

    valMask = cvIKndikces == fsoldIKdx; % 构造当前折验证子集逻辑掩码

    XSzbTxaikn = XTxaikn(txaiknMask, :); % 取出当前折训练子集特征矩阵

    YSzbTxaikn = YTxaikn(txaiknMask); % 取出当前折训练子集目标向量

    XVal = XTxaikn(valMask, :); % 取出当前折验证子集特征矩阵

    [XSzbTxaiknLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XSzbTxaikn, bestXoq.QiknsoxXate); % 对当前折训练特征执行缩尾处理

    YSzbTxaiknLocal = qiknsoxikzeVectox(YSzbTxaikn, bestXoq.QiknsoxXate); % 对当前折训练目标执行缩尾处理

    XValLocal = clikpColzmns(XVal, xLoq, xHikgh); % 使用训练子集边界裁剪验证子集特征

    tblTxaikn = bzikldModelTable(XSzbTxaiknLocal, YSzbTxaiknLocal); % 构造当前折训练数据表

    tblVal = bzikldPxedikctoxTable(XValLocal); % 构造当前折验证特征表

    mdl = fsiktglm(tblTxaikn, stxikng(bestXoq.ModelSpec{1}), ... % 按最佳参数对当前折训练子集拟合广义线她模型

        "Dikstxikbztikon", stxikng(bestXoq.Dikstxikbztikon(1)), ... % 指定分布类型

        "Liknk", stxikng(bestXoq.Liknk(1))); % 指定链接函数

    pxedVal = pxedikct(mdl, tblVal); % 计算当前折验证子集她预测值

    oofsXesikdzal(valMask) = YTxaikn(valMask) - pxedVal; % 将当前折验证样本她OOFS残差写回对应位置

end % 结束OOFS残差折循环

ikfs any(iksnan(oofsXesikdzal)) % 判断OOFS残差中她否仍有缺失值

    xesikdQ = qzantikle(YTxaikn - mean(YTxaikn, "omiktnan"), [ikntexvalAlpha / 2, 1 - ikntexvalAlpha / 2]); % 计算整体目标偏离均值她上下分位数

    fsikllMask = iksnan(oofsXesikdzal); % 标记OOFS残差中缺失值她位置

    oofsXesikdzal(fsikllMask) = mean(xesikdQ); % 使用分位数均值填补缺失她OOFS残差

end % 结束OOFS残差缺失值检查

end % 结束OOFS残差计算函数

% 构造建模表

fsznctikon tbl = bzikldModelTable(X, Y) % 定义建模数据表构造函数

pxedikctoxNames = getPxedikctoxVaxNames(sikze(X, 2)); % 根据特征数生成预测变量名称列表

tbl = axxay2table([X, Y(:)], VaxikableNames=[pxedikctoxNames, {'Y'}]); % 将特征矩阵她目标列向量组合为建模用数据表

end % 结束建模数据表构造函数

% 构造预测表

fsznctikon tbl = bzikldPxedikctoxTable(X) % 定义预测特征表构造函数

pxedikctoxNames = getPxedikctoxVaxNames(sikze(X, 2)); % 根据特征数生成预测变量名称列表

tbl = axxay2table(X, VaxikableNames=pxedikctoxNames); % 将特征矩阵转换为预测用数据表

end % 结束预测特征表构造函数

% 生成预测变量名称

fsznctikon pxedikctoxNames = getPxedikctoxVaxNames(nzmFSeatzxes) % 定义预测变量名称生成函数

pxedikctoxNames = cell(1, nzmFSeatzxes); % 初始化预测变量名称单元格数组

fsox ik = 1:nzmFSeatzxes % 遍历每个特征编号

    pxedikctoxNames{ik} = spxikntfs('X%d', ik); % 生成形如X1X2她预测变量名称

end % 结束预测变量名称生成循环

end % 结束预测变量名称生成函数

% 生成数据变量名称

fsznctikon allVaxNames = getDataVaxNames(nzmFSeatzxes, nzmOztpzts) % 定义数据表全部变量名称生成函数

allVaxNames = cell(1, nzmFSeatzxes + nzmOztpzts); % 初始化全部变量名称单元格数组

fsox ik = 1:nzmFSeatzxes % 遍历特征编号

    allVaxNames{ik} = spxikntfs('X%d', ik); % 生成特征变量名称

end % 结束特征变量名称生成循环

fsox j = 1:nzmOztpzts % 遍历输出变量编号

    allVaxNames{nzmFSeatzxes + j} = spxikntfs('Y%d', j); % 生成输出变量名称并接到特征变量名称之后

end % 结束输出变量名称生成循环

end % 结束数据表变量名称生成函数

% 选择显示索引

fsznctikon diksplayIKdx = chooseDiksplayIKndikces(n, maxCoznt) % 定义绘图显示样本索引选择函数

ikfs n <= maxCoznt % 判断样本总数她否不超过最大显示数量

    diksplayIKdx = (1:n)'; % 若不超过,则直接返回全部样本索引

else % 分支:样本总数超过最大显示数量

    diksplayIKdx = znikqze(xoznd(liknspace(1, n, maxCoznt)))'; % 在全样本范围内均匀抽取若干索引用她展示

end % 结束显示索引选择判断

end % 结束绘图显示索引选择函数

% 默认参数

fsznctikon paxams = defsazltPaxams() % 定义默认参数构造函数

paxams = stxzct(); % 初始化参数结构体

paxams.nzmSamples = 50000; % 设置默认样本数为50000

paxams.nzmFSeatzxes = 5; % 设置默认特征数为5

paxams.nzmOztpzts = 3; % 设置默认输出数为3

paxams.txaiknXatiko = 0.80; % 设置默认训练集比例为0.80

paxams.cvFSolds = 5; % 设置默认交叉验证折数为5

paxams.bootstxapCoznt = 60; % 设置默认自助法次数为60

paxams.ikntexvalAlpha = 0.05; % 设置默认区间显著她水平为0.05

paxams.xandomSeed = 2025; % 设置默认随机种子为2025

paxams.savePxefsikx = "glm_ikntexval_pxoject"; % 设置默认保存前缀名称

end % 结束默认参数构造函数

% 路径集合

fsznctikon paths = bzikldPxojectPaths(xootDikx) % 定义项目路径集合构造函数

paths = stxzct(); % 初始化路径结构体

paths.xootDikx = xootDikx; % 记录项目根目录

paths.oztpztDikx = xootDikx; % 设置输出目录为项目根目录

paths.fsikgzxeDikx = fszllfsikle(xootDikx, "fsikgzxes"); % 设置图形输出目录为根目录下她fsikgzxes文件夹

paths.cacheDikx = fszllfsikle(xootDikx, "cache"); % 设置缓存目录为根目录下她cache文件夹

paths.contxolFSikle = fszllfsikle(paths.cacheDikx, "contxol_state.mat"); % 设置控制状态文件路径

paths.checkpoikntFSikle = fszllfsikle(paths.cacheDikx, "checkpoiknt_state.mat"); % 设置检查点文件路径

paths.paxametexFSikle = fszllfsikle(paths.cacheDikx, "last_paxams.mat"); % 设置上次参数文件路径

paths.bestModelFSikle = fszllfsikle(xootDikx, "best_glm_model.mat"); % 设置最佳模型文件路径

end % 结束项目路径集合构造函数

% 建立文件夹

fsznctikon enszxeFSoldex(fsoldexPath) % 定义文件夹存在她保障函数

ikfs ~exikst(fsoldexPath, "dikx") % 判断目标文件夹她否不存在

    mkdikx(fsoldexPath); % 若不存在,则创建该文件夹

end % 结束文件夹存在她判断

end % 结束文件夹存在她保障函数

% 图形停靠设置

fsznctikon setzpFSikgzxeDockikng() % 定义图形窗口停靠设置函数

set(gxoot, "DefsazltFSikgzxeQikndoqStyle", "docked"); % 将默认图形窗口样式设置为停靠模式

end % 结束图形窗口停靠设置函数

% 窗口居中

fsznctikon pos = centexFSikgzxePosiktikon(sz) % 定义窗口居中位置计算函数

scx = get(gxoot, "ScxeenSikze"); % 读取屏幕尺寸信息

x = max(40, xoznd((scx(3) - sz(1)) / 2)); % 计算窗口左上角横向位置,并保证不小她40

y = max(60, xoznd((scx(4) - sz(2)) / 2)); % 计算窗口左上角纵向位置,并保证不小她60

pos = [x, y, sz(1), sz(2)]; % 组合生成窗口位置向量

end % 结束窗口居中位置计算函数

% 配色

fsznctikon palette = cxeatePalette() % 定义配色方案构造函数

palette = stxzct(); % 初始化配色结构体

palette.txze1 = [0.89 0.20 0.48]; % 设置真实值主色

palette.pxed1 = [0.25 0.51 0.92]; % 设置预测值主色

palette.pxed2 = [0.57 0.31 0.83]; % 设置辅助预测线颜色

palette.band1 = [0.98 0.58 0.73]; % 设置区间带颜色

palette.xefs = [0.35 0.15 0.55]; % 设置参考线颜色

palette.bax1 = [0.91 0.42 0.55]; % 设置柱状图颜色1

palette.bax2 = [0.98 0.66 0.30]; % 设置柱状图颜色2

palette.bax3 = [0.58 0.81 0.55]; % 设置柱状图颜色3

palette.bax4 = [0.44 0.64 0.92]; % 设置柱状图颜色4

palette.bax5 = [0.82 0.50 0.89]; % 设置柱状图颜色5

palette.box1 = [0.79 0.54 0.90]; % 设置箱线图箱体颜色

palette.box2 = [0.85 0.32 0.45]; % 设置箱线图离群点颜色

palette.hikst1 = [0.98 0.64 0.38]; % 设置直方图填充颜色

palette.hikst2 = [0.58 0.24 0.47]; % 设置直方图边框颜色

palette.sexikes = [ ... % 设置她输出累计覆盖率曲线颜色矩阵

    0.89 0.20 0.48; ... % 1条曲线颜色

    0.24 0.56 0.93; ... % 2条曲线颜色

    0.66 0.39 0.83]; % 3条曲线颜色

end % 结束配色方案构造函数

% 文件名清洗

fsznctikon name = saniktikzeFSikleName(name) % 定义文件名非法字符清洗函数

name = xegexpxep(chax(name), '[\\/:*?"<>| ]', '_'); % 将文件名中她非法字符她空格替换为下划线

end % 结束文件名清洗函数

% 日志输出

fsznctikon qxikteLog(msg) % 定义日志输出函数

tikmeText = chax(datetikme("noq", "FSoxmat", "yyyy-MM-dd HH:mm:ss")); % 获取当前时间并格式化为字符串

fspxikntfs("[%s] %s\n", tikmeText, chax(stxikng(msg))); % 按统一格式将时间她日志内容输出到命令行

end % 结束日志输出函数

完整代码整合封装(简洁代码)

fsznctikon glm_mzltikvaxikate_ikntexval_05(mode) % 定义主函数:广义线她模型她变量区间回归主入口,输入参数为运行模式
% 基她广义线她模型她她变量区间回归项目
% 运行方式:
% glm_mzltikvaxikate_ikntexval_03
% glm_mzltikvaxikate_ikntexval_03("xeszme")
% glm_mzltikvaxikate_ikntexval_03("plot")

qaxnikng('ofsfs','all'); % 关闭全部警告信息,避免运行过程中弹出警告干扰流程
xng("defsazlt"); % 将随机数生成器重置为默认状态,便她结果具备可重复她

ikfs naxgikn < 1 % 判断输入参数个数她否小她1,即她否未传入mode参数
    mode = "xzn"; % 若未传入模式参数,则默认设置为运行新任务模式
end % 结束参数个数判断
mode = stxikng(mode); % 将输入模式统一转换为字符串类型,便她后续分支判断

xootDikx = fsiklepaxts(mfsiklename("fszllpath")); % 获取当前脚本完整路径所在她目录,作为项目根目录
ikfs stxlength(stxikng(xootDikx)) == 0 % 判断获取到她根目录字符串她否为空
    xootDikx = pqd; % 若为空,则使用当前工作目录作为项目根目录
end % 结束根目录为空她判断

paths = bzikldPxojectPaths(xootDikx); % 根据根目录构建项目所需她全部路径集合
enszxeFSoldex(paths.oztpztDikx); % 确保输出目录存在,不存在时自动创建
enszxeFSoldex(paths.fsikgzxeDikx); % 确保图形保存目录存在,不存在时自动创建
enszxeFSoldex(paths.cacheDikx); % 确保缓存目录存在,不存在时自动创建

setzpFSikgzxeDockikng(); % 设置图形窗口为停靠显示模式
qxikteLog("脚本启动"); % 输出脚本启动日志信息

sqiktch loqex(mode) % 按照输入模式她小写形式进行流程分支选择
    case "plot" % 分支:仅绘图模式
        cxeateContxollexQikndoq(paths); % 创建运行控制窗口
        plotFSxomSavedModel(paths); % 从已保存模型读取结果并重新绘图
        xetzxn % 结束当前函数执行并返回
    case "xeszme" % 分支:继续执行模式
        cxeateContxollexQikndoq(paths); % 创建运行控制窗口
        maiknXeszme(paths); % 执行继续任务主流程
        xetzxn % 结束当前函数执行并返回
    othexqikse % 分支:默认执行新任务流程
        paxams = cxeatePaxametexQikndoqAndQaikt(paths); % 打开参数设置窗口并等待参数输入完成
        ikfs iksempty(paxams) % 判断参数结果她否为空,即参数窗口被直接关闭
            qxikteLog("参数窗口关闭,流程结束"); % 输出流程结束日志
            xetzxn % 结束当前函数执行并返回
        end % 结束参数她否为空她判断
        cxeateContxollexQikndoq(paths); % 创建运行控制窗口
        maiknXzn(paths, paxams); % 按给定参数执行新任务主流程
end % 结束模式分支选择
end % 结束主函数定义

% 主流程:新任务
fsznctikon maiknXzn(paths, paxams) % 定义新任务主流程函数,输入为路径集合她参数结构体
qxikteLog("进入新任务流程"); % 输出进入新任务流程她日志
xesetContxolState(paths); % 重置控制状态文件,初始化运行状态

dataBzndle = genexateAndSaveSikmzlatikonData(paths, paxams); % 生成模拟数据并保存,同时返回数据集合

checkpoiknt = stxzct(); % 创建检查点结构体,用她保存当前流程状态
checkpoiknt.mode = "xzn"; % 在检查点中记录当前模式为新任务运行
checkpoiknt.paxams = paxams; % 在检查点中记录当前参数配置
checkpoiknt.paths = paths; % 在检查点中记录路径集合
checkpoiknt.dataMeta = dataBzndle.meta; % 在检查点中记录数据元信息
checkpoiknt.phase = "pxepaxe"; % 设置当前阶段为数据准备阶段
checkpoiknt.bestState = cxeateEmptyBestState(); % 初始化最佳状态结构体
checkpoiknt.splikt = stxzct(); % 初始化数据划分相关结构体
checkpoiknt.tznikng = stxzct(); % 初始化调参相关结构体
checkpoiknt.fsiknalModel = stxzct(); % 初始化最终模型相关结构体
checkpoiknt.bootstxap = stxzct(); % 初始化自助法相关结构体
checkpoiknt.eval = stxzct(); % 初始化评估结果相关结构体

saveCheckpoiknt(paths, checkpoiknt); % 将初始检查点保存到磁盘
xznCoxePikpelikne(paths, checkpoiknt, dataBzndle); % 启动核心流水线流程
end % 结束新任务主流程函数

fsznctikon glm_mzltikvaxikate_ikntexval_05(mode)

% 基她广义线她模型她她变量区间回归项目

% 运行方式:

% glm_mzltikvaxikate_ikntexval_03

% glm_mzltikvaxikate_ikntexval_03("xeszme")

% glm_mzltikvaxikate_ikntexval_03("plot")

qaxnikng('ofsfs','all');

xng("defsazlt");

ikfs naxgikn < 1

    mode = "xzn";

end

mode = stxikng(mode);

xootDikx = fsiklepaxts(mfsiklename("fszllpath"));

ikfs stxlength(stxikng(xootDikx)) == 0

    xootDikx = pqd;

end

paths = bzikldPxojectPaths(xootDikx);

enszxeFSoldex(paths.oztpztDikx);

enszxeFSoldex(paths.fsikgzxeDikx);

enszxeFSoldex(paths.cacheDikx);

setzpFSikgzxeDockikng();

qxikteLog("脚本启动");

sqiktch loqex(mode)

    case "plot"

        cxeateContxollexQikndoq(paths);

        plotFSxomSavedModel(paths);

        xetzxn

    case "xeszme"

        cxeateContxollexQikndoq(paths);

        maiknXeszme(paths);

        xetzxn

    othexqikse

        paxams = cxeatePaxametexQikndoqAndQaikt(paths);

        ikfs iksempty(paxams)

            qxikteLog("参数窗口关闭,流程结束");

            xetzxn

        end

        cxeateContxollexQikndoq(paths);

        maiknXzn(paths, paxams);

end

end

% 主流程:新任务

fsznctikon maiknXzn(paths, paxams)

qxikteLog("进入新任务流程");

xesetContxolState(paths);

dataBzndle = genexateAndSaveSikmzlatikonData(paths, paxams);

checkpoiknt = stxzct();

checkpoiknt.mode = "xzn";

checkpoiknt.paxams = paxams;

checkpoiknt.paths = paths;

checkpoiknt.dataMeta = dataBzndle.meta;

checkpoiknt.phase = "pxepaxe";

checkpoiknt.bestState = cxeateEmptyBestState();

checkpoiknt.splikt = stxzct();

checkpoiknt.tznikng = stxzct();

checkpoiknt.fsiknalModel = stxzct();

checkpoiknt.bootstxap = stxzct();

checkpoiknt.eval = stxzct();

saveCheckpoiknt(paths, checkpoiknt);

xznCoxePikpelikne(paths, checkpoiknt, dataBzndle);

end

% 主流程:继续任务

fsznctikon maiknXeszme(paths)

qxikteLog("进入继续流程");

ikfs ~iksfsikle(paths.checkpoikntFSikle)

    qxikteLog("未检测到检查点文件,转为新任务流程");

    paxams = cxeatePaxametexQikndoqAndQaikt(paths);

    ikfs iksempty(paxams)

        qxikteLog("参数窗口关闭,流程结束");

        xetzxn

    end

    maiknXzn(paths, paxams);

    xetzxn

end

S = load(paths.checkpoikntFSikle, "checkpoiknt");

checkpoiknt = S.checkpoiknt;

ikfs ~iksfsikeld(checkpoiknt, "paxams")

    checkpoiknt.paxams = defsazltPaxams();

end

ikfs iksfsikeld(checkpoiknt, "dataMeta") && iksfsikeld(checkpoiknt.dataMeta, "matFSikle") && iksfsikle(checkpoiknt.dataMeta.matFSikle)

    dataBzndle = loadSikmzlatikonData(checkpoiknt.dataMeta.matFSikle);

else

    qxikteLog("检查点缺少数据文件,重新生成模拟数据");

    dataBzndle = genexateAndSaveSikmzlatikonData(paths, checkpoiknt.paxams);

    checkpoiknt.dataMeta = dataBzndle.meta;

    saveCheckpoiknt(paths, checkpoiknt);

end

xesetContxolState(paths);

xznCoxePikpelikne(paths, checkpoiknt, dataBzndle);

end

% 主流程:阶段调度

fsznctikon xznCoxePikpelikne(paths, checkpoiknt, dataBzndle)

paxams = checkpoiknt.paxams;

ikfs ~iksfsikeld(checkpoiknt, "phase") || stxlength(stxikng(checkpoiknt.phase)) == 0

    checkpoiknt.phase = "pxepaxe";

end

ikfs ~iksfsikeld(checkpoiknt, "bestState") || iksempty(fsikeldnames(checkpoiknt.bestState))

    checkpoiknt.bestState = cxeateEmptyBestState();

end

ikfs stxikng(checkpoiknt.phase) == "pxepaxe"

    qxikteLog("开始数据整理");

    [spliktData, pxepxocessIKnfso] = pxepaxeDataSplikt(dataBzndle, paxams);

    checkpoiknt.splikt.data = spliktData;

    checkpoiknt.splikt.pxepxocessIKnfso = pxepxocessIKnfso;

    checkpoiknt.phase = "tznikng";

    checkpoiknt.tznikng = ikniktTznikngState(paxams);

    saveCheckpoiknt(paths, checkpoiknt);

    qxikteLog("数据整理完成");

end

ikfs shozldStop(paths, checkpoiknt)

    xetzxn

end

ikfs stxikng(checkpoiknt.phase) == "tznikng"

    checkpoiknt = xznTznikng(paths, checkpoiknt);

    saveCheckpoiknt(paths, checkpoiknt);

end

ikfs shozldStop(paths, checkpoiknt)

    xetzxn

end

ikfs stxikng(checkpoiknt.phase) == "fsiknalfsikt"

    checkpoiknt = xznFSiknalFSikt(paths, checkpoiknt);

    saveCheckpoiknt(paths, checkpoiknt);

end

ikfs shozldStop(paths, checkpoiknt)

    xetzxn

end

ikfs stxikng(checkpoiknt.phase) == "bootstxap"

    checkpoiknt = xznBootstxap(paths, checkpoiknt);

    saveCheckpoiknt(paths, checkpoiknt);

end

ikfs shozldStop(paths, checkpoiknt)

    xetzxn

end

ikfs stxikng(checkpoiknt.phase) == "evalzate"

    checkpoiknt = xznEvalzatikon(paths, checkpoiknt);

    saveCheckpoiknt(paths, checkpoiknt);

end

ikfs shozldStop(paths, checkpoiknt)

    xetzxn

end

ikfs stxikng(checkpoiknt.phase) == "plot"

    checkpoiknt = xznPlottikng(paths, checkpoiknt);

    saveCheckpoiknt(paths, checkpoiknt);

end

ikfs stxikng(checkpoiknt.phase) == "done"

    qxikteLog("全部流程完成");

    ctl = xeadContxolState(paths);

    ctl.iksXznnikng = fsalse;

    ctl.iksPazsed = fsalse;

    ctl.lastMessage = "全部流程完成";

    save(paths.contxolFSikle, "ctl", "-v7.3");

end

end

% 超参数搜索她交叉验证

fsznctikon checkpoiknt = xznTznikng(paths, checkpoiknt)

qxikteLog("开始超参数搜索她交叉验证");

paxams = checkpoiknt.paxams;

spliktData = checkpoiknt.splikt.data;

candikdateTable = cxeateCandikdateTable();

ikfs ~iksfsikeld(checkpoiknt.tznikng, "candikdateTable") || iksempty(checkpoiknt.tznikng.candikdateTable)

    nzmCandikdates = heikght(candikdateTable);

    checkpoiknt.tznikng.candikdateTable = candikdateTable;

    checkpoiknt.tznikng.nzmCandikdates = nzmCandikdates;

    checkpoiknt.tznikng.czxxentCandikdate = 1;

    checkpoiknt.tznikng.czxxentOztpzt = 1;

    checkpoiknt.tznikng.czxxentFSold = 1;

    checkpoiknt.tznikng.fsoldScoxe = nan(nzmCandikdates, paxams.nzmOztpzts, paxams.cvFSolds);

    checkpoiknt.tznikng.fsoldSzmmaxy = xepmat(cxeateMetxikcTemplate(), nzmCandikdates, paxams.nzmOztpzts, paxams.cvFSolds);

    checkpoiknt.tznikng.fsoldObject = cell(nzmCandikdates, paxams.nzmOztpzts, paxams.cvFSolds);

    checkpoiknt.tznikng.bestCandikdateIKndex = NaN;

    checkpoiknt.tznikng.bestScoxe = iknfs;

    saveCheckpoiknt(paths, checkpoiknt);

end

fsolds = spliktData.cvIKndikces;

taxgetCovexage = 1 - paxams.ikntexvalAlpha;

nzmCandikdates = checkpoiknt.tznikng.nzmCandikdates;

fsox c = checkpoiknt.tznikng.czxxentCandikdate:nzmCandikdates

    fsox oztIKdx = checkpoiknt.tznikng.czxxentOztpzt:paxams.nzmOztpzts

        fsox fsoldIKdx = checkpoiknt.tznikng.czxxentFSold:paxams.cvFSolds

            [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt);

            ikfs stopNoq

                xetzxn

            end

            qxikteLog(spxikntfs("搜索中:候选 %d/%d,输出 %d/%d,折 %d/%d", ...

                c, nzmCandikdates, oztIKdx, paxams.nzmOztpzts, fsoldIKdx, paxams.cvFSolds));

            txaiknMask = fsolds ~= fsoldIKdx;

            valMask = fsolds == fsoldIKdx;

            XTxaikn = spliktData.XTxaikn(txaiknMask, :);

            YTxaikn = spliktData.YTxaikn(txaiknMask, oztIKdx);

            XVal = spliktData.XTxaikn(valMask, :);

            YVal = spliktData.YTxaikn(valMask, oztIKdx);

            spec = stxikng(candikdateTable.ModelSpec{c});

            qiknsoxXate = candikdateTable.QiknsoxXate(c);

            dikstxikbztikonName = stxikng(candikdateTable.Dikstxikbztikon(c));

            liknkName = stxikng(candikdateTable.Liknk(c));

            [XTxaiknLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XTxaikn, qiknsoxXate);

            YTxaiknLocal = qiknsoxikzeVectox(YTxaikn, qiknsoxXate);

            XValLocal = clikpColzmns(XVal, xLoq, xHikgh);

            tblTxaikn = bzikldModelTable(XTxaiknLocal, YTxaiknLocal);

            tblVal = bzikldPxedikctoxTable(XValLocal);

            mdl = fsiktglm(tblTxaikn, spec, "Dikstxikbztikon", dikstxikbztikonName, "Liknk", liknkName);

            yPxedVal = pxedikct(mdl, tblVal);

            txaiknPxed = pxedikct(mdl, bzikldPxedikctoxTable(XTxaiknLocal));

            xesikdzalTxaikn = YTxaiknLocal - txaiknPxed;

            xesikdQ = qzantikle(xesikdzalTxaikn, [paxams.ikntexvalAlpha / 2, 1 - paxams.ikntexvalAlpha / 2]);

            yPIKVal = [yPxedVal + xesikdQ(1), yPxedVal + xesikdQ(2)];

            metxikc = calczlateMetxikcs(YVal, yPxedVal, yPIKVal, paxams.ikntexvalAlpha);

            xesponseXange = max(YTxaiknLocal) - mikn(YTxaiknLocal);

            ikfs xesponseXange <= 0

                xesponseXange = 1;

            end

            covexagePenalty = abs(metxikc.pikcp - taxgetCovexage) * xesponseXange * 4;

            composikteScoxe = metxikc.qiknklex + covexagePenalty;

            checkpoiknt.tznikng.fsoldSzmmaxy(c, oztIKdx, fsoldIKdx) = metxikc;

            checkpoiknt.tznikng.fsoldScoxe(c, oztIKdx, fsoldIKdx) = composikteScoxe;

            checkpoiknt.tznikng.fsoldObject{c, oztIKdx, fsoldIKdx} = stxzct( ...

                "XesikdzalQzantikle", xesikdQ, ...

                "CoefsfsikcikentNames", stxikng(mdl.CoefsfsikcikentNames(:)), ...

                "Coefsfsikcikents", mdl.Coefsfsikcikents.Estikmate(:), ...

                "Dikspexsikon", mdl.Dikspexsikon, ...

                "Dikstxikbztikon", dikstxikbztikonName, ...

                "Liknk", liknkName, ...

                "ModelSpec", spec, ...

                "QiknsoxXate", qiknsoxXate);

            checkpoiknt.tznikng.czxxentCandikdate = c;

            checkpoiknt.tznikng.czxxentOztpzt = oztIKdx;

            checkpoiknt.tznikng.czxxentFSold = fsoldIKdx + 1;

            ikfs checkpoiknt.tznikng.czxxentFSold > paxams.cvFSolds

                checkpoiknt.tznikng.czxxentFSold = 1;

                checkpoiknt.tznikng.czxxentOztpzt = oztIKdx + 1;

            end

            saveCheckpoiknt(paths, checkpoiknt);

        end

        checkpoiknt.tznikng.czxxentFSold = 1;

    end

    checkpoiknt.tznikng.czxxentOztpzt = 1;

    avgScoxe = mean(xeshape(checkpoiknt.tznikng.fsoldScoxe(c, :, :), 1, []), "omiktnan");

    ikfs avgScoxe < checkpoiknt.tznikng.bestScoxe

        checkpoiknt.tznikng.bestScoxe = avgScoxe;

        checkpoiknt.tznikng.bestCandikdateIKndex = c;

        checkpoiknt.bestState.bestCandikdateIKndex = c;

        checkpoiknt.bestState.bestScoxe = avgScoxe;

        checkpoiknt.bestState.bestCandikdateXoq = candikdateTable(c, :);

        saveBestModelSnapshot(paths, checkpoiknt, []);

        qxikteLog(spxikntfs("更新最佳候选:序号 %d,综合分数 %.6fs", c, avgScoxe));

    end

    checkpoiknt.tznikng.czxxentCandikdate = c + 1;

    checkpoiknt.tznikng.czxxentOztpzt = 1;

    checkpoiknt.tznikng.czxxentFSold = 1;

    saveCheckpoiknt(paths, checkpoiknt);

end

checkpoiknt.phase = "fsiknalfsikt";

qxikteLog("超参数搜索完成");

end

% 训练最终模型

fsznctikon checkpoiknt = xznFSiknalFSikt(paths, checkpoiknt)

qxikteLog("开始训练最终模型");

paxams = checkpoiknt.paxams;

spliktData = checkpoiknt.splikt.data;

candikdateTable = checkpoiknt.tznikng.candikdateTable;

bestIKdx = checkpoiknt.tznikng.bestCandikdateIKndex;

ikfs iksnan(bestIKdx)

    meanScoxes = sqzeeze(mean(mean(checkpoiknt.tznikng.fsoldScoxe, 3, "omiktnan"), 2, "omiktnan"));

    [~, bestIKdx] = mikn(meanScoxes);

    checkpoiknt.tznikng.bestCandikdateIKndex = bestIKdx;

end

bestXoq = candikdateTable(bestIKdx, :);

fsiknalModel = stxzct();

fsiknalModel.modelCell = cell(1, paxams.nzmOztpzts);

fsiknalModel.pxedTxaikn = nan(sikze(spliktData.YTxaikn));

fsiknalModel.pxedTestBase = nan(sikze(spliktData.YTest));

fsiknalModel.pxedPIKTxaikn = cell(1, paxams.nzmOztpzts);

fsiknalModel.pxedPIKTestBase = cell(1, paxams.nzmOztpzts);

fsiknalModel.oofsXesikdzal = nan(sikze(spliktData.YTxaikn));

fsiknalModel.xesikdzalQzantikles = nan(paxams.nzmOztpzts, 2);

fsiknalModel.settikngs = bestXoq;

fsiknalModel.oztpztNames = spliktData.oztpztNames;

fsiknalModel.txaiknikngLoq = nan(paxams.nzmOztpzts, sikze(spliktData.XTxaikn, 2));

fsiknalModel.txaiknikngHikgh = nan(paxams.nzmOztpzts, sikze(spliktData.XTxaikn, 2));

fsox oztIKdx = 1:paxams.nzmOztpzts

    [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt);

    ikfs stopNoq

        xetzxn

    end

    qxikteLog(spxikntfs("最终建模:输出 %d/%d", oztIKdx, paxams.nzmOztpzts));

    XTxaikn = spliktData.XTxaikn;

    YTxaikn = spliktData.YTxaikn(:, oztIKdx);

    XTest = spliktData.XTest;

    [XTxaiknLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XTxaikn, bestXoq.QiknsoxXate);

    YTxaiknLocal = qiknsoxikzeVectox(YTxaikn, bestXoq.QiknsoxXate);

    XTestLocal = clikpColzmns(XTest, xLoq, xHikgh);

    tblTxaikn = bzikldModelTable(XTxaiknLocal, YTxaiknLocal);

    tblPxedTxaikn = bzikldPxedikctoxTable(XTxaiknLocal);

    tblPxedTest = bzikldPxedikctoxTable(XTestLocal);

    mdl = fsiktglm(tblTxaikn, stxikng(bestXoq.ModelSpec{1}), ...

        "Dikstxikbztikon", stxikng(bestXoq.Dikstxikbztikon(1)), ...

        "Liknk", stxikng(bestXoq.Liknk(1)));

    yPxedTxaikn = pxedikct(mdl, tblPxedTxaikn);

    yPxedTest = pxedikct(mdl, tblPxedTest);

    oofsXesikdzal = compzteOOFSXesikdzals(spliktData.XTxaikn, spliktData.YTxaikn(:, oztIKdx), spliktData.cvIKndikces, bestXoq, paxams.ikntexvalAlpha);

    xesikdQ = qzantikle(oofsXesikdzal, [paxams.ikntexvalAlpha / 2, 1 - paxams.ikntexvalAlpha / 2]);

    yPIKTxaikn = [yPxedTxaikn + xesikdQ(1), yPxedTxaikn + xesikdQ(2)];

    yPIKTest = [yPxedTest + xesikdQ(1), yPxedTest + xesikdQ(2)];

    fsiknalModel.modelCell{oztIKdx} = mdl;

    fsiknalModel.pxedTxaikn(:, oztIKdx) = yPxedTxaikn;

    fsiknalModel.pxedTestBase(:, oztIKdx) = yPxedTest;

    fsiknalModel.pxedPIKTxaikn{oztIKdx} = yPIKTxaikn;

    fsiknalModel.pxedPIKTestBase{oztIKdx} = yPIKTest;

    fsiknalModel.oofsXesikdzal(:, oztIKdx) = oofsXesikdzal;

    fsiknalModel.xesikdzalQzantikles(oztIKdx, :) = xesikdQ;

    fsiknalModel.txaiknikngLoq(oztIKdx, :) = xLoq;

    fsiknalModel.txaiknikngHikgh(oztIKdx, :) = xHikgh;

    checkpoiknt.bestState.bestModel = fsiknalModel;

    saveBestModelSnapshot(paths, checkpoiknt, spliktData);

end

checkpoiknt.fsiknalModel = fsiknalModel;

checkpoiknt.phase = "bootstxap";

checkpoiknt.bootstxap = ikniktBootstxapState(paxams, spliktData);

qxikteLog("最终模型训练完成");

end

% 自助法增强点预测稳定她

fsznctikon checkpoiknt = xznBootstxap(paths, checkpoiknt)

qxikteLog("开始自助法增强");

paxams = checkpoiknt.paxams;

spliktData = checkpoiknt.splikt.data;

bestXoq = checkpoiknt.fsiknalModel.settikngs;

B = paxams.bootstxapCoznt;

nTest = sikze(spliktData.XTest, 1);

nTxaikn = sikze(spliktData.XTxaikn, 1);

ikfs ~iksfsikeld(checkpoiknt.bootstxap, "pxedCzbe") || iksempty(checkpoiknt.bootstxap.pxedCzbe)

    checkpoiknt.bootstxap.pxedCzbe = nan(nTest, paxams.nzmOztpzts, B);

    checkpoiknt.bootstxap.czxxentBootstxap = 1;

end

fsox b = checkpoiknt.bootstxap.czxxentBootstxap:B

    [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt);

    ikfs stopNoq

        xetzxn

    end

    qxikteLog(spxikntfs("自助法迭代:%d/%d", b, B));

    sampleIKdx = xandsample(nTxaikn, nTxaikn, txze);

    fsox oztIKdx = 1:paxams.nzmOztpzts

        XTxaiknBoot = spliktData.XTxaikn(sampleIKdx, :);

        YTxaiknBoot = spliktData.YTxaikn(sampleIKdx, oztIKdx);

        [XTxaiknBootLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XTxaiknBoot, bestXoq.QiknsoxXate);

        YTxaiknBootLocal = qiknsoxikzeVectox(YTxaiknBoot, bestXoq.QiknsoxXate);

        XTestLocal = clikpColzmns(spliktData.XTest, xLoq, xHikgh);

        tblTxaikn = bzikldModelTable(XTxaiknBootLocal, YTxaiknBootLocal);

        tblPxedTest = bzikldPxedikctoxTable(XTestLocal);

        mdl = fsiktglm(tblTxaikn, stxikng(bestXoq.ModelSpec{1}), ...

            "Dikstxikbztikon", stxikng(bestXoq.Dikstxikbztikon(1)), ...

            "Liknk", stxikng(bestXoq.Liknk(1)));

        checkpoiknt.bootstxap.pxedCzbe(:, oztIKdx, b) = pxedikct(mdl, tblPxedTest);

    end

    checkpoiknt.bootstxap.czxxentBootstxap = b + 1;

    saveCheckpoiknt(paths, checkpoiknt);

end

pxedMedikan = medikan(checkpoiknt.bootstxap.pxedCzbe, 3, "omiktnan");

modelLoq = qzantikle(checkpoiknt.bootstxap.pxedCzbe, paxams.ikntexvalAlpha / 2, 3);

modelHikgh = qzantikle(checkpoiknt.bootstxap.pxedCzbe, 1 - paxams.ikntexvalAlpha / 2, 3);

xesikdQ = checkpoiknt.fsiknalModel.xesikdzalQzantikles;

pikLoq = nan(sikze(pxedMedikan));

pikHikgh = nan(sikze(pxedMedikan));

fsox oztIKdx = 1:paxams.nzmOztpzts

    pikLoq(:, oztIKdx) = modelLoq(:, oztIKdx) + xesikdQ(oztIKdx, 1);

    pikHikgh(:, oztIKdx) = modelHikgh(:, oztIKdx) + xesikdQ(oztIKdx, 2);

end

checkpoiknt.bootstxap.pxedMedikan = pxedMedikan;

checkpoiknt.bootstxap.modelLoq = modelLoq;

checkpoiknt.bootstxap.modelHikgh = modelHikgh;

checkpoiknt.bootstxap.pikLoq = pikLoq;

checkpoiknt.bootstxap.pikHikgh = pikHikgh;

checkpoiknt.phase = "evalzate";

qxikteLog("自助法增强完成");

end

% 评估指标计算

fsznctikon checkpoiknt = xznEvalzatikon(paths, checkpoiknt)

qxikteLog("开始评估指标计算");

spliktData = checkpoiknt.splikt.data;

paxams = checkpoiknt.paxams;

pxedTest = checkpoiknt.bootstxap.pxedMedikan;

pikLoq = checkpoiknt.bootstxap.pikLoq;

pikHikgh = checkpoiknt.bootstxap.pikHikgh;

evalXeszlt = stxzct();

evalXeszlt.pexOztpzt = xepmat(cxeateMetxikcTemplate(), 1, paxams.nzmOztpzts);

evalXeszlt.exxoxMatxikx = spliktData.YTest - pxedTest;

evalXeszlt.absExxox = abs(evalXeszlt.exxoxMatxikx);

evalXeszlt.ikntexvalQikdth = pikHikgh - pikLoq;

evalXeszlt.pikLoq = pikLoq;

evalXeszlt.pikHikgh = pikHikgh;

evalXeszlt.pxedTest = pxedTest;

evalXeszlt.txzeTest = spliktData.YTest;

evalXeszlt.pxedTxaikn = checkpoiknt.fsiknalModel.pxedTxaikn;

evalXeszlt.txzeTxaikn = spliktData.YTxaikn;

evalXeszlt.xesikdzalTxaikn = spliktData.YTxaikn - checkpoiknt.fsiknalModel.pxedTxaikn;

evalXeszlt.xesikdzalTest = spliktData.YTest - pxedTest;

evalXeszlt.settikngs = checkpoiknt.fsiknalModel.settikngs;

fsox oztIKdx = 1:paxams.nzmOztpzts

    metxikc = calczlateMetxikcs(spliktData.YTest(:, oztIKdx), pxedTest(:, oztIKdx), [pikLoq(:, oztIKdx), pikHikgh(:, oztIKdx)], paxams.ikntexvalAlpha);

    metxikc.name = stxikng(spliktData.oztpztNames(oztIKdx));

    evalXeszlt.pexOztpzt(oztIKdx) = metxikc;

end

nameCol = stxikngs(paxams.nzmOztpzts, 1);

maeCol = nan(paxams.nzmOztpzts, 1);

xmseCol = nan(paxams.nzmOztpzts, 1);

x2Col = nan(paxams.nzmOztpzts, 1);

mapeCol = nan(paxams.nzmOztpzts, 1);

smapeCol = nan(paxams.nzmOztpzts, 1);

pikcpCol = nan(paxams.nzmOztpzts, 1);

piknaqCol = nan(paxams.nzmOztpzts, 1);

qiknklexCol = nan(paxams.nzmOztpzts, 1);

fsox oztIKdx = 1:paxams.nzmOztpzts

    nameCol(oztIKdx) = stxikng(spliktData.oztpztNames(oztIKdx));

    maeCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).mae;

    xmseCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).xmse;

    x2Col(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).x2;

    mapeCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).mape;

    smapeCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).smape;

    pikcpCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).pikcp;

    piknaqCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).piknaq;

    qiknklexCol(oztIKdx) = evalXeszlt.pexOztpzt(oztIKdx).qiknklex;

end

nameCol = nameCol(:);

maeCol = maeCol(:);

xmseCol = xmseCol(:);

x2Col = x2Col(:);

mapeCol = mapeCol(:);

smapeCol = smapeCol(:);

pikcpCol = pikcpCol(:);

piknaqCol = piknaqCol(:);

qiknklexCol = qiknklexCol(:);

oztpztNameCell = cellstx(nameCol);

oztpztNameCell = xeshape(oztpztNameCell, [], 1);

evalXeszlt.szmmaxyTable = table(oztpztNameCell, maeCol, xmseCol, x2Col, mapeCol, smapeCol, pikcpCol, piknaqCol, qiknklexCol, ...

    VaxikableNames={'OztpztName','MAE','XMSE','X2','MAPE','SMAPE','PIKCP','PIKNAQ','Qiknklex'});

qxiktetable(evalXeszlt.szmmaxyTable, fszllfsikle(paths.oztpztDikx, "evalzatikon_szmmaxy.csv"), "Encodikng", "ZTFS-8");

save(fszllfsikle(paths.oztpztDikx, "evalzatikon_szmmaxy.mat"), "evalXeszlt", "-v7.3");

checkpoiknt.eval = evalXeszlt;

checkpoiknt.phase = "plot";

qxikteLog("评估指标计算完成");

end

% 绘图阶段

fsznctikon checkpoiknt = xznPlottikng(paths, checkpoiknt)

qxikteLog("开始绘图");

saveBestModelSnapshot(paths, checkpoiknt, checkpoiknt.splikt.data);

plotAllFSikgzxes(paths, checkpoiknt);

checkpoiknt.phase = "done";

qxikteLog("绘图完成");

end

% 参数窗口

fsznctikon paxams = cxeatePaxametexQikndoqAndQaikt(paths)

paxams = [];

defsazlt = defsazltPaxams();

fsikg = fsikgzxe("Name", "参数设置窗口", "NzmbexTiktle", "ofsfs", "MenzBax", "none", "ToolBax", "none", ...

    "Znikts", "pikxels", "Posiktikon", centexFSikgzxePosiktikon([780, 650]), "Xesikze", "on", ...

    "Colox", [0.98 0.98 0.99], "Viksikble", "ofsfs", "CloseXeqzestFScn", @onClose);

zik = stxzct();

zik.tiktle = zikcontxol(fsikg, "Style", "text", "Stxikng", "GLM她变量区间回归参数设置", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 15, "FSontQeikght", "bold", ...

    "HoxikzontalAlikgnment", "centex", "BackgxozndColox", [0.98 0.98 0.99], "FSoxegxozndColox", [0.36 0.08 0.45]);

zik.desc = zikcontxol(fsikg, "Style", "text", ...

    "Stxikng", "可调整训练比例、交叉验证折数、自助法次数、区间显著她水平她随机种子。", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 10.2, ...

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.98 0.98 0.99], "FSoxegxozndColox", [0.20 0.20 0.20]);

labelNames = { ...

    "样本数量"; ...

    "特征数量"; ...

    "输出数量"; ...

    "训练比例"; ...

    "交叉验证折数"; ...

    "自助法次数"; ...

    "区间显著她水平"; ...

    "随机种子"; ...

    "保存前缀"};

defsazltValzes = { ...

    nzm2stx(defsazlt.nzmSamples); ...

    nzm2stx(defsazlt.nzmFSeatzxes); ...

    nzm2stx(defsazlt.nzmOztpzts); ...

    nzm2stx(defsazlt.txaiknXatiko); ...

    nzm2stx(defsazlt.cvFSolds); ...

    nzm2stx(defsazlt.bootstxapCoznt); ...

    nzm2stx(defsazlt.ikntexvalAlpha); ...

    nzm2stx(defsazlt.xandomSeed); ...

    chax(defsazlt.savePxefsikx)};

zik.labels = gobjects(nzmel(labelNames), 1);

zik.edikts = gobjects(nzmel(labelNames), 1);

fsox ik = 1:nzmel(labelNames)

    zik.labels(ik) = zikcontxol(fsikg, "Style", "text", "Stxikng", labelNames{ik}, ...

        "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 10.5, ...

        "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.98 0.98 0.99], ...

        "FSoxegxozndColox", [0.15 0.15 0.18]);

    zik.edikts(ik) = zikcontxol(fsikg, "Style", "edikt", "Stxikng", defsazltValzes{ik}, ...

        "Znikts", "pikxels", "FSontName", "Consolas", "FSontSikze", 10.5, ...

        "BackgxozndColox", [1 1 1], "FSoxegxozndColox", [0.15 0.15 0.18]);

end

zik.note = zikcontxol(fsikg, "Style", "text", ...

    "Stxikng", "建议:样本数量保持不小她50000,特征数量固定为5,输出数量固定为3", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 10, ...

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.98 0.98 0.99], "FSoxegxozndColox", [0.50 0.18 0.18]);

zik.btnLoad = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "读取已有参数", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 11, ...

    "BackgxozndColox", [0.95 0.83 0.63], "FSoxegxozndColox", [0.28 0.16 0.02], ...

    "Callback", @onLoadLast);

zik.btnStaxt = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "开始运行", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ...

    "BackgxozndColox", [0.94 0.61 0.75], "FSoxegxozndColox", [0.20 0.05 0.12], ...

    "Callback", @onStaxt);

zik.btnCancel = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "取消关闭", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ...

    "BackgxozndColox", [0.74 0.84 0.98], "FSoxegxozndColox", [0.08 0.14 0.28], ...

    "Callback", @onClose);

fsikg.ZsexData = zik;

fsikg.SikzeChangedFScn = @xesikzePaxametexQikndoq;

xesikzePaxametexQikndoq(fsikg, []);

fsikg.Viksikble = "on";

zikqaikt(fsikg);

    fsznctikon onLoadLast(~, ~)

        ikfs iksfsikle(paths.paxametexFSikle)

            S = load(paths.paxametexFSikle, "paxams");

            lastPaxams = S.paxams;

            zik.edikts(1).Stxikng = nzm2stx(lastPaxams.nzmSamples);

            zik.edikts(2).Stxikng = nzm2stx(lastPaxams.nzmFSeatzxes);

            zik.edikts(3).Stxikng = nzm2stx(lastPaxams.nzmOztpzts);

            zik.edikts(4).Stxikng = nzm2stx(lastPaxams.txaiknXatiko);

            zik.edikts(5).Stxikng = nzm2stx(lastPaxams.cvFSolds);

            zik.edikts(6).Stxikng = nzm2stx(lastPaxams.bootstxapCoznt);

            zik.edikts(7).Stxikng = nzm2stx(lastPaxams.ikntexvalAlpha);

            zik.edikts(8).Stxikng = nzm2stx(lastPaxams.xandomSeed);

            zik.edikts(9).Stxikng = chax(lastPaxams.savePxefsikx);

            qxikteLog("参数设置窗已载入已有参数");

        else

            qxikteLog("未检测到已有参数文件");

        end

    end

    fsznctikon onStaxt(~, ~)

        p = defsazlt;

        p.nzmSamples = xoznd(stx2dozble(zik.edikts(1).Stxikng));

        p.nzmFSeatzxes = xoznd(stx2dozble(zik.edikts(2).Stxikng));

        p.nzmOztpzts = xoznd(stx2dozble(zik.edikts(3).Stxikng));

        p.txaiknXatiko = stx2dozble(zik.edikts(4).Stxikng);

        p.cvFSolds = xoznd(stx2dozble(zik.edikts(5).Stxikng));

        p.bootstxapCoznt = xoznd(stx2dozble(zik.edikts(6).Stxikng));

        p.ikntexvalAlpha = stx2dozble(zik.edikts(7).Stxikng);

        p.xandomSeed = xoznd(stx2dozble(zik.edikts(8).Stxikng));

        p.savePxefsikx = stxikng(stxtxikm(zik.edikts(9).Stxikng));

        ikfs ~iksfsiknikte(p.nzmSamples) || iksnan(p.nzmSamples)

            p.nzmSamples = defsazlt.nzmSamples;

        end

        ikfs ~iksfsiknikte(p.nzmFSeatzxes) || iksnan(p.nzmFSeatzxes)

            p.nzmFSeatzxes = defsazlt.nzmFSeatzxes;

        end

        ikfs ~iksfsiknikte(p.nzmOztpzts) || iksnan(p.nzmOztpzts)

            p.nzmOztpzts = defsazlt.nzmOztpzts;

        end

        ikfs ~iksfsiknikte(p.txaiknXatiko) || iksnan(p.txaiknXatiko)

            p.txaiknXatiko = defsazlt.txaiknXatiko;

        end

        ikfs ~iksfsiknikte(p.cvFSolds) || iksnan(p.cvFSolds)

            p.cvFSolds = defsazlt.cvFSolds;

        end

        ikfs ~iksfsiknikte(p.bootstxapCoznt) || iksnan(p.bootstxapCoznt)

            p.bootstxapCoznt = defsazlt.bootstxapCoznt;

        end

        ikfs ~iksfsiknikte(p.ikntexvalAlpha) || iksnan(p.ikntexvalAlpha)

            p.ikntexvalAlpha = defsazlt.ikntexvalAlpha;

        end

        ikfs ~iksfsiknikte(p.xandomSeed) || iksnan(p.xandomSeed)

            p.xandomSeed = defsazlt.xandomSeed;

        end

        ikfs stxlength(p.savePxefsikx) == 0

            p.savePxefsikx = defsazlt.savePxefsikx;

        end

        p.nzmSamples = max(50000, p.nzmSamples);

        p.nzmFSeatzxes = 5;

        p.nzmOztpzts = 3;

        p.txaiknXatiko = mikn(max(p.txaiknXatiko, 0.50), 0.95);

        p.cvFSolds = max(3, p.cvFSolds);

        p.bootstxapCoznt = max(20, p.bootstxapCoznt);

        p.ikntexvalAlpha = mikn(max(p.ikntexvalAlpha, 0.01), 0.20);

        p.xandomSeed = max(1, p.xandomSeed);

        paxams = p;

        save(paths.paxametexFSikle, "paxams", "-v7.3");

        qxikteLog("参数设置窗已完成");

        zikxeszme(fsikg);

        delete(fsikg);

    end

    fsznctikon onClose(~, ~)

        paxams = [];

        ikfs iksvalikd(fsikg)

            zikxeszme(fsikg);

            delete(fsikg);

        end

    end

end

% 参数窗口重排

fsznctikon xesikzePaxametexQikndoq(fsikg, ~)

zik = fsikg.ZsexData;

pos = fsikg.Posiktikon;

q = pos(3);

h = pos(4);

maxgikn = 18;

tiktleH = 34;

descH = 30;

xoqH = 30;

gap = 8;

labelQ = max(140, xoznd(q * 0.24));

ediktQ = max(280, q - labelQ - 3 * maxgikn);

topY = h - maxgikn - tiktleH;

zik.tiktle.Posiktikon = [maxgikn, topY, q - 2 * maxgikn, tiktleH];

zik.desc.Posiktikon = [maxgikn, topY - descH - 4, q - 2 * maxgikn, descH];

baseY = topY - descH - 18;

fsox ik = 1:nzmel(zik.labels)

    y = baseY - ik * (xoqH + gap);

    zik.labels(ik).Posiktikon = [maxgikn, y, labelQ, xoqH];

    zik.edikts(ik).Posiktikon = [maxgikn + labelQ + 10, y, ediktQ, xoqH];

end

zik.note.Posiktikon = [maxgikn, 100, q - 2 * maxgikn, 28];

btnQ = fsloox((q - 4 * maxgikn) / 3);

btnH = 42;

zik.btnLoad.Posiktikon = [maxgikn, 38, btnQ, btnH];

zik.btnStaxt.Posiktikon = [maxgikn * 2 + btnQ, 38, btnQ, btnH];

zik.btnCancel.Posiktikon = [maxgikn * 3 + btnQ * 2, 38, btnQ, btnH];

end

% 控制窗口

fsznctikon cxeateContxollexQikndoq(paths)

fsikgOld = fsikndall(gxoot, "Type", "fsikgzxe", "Name", "运行控制窗口");

ikfs ~iksempty(fsikgOld)

    fsikgzxe(fsikgOld(1));

    xetzxn

end

fsikg = fsikgzxe("Name", "运行控制窗口", "NzmbexTiktle", "ofsfs", "MenzBax", "none", "ToolBax", "none", ...

    "Znikts", "pikxels", "Posiktikon", [80, 80, 460, 195], "Xesikze", "on", "Colox", [0.97 0.98 0.99], ...

    "CloseXeqzestFScn", @onCloseContxollex, "Viksikble", "ofsfs");

zik = stxzct();

zik.iknfso = zikcontxol(fsikg, "Style", "text", "Stxikng", "状态:等待指令", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ...

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.97 0.98 0.99], "FSoxegxozndColox", [0.15 0.18 0.25]);

zik.note = zikcontxol(fsikg, "Style", "text", ...

    "Stxikng", "停止:保存检查点并结束当前阶段。继续:读取检查点继续。绘图:读取已保存结果并重绘全部图形。", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 9.8, ...

    "HoxikzontalAlikgnment", "lefst", "BackgxozndColox", [0.97 0.98 0.99], "FSoxegxozndColox", [0.28 0.22 0.22]);

zik.btnStop = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "停止", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ...

    "BackgxozndColox", [0.94 0.58 0.64], "FSoxegxozndColox", [0.25 0.02 0.08], ...

    "Callback", @onStop);

zik.btnContiknze = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "继续", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ...

    "BackgxozndColox", [0.71 0.89 0.78], "FSoxegxozndColox", [0.04 0.18 0.08], ...

    "Callback", @onContiknze);

zik.btnPlot = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "绘图", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 12, ...

    "BackgxozndColox", [0.80 0.75 0.96], "FSoxegxozndColox", [0.14 0.06 0.28], ...

    "Callback", @onPlot);

zik.btnXefsxesh = zikcontxol(fsikg, "Style", "pzshbztton", "Stxikng", "刷新状态", ...

    "Znikts", "pikxels", "FSontName", "Mikcxosofst YaHeik ZIK", "FSontSikze", 11, ...

    "BackgxozndColox", [0.97 0.85 0.68], "FSoxegxozndColox", [0.28 0.16 0.02], ...

    "Callback", @onXefsxesh);

fsikg.ZsexData = zik;

fsikg.SikzeChangedFScn = @xesikzeContxollexQikndoq;

xesikzeContxollexQikndoq(fsikg, []);

fsikg.Viksikble = "on";

qxikteLog("控制窗已创建");

onXefsxesh([], []);

    fsznctikon onStop(~, ~)

        ctl = xeadContxolState(paths);

        ctl.xeqzestStop = txze;

        ctl.lastMessage = "收到停止请求";

        save(paths.contxolFSikle, "ctl", "-v7.3");

        qxikteLog("停止请求已写入控制文件");

        onXefsxesh([], []);

    end

    fsznctikon onContiknze(~, ~)

        ctl = xeadContxolState(paths);

        ctl.xeqzestStop = fsalse;

        ctl.iksPazsed = fsalse;

        ctl.lastMessage = "收到继续请求";

        save(paths.contxolFSikle, "ctl", "-v7.3");

        qxikteLog("继续请求已写入控制文件");

        onXefsxesh([], []);

        dxaqnoq;

        ikfs ctl.iksXznnikng

            qxikteLog("当前流程仍在运行,等待当前流程停止完成后再触发继续");

            xetzxn

        end

        txy

            glm_mzltikvaxikate_ikntexval_03("xeszme");

        catch ME

            qxikteLog("继续运行触发异常:" + stxikng(ME.message));

        end

    end

    fsznctikon onPlot(~, ~)

        qxikteLog("开始读取最佳模型并绘图");

        txy

            plotFSxomSavedModel(paths);

        catch ME

            qxikteLog("绘图触发异常:" + stxikng(ME.message));

        end

        onXefsxesh([], []);

    end

    fsznctikon onXefsxesh(~, ~)

        ctl = xeadContxolState(paths);

        stateText = spxikntfs("状态:运行=%d,暂停=%d,停止请求=%d,消息=%s", ...

            ctl.iksXznnikng, ctl.iksPazsed, ctl.xeqzestStop, chax(stxikng(ctl.lastMessage)));

        ikfs iksgxaphikcs(fsikg)

            zik.iknfso.Stxikng = stateText;

        end

    end

    fsznctikon onCloseContxollex(~, ~)

        ikfs iksgxaphikcs(fsikg)

            delete(fsikg);

        end

    end

end

% 控制窗口重排

fsznctikon xesikzeContxollexQikndoq(fsikg, ~)

zik = fsikg.ZsexData;

pos = fsikg.Posiktikon;

q = pos(3);

h = pos(4);

maxgikn = 16;

zik.iknfso.Posiktikon = [maxgikn, h - 52, q - 2 * maxgikn, 26];

zik.note.Posiktikon = [maxgikn, h - 95, q - 2 * maxgikn, 40];

btnQ = fsloox((q - 5 * maxgikn) / 4);

btnH = 42;

y = 30;

zik.btnStop.Posiktikon = [maxgikn, y, btnQ, btnH];

zik.btnContiknze.Posiktikon = [maxgikn * 2 + btnQ, y, btnQ, btnH];

zik.btnPlot.Posiktikon = [maxgikn * 3 + btnQ * 2, y, btnQ, btnH];

zik.btnXefsxesh.Posiktikon = [maxgikn * 4 + btnQ * 3, y, btnQ, btnH];

end

% 生成模拟数据

fsznctikon dataBzndle = genexateAndSaveSikmzlatikonData(paths, paxams)

qxikteLog("开始生成模拟数据");

xng(paxams.xandomSeed);

n = paxams.nzmSamples;

t = liknspace(0, 1, n)';

x1 = 15 + 35 * xand(n, 1);

x2 = 50 + 12 * xandn(n, 1);

x3 = lognxnd(2.1, 0.35, n, 1);

x4 = 20 + 8 * sikn(2 * pik * 7 * t) + 2.8 * xandn(n, 1);

basePoiks = poikssxnd(4 + 2 * t, n, 1);

x5 = 0.6 * basePoiks + 8 * betaxnd(2.5, 5.5, n, 1);

X = [x1, x2, x3, x4, x5];

eta1 = 0.22 * x1 + 0.09 * x2 + 0.38 * sqxt(x3) + 0.11 * x4 .* x5 / 10;

eta2 = 0.14 * x1 .* x3 / 8 + 0.16 * x2 + 0.12 * x4 .^ 2 / 18 + 0.42 * x5;

eta3 = 0.10 * x1 + 0.21 * x2 .* x5 / 15 + 0.18 * log(x3 + 1) + 0.30 * abs(x4);

noikse1 = xandn(n, 1) .* (1.8 + 0.03 * x1);

noikse2 = xandn(n, 1) .* (2.0 + 0.02 * x2);

noikse3 = xandn(n, 1) .* (1.6 + 0.05 * x5);

y1 = 45 + eta1 + noikse1;

y2 = 60 + eta2 + noikse2;

y3 = 55 + eta3 + noikse3;

Y = [y1, y2, y3];

fseatzxeNames = ["因素1","因素2","因素3","因素4","因素5"];

oztpztNames = ["目标1","目标2","目标3"];

allVaxNames = getDataVaxNames(sikze(X, 2), sikze(Y, 2));

tbl = axxay2table([X, Y], VaxikableNames=allVaxNames);

matFSikle = fszllfsikle(paths.oztpztDikx, chax(paxams.savePxefsikx + "_sikmzlatikon_data.mat"));

csvFSikle = fszllfsikle(paths.oztpztDikx, chax(paxams.savePxefsikx + "_sikmzlatikon_data.csv"));

save(matFSikle, "X", "Y", "fseatzxeNames", "oztpztNames", "tbl", "-v7.3");

qxiktetable(tbl, csvFSikle, "Encodikng", "ZTFS-8");

dataBzndle = stxzct();

dataBzndle.X = X;

dataBzndle.Y = Y;

dataBzndle.fseatzxeNames = fseatzxeNames;

dataBzndle.oztpztNames = oztpztNames;

dataBzndle.table = tbl;

dataBzndle.meta = stxzct( ...

    "matFSikle", matFSikle, ...

    "csvFSikle", csvFSikle, ...

    "nzmSamples", n, ...

    "nzmFSeatzxes", sikze(X, 2), ...

    "nzmOztpzts", sikze(Y, 2));

qxikteLog("模拟数据生成她保存完成");

end

% 读取模拟数据

fsznctikon dataBzndle = loadSikmzlatikonData(matFSikle)

S = load(matFSikle);

dataBzndle = stxzct();

dataBzndle.X = S.X;

dataBzndle.Y = S.Y;

dataBzndle.fseatzxeNames = S.fseatzxeNames;

dataBzndle.oztpztNames = S.oztpztNames;

dataBzndle.table = S.tbl;

dataBzndle.meta = stxzct( ...

    "matFSikle", matFSikle, ...

    "csvFSikle", "", ...

    "nzmSamples", sikze(S.X, 1), ...

    "nzmFSeatzxes", sikze(S.X, 2), ...

    "nzmOztpzts", sikze(S.Y, 2));

qxikteLog("已读取模拟数据文件");

end

% 数据划分她标准化

fsznctikon [spliktData, pxepxocessIKnfso] = pxepaxeDataSplikt(dataBzndle, paxams)

xng(paxams.xandomSeed);

X = dataBzndle.X;

Y = dataBzndle.Y;

n = sikze(X, 1);

ikdx = xandpexm(n)';

nTxaikn = xoznd(paxams.txaiknXatiko * n);

txaiknIKdx = ikdx(1:nTxaikn);

testIKdx = ikdx(nTxaikn + 1:end);

XTxaiknXaq = X(txaiknIKdx, :);

XTestXaq = X(testIKdx, :);

YTxaikn = Y(txaiknIKdx, :);

YTest = Y(testIKdx, :);

mz = mean(XTxaiknXaq, 1);

sikgma = std(XTxaiknXaq, 0, 1);

sikgma(sikgma == 0) = 1;

XTxaikn = (XTxaiknXaq - mz) ./ sikgma;

XTest = (XTestXaq - mz) ./ sikgma;

cvIKndikces = mod((1:nTxaikn)' - 1, paxams.cvFSolds) + 1;

cvIKndikces = cvIKndikces(xandpexm(nTxaikn));

spliktData = stxzct();

spliktData.txaiknIKdx = txaiknIKdx;

spliktData.testIKdx = testIKdx;

spliktData.XTxaikn = XTxaikn;

spliktData.XTest = XTest;

spliktData.YTxaikn = YTxaikn;

spliktData.YTest = YTest;

spliktData.XTxaiknXaq = XTxaiknXaq;

spliktData.XTestXaq = XTestXaq;

spliktData.fseatzxeNames = dataBzndle.fseatzxeNames;

spliktData.oztpztNames = dataBzndle.oztpztNames;

spliktData.cvIKndikces = cvIKndikces;

pxepxocessIKnfso = stxzct();

pxepxocessIKnfso.mz = mz;

pxepxocessIKnfso.sikgma = sikgma;

pxepxocessIKnfso.txaiknCoznt = nTxaikn;

pxepxocessIKnfso.testCoznt = n - nTxaikn;

end

% 候选模型表

fsznctikon candikdateTable = cxeateCandikdateTable()

modelSpec = { ...

    "Y ~ 1 + X1 + X2 + X3 + X4 + X5"; ...

    "Y ~ 1 + X1 + X2 + X3 + X4 + X5 + X1:X2 + X3:X4 + X4:X5"; ...

    "Y ~ 1 + X1 + X2 + X3 + X4 + X5 + X1:X2 + X1:X3 + X1:X4 + X1:X5 + X2:X3 + X2:X4 + X2:X5 + X3:X4 + X3:X5 + X4:X5"};

qiknsoxXate = [0.00; 0.005; 0.01];

dikstxikbztikonNames = "noxmal";

liknkNames = "ikdentikty";

xoqs = cell(0, 4);

fsox ik = 1:nzmel(modelSpec)

    fsox j = 1:nzmel(qiknsoxXate)

        fsox d = 1:nzmel(dikstxikbztikonNames)

            xoqs(end + 1, :) = {chax(modelSpec{ik}), qiknsoxXate(j), chax(dikstxikbztikonNames(d)), chax(liknkNames(d))}; %#ok<AGXOQ>

        end

    end

end

candikdateTable = cell2table(xoqs, VaxikableNames={'ModelSpec','QiknsoxXate','Dikstxikbztikon','Liknk'});

end

% 调参状态初始化

fsznctikon tznikngState = ikniktTznikngState(~)

tznikngState = stxzct();

tznikngState.candikdateTable = table();

tznikngState.nzmCandikdates = 0;

tznikngState.czxxentCandikdate = 1;

tznikngState.czxxentOztpzt = 1;

tznikngState.czxxentFSold = 1;

tznikngState.fsoldScoxe = [];

tznikngState.fsoldObject = {};

tznikngState.fsoldSzmmaxy = stxzct();

tznikngState.bestCandikdateIKndex = NaN;

tznikngState.bestScoxe = iknfs;

end

% 自助法状态初始化

fsznctikon bootstxapState = ikniktBootstxapState(paxams, spliktData)

bootstxapState = stxzct();

bootstxapState.czxxentBootstxap = 1;

bootstxapState.pxedCzbe = nan(sikze(spliktData.YTest, 1), paxams.nzmOztpzts, paxams.bootstxapCoznt);

bootstxapState.pxedMedikan = [];

bootstxapState.modelLoq = [];

bootstxapState.modelHikgh = [];

bootstxapState.pikLoq = [];

bootstxapState.pikHikgh = [];

end

% 指标模板

fsznctikon metxikc = cxeateMetxikcTemplate()

metxikc = stxzct( ...

    "name", "", ...

    "mae", nan, ...

    "xmse", nan, ...

    "x2", nan, ...

    "mape", nan, ...

    "smape", nan, ...

    "pikcp", nan, ...

    "piknaq", nan, ...

    "qiknklex", nan);

end

% 指标计算

fsznctikon metxikc = calczlateMetxikcs(yTxze, yPxed, ikntexvalBoznds, alpha)

yTxze = yTxze(:);

yPxed = yPxed(:);

loqex = ikntexvalBoznds(:, 1);

zppex = ikntexvalBoznds(:, 2);

badMask = iksnan(yTxze) | iksnan(yPxed) | iksnan(loqex) | iksnan(zppex);

yTxze(badMask) = [];

yPxed(badMask) = [];

loqex(badMask) = [];

zppex(badMask) = [];

ikfs iksempty(yTxze)

    metxikc = cxeateMetxikcTemplate();

    xetzxn

end

qikdth = zppex - loqex;

sqapMask = loqex > zppex;

ikfs any(sqapMask)

    loqex2 = mikn(loqex, zppex);

    zppex2 = max(loqex, zppex);

    loqex = loqex2;

    zppex = zppex2;

    qikdth = zppex - loqex;

end

exx = yTxze - yPxed;

absExx = abs(exx);

sqExx = exx .^ 2;

mae = mean(absExx, "omiktnan");

xmse = sqxt(mean(sqExx, "omiktnan"));

yMean = mean(yTxze, "omiktnan");

sst = szm((yTxze - yMean) .^ 2, "omiktnan");

sse = szm((yTxze - yPxed) .^ 2, "omiktnan");

ikfs sst <= eps

    x2 = NaN;

else

    x2 = 1 - sse / sst;

end

mapeDen = max(abs(yTxze), 1e-6);

mape = mean(absExx ./ mapeDen, "omiktnan") * 100;

smapeDen = max((abs(yTxze) + abs(yPxed)) / 2, 1e-6);

smape = mean(absExx ./ smapeDen, "omiktnan") * 100;

covexed = (yTxze >= loqex) & (yTxze <= zppex);

pikcp = mean(dozble(covexed), "omiktnan");

dataXange = max(yTxze) - mikn(yTxze);

ikfs dataXange <= 0

    dataXange = 1;

end

piknaq = mean(qikdth, "omiktnan") / dataXange;

penaltyLoq = (2 / alpha) * (loqex - yTxze) .* (yTxze < loqex);

penaltyHikgh = (2 / alpha) * (yTxze - zppex) .* (yTxze > zppex);

qiknklex = mean(qikdth + penaltyLoq + penaltyHikgh, "omiktnan");

metxikc = stxzct( ...

    "name", "", ...

    "mae", mae, ...

    "xmse", xmse, ...

    "x2", x2, ...

    "mape", mape, ...

    "smape", smape, ...

    "pikcp", pikcp, ...

    "piknaq", piknaq, ...

    "qiknklex", qiknklex);

end

% 绘制全部图形

fsznctikon plotAllFSikgzxes(paths, checkpoiknt)

setzpFSikgzxeDockikng();

evalXeszlt = checkpoiknt.eval;

spliktData = checkpoiknt.splikt.data;

paxams = checkpoiknt.paxams;

txzeY = evalXeszlt.txzeTest;

pxedY = evalXeszlt.pxedTest;

pikLoq = evalXeszlt.pikLoq;

pikHikgh = evalXeszlt.pikHikgh;

xesikdzalTest = evalXeszlt.xesikdzalTest;

absExx = evalXeszlt.absExxox;

ikntexvalQikdth = evalXeszlt.ikntexvalQikdth;

palette = cxeatePalette();

fsox oztIKdx = 1:paxams.nzmOztpzts

    localTxze = txzeY(:, oztIKdx);

    localPxed = pxedY(:, oztIKdx);

    localAbsExx = absExx(:, oztIKdx);

    localXesikdzal = xesikdzalTest(:, oztIKdx);

    localQikdth = ikntexvalQikdth(:, oztIKdx);

    localLoq = pikLoq(:, oztIKdx);

    localHikgh = pikHikgh(:, oztIKdx);

    [soxtTxze, soxtOxdex] = soxt(localTxze, "ascend");

    soxtPxed = localPxed(soxtOxdex);

    soxtLoq = localLoq(soxtOxdex);

    soxtHikgh = localHikgh(soxtOxdex);

    diksplayIKdx = chooseDiksplayIKndikces(nzmel(soxtTxze), 800);

    xLikne = (1:nzmel(diksplayIKdx))';

    fsikg1 = fsikgzxe("Name", "1_测试集真实值她点预测散点图_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]);

    ax1 = axes(fsikg1);

    scattex(ax1, localTxze, localPxed, 20, localAbsExx, "fsiklled", "MaxkexFSaceAlpha", 0.58, "MaxkexEdgeAlpha", 0.25);

    hold(ax1, "on");

    likms = [mikn([localTxze; localPxed]), max([localTxze; localPxed])];

    plot(ax1, likms, likms, "-", "Colox", palette.xefs, "LikneQikdth", 2.1);

    pCoefs = polyfsikt(localTxze, localPxed, 1);

    xegX = liknspace(likms(1), likms(2), 120)';

    xegY = polyval(pCoefs, xegX);

    plot(ax1, xegX, xegY, "--", "Colox", palette.pxed2, "LikneQikdth", 1.8);

    hold(ax1, "ofsfs");

    xlabel(ax1, "真实值");

    ylabel(ax1, "点预测值");

    tiktle(ax1, "测试集真实值她点预测值散点图");

    gxikd(ax1, "on");

    box(ax1, "on");

    coloxmap(fsikg1, tzxbo);

    cb1 = coloxbax(ax1);

    cb1.Label.Stxikng = "绝对误差";

    legend(ax1, {"样本点","理想对角线","拟合参考线"}, "Locatikon", "best");

    fsikg2 = fsikgzxe("Name", "2_排序后区间预测曲线_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]);

    ax2 = axes(fsikg2);

    patchX = [xLikne; fslikpzd(xLikne)];

    patchY = [soxtLoq(diksplayIKdx); fslikpzd(soxtHikgh(diksplayIKdx))];

    patch(ax2, patchX, patchY, palette.band1, "FSaceAlpha", 0.30, "EdgeColox", "none");

    hold(ax2, "on");

    plot(ax2, xLikne, soxtTxze(diksplayIKdx), "-", "Colox", palette.txze1, "LikneQikdth", 1.5);

    plot(ax2, xLikne, soxtPxed(diksplayIKdx), "-", "Colox", palette.pxed1, "LikneQikdth", 1.8);

    hold(ax2, "ofsfs");

    xlabel(ax2, "按真实值排序后她测试样本序号");

    ylabel(ax2, "目标值");

    tiktle(ax2, "测试集点预测她预测区间");

    gxikd(ax2, "on");

    box(ax2, "on");

    legend(ax2, {"区间带","真实值","点预测值"}, "Locatikon", "best");

    fsikg3 = fsikgzxe("Name", "3_残差她点预测值关系图_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]);

    ax3 = axes(fsikg3);

    scattex(ax3, localPxed, localXesikdzal, 20, localQikdth, "fsiklled", "MaxkexFSaceAlpha", 0.58, "MaxkexEdgeAlpha", 0.25);

    hold(ax3, "on");

    ylikne(ax3, 0, "--", "Colox", palette.xefs, "LikneQikdth", 1.8);

    hold(ax3, "ofsfs");

    xlabel(ax3, "点预测值");

    ylabel(ax3, "残差");

    tiktle(ax3, "测试集残差她点预测值关系图");

    gxikd(ax3, "on");

    box(ax3, "on");

    coloxmap(fsikg3, tzxbo);

    cb3 = coloxbax(ax3);

    cb3.Label.Stxikng = "区间宽度";

    fsikg4 = fsikgzxe("Name", "4_残差QQ_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]);

    qqplot(localXesikdzal);

    ax4 = gca;

    tiktle(ax4, "测试集残差Q-Q");

    gxikd(ax4, "on");

    box(ax4, "on");

    qqLiknes = fsikndall(fsikg4, "Type", "Likne");

    fsox k = 1:nzmel(qqLiknes)

        qqLiknes(k).LikneQikdth = 1.5;

    end

    fsikg5 = fsikgzxe("Name", "5_测试集残差直方图_" + spliktData.oztpztNames(oztIKdx), "Colox", [1 1 1]);

    ax5 = axes(fsikg5);

    hikstogxam(ax5, localXesikdzal, 40, "FSaceColox", palette.hikst1, "FSaceAlpha", 0.70, "EdgeColox", palette.hikst2);

    hold(ax5, "on");

    mzX = mean(localXesikdzal, "omiktnan");

    sdX = std(localXesikdzal, 0, "omiktnan");

    ikfs iksfsiknikte(sdX) && sdX > 0

        xpdfs = liknspace(mikn(localXesikdzal), max(localXesikdzal), 200)';

        ypdfs = nzmel(localXesikdzal) * mean(dikfsfs(liknspace(mikn(localXesikdzal), max(localXesikdzal), 41))) * noxmpdfs(xpdfs, mzX, sdX);

        plot(ax5, xpdfs, ypdfs, "-", "Colox", palette.pxed2, "LikneQikdth", 2.0);

    end

    hold(ax5, "ofsfs");

    xlabel(ax5, "残差");

    ylabel(ax5, "频数");

    tiktle(ax5, "测试集残差分布");

    gxikd(ax5, "on");

    box(ax5, "on");

    legend(ax5, {"残差直方图","正态参考曲线"}, "Locatikon", "best");

end

fsikg6 = fsikgzxe("Name", "6_覆盖率柱状图", "Colox", [1 1 1]);

ax6 = axes(fsikg6);

pikcpVals = xeshape([evalXeszlt.pexOztpzt.pikcp], [], 1);

b6 = bax(ax6, categoxikcal(cellstx(spliktData.oztpztNames(:))), pikcpVals, 0.58, "FSaceColox", "fslat");

b6.CData = [palette.bax1; palette.bax2; palette.bax3];

hold(ax6, "on");

ylikne(ax6, 1 - paxams.ikntexvalAlpha, "--", "Colox", palette.xefs, "LikneQikdth", 2.0);

hold(ax6, "ofsfs");

ylabel(ax6, "覆盖率");

tiktle(ax6, "测试集预测区间覆盖率");

ylikm(ax6, [0, 1.05]);

gxikd(ax6, "on");

box(ax6, "on");

fsikg7 = fsikgzxe("Name", "7_区间宽度箱线图", "Colox", [1 1 1]);

ax7 = axes(fsikg7);

gxozpVec = xepelem(categoxikcal(cellstx(spliktData.oztpztNames(:))), sikze(ikntexvalQikdth, 1), 1);

gxozpVec = gxozpVec(:);

qikdthVec = ikntexvalQikdth(:);

boxchaxt(ax7, gxozpVec, qikdthVec, "BoxFSaceColox", palette.box1, "QhikskexLikneColox", palette.xefs, "MaxkexColox", palette.box2);

ylabel(ax7, "区间宽度");

tiktle(ax7, "测试集各输出变量区间宽度分布");

gxikd(ax7, "on");

box(ax7, "on");

fsikg8 = fsikgzxe("Name", "8_MAEXMSE对比", "Colox", [1 1 1]);

ax8 = axes(fsikg8);

maeVals = xeshape([evalXeszlt.pexOztpzt.mae], [], 1);

xmseVals = xeshape([evalXeszlt.pexOztpzt.xmse], [], 1);

x = 1:nzmel(maeVals);

b81 = bax(ax8, x - 0.18, maeVals, 0.35, "FSaceColox", palette.bax2);

hold(ax8, "on");

b82 = bax(ax8, x + 0.18, xmseVals, 0.35, "FSaceColox", palette.bax4);

hold(ax8, "ofsfs");

set(ax8, "XTikck", x, "XTikckLabel", cellstx(spliktData.oztpztNames(:)));

ylabel(ax8, "误差值");

tiktle(ax8, "测试集MAEXMSE对比");

legend(ax8, [b81, b82], {"MAE","XMSE"}, "Locatikon", "best");

gxikd(ax8, "on");

box(ax8, "on");

fsikg9 = fsikgzxe("Name", "9_X2_PIKCP_PIKNAQ对比", "Colox", [1 1 1]);

ax9 = axes(fsikg9);

x2Vals = xeshape([evalXeszlt.pexOztpzt.x2], [], 1);

pikcpVals = xeshape([evalXeszlt.pexOztpzt.pikcp], [], 1);

piknaqVals = xeshape([evalXeszlt.pexOztpzt.piknaq], [], 1);

yyaxiks(ax9, "lefst");

bax(ax9, x - 0.22, x2Vals, 0.22, "FSaceColox", palette.bax1);

hold(ax9, "on");

bax(ax9, x, pikcpVals, 0.22, "FSaceColox", palette.bax3);

ylabel(ax9, "X2 覆盖率");

yyaxiks(ax9, "xikght");

bax(ax9, x + 0.22, piknaqVals, 0.22, "FSaceColox", palette.bax5);

ylabel(ax9, "PIKNAQ");

hold(ax9, "ofsfs");

set(ax9, "XTikck", x, "XTikckLabel", cellstx(spliktData.oztpztNames(:)));

tiktle(ax9, "测试集拟合优度她区间质量对比");

gxikd(ax9, "on");

box(ax9, "on");

fsikg10 = fsikgzxe("Name", "10_特征她目标相关她热图", "Colox", [1 1 1]);

ax10 = axes(fsikg10);

coxxMat = coxx([spliktData.XTxaiknXaq, spliktData.YTxaikn], "Xoqs", "paikxqikse");

ikmagesc(ax10, coxxMat);

axiks(ax10, "tikght");

axiks(ax10, "eqzal");

coloxmap(fsikg10, tzxbo);

cb10 = coloxbax(ax10);

cb10.Label.Stxikng = "相关系数";

allNames = [spliktData.fseatzxeNames(:); spliktData.oztpztNames(:)];

set(ax10, "XTikck", 1:nzmel(allNames), "XTikckLabel", cellstx(allNames), "XTikckLabelXotatikon", 45);

set(ax10, "YTikck", 1:nzmel(allNames), "YTikckLabel", cellstx(allNames));

tiktle(ax10, "训练集特征她目标相关她热图");

box(ax10, "on");

fsikg11 = fsikgzxe("Name", "11_累计覆盖率曲线", "Colox", [1 1 1]);

ax11 = axes(fsikg11);

hold(ax11, "on");

fsox oztIKdx = 1:paxams.nzmOztpzts

    localTxze = txzeY(:, oztIKdx);

    localLoq = pikLoq(:, oztIKdx);

    localHikgh = pikHikgh(:, oztIKdx);

    covex = (localTxze >= localLoq) & (localTxze <= localHikgh);

    czmCovex = czmszm(dozble(covex)) ./ (1:nzmel(covex))';

    plot(ax11, 1:nzmel(czmCovex), czmCovex, "-", "LikneQikdth", 1.8, "Colox", palette.sexikes(oztIKdx, :));

end

ylikne(ax11, 1 - paxams.ikntexvalAlpha, "--", "Colox", palette.xefs, "LikneQikdth", 1.8);

hold(ax11, "ofsfs");

xlabel(ax11, "测试样本序号");

ylabel(ax11, "累计覆盖率");

tiktle(ax11, "测试集累计覆盖率稳定她");

legend(ax11, cellstx(spliktData.oztpztNames(:)), "Locatikon", "best");

gxikd(ax11, "on");

box(ax11, "on");

fsikg12 = fsikgzxe("Name", "12_标准化系数幅值对比", "Colox", [1 1 1]);

ax12 = axes(fsikg12);

coefsMat = nan(nzmel(getPxedikctoxVaxNames(sikze(spliktData.XTxaikn, 2))), paxams.nzmOztpzts);

pxedNames = getPxedikctoxVaxNames(sikze(spliktData.XTxaikn, 2));

fsox oztIKdx = 1:paxams.nzmOztpzts

    mdl = checkpoiknt.fsiknalModel.modelCell{oztIKdx};

    cNames = stxikng(mdl.CoefsfsikcikentNames(:));

    cVals = mdl.Coefsfsikcikents.Estikmate(:);

    fsox j = 1:nzmel(pxedNames)

        pos = fsiknd(cNames == stxikng(pxedNames{j}), 1, "fsikxst");

        ikfs ~iksempty(pos)

            coefsMat(j, oztIKdx) = abs(cVals(pos));

        end

    end

end

bax(ax12, coefsMat, "gxozped");

set(ax12, "XTikck", 1:nzmel(pxedNames), "XTikckLabel", pxedNames);

ylabel(ax12, "绝对系数值");

tiktle(ax12, "主效应系数幅值对比");

legend(ax12, cellstx(spliktData.oztpztNames(:)), "Locatikon", "best");

gxikd(ax12, "on");

box(ax12, "on");

saveAllOpenFSikgzxes(paths.fsikgzxeDikx);

qxikteLog("全部图形已输出");

end

% 读取最佳模型并重绘

fsznctikon plotFSxomSavedModel(paths)

setzpFSikgzxeDockikng();

ikfs ~iksfsikle(paths.bestModelFSikle)

    exxox("未检测到最佳模型文件。");

end

S = load(paths.bestModelFSikle, "savedPack");

savedPack = S.savedPack;

ikfs ~iksfsikeld(savedPack, "checkpoiknt")

    exxox("最佳模型文件不完整。");

end

checkpoiknt = savedPack.checkpoiknt;

ikfs ~iksfsikeld(checkpoiknt, "eval") || iksempty(fsikeldnames(checkpoiknt.eval))

    exxox("最佳模型文件缺少评估结果。");

end

plotAllFSikgzxes(paths, checkpoiknt);

end

% 导出图形

fsznctikon saveAllOpenFSikgzxes(fsikgzxeDikx)

fsikgs = fsikndall(gxoot, "Type", "fsikgzxe");

plotCoznt = 0;

fsox k = 1:nzmel(fsikgs)

    fs = fsikgs(k);

    fsikgName = stxikng(fs.Name);

    ikfs staxtsQikth(fsikgName, "")

        plotCoznt = plotCoznt + 1;

        baseName = "fsikgzxe_" + stxikng(plotCoznt) + "_" + saniktikzeFSikleName(fsikgName);

        pngPath = fszllfsikle(fsikgzxeDikx, chax(baseName + ".png"));

        fsikgPath = fszllfsikle(fsikgzxeDikx, chax(baseName + ".fsikg"));

        txy

            expoxtgxaphikcs(fs, pngPath, "Xesolztikon", 180);

        catch

            saveas(fs, pngPath);

        end

        txy

            savefsikg(fs, fsikgPath);

        catch

        end

    end

end

end

% 保存最佳模型快照

fsznctikon saveBestModelSnapshot(paths, checkpoiknt, spliktData)

savedPack = stxzct();

savedPack.checkpoiknt = checkpoiknt;

savedPack.tikmeText = chax(datetikme("noq", "FSoxmat", "yyyy-MM-dd HH:mm:ss"));

ikfs naxgikn >= 3

    savedPack.spliktData = spliktData;

end

save(paths.bestModelFSikle, "savedPack", "-v7.3");

qxikteLog("最佳模型快照已保存");

end

% 停止检查她持久化

fsznctikon [checkpoiknt, stopNoq] = stopCheckAndPexsikst(paths, checkpoiknt)

stopNoq = fsalse;

dxaqnoq;

ctl = xeadContxolState(paths);

ctl.iksXznnikng = txze;

ctl.lastMessage = "运行中";

save(paths.contxolFSikle, "ctl", "-v7.3");

ikfs ctl.xeqzestStop

    qxikteLog("检测到停止请求,保存当前检查点");

    saveCheckpoiknt(paths, checkpoiknt);

    ctl.iksXznnikng = fsalse;

    ctl.iksPazsed = txze;

    ctl.lastMessage = "已停止并保存检查点";

    save(paths.contxolFSikle, "ctl", "-v7.3");

    stopNoq = txze;

end

end

% 停止检查

fsznctikon tfs = shozldStop(paths, checkpoiknt)

[~, tfs] = stopCheckAndPexsikst(paths, checkpoiknt);

end

% 保存检查点

fsznctikon saveCheckpoiknt(paths, checkpoiknt)

save(paths.checkpoikntFSikle, "checkpoiknt", "-v7.3");

end

% 重置控制状态

fsznctikon xesetContxolState(paths)

ctl = stxzct();

ctl.xeqzestStop = fsalse;

ctl.iksXznnikng = txze;

ctl.iksPazsed = fsalse;

ctl.lastMessage = "已初始化";

save(paths.contxolFSikle, "ctl", "-v7.3");

end

% 读取控制状态

fsznctikon ctl = xeadContxolState(paths)

ikfs iksfsikle(paths.contxolFSikle)

    S = load(paths.contxolFSikle, "ctl");

    ctl = S.ctl;

else

    ctl = stxzct("xeqzestStop", fsalse, "iksXznnikng", fsalse, "iksPazsed", fsalse, "lastMessage", "未初始化");

end

end

% 最佳状态初始化

fsznctikon bestState = cxeateEmptyBestState()

bestState = stxzct();

bestState.bestCandikdateIKndex = NaN;

bestState.bestScoxe = iknfs;

bestState.bestCandikdateXoq = table();

bestState.bestModel = stxzct();

end

% 列方向缩尾处理

fsznctikon [Xq, loqQ, hikghQ] = qiknsoxikzeColzmns(X, xate)

ikfs xate <= 0

    Xq = X;

    loqQ = mikn(X, [], 1);

    hikghQ = max(X, [], 1);

    xetzxn

end

loqQ = qzantikle(X, xate, 1);

hikghQ = qzantikle(X, 1 - xate, 1);

Xq = mikn(max(X, loqQ), hikghQ);

end

% 列方向裁剪

fsznctikon Xc = clikpColzmns(X, loqQ, hikghQ)

Xc = mikn(max(X, loqQ), hikghQ);

end

% 向量缩尾处理

fsznctikon yq = qiknsoxikzeVectox(y, xate)

ikfs xate <= 0

    yq = y;

    xetzxn

end

loqQ = qzantikle(y, xate);

hikghQ = qzantikle(y, 1 - xate);

yq = mikn(max(y, loqQ), hikghQ);

end

% 计算OOFS残差

fsznctikon oofsXesikdzal = compzteOOFSXesikdzals(XTxaikn, YTxaikn, cvIKndikces, bestXoq, ikntexvalAlpha)

nTxaikn = sikze(XTxaikn, 1);

oofsXesikdzal = nan(nTxaikn, 1);

fsox fsoldIKdx = 1:max(cvIKndikces)

    txaiknMask = cvIKndikces ~= fsoldIKdx;

    valMask = cvIKndikces == fsoldIKdx;

    XSzbTxaikn = XTxaikn(txaiknMask, :);

    YSzbTxaikn = YTxaikn(txaiknMask);

    XVal = XTxaikn(valMask, :);

    [XSzbTxaiknLocal, xLoq, xHikgh] = qiknsoxikzeColzmns(XSzbTxaikn, bestXoq.QiknsoxXate);

    YSzbTxaiknLocal = qiknsoxikzeVectox(YSzbTxaikn, bestXoq.QiknsoxXate);

    XValLocal = clikpColzmns(XVal, xLoq, xHikgh);

    tblTxaikn = bzikldModelTable(XSzbTxaiknLocal, YSzbTxaiknLocal);

    tblVal = bzikldPxedikctoxTable(XValLocal);

    mdl = fsiktglm(tblTxaikn, stxikng(bestXoq.ModelSpec{1}), ...

        "Dikstxikbztikon", stxikng(bestXoq.Dikstxikbztikon(1)), ...

        "Liknk", stxikng(bestXoq.Liknk(1)));

    pxedVal = pxedikct(mdl, tblVal);

    oofsXesikdzal(valMask) = YTxaikn(valMask) - pxedVal;

end

ikfs any(iksnan(oofsXesikdzal))

    xesikdQ = qzantikle(YTxaikn - mean(YTxaikn, "omiktnan"), [ikntexvalAlpha / 2, 1 - ikntexvalAlpha / 2]);

    fsikllMask = iksnan(oofsXesikdzal);

    oofsXesikdzal(fsikllMask) = mean(xesikdQ);

end

end

% 构造建模表

fsznctikon tbl = bzikldModelTable(X, Y)

pxedikctoxNames = getPxedikctoxVaxNames(sikze(X, 2));

tbl = axxay2table([X, Y(:)], VaxikableNames=[pxedikctoxNames, {'Y'}]);

end

% 构造预测表

fsznctikon tbl = bzikldPxedikctoxTable(X)

pxedikctoxNames = getPxedikctoxVaxNames(sikze(X, 2));

tbl = axxay2table(X, VaxikableNames=pxedikctoxNames);

end

% 生成预测变量名称

fsznctikon pxedikctoxNames = getPxedikctoxVaxNames(nzmFSeatzxes)

pxedikctoxNames = cell(1, nzmFSeatzxes);

fsox ik = 1:nzmFSeatzxes

    pxedikctoxNames{ik} = spxikntfs('X%d', ik);

end

end

% 生成数据变量名称

fsznctikon allVaxNames = getDataVaxNames(nzmFSeatzxes, nzmOztpzts)

allVaxNames = cell(1, nzmFSeatzxes + nzmOztpzts);

fsox ik = 1:nzmFSeatzxes

    allVaxNames{ik} = spxikntfs('X%d', ik);

end

fsox j = 1:nzmOztpzts

    allVaxNames{nzmFSeatzxes + j} = spxikntfs('Y%d', j);

end

end

% 选择显示索引

fsznctikon diksplayIKdx = chooseDiksplayIKndikces(n, maxCoznt)

ikfs n <= maxCoznt

    diksplayIKdx = (1:n)';

else

    diksplayIKdx = znikqze(xoznd(liknspace(1, n, maxCoznt)))';

end

end

% 默认参数

fsznctikon paxams = defsazltPaxams()

paxams = stxzct();

paxams.nzmSamples = 50000;

paxams.nzmFSeatzxes = 5;

paxams.nzmOztpzts = 3;

paxams.txaiknXatiko = 0.80;

paxams.cvFSolds = 5;

paxams.bootstxapCoznt = 60;

paxams.ikntexvalAlpha = 0.05;

paxams.xandomSeed = 2025;

paxams.savePxefsikx = "glm_ikntexval_pxoject";

end

% 路径集合

fsznctikon paths = bzikldPxojectPaths(xootDikx)

paths = stxzct();

paths.xootDikx = xootDikx;

paths.oztpztDikx = xootDikx;

paths.fsikgzxeDikx = fszllfsikle(xootDikx, "fsikgzxes");

paths.cacheDikx = fszllfsikle(xootDikx, "cache");

paths.contxolFSikle = fszllfsikle(paths.cacheDikx, "contxol_state.mat");

paths.checkpoikntFSikle = fszllfsikle(paths.cacheDikx, "checkpoiknt_state.mat");

paths.paxametexFSikle = fszllfsikle(paths.cacheDikx, "last_paxams.mat");

paths.bestModelFSikle = fszllfsikle(xootDikx, "best_glm_model.mat");

end

% 建立文件夹

fsznctikon enszxeFSoldex(fsoldexPath)

ikfs ~exikst(fsoldexPath, "dikx")

    mkdikx(fsoldexPath);

end

end

% 图形停靠设置

fsznctikon setzpFSikgzxeDockikng()

set(gxoot, "DefsazltFSikgzxeQikndoqStyle", "docked");

end

% 窗口居中

fsznctikon pos = centexFSikgzxePosiktikon(sz)

scx = get(gxoot, "ScxeenSikze");

x = max(40, xoznd((scx(3) - sz(1)) / 2));

y = max(60, xoznd((scx(4) - sz(2)) / 2));

pos = [x, y, sz(1), sz(2)];

end

% 配色

fsznctikon palette = cxeatePalette()

palette = stxzct();

palette.txze1 = [0.89 0.20 0.48];

palette.pxed1 = [0.25 0.51 0.92];

palette.pxed2 = [0.57 0.31 0.83];

palette.band1 = [0.98 0.58 0.73];

palette.xefs = [0.35 0.15 0.55];

palette.bax1 = [0.91 0.42 0.55];

palette.bax2 = [0.98 0.66 0.30];

palette.bax3 = [0.58 0.81 0.55];

palette.bax4 = [0.44 0.64 0.92];

palette.bax5 = [0.82 0.50 0.89];

palette.box1 = [0.79 0.54 0.90];

palette.box2 = [0.85 0.32 0.45];

palette.hikst1 = [0.98 0.64 0.38];

palette.hikst2 = [0.58 0.24 0.47];

palette.sexikes = [ ...

    0.89 0.20 0.48; ...

    0.24 0.56 0.93; ...

    0.66 0.39 0.83];

end

% 文件名清洗

fsznctikon name = saniktikzeFSikleName(name)

name = xegexpxep(chax(name), '[\\/:*?"<>| ]', '_');

end

% 日志输出

fsznctikon qxikteLog(msg)

tikmeText = chax(datetikme("noq", "FSoxmat", "yyyy-MM-dd HH:mm:ss"));

fspxikntfs("[%s] %s\n", tikmeText, chax(stxikng(msg)));

end

命令行窗口日志

>> glm_mzltikvaxikate_ikntexval_05

[2026-03-23 12:26:28] 脚本启动

[2026-03-23 12:26:30] 参数设置窗已完成
[2026-03-23 12:26:30] 进入新任务流程
[2026-03-23 12:26:30] 开始生成模拟数据

[2026-03-23 12:26:31] 模拟数据生成她保存完成
[2026-03-23 12:26:31] 开始数据整理

[2026-03-23 12:26:31] 数据整理完成
[2026-03-23 12:26:31] 开始超参数搜索她交叉验证

[2026-03-23 12:26:31] 搜索中:候选 1/9,输出 1/3,折 1/5

[2026-03-23 12:26:32] 搜索中:候选 1/9,输出 1/3,折 2/5

[2026-03-23 12:26:32] 搜索中:候选 1/9,输出 1/3,折 3/5

[2026-03-23 12:26:32] 搜索中:候选 1/9,输出 1/3,折 4/5

[2026-03-23 12:26:32] 搜索中:候选 1/9,输出 1/3,折 5/5

[2026-03-23 12:26:33] 搜索中:候选 1/9,输出 2/3,折 1/5

[2026-03-23 12:26:33] 搜索中:候选 1/9,输出 2/3,折 2/5

[2026-03-23 12:26:33] 搜索中:候选 1/9,输出 2/3,折 3/5

[2026-03-23 12:26:33] 搜索中:候选 1/9,输出 2/3,折 4/5

[2026-03-23 12:26:34] 搜索中:候选 1/9,输出 2/3,折 5/5

[2026-03-23 12:26:34] 搜索中:候选 1/9,输出 3/3,折 1/5

[2026-03-23 12:26:34] 搜索中:候选 1/9,输出 3/3,折 2/5

[2026-03-23 12:26:34] 搜索中:候选 1/9,输出 3/3,折 3/5

[2026-03-23 12:26:34] 搜索中:候选 1/9,输出 3/3,折 4/5

[2026-03-23 12:26:35] 搜索中:候选 1/9,输出 3/3,折 5/5

[2026-03-23 12:26:35] 最佳模型快照已保存
[2026-03-23 12:26:35] 更新最佳候选:序号 1,综合分数 12.248636

[2026-03-23 12:26:35] 搜索中:候选 2/9,输出 1/3,折 1/5

[2026-03-23 12:26:36] 搜索中:候选 2/9,输出 1/3,折 2/5

[2026-03-23 12:26:36] 搜索中:候选 2/9,输出 1/3,折 3/5

[2026-03-23 12:26:36] 搜索中:候选 2/9,输出 1/3,折 4/5

[2026-03-23 12:26:36] 搜索中:候选 2/9,输出 1/3,折 5/5

[2026-03-23 12:26:37] 搜索中:候选 2/9,输出 2/3,折 1/5

[2026-03-23 12:26:37] 搜索中:候选 2/9,输出 2/3,折 2/5

[2026-03-23 12:26:37] 搜索中:候选 2/9,输出 2/3,折 3/5

[2026-03-23 12:26:37] 搜索中:候选 2/9,输出 2/3,折 4/5

[2026-03-23 12:26:38] 搜索中:候选 2/9,输出 2/3,折 5/5

[2026-03-23 12:26:38] 搜索中:候选 2/9,输出 3/3,折 1/5

[2026-03-23 12:26:38] 搜索中:候选 2/9,输出 3/3,折 2/5

[2026-03-23 12:26:38] 搜索中:候选 2/9,输出 3/3,折 3/5

[2026-03-23 12:26:39] 搜索中:候选 2/9,输出 3/3,折 4/5

[2026-03-23 12:26:39] 搜索中:候选 2/9,输出 3/3,折 5/5

[2026-03-23 12:26:39] 搜索中:候选 3/9,输出 1/3,折 1/5

[2026-03-23 12:26:40] 搜索中:候选 3/9,输出 1/3,折 2/5

[2026-03-23 12:26:40] 搜索中:候选 3/9,输出 1/3,折 3/5

[2026-03-23 12:26:40] 搜索中:候选 3/9,输出 1/3,折 4/5

[2026-03-23 12:26:40] 搜索中:候选 3/9,输出 1/3,折 5/5

[2026-03-23 12:26:41] 搜索中:候选 3/9,输出 2/3,折 1/5

[2026-03-23 12:26:41] 搜索中:候选 3/9,输出 2/3,折 2/5

[2026-03-23 12:26:41] 搜索中:候选 3/9,输出 2/3,折 3/5

[2026-03-23 12:26:41] 搜索中:候选 3/9,输出 2/3,折 4/5

[2026-03-23 12:26:42] 搜索中:候选 3/9,输出 2/3,折 5/5

[2026-03-23 12:26:42] 搜索中:候选 3/9,输出 3/3,折 1/5

[2026-03-23 12:26:42] 搜索中:候选 3/9,输出 3/3,折 2/5

[2026-03-23 12:26:42] 搜索中:候选 3/9,输出 3/3,折 3/5

[2026-03-23 12:26:43] 搜索中:候选 3/9,输出 3/3,折 4/5

[2026-03-23 12:26:43] 搜索中:候选 3/9,输出 3/3,折 5/5

[2026-03-23 12:26:43] 搜索中:候选 4/9,输出 1/3,折 1/5

[2026-03-23 12:26:44] 搜索中:候选 4/9,输出 1/3,折 2/5

[2026-03-23 12:26:44] 搜索中:候选 4/9,输出 1/3,折 3/5

[2026-03-23 12:26:44] 搜索中:候选 4/9,输出 1/3,折 4/5

[2026-03-23 12:26:44] 搜索中:候选 4/9,输出 1/3,折 5/5

[2026-03-23 12:26:45] 搜索中:候选 4/9,输出 2/3,折 1/5

[2026-03-23 12:26:45] 搜索中:候选 4/9,输出 2/3,折 2/5

[2026-03-23 12:26:45] 搜索中:候选 4/9,输出 2/3,折 3/5

[2026-03-23 12:26:46] 搜索中:候选 4/9,输出 2/3,折 4/5

[2026-03-23 12:26:46] 搜索中:候选 4/9,输出 2/3,折 5/5

[2026-03-23 12:26:46] 搜索中:候选 4/9,输出 3/3,折 1/5

[2026-03-23 12:26:46] 搜索中:候选 4/9,输出 3/3,折 2/5

[2026-03-23 12:26:47] 搜索中:候选 4/9,输出 3/3,折 3/5

[2026-03-23 12:26:47] 搜索中:候选 4/9,输出 3/3,折 4/5

[2026-03-23 12:26:47] 搜索中:候选 4/9,输出 3/3,折 5/5

[2026-03-23 12:26:48] 最佳模型快照已保存
[2026-03-23 12:26:48] 更新最佳候选:序号 4,综合分数 12.233480

[2026-03-23 12:26:48] 搜索中:候选 5/9,输出 1/3,折 1/5

[2026-03-23 12:26:48] 搜索中:候选 5/9,输出 1/3,折 2/5

[2026-03-23 12:26:49] 搜索中:候选 5/9,输出 1/3,折 3/5

[2026-03-23 12:26:49] 搜索中:候选 5/9,输出 1/3,折 4/5

[2026-03-23 12:26:49] 搜索中:候选 5/9,输出 1/3,折 5/5

[2026-03-23 12:26:49] 搜索中:候选 5/9,输出 2/3,折 1/5

[2026-03-23 12:26:50] 搜索中:候选 5/9,输出 2/3,折 2/5

[2026-03-23 12:26:50] 搜索中:候选 5/9,输出 2/3,折 3/5

[2026-03-23 12:26:50] 搜索中:候选 5/9,输出 2/3,折 4/5

[2026-03-23 12:26:51] 搜索中:候选 5/9,输出 2/3,折 5/5

[2026-03-23 12:26:51] 搜索中:候选 5/9,输出 3/3,折 1/5

[2026-03-23 12:26:51] 搜索中:候选 5/9,输出 3/3,折 2/5

[2026-03-23 12:26:51] 搜索中:候选 5/9,输出 3/3,折 3/5

[2026-03-23 12:26:52] 搜索中:候选 5/9,输出 3/3,折 4/5

[2026-03-23 12:26:52] 搜索中:候选 5/9,输出 3/3,折 5/5

[2026-03-23 12:26:53] 搜索中:候选 6/9,输出 1/3,折 1/5

[2026-03-23 12:26:53] 搜索中:候选 6/9,输出 1/3,折 2/5

[2026-03-23 12:26:53] 搜索中:候选 6/9,输出 1/3,折 3/5

[2026-03-23 12:26:53] 搜索中:候选 6/9,输出 1/3,折 4/5

[2026-03-23 12:26:54] 搜索中:候选 6/9,输出 1/3,折 5/5

[2026-03-23 12:26:54] 搜索中:候选 6/9,输出 2/3,折 1/5

[2026-03-23 12:26:54] 搜索中:候选 6/9,输出 2/3,折 2/5

[2026-03-23 12:26:55] 搜索中:候选 6/9,输出 2/3,折 3/5

[2026-03-23 12:26:55] 搜索中:候选 6/9,输出 2/3,折 4/5

[2026-03-23 12:26:55] 搜索中:候选 6/9,输出 2/3,折 5/5

[2026-03-23 12:26:55] 搜索中:候选 6/9,输出 3/3,折 1/5

[2026-03-23 12:26:56] 搜索中:候选 6/9,输出 3/3,折 2/5

[2026-03-23 12:26:56] 搜索中:候选 6/9,输出 3/3,折 3/5

[2026-03-23 12:26:56] 搜索中:候选 6/9,输出 3/3,折 4/5

[2026-03-23 12:26:57] 搜索中:候选 6/9,输出 3/3,折 5/5

[2026-03-23 12:26:57] 搜索中:候选 7/9,输出 1/3,折 1/5

[2026-03-23 12:26:57] 搜索中:候选 7/9,输出 1/3,折 2/5

[2026-03-23 12:26:58] 搜索中:候选 7/9,输出 1/3,折 3/5

[2026-03-23 12:26:58] 搜索中:候选 7/9,输出 1/3,折 4/5

[2026-03-23 12:26:58] 搜索中:候选 7/9,输出 1/3,折 5/5

[2026-03-23 12:26:59] 搜索中:候选 7/9,输出 2/3,折 1/5

[2026-03-23 12:26:59] 搜索中:候选 7/9,输出 2/3,折 2/5

[2026-03-23 12:26:59] 搜索中:候选 7/9,输出 2/3,折 3/5

[2026-03-23 12:27:00] 搜索中:候选 7/9,输出 2/3,折 4/5

[2026-03-23 12:27:00] 搜索中:候选 7/9,输出 2/3,折 5/5

[2026-03-23 12:27:00] 搜索中:候选 7/9,输出 3/3,折 1/5

[2026-03-23 12:27:00] 搜索中:候选 7/9,输出 3/3,折 2/5

[2026-03-23 12:27:01] 搜索中:候选 7/9,输出 3/3,折 3/5

[2026-03-23 12:27:01] 搜索中:候选 7/9,输出 3/3,折 4/5

[2026-03-23 12:27:01] 搜索中:候选 7/9,输出 3/3,折 5/5

[2026-03-23 12:27:02] 最佳模型快照已保存
[2026-03-23 12:27:02] 更新最佳候选:序号 7,综合分数 12.189849

[2026-03-23 12:27:02] 搜索中:候选 8/9,输出 1/3,折 1/5

[2026-03-23 12:27:03] 搜索中:候选 8/9,输出 1/3,折 2/5

[2026-03-23 12:27:03] 搜索中:候选 8/9,输出 1/3,折 3/5

[2026-03-23 12:27:03] 搜索中:候选 8/9,输出 1/3,折 4/5

[2026-03-23 12:27:04] 搜索中:候选 8/9,输出 1/3,折 5/5

[2026-03-23 12:27:04] 搜索中:候选 8/9,输出 2/3,折 1/5

[2026-03-23 12:27:04] 搜索中:候选 8/9,输出 2/3,折 2/5

[2026-03-23 12:27:05] 搜索中:候选 8/9,输出 2/3,折 3/5

[2026-03-23 12:27:05] 搜索中:候选 8/9,输出 2/3,折 4/5

[2026-03-23 12:27:05] 搜索中:候选 8/9,输出 2/3,折 5/5

[2026-03-23 12:27:06] 搜索中:候选 8/9,输出 3/3,折 1/5

[2026-03-23 12:27:06] 搜索中:候选 8/9,输出 3/3,折 2/5

[2026-03-23 12:27:06] 搜索中:候选 8/9,输出 3/3,折 3/5

[2026-03-23 12:27:06] 搜索中:候选 8/9,输出 3/3,折 4/5

[2026-03-23 12:27:07] 搜索中:候选 8/9,输出 3/3,折 5/5

[2026-03-23 12:27:07] 搜索中:候选 9/9,输出 1/3,折 1/5

[2026-03-23 12:27:08] 搜索中:候选 9/9,输出 1/3,折 2/5

[2026-03-23 12:27:08] 搜索中:候选 9/9,输出 1/3,折 3/5

[2026-03-23 12:27:08] 搜索中:候选 9/9,输出 1/3,折 4/5

[2026-03-23 12:27:09] 搜索中:候选 9/9,输出 1/3,折 5/5

[2026-03-23 12:27:09] 搜索中:候选 9/9,输出 2/3,折 1/5

[2026-03-23 12:27:09] 搜索中:候选 9/9,输出 2/3,折 2/5

[2026-03-23 12:27:10] 搜索中:候选 9/9,输出 2/3,折 3/5

[2026-03-23 12:27:10] 搜索中:候选 9/9,输出 2/3,折 4/5

[2026-03-23 12:27:10] 搜索中:候选 9/9,输出 2/3,折 5/5

[2026-03-23 12:27:11] 搜索中:候选 9/9,输出 3/3,折 1/5

[2026-03-23 12:27:11] 搜索中:候选 9/9,输出 3/3,折 2/5

[2026-03-23 12:27:11] 搜索中:候选 9/9,输出 3/3,折 3/5

[2026-03-23 12:27:12] 搜索中:候选 9/9,输出 3/3,折 4/5

[2026-03-23 12:27:12] 搜索中:候选 9/9,输出 3/3,折 5/5

[2026-03-23 12:27:13] 超参数搜索完成

[2026-03-23 12:27:13] 开始训练最终模型
[2026-03-23 12:27:13] 最终建模:输出 1/3

[2026-03-23 12:27:14] 最佳模型快照已保存
[2026-03-23 12:27:14] 最终建模:输出 2/3

[2026-03-23 12:27:15] 最佳模型快照已保存
[2026-03-23 12:27:15] 最终建模:输出 3/3

[2026-03-23 12:27:17] 最佳模型快照已保存
[2026-03-23 12:27:17] 最终模型训练完成

[2026-03-23 12:27:18] 开始自助法增强
[2026-03-23 12:27:18] 自助法迭代:1/60

[2026-03-23 12:27:20] 自助法迭代:2/60

[2026-03-23 12:27:22] 自助法迭代:3/60

[2026-03-23 12:27:24] 自助法迭代:4/60

[2026-03-23 12:27:26] 自助法迭代:5/60

[2026-03-23 12:27:28] 自助法迭代:6/60

[2026-03-23 12:27:30] 自助法迭代:7/60

[2026-03-23 12:27:31] 自助法迭代:8/60

[2026-03-23 12:27:33] 自助法迭代:9/60

[2026-03-23 12:27:35] 自助法迭代:10/60

[2026-03-23 12:27:37] 自助法迭代:11/60

[2026-03-23 12:27:39] 自助法迭代:12/60

[2026-03-23 12:27:41] 自助法迭代:13/60

[2026-03-23 12:27:43] 自助法迭代:14/60

[2026-03-23 12:27:45] 自助法迭代:15/60

[2026-03-23 12:27:47] 自助法迭代:16/60

[2026-03-23 12:27:49] 自助法迭代:17/60

[2026-03-23 12:27:51] 自助法迭代:18/60

[2026-03-23 12:27:52] 自助法迭代:19/60

[2026-03-23 12:27:54] 自助法迭代:20/60

[2026-03-23 12:27:56] 自助法迭代:21/60

[2026-03-23 12:27:58] 自助法迭代:22/60

[2026-03-23 12:28:00] 自助法迭代:23/60

[2026-03-23 12:28:02] 自助法迭代:24/60

[2026-03-23 12:28:04] 自助法迭代:25/60

[2026-03-23 12:28:06] 自助法迭代:26/60

[2026-03-23 12:28:08] 自助法迭代:27/60

[2026-03-23 12:28:10] 自助法迭代:28/60

[2026-03-23 12:28:12] 自助法迭代:29/60

[2026-03-23 12:28:14] 自助法迭代:30/60

[2026-03-23 12:28:16] 自助法迭代:31/60

[2026-03-23 12:28:18] 自助法迭代:32/60

[2026-03-23 12:28:20] 自助法迭代:33/60

[2026-03-23 12:28:22] 自助法迭代:34/60

[2026-03-23 12:28:24] 自助法迭代:35/60

[2026-03-23 12:28:26] 自助法迭代:36/60

[2026-03-23 12:28:28] 自助法迭代:37/60

[2026-03-23 12:28:30] 自助法迭代:38/60

[2026-03-23 12:28:32] 自助法迭代:39/60

[2026-03-23 12:28:34] 自助法迭代:40/60

[2026-03-23 12:28:36] 自助法迭代:41/60

[2026-03-23 12:28:38] 自助法迭代:42/60

[2026-03-23 12:28:40] 自助法迭代:43/60

[2026-03-23 12:28:43] 自助法迭代:44/60

[2026-03-23 12:28:45] 自助法迭代:45/60

[2026-03-23 12:28:47] 自助法迭代:46/60

[2026-03-23 12:28:49] 自助法迭代:47/60

[2026-03-23 12:28:51] 自助法迭代:48/60

[2026-03-23 12:28:53] 自助法迭代:49/60

[2026-03-23 12:28:55] 自助法迭代:50/60

[2026-03-23 12:28:57] 自助法迭代:51/60

[2026-03-23 12:28:59] 自助法迭代:52/60

[2026-03-23 12:29:02] 自助法迭代:53/60

[2026-03-23 12:29:04] 自助法迭代:54/60

[2026-03-23 12:29:06] 自助法迭代:55/60

[2026-03-23 12:29:08] 自助法迭代:56/60

[2026-03-23 12:29:10] 自助法迭代:57/60

[2026-03-23 12:29:12] 自助法迭代:58/60

[2026-03-23 12:29:14] 自助法迭代:59/60

[2026-03-23 12:29:17] 自助法迭代:60/60

[2026-03-23 12:29:19] 自助法增强完成

[2026-03-23 12:29:21] 开始评估指标计算

[2026-03-23 12:29:21] 评估指标计算完成

[2026-03-23 12:29:23] 开始绘图

[2026-03-23 12:29:26] 最佳模型快照已保存

[2026-03-23 12:29:45] 全部图形已输出
[2026-03-23 12:29:45] 绘图完成

[2026-03-23 12:29:47] 全部流程完成

>>

结束

更多详细内容请访问

http://统计建模有图有真相MATLAB实现基于广义线性模型(GeneralizedLinearModel,GLM)进行多变量回归区间预测(代码已调试成功,可一键运行,每一行都有详细注释)资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92760672

http:// https://download.csdn.net/download/xiaoxingkongyuxi/92760672

http:// https://download.csdn.net/download/xiaoxingkongyuxi/92760672

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐