CEEMDAN-ISOS-VMD-GRU-ARIMA:非平稳时间序列预测全链路拆解
简介这份资源面向计算机、电子信息工程、数学等专业的大学生及算法初学者提供一套完整的CEEMDAN-ISOS-VMD-GRU-ARIMA时间序列预测实现方案可用于课程设计、期末大作业或毕业设计。资源包共3个文件包含2个CSV数据文件与1个Python源码文件压缩包约52KB数据文件用于模型训练与验证源码文件则串联起信号分解、组合预测与结果评估的完整流程。代码基于Anaconda、PyCharm与TensorFlow环境编写采用参数化编程思路参数修改方便且配有保姆级注释几乎一行一注释便于新手理解每一步的算法逻辑与数据流向。目前已有388人学习下载读者可从中获得从数据预处理、分解策略到GRU与ARIMA组合建模的完整实现路径并借助清晰注释快速上手调试与二次开发。1. CEEMDAN-ISOS-VMD-GRU-ARIMA一条把非平稳序列拆到骨头里的预测链路风电功率、光伏出力、负荷曲线这类时间序列直接丢进 GRU 或 ARIMAMAPE 经常卡在 8% 到 15% 下不去。不是模型不行是序列本身太脏——趋势、周期、突变、噪声全叠在一起单一模型学不动。这套 CEEMDAN-ISOS-VMD-GRU-ARIMA 的思路就是先把序列按频率拆开再用不同模型分别吃不同分量最后重构。CEEMDAN 负责自适应分解出 IMF 分量ISOS 做分量复杂度评估和重构VMD 对高频分量二次分解GRU 抓非线性残差ARIMA 兜底线性趋势。适合做电力负荷、风速、光伏、销量这类强非平稳预测的从业者也适合想把信号分解和深度学习串起来的新手。下面按为什么这么拆 → 每步怎么落地 → 参数怎么调 → 哪里会翻车的顺序讲透。2. 分解链路的设计逻辑为什么不是直接上 GRU2.1 CEEMDAN 相比 EMD、EEMD 解决了什么EMD 的模态混叠是老问题一个 IMF 里混着不同时间尺度的成分后续建模根本没法对应。EEMD 靠加高斯白噪声再做集合平均缓解但噪声残留和重构误差又冒出来。CEEMDANComplete Ensemble EMD with Adaptive Noise在每个分解阶段加入自适应白噪声并且把每阶残差作为下一阶的输入最终重构误差几乎为零。落地时核心参数只有三个参数含义常用取值调整方向Nstd加入噪声的标准差比例0.2序列噪声大调到 0.3干净序列 0.1NR噪声实现次数100~500次数越多越稳但耗时线性增长MaxIter单次 EMD 最大迭代5000出现分量爆炸时调大常见做法是用PyEMD库的 CEEMDAN 实现或者自己按 Torres 2011 的论文写。我一般直接用 PyEMD省得在筛选停止准则上反复调。from PyEMD import CEEMDAN import numpy as np # signal: 一维 numpy 数组长度建议 1000 ceemdan CEEMDAN(trials200) # trials 对应 NR IMFs ceemdan(signal) # 返回 shape(n_imfs, len(signal)) residue signal - IMFs.sum(axis0) print(重构误差 max:, np.abs(residue).max())trials就是噪声实现次数200 是精度和耗时的平衡点。IMFs最后一行通常是趋势项前面几行是高频到低频。重构误差如果大于 1e-10说明 trials 太小或者信号里有 NaN先查数据再做分解。2.2 ISOS 做分量重构把相似分量合并降维CEEMDAN 出来十几个 IMF 是常态逐个建模计算量爆炸而且相邻 IMF 频率接近分开建模反而引入冗余。ISOSImproved Symbiotic Organisms Search在这里的作用是以样本熵或排列熵为相似度指标把熵值接近的 IMF 聚成一组组内叠加成一个新分量。为什么用 ISOS 而不是直接 K-means因为分量个数不确定K-means 要预设 K。ISOS 是群智能优化把分组方案编码成生物体位置适应度函数用组内熵方差最小化自动搜出最优分组数。import antropy as ant import numpy as np def entropy_features(IMFs): # 对每个 IMF 算排列熵作为 ISOS 的输入特征 return np.array([ant.perm_entropy(imf, normalizeTrue) for imf in IMFs]) def fitness(groups, entropies): # groups: list of list, 每个子列表是 IMF 索引 score 0.0 for g in groups: if len(g) 0: return 1e9 score np.var(entropies[g]) return scoreperm_entropy的order默认 3序列短可以降到 2。适应度里组内方差越小说明同组分量越相似。ISOS 的种群规模和迭代次数一般设 30 和 100再大收益递减。分组完成后每组 IMF 直接相加得到重构分量分量数通常从 12~15 降到 4~6。2.3 VMD 二次分解高频分量把突变拎出来重构后频率最高的那个分量往往还混着突变和噪声直接喂 GRU 会让模型去拟合噪声。VMDVariational Mode Decomposition把信号分解成 K 个窄带模态对高频分量再拆一次能把突变单独分离。VMD 两个关键参数K模态数和alpha带宽约束。K 太小欠分解太大会过分解出虚假模态。常用做法是先设 K3~5看中心频率有没有重叠重叠就减 K。from vmdpy import VMD # 对高频重构分量 high_freq 做二次分解 alpha 2000 # 带宽约束越大带宽越窄 tau 0 # 噪声容限无先验噪声设 0 K 4 # 模态数 DC 0 # 不含直流分量 init 1 # 中心频率初始化 tol 1e-7 u, u_hat, omega VMD(high_freq, alpha, tau, K, DC, init, tol) # u shape(K, len), 每行是一个模态alpha2000对负荷类信号比较稳风速信号可以降到 1000。分解完看omega最后一行的中心频率如果两个模态频率差小于 0.01说明 K 设大了。VMD 对端点效应敏感序列首尾各截掉 5% 再分解或者做镜像延拓。2.4 GRU 与 ARIMA 的分工线性和非线性各管一段重构后的分量按熵值分两类低熵分量规律性强用 ARIMA 建模高熵分量非线性强用 GRU。这不是拍脑袋是 ARIMA 对线性自相关结构建模效率高GRU 对非线性映射强混着用反而互相干扰。GRU 输入用滑动窗口构造窗口长度一般取 24日周期或 168周周期。ARIMA 的 (p,d,q) 用 AIC 定阶d 用 ADF 检验确定。两部分预测结果按分量重构的逆运算叠加得到最终预测。提示GRU 和 ARIMA 的预测误差量级要统一建议都在归一化空间里建模最后再反归一化否则叠加时高幅值分量会淹没低幅值分量。3. 从原始序列到预测结果完整落地步骤3.1 数据准备与平稳性检验拿到序列先做三件事缺失值插补、异常值剔除、平稳性检验。缺失值用线性插值异常值用 3σ 或 IQR别用均值填充会抹掉突变信息。import pandas as pd import numpy as np from statsmodels.tsa.stattools import adfuller df pd.read_csv(load.csv, parse_dates[timestamp]) series df[value].interpolate(methodlinear).values # 3σ 异常值替换为插值 mu, sigma series.mean(), series.std() outliers np.abs(series - mu) 3 * sigma series[outliers] np.nan series pd.Series(series).interpolate().values # ADF 检验 adf_stat, p_value, _, _, crit, _ adfuller(series, autolagAIC) print(fADF{adf_stat:.4f}, p{p_value:.4f}) if p_value 0.05: print(非平稳ARIMA 需要差分)autolagAIC自动选滞后阶。p 值大于 0.05 说明非平稳ARIMA 的 d 至少取 1。做完差分再检验一次直到平稳。注意 CEEMDAN 分解前不需要序列平稳分解本身就能处理非平稳但 ARIMA 分支必须平稳。3.2 CEEMDAN 分解与分量导出from PyEMD import CEEMDAN ceemdan CEEMDAN(trials200) IMFs ceemdan(series) np.save(imfs.npy, IMFs) # 检查分量数和重构误差 print(IMF 数量:, IMFs.shape[0]) recon IMFs.sum(axis0) print(最大重构误差:, np.abs(recon - series).max())trials200对长度 5000 以内的序列够用。如果分量数超过 15先别急着调参检查序列里有没有阶跃或断点CEEMDAN 对突变会过度分解。导出后每个 IMF 单独存一列方便后续按索引分组。3.3 ISOS 分组重构import antropy as ant import numpy as np IMFs np.load(imfs.npy) entropies np.array([ant.perm_entropy(imf, normalizeTrue) for imf in IMFs]) # ISOS 简化实现种群初始化 共生阶段 def isos_group(entropies, pop_size30, max_iter100): n len(entropies) # 每个个体是一个分组边界向量排序后切分 pop np.random.rand(pop_size, n) best_pos, best_fit None, 1e9 for _ in range(max_iter): for i in range(pop_size): order np.argsort(pop[i]) # 按熵值排序后均分 4 组 groups np.array_split(order, 4) fit sum(np.var(entropies[g]) for g in groups) if fit best_fit: best_fit, best_pos fit, groups # 共生更新简化向最优靠拢 for i in range(pop_size): pop[i] 0.1 * (pop[np.argmin([sum(np.var(entropies[g]) for g in np.array_split(np.argsort(p), 4)) for p in pop])] - pop[i]) return best_pos groups isos_group(entropies) reconstructed np.array([IMFs[g].sum(axis0) for g in groups]) np.save(reconstructed.npy, reconstructed) print(重构后分量数:, reconstructed.shape[0])这里把分组问题简化成排序切分实际 ISOS 要做互利、偏利、寄生三个阶段。pop_size30、max_iter100对 15 个 IMF 足够。分组数不固定时可以在适应度里加惩罚项控制组数。重构后分量按熵值从低到高排列低熵走 ARIMA高熵走 GRU。3.4 VMD 二次分解高频分量from vmdpy import VMD import numpy as np reconstructed np.load(reconstructed.npy) high_freq reconstructed[-1] # 熵值最高的分量 u, u_hat, omega VMD(high_freq, alpha2000, tau0, K4, DC0, init1, tol1e-7) np.save(vmd_modes.npy, u) print(中心频率:, omega[-1])K4是起点看omega[-1]里相邻频率差小于 0.01 就减 K。VMD 分解出的模态按频率从低到高排列低频模态可以并回 ARIMA 分支高频模态进 GRU。端点效应处理分解前对high_freq做镜像延拓分解后截掉延拓部分。3.5 GRU 建模与训练import torch import torch.nn as nn import numpy as np class GRUNet(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, output_size1): super().__init__() self.gru nn.GRU(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, output_size) def forward(self, x): out, _ self.gru(x) return self.fc(out[:, -1, :]) def make_windows(data, window24): X, y [], [] for i in range(len(data) - window): X.append(data[i:iwindow]) y.append(data[iwindow]) return np.array(X), np.array(y) # 对每个高频模态分别建模 modes np.load(vmd_modes.npy) preds [] for mode in modes: X, y make_windows(mode, window24) X torch.tensor(X, dtypetorch.float32).unsqueeze(-1) y torch.tensor(y, dtypetorch.float32).unsqueeze(-1) model GRUNet() opt torch.optim.Adam(model.parameters(), lr1e-3) loss_fn nn.MSELoss() for epoch in range(200): model.train() opt.zero_grad() loss loss_fn(model(X), y) loss.backward() opt.step() model.eval() with torch.no_grad(): pred model(X).squeeze().numpy() preds.append(pred)hidden_size64、num_layers2对大多数分量够用分量长可以加到 128。window24对应日周期周周期数据改 168。学习率 1e-3 配 Adam200 epoch 后看 loss 曲线震荡就降到 5e-4。每个模态单独训练别把所有模态堆一起否则模型会偏向高幅值模态。3.6 ARIMA 建模与叠加from statsmodels.tsa.arima.model import ARIMA import numpy as np reconstructed np.load(reconstructed.npy) low_freq reconstructed[:-1] # 低熵分量走 ARIMA arima_preds [] for comp in low_freq: model ARIMA(comp, order(2, 1, 2)) fit model.fit() pred fit.forecast(stepslen(comp) - 24) arima_preds.append(pred) # 叠加ARIMA 预测 GRU 预测 VMD 模态预测 final np.sum(arima_preds, axis0) np.sum(preds, axis0)order(2,1,2)是起点用 AIC 网格搜 (0~3, d, 0~3)。forecast的 steps 要和 GRU 预测长度对齐。叠加时注意各分支预测起点一致差一个时间步结果就全错。反归一化如果做了归一化在这一步之后做。4. 避坑与排查这套链路最容易翻车的 5 个地方4.1 CEEMDAN 分量数爆炸跑到 20 个以上现象分解出来 20 多个 IMF后面几个幅值接近零。原因序列里有阶跃或异常值CEEMDAN 把突变当成独立模态反复拆。解决分解前做异常值检测阶跃点做平滑或分段处理trials从 200 降到 100噪声标准差Nstd从 0.2 降到 0.1。4.2 ISOS 分组结果每次跑都不一样现象同一份数据跑两次分组数差 2 个。原因ISOS 是随机初始化种群没设随机种子。解决np.random.seed(42)固定种子种群规模从 30 加到 50迭代从 100 加到 200让搜索更稳定。如果还抖改用熵值阈值直接切分牺牲一点最优性换可复现。4.3 VMD 分解出虚假模态中心频率重叠现象omega[-1]里两个模态频率差小于 0.005。原因K 设大了或者alpha太小导致带宽过宽。解决K 从 4 降到 3alpha从 2000 加到 3000。判断标准分解后每个模态的频谱应该只有一个主峰多峰就是过分解。4.4 GRU 预测滞后一个时间步现象预测曲线整体右移跟真实值错位。原因滑动窗口构造时y的索引偏了或者归一化用了全局均值导致信息泄漏。解决检查make_windows里y.append(data[iwindow])是否正确归一化用训练集均值方差别用全序列。滞后一步在时序预测里是血泪经验MAPE 能差 3 个点。4.5 ARIMA 和 GRU 叠加后误差反而变大现象单分支 MAPE 8%叠加后 12%。原因两个分支预测误差量级不匹配或者叠加时没对齐时间索引。解决所有分支在归一化空间预测叠加后再反归一化检查forecast的 steps 和 GRU 预测长度是否一致如果还差给每个分支按验证集误差加权别直接等权相加。5. 进阶技巧用滚动预测和误差加权把 MAPE 再压 2 个点这套链路跑通后单次预测 MAPE 大概在 5%~8%。想再往下压两个技巧最实用。第一个是滚动预测。别一次性预测未来 24 步改成每预测一步就把真实值喂回去重新构造窗口。GRU 分支用model(X)单步推理ARIMA 用fit.append()更新。代价是计算量翻 24 倍但 MAPE 能降 1~2 个点。代码上把make_windows改成在线版本def rolling_forecast(model, init_window, steps): window list(init_window) preds [] for _ in range(steps): x torch.tensor(window[-24:], dtypetorch.float32).view(1, 24, 1) with torch.no_grad(): p model(x).item() preds.append(p) window.append(p) # 用预测值滚动有真实值就替换成真实值 return np.array(preds)有真实值时把window.append(p)换成window.append(true_value)误差不会累积。第二个是分支误差加权。验证集上分别算 ARIMA 和 GRU 的 MAPE权重取倒数归一化分支验证集 MAPE权重ARIMA6.2%0.45GRU4.8%0.55叠加时final w_arima * arima_pred w_gru * gru_pred。权重每月用最近数据重算一次别固定死。我一般还会留一个 10% 的验证集做早停GRU 训练时loss连续 20 epoch 不降就停省得过拟合。最后一个习惯每次改参数前先存一版预测结果和 MAPE改完对比。这套链路参数多不记录的话跑两周就忘了哪版最好。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

