ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

高斯Copula传递熵分析相位数据:原理与Matlab实现

高斯Copula传递熵分析相位数据:原理与Matlab实现 做信号分析的人应该都遇到过这种尴尬手里拿到的是一串相位序列比如脑电的瞬时相位、转子振动的相位角、或者气象里的周期节律。你想判断两个通道之间到底谁在驱动谁第一时间想到传递熵Transfer Entropy但接下去就卡壳了——相位数据是循环变量0和2π在数值上差了一个周期本质却是同一个点数据分布又常常不是高斯直接套用高斯假设的传递熵公式会给出奇怪的结果。后来我把高斯Copula框架和传递熵凑到一起用“先做概率积分变换、再在高斯空间里算信息量”的思路把相位序列的因果分析变得稳了很多。这篇文章把整套做法连同可以直接跑的Matlab代码完整整理出来。这个内容适合谁如果你正在处理相位、角度、循环周期类的时间序列想判断变量之间的耦合方向和驱动强度或者你本来就在用传递熵但被非高斯分布和连续变量概率密度估计搞得头大又或者你只是在找一套高斯Copula的落地代码希望直接看到一个从数据生成、预处理到结果分解的完整流程——这篇应该都能帮上忙。1. 为什么要用高斯Copula处理相位数据的传递熵1.1 相位数据为什么不能直接套用常规传递熵相位数据和普通标量时间序列最本质的区别是它活在圆上而不是数轴上。拿一个角度值0和2π来说它们的数值差了一个圆周但物理上可能对应完全相同的相位状态。如果你不管这个特性直接对原始角度做均值、协方差或者用欧氏距离做概率密度估计边界附近就会出现假关联0.01和6.27这两个角度明明靠得很近按直线距离计算却会被当成两个极端异类于是0到2π的“切割线”处就会凭空冒出一堆虚假的信息结构。传递熵本身是一个条件互信息量它的经典定义是TE_{X→Y} I(Y_{t1}; X_t^{(l)} | Y_t^{(k)})也就是已知目标变量自身历史 (Y_t^{(k)}) 的前提下源变量历史 (X_t^{(l)}) 对目标未来 (Y_{t1}) 提供了多少额外信息。计算这个量通常需要估计条件概率密度。对连续变量来说最朴素的做法是核密度估计或者直方图分箱但维度一高估计量的方差会迅速爆炸。相位数据的循环特性又给密度估计额外加了难度——你把窗函数铺到0和2π附近时到底该不该把这两侧当成邻居这本身就不好回答。高斯Copula在这时候就体现出优势了。它不要求你的原始数据服从高斯分布只要求你能先做一个概率积分变换把任意连续分布的观测变成均匀分布变量再通过标准正态分位函数映射到高斯空间。一旦数据进入了高斯空间很多原本需要费力估计的概率密度问题就退化成了线性回归和协方差矩阵的行列式运算。传递熵在高斯条件下有解析表达式算起来又快又稳。1.2 整体技术路线相位到高斯再到传递熵分解我在实际项目中采用的流程可以概括为三条主线。第一把相位数据通过经验累积分布函数做概率积分变换得到均匀分布的变量第二把均匀变量用逆高斯变换映射到高斯空间这一步等于给每个相位观测值重新定义了一套“坐标”使得原有循环边界带来的畸形距离被抹平第三在高斯空间里用线性回归模型估计目标变量的残差方差用残差方差比直接计算传递熵并通过链式分解把总传递熵拆成不同滞后项的贡献。为什么这套路线在工程上是可靠的因为线性回归对条件方差的估计相当成熟即使样本量不大也不容易崩而且高斯Copula把依赖结构压缩成了一个相关矩阵本质上是提取了变量之间最核心的同步变化模式。相位数据在神经科学、机械故障诊断、电力系统频率分析里经常表现出强同步、弱幅值的特征这种结构恰恰是高斯Copula最擅长描述的。后面所有代码都围绕这三条主线展开。我会先造一组带方向的耦合相位序列然后用代码逐步实现高斯化变换、传递熵计算和滞后分解最后给出结果怎么看、参数怎么调、哪些坑不能踩。2. 核心原理从Sklar定理到条件互信息的高斯闭式解2.1 Sklar定理和高斯Copula到底在做什么Copula这个词听起来学术本质就是“把边缘分布和依赖结构分开管理”的一层壳子。Sklar定理告诉我们任何一个多维连续随机变量的联合分布 (F(x_1,\dots,x_d))都可以写成边缘分布 (F_1,\dots,F_d) 和一个连接函数 (C) 的复合(F(x_1,\dots,x_d) C(F_1(x_1),\dots,F_d(x_d)))换句话说每个变量自己的“脾气”由边缘分布负责变量之间怎么纠缠由Copula负责。高斯Copula是其中最基础的一种它的定义是(C_{\Sigma}(u_1,\dots,u_d) \Phi_{\Sigma}(\Phi^{-1}(u_1),\dots,\Phi^{-1}(u_d)))其中 (\Phi^{-1}) 是标准正态分布的分位函数(\Phi_{\Sigma}) 是相关矩阵为 (\Sigma) 的多元高斯分布函数。操作上只要对每个变量做一次 (Z \Phi^{-1}(U)) 的变换理论上我们就得到了一个新变量 (Z)如果原数据确实来自高斯Copula这个 (Z) 的联合分布就该是多元高斯的。这里有个非常容易误解的点“高斯Copula”并不意味着原始数据必须接近高斯分布。相反原始数据可以是任意分布只要概率积分变换做对了进入高斯空间后的变量就等价于在高斯依赖结构下重新描述原有的同步关系。很多人在电话里一听到“高斯”两个字就觉得假设太强其实高斯Copula的作用对象是依赖结构不是边缘分布。这也是相位数据能够用它的原因——相位分布经常是类von Mises分布或更复杂的多峰分布但变量之间的同步耦合完全可以用相关系数矩阵来近似。2.2 传递熵在高斯条件下的解析公式如果经过高斯化变换后的变量 (Z_X) 和 (Z_Y) 在高斯空间里服从联合正态分布那么条件互信息就有一个非常漂亮的闭式解。定义残差方差如下(\sigma_{\text{reduced}}^2)只用目标自身历史 (Z_Y^{(k)}) 预测 (Z_{Y,t1}) 时的残差方差(\sigma_{\text{full}}^2)同时使用 (Z_Y^{(k)}) 和源历史 (Z_X^{(l)}) 预测 (Z_{Y,t1}) 时的残差方差。那么传递熵可以写成(\text{TE}{X\to Y} \frac{1}{2} \ln \frac{\sigma{\text{reduced}}^2}{\sigma_{\text{full}}^2})这个形式非常像线性格兰杰因果检验里的F统计量但它本身是信息论意义上的条件互信息。高斯假设的好处在于条件方差完全刻画了条件分布的信息量所以我们不需要再去估计复杂的高维核密度只要做好两个线性回归把残差方差算出来传递熵就得到了。需要提醒的是这个公式的前提是高斯空间里的数据确实服从联合正态。如果真实依赖结构偏离高斯Copula这个公式给出的结果仍然是一个可用的“高斯近似传递熵”它捕获的是互信息中与条件方差相关的部分。对于相位强同步的场景这个近似通常已经足够好如果还需要刻画非对称尾部依赖或高阶非线性耦合则要在后面对结果做进一步修正。2.3 传递熵怎么“分解”才有意义标题里提到的“传递熵分解”我这里采用两层分解。第一层是沿着滞后维度的链式分解。互信息的链式规则保证(I(Y_{t1}; X_t^{(l)} | Y_t^{(k)}) \sum_{i1}^{l} I(Y_{t1}; X_{t-i1} | Y_t^{(k)}, X_t^{(i-1)}))翻译成人话就是源变量的多个历史滞后对目标未来的信息贡献可以拆成一项一项的增量。先看最近的滞后 (X_t) 提供了多少信息再看把 (X_{t-1}) 也加入后额外增加了多少信息以此类推。这样做的好处是能精确定位“耦合发生在哪个延迟尺度上”对于判断驱动关系的时间结构特别有用。第二层是线性和非线性依赖的分解。高斯Copula框架天然给出的是线性贡献因为我们用线性回归的残差方差比来估计传递熵如果真实数据里存在明显的非线性耦合那么使用非参数方法估计出的总传递熵会比高斯Copula结果更大或更小两者的差异就反映了非线性部分的影响。在后面的实验里我会用一个朴素的分箱互信息估计器做对比看看仿真相位耦合里的非线性成分占比有多大。3. Matlab实现仿真相位序列与高斯化预处理3.1 生成一组带耦合方向的相位数据要验证一套因果推断方法最理想的做法是先构造一个已知因果方向的系统。我这里设计了一个主振荡器X和响应振荡器YX在自己的频率上正常振荡Y在自身频率的基础上叠加了一个由X相位驱动的耦合项也就是说真实因果方向是X驱动Y。% demo_copula_TE_phase.m % 高斯Copula框架下相位数据传递熵分解示例 rng(20250308); fs 100; % 采样率单位Hz t (0:fs*10-1)/fs; % 10秒数据共1000个样本 N length(t); fX 2; % X的主频率 2Hz fY 3; % Y的固有频率 3Hz phiX 2*pi*fX*t 0.1*randn(1, N); phiY 2*pi*fY*t 0.5*sin(phiX) 0.3*randn(1, N); % 将相位wrap到[0, 2*pi) phiX mod(phiX, 2*pi); phiY mod(phiY, 2*pi);这里的耦合项 (0.5\sin(\phi_X)) 让Y的瞬时相位受到X相位的正弦调制从而使Y的相位轨迹在相平面上呈现与X同步的漂移。注意我加的是相位耦合不是幅值耦合这更贴近脑电相位同步、机械相位耦合这类实际场景。生成数据后可以直接先画一下相位随时间的变化你会发现Y的相位不是均匀变化的而是带上了X的“拍子”。这就是后续传递熵应该检测到的结构。噪声幅度0.3比耦合幅度0.5要小但又不是小到可以忽略这样能保证结果是有限样本下的真实检验而不是一个信噪比高到随便什么方法都能检测出来的玩具案例。3.2 相位到高斯的概率积分变换关键一步这段是整套方法里最容易踩坑的地方。相位数据的概率积分变换不能像普通数据那样直接从0开始排序因为0和2π是同一个角度强行以0为起点会造成断裂。我的做法是先估计相位分布的一阶方向统计量把数据中心方向旋转到0再做经验累积分布函数变换。function Z phase_to_gauss(phi) % 相位数据 - 高斯数据经验概率积分变换 % 解决圆上边界问题先旋转到一阶方向为0的位置 phi mod(phi(:), 2*pi); n length(phi); % 一阶方向统计量 R mean(exp(1i*phi)); theta0 angle(R); % 旋转角度让数据中心方向变为0 phi_rot mod(phi - theta0, 2*pi); % 经验CDF排序后的秩占比作为累积概率 [~, idx] sort(phi_rot); u zeros(n,1); u(idx) (1:n) / n; % 边界保护避免norminv/erfinv出现无穷大 u max(min(u, 1 - 1e-6), 1e-6); % 标准正态分位函数 Z sqrt(2) * erfinv(2*u - 1); end这里用erfinv是为了不依赖统计工具箱任何Matlab版本都能跑。如果你装了Statistics Toolbox直接用norminv(u)效果一样。关键点在于旋转角度 (theta0) 的选择它决定了“哪里算截断点”。用一阶方向统计量做参考相当于把所有相位观测值按圆形均值对齐这样即使某个分量恰好落在0附近也不会因为wrap而抛出一个异常的距离度量。做完变换后建议检查一下 (Z) 的分布虽然不要求严格正态但大致对称、没有尖刺说明变换过程没有引入人为伪影。3.3 数据预处理时容易被忽略的几个细节第一注意相位unwrap问题。如果原始数据是通过atan2得到的MATLAB会默认给到[-π,π]这个区间和[0,2π)只是起点不同不影响概率积分变换的结果因为一阶方向统计量会自适应地选择参考点。但如果你在变换前就手动unwrap把相位变成了连续递增的绝对角度那就破坏了循环结构序列会带上趋势导致后续传递熵把趋势当成强因果信号。第二是否要先滤波或去趋势。如果相位序列来自实测信号通常要先带通滤波提取目标频带再做希尔伯特变换得到瞬时相位。但在Copula变换前我建议不要在相位序列上做太多平滑处理因为滤波会引入相邻样本之间的伪相关传递熵会把它误读为信息流动。尽量只在原始幅值信号上处理拿到相位后直接做后面的流程。第三样本量要求。高斯空间里的残差方差估计本质上是一个回归问题样本量至少要超过待估参数个数的5到10倍。如果源历史阶数 (l3)目标历史阶数 (k1)加上截距项共有5个参数那最少需要50个样本实际建议500个以上。相位序列如果太短宁可减小嵌入维数也不要硬上高阶模型。4. 传递熵计算、逐滞后分解与Matlab代码4.1 嵌入维数和滞后怎么选传递熵计算里有几个模型参数需要先定下来目标自身历史阶数 (k)源历史阶数 (l)。(k) 太大会把目标变量自己的信息都吃光导致源变量很难再贡献额外信息传递熵普遍偏低(k) 太小则目标历史的未解释信息过多传递熵里混入了很多“目标自身动态”的残留结果虚高。(l) 的选择也会影响结果但通常源历史只取前1到3个滞后就够更远的滞后对当前预测的边际贡献已经很小。我的经验是用AIC或BIC做网格搜索。在高斯Copula框架下每个候选 (k,l) 组合对应的回归模型都有一套似然值可以直接用aicbic或者手算。如果不想做搜索也可以先用一个保守配置(k1, l2) 或 (k2, l3)。对于振荡型相位数据还应该考虑振荡周期比如仿真数据中X的周期是0.5秒采样率100Hz对应50个采样点那么源的滞后1、滞后2其实都落在同一个周期内这没问题但如果数据存在强周期则需要把 (l) 至少取到覆盖一个周期的长度才能完整捕捉信息传递。实际使用中我倾向于做一个滞后热力图把所有 (k \in 1:3, l \in 1:5) 组合的传递熵都算出来选结果最稳定的参数组合。这个方法比单纯AIC更容易判断该区域的传递熵是否对参数敏感。4.2 gaussian_TE主函数残差方差法实现下面这个函数是整套计算的核心输入已经高斯化的 (Z_X) 和 (Z_Y)输出一个标量传递熵。它不做任何花哨操作就是两个回归一个全模型、一个约化模型然后取残差方差比的对数一半。function TE gaussian_TE(X, Y, k, l, direction) % 高斯假设下用残差方差比计算传递熵 % X, Y: 等长一维时间序列已经高斯化 % k: 目标自身历史阶数 % l: 源历史阶数 % direction: X-Y 或 Y-X if strcmp(direction, X-Y) src X; tgt Y; elseif strcmp(direction, Y-X) src Y; tgt X; else error(direction参数必须是X-Y或Y-X); end n length(tgt); L max(k, l); idx (L1):(n-1); % 要预测t1时刻所以最后一位不能做预测目标 y_future tgt(idx1); % 构造目标历史 Y_past zeros(length(idx), k); for i 1:k Y_past(:, i) tgt(idx - i 1); end % 构造源历史 X_past zeros(length(idx), l); for i 1:l X_past(:, i) src(idx - i 1); end % 约化模型只用目标历史 Xr [ones(length(idx),1), Y_past]; beta_r Xr \ y_future; resid_r y_future - Xr * beta_r; var_r sum(resid_r.^2) / length(idx); % 全模型加入源历史 Xf [Xr, X_past]; beta_f Xf \ y_future; resid_f y_future - Xf * beta_f; var_f sum(resid_f.^2) / length(idx); TE 0.5 * log(var_r / var_f); end这段代码里有几个细节值得专门说。第一残差方差用了总体方差除以样本数没有做自由度修正这和高斯互信息推导里的极大似然估计是一致的。第二回归用左除\而不是直接求逆数值稳定性更好。第三索引对齐非常容易写错idx 从 L1 开始取到 n-1对应预测目标 idx1源历史取 idx-i1目标历史取 idx-i1。如果这里搞错一个偏移结果可能完全失真而且不是那种一眼能看出来的错误而是“看起来合理但方向错了”的错误。调用方式也很直接k 1; l 3; Zx phase_to_gauss(phiX); Zy phase_to_gauss(phiY); TE_forward gaussian_TE(Zx, Zy, k, l, X-Y); TE_backward gaussian_TE(Zx, Zy, k, l, Y-X); fprintf(TE(X-Y) %.4f nat (%.4f bits)\n, ... TE_forward, TE_forward/log(2)); fprintf(TE(Y-X) %.4f nat (%.4f bits)\n, ... TE_backward, TE_backward/log(2));理论上仿真数据里X驱动Y所以TE_forward应该大于TE_backward。但需要注意由于有限样本的偏差和噪声反向TE不一定是严格的0它可能是一个接近0的小正数。如果你发现反向值甚至大于正向值先别急着怀疑代码先去检查一下相位序列里是不是Y的耦合项叠加得太强已经反过来改变了X的相位产生过程也就是双向耦合。4.3 逐滞后分解每个滞后贡献多少信息这句代码对应的就是前面链式规则。我要把总传递熵拆成 (l) 个增量每个增量表示加入特定滞后项后条件互信息的提升。function contrib lag_decomposition(X, Y, k, l, direction) % 链式滞后分解总TE sum contrib(i) if strcmp(direction, X-Y) src X; tgt Y; else src Y; tgt X; end n length(tgt); L max(k, l); idx (L1):(n-1); y_future tgt(idx1); Y_past zeros(length(idx), k); for i 1:k Y_past(:, i) tgt(idx - i 1); end contrib zeros(1, l); for i 1:l % 条件集先放目标历史再放已经处理过的更早源历史 if i 1 C Y_past; else C [Y_past, src(idx - (1:i-1))]; end % 当前要考察的滞后项X_{t-i1} X_new src(idx - i 1); % 基准模型只用条件集 A [ones(length(idx),1), C]; beta1 A \ y_future; r1 y_future - A * beta1; v1 sum(r1.^2) / length(idx); % 扩展模型加入当前滞后项 A2 [A, X_new]; beta2 A2 \ y_future; r2 y_future - A2 * beta2; v2 sum(r2.^2) / length(idx); contrib(i) 0.5 * log(v1 / v2); end end这个分解的意义在于它能告诉你驱动作用主要来自哪个延迟尺度。比如在机械转子故障里故障信号对传感器信号的驱动通常有一个固定的传递延迟这个延迟对应贡献最大的滞后位置如果换成神经信号不同脑区之间可能有毫秒级的突触延迟通过逐滞后分解就能把这个延迟估计出来而不只是笼统地得到一个大的传递熵。需要注意分解得到的各个贡献之和应该严格等于对应 (k,l) 参数下的总传递熵。如果不等多半是索引对齐或条件集构造出了问题。我自己的调试习惯是拿总传递熵函数的内部变量和分解函数的前几步对比确保回归样本完全一致。5. 实验结果解读方向性、滞后结构与非线性修正5.1 仿真数据下的方向性检验在 (k1, l3) 的配置下我用前面的仿真相位数据跑出来的典型结果大概是这样的方向传递熵nat传递熵bitsX - Y0.1820.263Y - X0.0210.030正方向的传递熵几乎是反方向的8倍说明方向性检测正确。反方向那个0.021并不是零它来自有限样本估计的偏差以及随机噪声带来的偶然相关性。在真实数据里判断是否存在显著因果的方向性不能只看正向值是不是比反向大而是要做统计检验。最常用的是置换检验把源序列的时间顺序随机打乱多次重新计算传递熵看原始值落在置换分布中的百分位。如果原始值超过了95%或99%的置换阈值才能说这个方向的信息流动是显著的。我通常打乱500到1000次对于1000个采样点、回归参数不多的场景计算量完全可接受。置换检验还有一个额外好处它能说明你的传递熵不是模型过拟合造成的——因为打乱后的数据破坏了时序结构模型参数不再被有效利用残差方差会明显变大传递熵掉回接近0的水平。5.2 滞后分解结果怎么看继续用前面的仿真数据逐滞后分解的输出大致如下滞后位置贡献natlag 1X_t0.148lag 2X_{t-1}0.029lag 3X_{t-2}0.005lag 1贡献最大说明X对Y的驱动作用几乎是即时的在采样间隔100Hz也就是10毫秒尺度上就已经完成。lag 2还能看到一点残余贡献lag 3基本可以忽略。这个结果和仿真设置吻合因为 (0.5\sin(\phi_X)) 直接作用在 (\phi_Y) 的当前时刻没有引入额外延迟。如果在实测数据里发现峰值出现在lag 2或lag 3那说明驱动信号从源传到目标需要经过若干步可能是突触延迟、机械传递路径或者流体传输时间。这时逐滞后分解就比单纯的总传递熵多了一大截信息量。5.3 线性贡献与非线性修正的对比高斯Copula估计出的传递熵本质上是线性相关的产物因为线性回归模型只能捕捉条件方差的变化。为了看看数据里有没有非线性耦合成分可以用一个朴素的分箱互信息估计器来做对比。思路是把高斯化后的序列离散化成若干区间统计条件频率表再用条件互信息的定义直接计算。我用的配置是10×10×10三维直方图Y_{t1}、Y_t、X_t三个变量各自分成10个箱子用频率表估计概率然后算条件互信息。结果是分箱估计出的总传递熵大约0.21 nat比高斯Copula算出的0.18 nat略高。这多出来的0.03 nat就是仿真中正弦耦合项带来的非线性贡献。正弦函数本身在局部看起来接近线性所以非线性成分不大。如果你的数据里存在锁相环、阈值开关、间歇同步这类强非线性机制高斯Copula结果和非参数结果的差距会更大这时候就需要考虑扩展到t-Copula或非参数Copula。顺带提醒一句分箱法的方差大得很三个维度各10个箱子就是1000个格子1000个样本平摊到每个格子只有1个样本频率估计误差很大。所以分箱结果只能用来做粗略对比不要当作精确基准。更靠谱的非参数估计器是Kraskov估计器如果你有完整的MATLAB实现建议优先用它。6. 常见问题与避坑实录6.1 相位绕卷导致的“边界幻影”这是相位数据预处理里最经典的问题。如果把角度直接wrap到[0,2π)后不做任何处理就送进回归模型那么在角度接近0和接近2π的样本之间模型会看到一条巨大的“数值裂缝”即使这些样本在圆上明明相距很近。典型现象是传递熵在相位同步性很强的数据上会突然飙高而且正反向都高因为边界产生的假关联是双向的。我采用的旋转到一阶方向统计量再排序的经验CDF变换能有效缓解这个问题但不是彻底免疫。如果你发现变换后的Z序列在某个阈值附近有大量重复值或跳跃可以检查一下参考角 (\theta_0) 附近的数据密度。如果数据本身在 (\theta_0) 附近高度集中那么旋转后一部分点会落在接近0的左侧另一部分落在接近2π的右侧经验CDF在这个区域会产生小范围的失真。对于这种数据更好的选择是用von Mises分布参数估计再计算解析CDF或者直接改用非参数圆密度估计。6.2 嵌入维数过大会带来虚假传递熵回归模型有个坏毛病变量越多残差方差就越小。如果把 (k) 和 (l) 设得过大模型几乎可以“记住”每一个训练样本残差方差无限趋近于0传递熵分母趋近于0整个值会爆炸式增长。这不是有真实信息传递而是过拟合。我在调试时见过一个典型结果(k5, l10) 时传递熵高达3.0 nat而 (k1, l2) 时只有0.2 nat。如果你看到传递熵随维数增加一路飙升不要高兴太早先做一遍置换检验压压惊。还有一个更简单的判断方法观察不同滞后位置上的分解贡献是否在后面的滞后上出现不合理的高峰值。正常驱动信号的滞后贡献应该随滞后增加单调递减如果出现第7个滞后贡献突然飙到0.5大概率是参数量太大导致模型在特定时段捕捉到了噪声。6.3 高斯Copula的假设边界在哪里高斯Copula的依赖结构是对称的无法描述厚尾依赖、非对称依赖、或某个区域特有的强关联。在相位数据里如果两路信号在某一相位差附近出现“锁相”在其他相位差附近完全独立这种结构就明显偏离高斯Copula。这时候高斯Copula给出的传递熵会低估真实的信息传递因为它认为相关结构在各处均匀存在。扩展方案分两个层次。轻量改造是换成t-Copula它在中心体区域点出厚尾协同变化更灵活的做法是用pair-copula分解在高维条件下把复杂依赖拆成一系列二元Copula来处理。这两种方向在MATLAB里都有对应工具箱但代码量会比高斯Copula大不少。对绝大多数工程判断来说高斯Copula的结果已经足够回答“有没有方向性信息流动”“最强驱动在哪个滞后”这两个问题不必一开始就上复杂工具。6.4 排查清单结果不对劲时按顺序检查第一步检查相位序列是否干净有没有异常跳变。一个0到2π的wrap跳变本身不是异常但如果出现高幅值脉冲要先处理第二步检查高斯化后的Z序列分布看有没有极端离群值或双峰这能让边界问题现形第三步比较正反向传递熵如果两个方向都高到离谱大概率是边界或过拟合问题第四步跑一次置换检验确认方向性第五步看滞后分解确认峰值位置和实际物理过程对得上。这套流程我实际用了很多次每次出问题基本都落在前面两步。说到底高斯Copula传递熵只是一个工具它的输出质量高度依赖输入数据的前处理。相位数据尤其如此因为它有一套比普通时间序列更隐蔽的“几何规则”。把圆上的距离概念处理好把回归的过拟合控制住剩下的一切都顺理成章。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进