滑块验证中的UA动态生成与轨迹建模工程实践

滑块验证中的UA动态生成与轨迹建模工程实践

简介:本资源是一份面向Python安全研究与自动化开发者的滑块验证码逆向分析实践案例,聚焦阿里巴巴X82YX5SEC滑块验证机制的识别与模拟突破。内容涵盖核心算法实现、通用滑块处理逻辑及配套客户端环境,适用于Web安全学习、验证码对抗技术研究及…

2026/10/11 20:35:22 阅读更多 →
termite 1.8.4 多系统多架构发布包:安装配置与排错实战

termite 1.8.4 多系统多架构发布包:安装配置与排错实战

简介:Termite 1.8.4 是一套轻量级跨平台远程管理工具包,覆盖 Linux、macOS、Windows 等主流系统,并适配 x86、x64、arm、mips 多种硬件架构。工具整体分为管理端 admin 与客户端 agent,支持跳板机互联、正反向级联和内置 Shell 操…

2026/10/11 20:35:22 阅读更多 →
QT+PaddleOCR打造桌面OCR识别工具:架构与实战避坑指南

QT+PaddleOCR打造桌面OCR识别工具:架构与实战避坑指南

简介:面向需要快速搭建OCR应用界面的Qt开发者与PaddleOCR初学者,这套demo压缩包将源码与发布版本打包在一起,可作为从零开始接触文字识别界面开发的完整示例。压缩包整体大小约454.7MB,源码部分涵盖Qt窗口设计、调用PaddleOCR识别…

2026/10/11 20:35:22 阅读更多 →

最新新闻

拆解Amical的whisper.cpp封装:如何构建带Metal/CUDA/CPU自动回退的C++原生模块

拆解Amical的whisper.cpp封装:如何构建带Metal/CUDA/CPU自动回退的C++原生模块

【免费下载链接】amical 🎙️ AI Dictation App - Open Source and Local-first ⚡ Type 3x faster, no keyboard needed. 🆓 Powered by open source models, works offline, fast and accurate. 项目地址: https://gitcode.com/gh_mirrors/…

2026/10/12 0:27:12 阅读更多 →
基于YOLO的管道缺陷检测:980张图像训练实战与避坑指南

基于YOLO的管道缺陷检测:980张图像训练实战与避坑指南

简介:本资源为面向YOLO系列目标检测算法的下水管道缺陷检测数据集,适用于从事管道巡检、市政设施维护与工业视觉检测的开发者及研究人员,可解决缺陷样本稀缺、标注格式不统一等问题。压缩包共2000个文件,约33.89MB,包含…

2026/10/12 0:27:12 阅读更多 →
物联网模组柔性FPC天线方案全解析:选型、布局与调试

物联网模组柔性FPC天线方案全解析:选型、布局与调试

1. 项目背景与选型思路做物联网产品硬件设计的朋友,十有八九都遇到过同一个问题:模组选好了、主板画完了、结构堆叠也敲定了,结果天线没地方放。尤其是这两年,NB-IoT、Cat.1、BLE、LoRa 这些模组方案层出不穷,模组本身…

2026/10/12 0:27:12 阅读更多 →
用Tauri构建桌面天气应用:从技术选型到打包发布的完整实践

用Tauri构建桌面天气应用:从技术选型到打包发布的完整实践

桌面天气应用这个需求,看起来挺简单,但真做起来会发现它横跨了数据接口、桌面端集成、界面设计、异常处理好几个层面的问题。我前后用了两个周末把一套完整方案跑通,过程中踩了不少坑,这里把从选型到发布的完整链路梳理出来&#…

2026/10/12 0:27:12 阅读更多 →
UML四层建模实战:从用例图到部署图构建教务管理系统

UML四层建模实战:从用例图到部署图构建教务管理系统

简介:本资源是南京邮电大学软件工程课程设计的完整实验报告,面向高校计算机类专业本科生及软件工程初学者,聚焦教务管理系统的面向对象分析与UML建模实践。报告系统呈现了从需求分析到UML建模的全流程:涵盖用例图(管理…

2026/10/12 0:26:12 阅读更多 →
UML用例图与顺序图建模核心:抓准动作主体与交互时序

UML用例图与顺序图建模核心:抓准动作主体与交互时序

简介:本资源是一份面向软件工程专业学生、UML初学者及备考人员的系统性试题汇编,聚焦用例图、顺序图与协作图等核心交互建模技能,帮助读者深入理解UML动态建模原理与实际应用差异。资料以1个62KB的Word文档形式呈现,内容涵盖7大知…

2026/10/12 0:26:12 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →