COPOD-Copula 异常
COPOD-Copula 异常 的真实核心主要位于:
COPOD-Copula 异常
1. 方法概述
COPOD-Copula 异常 的真实核心主要位于:
core/copod_algorithm.pycore/data_prep.pycore/copod_runner.pyui/upload_widget.pyui/methods_widget.pyui/results_widget.pyutils/excel_handler.py
从当前真实代码看,这个模块并不是直接调用 pyod.models.copod.COPOD 的官方实现,而是自己写了一套简化版经验 copula 异常检测流程:
- 选择特征列,并把它们强制转成数值;
- 按配置处理缺失值,并可选做标准化 / Min-Max;
- 将每个特征列做秩变换,映射到 \((0,1)\);
- 计算每个样本的经验 copula 左尾 / 右尾概率;
- 取
max(-log(left_p), -log(right_p))作为异常分数; - 按
contamination固定选出 Top-K 异常样本; - 若用户提供标签列,则附加评估指标。
设经过预处理后的特征矩阵为
$$ X=\{x_i\}_{i=1}^{n}\in\mathbb{R}^{n\times d} \tag{1} $$
其中 \(n\) 是样本数,\(d\) 是特征数。当前实现直接在整张表上做无监督检测,并没有训练 / 测试集划分。
2. 数据约束与预处理
2.1 特征列与标签列
代码要求:
- 至少选择 1 个特征列;
- 所有特征列都必须在数据表中存在;
- 标签列可选,仅用于评估,不参与异常分数计算;
- 特征列会统一执行
pd.to_numeric(errors="coerce")。
这意味着当前实现对特征的真实要求是“最终必须能变成有限数值”。若某列是纯文本类别,通常会在数值化后变成 NaN,随后要么被删行,要么在后续有限性检查时报错。
2.2 缺失值处理
代码支持 4 种缺失策略:
dropmeanmedianzero
若使用 drop,则删除任何在特征列上出现缺失的样本行。
若使用填补策略,则对每列做
$$ x_{ij}^{(\text{fill})}= \begin{cases} \bar x_j, & \text{mean}\\ \mathrm{median}(x_{\cdot j}), & \text{median}\\ 0, & \text{zero} \end{cases} \tag{2} $$
其中 rows_dropped 与 fill_values 会被记录到预处理元信息中。
2.3 数值缩放
代码支持:
nonestandardminmax
标准化的真实实现为
$$ \tilde x_{ij}=\frac{x_{ij}-\mu_j}{\sigma_j+10^{-12}} \tag{3} $$
其中 \(\mu_j,\sigma_j\) 直接由当前整批数据计算。
Min-Max 缩放为
$$ \tilde x_{ij}=\frac{x_{ij}-\min(x_{\cdot j})}{\max(x_{\cdot j})-\min(x_{\cdot j})+10^{-12}} \tag{4} $$
要注意:这一步也不是基于训练集统计量,而是基于整张待分析数据表。
2.4 标签规范化
标签列如果存在,代码只接受:
{0,1}{-1,1}
并统一规范成“1=异常,0=正常”:
$$ y_i^{(\text{norm})}= \begin{cases} y_i, & y_i\in\{0,1\}\\ \mathbf{1}(y_i=-1), & y_i\in\{-1,1\} \end{cases} \tag{5} $$
因此在 {-1,1} 输入下,-1 会被视为异常。
3. 经验 Copula 分数的真实实现
3.1 秩变换到 \((0,1)\)
core/copod_algorithm.py 先对每个特征列做经验分布映射:
$$ u_{ij}=\frac{\mathrm{rank}(x_{ij})}{n+1} \tag{6} $$
代码优先用 scipy.stats.rankdata(method="average"),若不可用则回退到 pandas 的 rank(method="average")。
于是每个样本都会得到一个 copula 空间表示:
$$ u_i=(u_{i1},u_{i2},\dots,u_{id})\in(0,1)^d \tag{7} $$
3.2 左尾 / 右尾经验概率
对于样本 \(u_i\),左尾经验概率按“有多少样本在所有维度都不大于它”来计数:
$$ \mathrm{left}_i=\frac{1}{n}\sum_{k=1}^{n}\mathbf{1}(u_{k1}\le u_{i1},\dots,u_{kd}\le u_{id}) \tag{8} $$
右尾经验概率则按“有多少样本在所有维度都不小于它”来计数:
$$ \mathrm{right}_i=\frac{1}{n}\sum_{k=1}^{n}\mathbf{1}(u_{k1}\ge u_{i1},\dots,u_{kd}\ge u_{id}) \tag{9} $$
这就是当前代码里 _empirical_copula_tail_probs() 的真实统计口径。
3.3 概率裁剪与异常分数
为避免 log(0),代码先设定
$$ p_{\min}=\max\left(\frac{1}{n},\varepsilon\right) \tag{10} $$
其中默认 \(\varepsilon=10^{-12}\)。
再将左右尾概率裁剪到 \([p_{\min},1]\) 后,定义异常分数:
$$ s_i=\max\big(-\log(\mathrm{left}_i),\ -\log(\mathrm{right}_i)\big) \tag{11} $$
分数越大,表示样本越处于联合分布的稀疏尾部,越可能是异常点。
3.4 block_size 的作用
代码不是一次性构造完整的 \(n\times n\times d\) 比较张量,而是按块处理样本:
- 默认算法层
block_size = 64 - 参数页勾选“快速模式”时改为
128
这个参数影响的是分块计算方式与速度 / 内存占用,并不改变统计定义本身。
4. 阈值、预测标签与评估指标
4.1 按 contamination 固定选 Top-K
当前实现并不是简单按分位数比较 score >= threshold 后全打 1,而是先固定异常个数:
$$ k=\left\lceil \text{contamination}\cdot n \right\rceil \tag{12} $$
再把分数从大到小排序,只取前 \(k\) 个样本标成异常:
$$ \hat y_i= \begin{cases} 1, & s_i \text{ 位于前 } k \text{ 名}\\ 0, & \text{otherwise} \end{cases} \tag{13} $$
阈值 threshold 只是第 \(k\) 大分数本身,用来展示;真正的标记逻辑是“严格取前 \(k\) 个”,这样可以避免阈值相同导致异常样本数超过预期。
此外,代码还另外生成传统异常检测常见的 \(\{-1,1\}\) 形式:
pred_label_pm = -1表示异常pred_label_pm = 1表示正常
4.2 top_k 的真实用途
要注意参数页里的 top_k 不是阈值计算的核心参数。它只用于导出展示:
top_anomalies = result_df.sort_values("copod_score", ascending=False).head(top_k)
也就是说:
contamination决定最终被标记为异常的样本数;top_k只决定结果表里额外展示多少条最高分样本。
4.3 有标签时的评估
若提供标签列,代码会基于 y_true 与 pred_label 计算:
$$ \mathrm{Precision}=\frac{TP}{TP+FP} \tag{14} $$
$$ \mathrm{Recall}=\frac{TP}{TP+FN} \tag{15} $$
$$ \mathrm{F1}=\frac{2\cdot \mathrm{Precision}\cdot \mathrm{Recall}}{\mathrm{Precision}+\mathrm{Recall}} \tag{16} $$
并在 sklearn 可用时追加:
roc_aucpr_auc
这里不会输出 accuracy,因为当前实现更偏向异常检测常见的召回 / 排序能力评估。
5. 输出结果、Excel 与图表
5.1 结果字典的真实含义
run_copod() 返回的统一结果字典包含:
raw_dataprocessed_datastep_resultsfinal_resultschartsparameters
但这些字段的内容要注意:
raw_data不是完整原始 DataFrame,只保存了shape和columns;processed_data也不是完整处理后矩阵,只保存了prep元信息;- 真正逐样本结果在
step_results["scores"]; step_results["tail_probs"]保存的是left_p / right_p / score;final_results里保存threshold / contamination / n_samples / n_outliers / metrics。
因此论文说明中不能把它写成“导出完整原始数据与完整处理后数据矩阵”。
5.2 Scores 表的列
result_df 实际会包含:
- 预处理后保留下来的特征列;
- 可选的原始标签列;
copod_scoreis_outlierpred_label_pm- 若标签存在,还会再加一个规范化后的
y_true
因此当用户输入标签为 {-1,1} 时,结果表里可能同时看到:
- 原标签列:
-1/1 - 规范化后的
y_true:1/0
5.3 Excel 工作表
save_results() 实际导出:
ParametersPreprocessThresholdScoresTop_AnomaliesTail_ProbMetricsCharts
其中:
Preprocess保存的是预处理元信息,而不是矩阵;Threshold只保存threshold和n_outliers;Charts保存图表名称、用途和路径。
5.4 图表
当前只生成两张图:
scores_hist.pngtail_prob_scatter.png
前者是异常分数直方图,并在可用时画出阈值竖线;后者是 left_p 与 right_p 的散点图,用于观察联合尾概率结构。
5.5 一个容易忽略的导出细节
虽然算法参数里有:
block_sizeeps
但 run_copod() 返回的 parameters["cfg"] 实际只保存了:
contaminationtop_kfeature_colslabel_colmissing_strategyscaling
也就是说,block_size 和 eps 不会进入 Parameters 工作表。它们只会在结果页生成的 repro_copod.py 里被写出来。
6. 输出结果与复现脚本
6.1 自动导出
结果页完成计算后会自动新建目录:
results/COPOD-Copula 异常分析结果_时间戳/
并写出:
COPOD_results_时间戳.xlsx
若同一秒目录重名,则会自动补 _01、_02 这类后缀。
6.2 “导出Excel”按钮的真实行为
结果页上的“导出Excel”按钮并不是弹出文件另存为对话框,而是再次自动创建一个新的结果目录,然后重新调用 save_results()。
因此它更接近:
- “再导出一份”
而不是:
- “让我自己选路径另存为”
6.3 复现脚本
结果页还会自动写出:
repro_copod.py
并把当前内存中的 DataFrame 导出到:
repro_inputs/copod_repro_data.csv
这一步不会保留最初用户选择的原始 Excel / CSV 路径,而是统一转成新的 CSV 复现实验输入。
7. 实现说明与注意事项
根据当前真实代码,论文说明里建议明确以下边界:
- 这不是
pyod官方COPOD的直接封装,而是自写的简化经验 copula 异常评分实现。 - 它是无监督异常检测流程,没有训练 / 测试集划分;标签列仅用于后验评估。
- 所有特征列都会被强制数值化,因此当前实现本质上只适合数值特征。
- 阈值并不是简单“分位数以上全判异常”,而是按
contamination精确选取前 \(k\) 个高分样本。 top_k只影响Top_Anomalies展示行数,不影响最终异常标签。- 导出的
raw_data和processed_data都是元信息,不是完整数据表。 Parameters工作表并不会保留block_size和eps,若论文需要完整复现实验参数,应同时参考自动生成的repro_copod.py。
8. 论文写作模板
8.1 方法描述模板
“本文采用基于经验 copula 左尾与右尾联合稀有度的异常检测方法,对多维样本在联合分布尾部区域的偏离程度进行量化。具体而言,先对各维特征执行秩变换得到伪观测,再分别计算左尾与右尾的经验尾部概率,并以尾部概率的负对数强度构造样本异常分数。依据项目当前实现,最终异常标签并非由单纯分位数阈值给出,而是根据设定的 contamination 精确选取 Top-\(k\) 高分样本。因此,论文中应明确本方法采用的是项目自定义简化 COPOD 口径,而非 pyod 官方实现。”
8.2 结果解释模板
结果部分可写为:异常分数 \(s_i\) 越大,说明样本越处于联合分布的稀疏尾部区域。若左尾概率较小,说明样本在若干维度上更接近低值极端;若右尾概率较小,则说明样本更接近高值极端。由于最终异常样本数由 contamination 严格控制,论文中应把 contamination 解释为异常比例设定,把 top_k 解释为结果展示数量,而不应将两者混为同一统计含义。若存在真实标签,则可进一步结合 Precision、Recall、F1、ROC-AUC 与 PR-AUC 评价检测排序与阈值判别性能。
8.3 表格标题模板
- 表 1 经验 Copula 异常检测参数与污染率设置表
- 表 2 样本异常分数与尾部概率结果表
- 表 3 Top-K 异常样本排序结果表
- 表 4 带标签场景下 COPOD 检测性能指标表
8.4 图表题注模板
- 图 1 COPOD 异常分数分布图及阈值位置示意图。
- 图 2 Top 异常样本左尾与右尾经验概率对比图。
- 图 3 COPOD 异常样本排序结果可视化图。
8.5 表格示例
表 1 Top-K 异常样本结果表示例
| 样本编号 | copod_score |
left_p |
right_p |
预测标签 |
|---|---|---|---|---|
| \(i_1\) | ||||
| \(i_2\) |
表注:异常分数由左尾与右尾经验概率的负对数强度构造;最终异常标签按 contamination 控制的 Top-\(k\) 规则确定。
9. 单篇终审补充
9.1 图题与表题对齐建议
Parameters表可写为:表X COPOD 参数设置表。Preprocess表可写为:表X COPOD 数据预处理信息表。Threshold表可写为:表X COPOD 异常阈值与污染率设置表。Scores表可写为:表X COPOD 样本异常分数结果表。Top_Anomalies表可写为:表X COPOD Top-K 异常样本排序表。Tail_Prob表可写为:表X COPOD 左尾与右尾经验概率结果表。Metrics表可写为:表X COPOD 检测性能指标汇总表。Charts表可写为:表X COPOD 图表索引与路径清单。scores_hist.png建议写为:图X COPOD 异常分数分布图。tail_prob_scatter.png建议写为:图X COPOD 左尾与右尾经验概率散点图。
9.2 终审说明
- 当前代表性结果目录中的真实主工作簿为
COPOD_results_20260307_015513.xlsx,同时还存在一份时间戳相邻的再次导出副本COPOD_results_20260307_015517.xlsx。论文若列输出文件,应固定说明哪个是首次运行结果,避免把“再次导出”误判成独立实验。 - 真实工作表为
Parameters/Preprocess/Threshold/Scores/Top_Anomalies/Tail_Prob/Metrics/Charts,并不包含完整原始数据表或处理后数据表。因此正文若需要展示完整数据,应另引原始输入文件,而不能宣称工作簿已完整保存原始样本。 - 当前实体图文件位于
*_charts/目录下,真实图名主要是scores_hist.png与tail_prob_scatter.png。终稿图题应围绕“异常分数分布”和“尾部概率结构”展开,不要虚构 ROC 曲线或 PR 曲线图。 - 真实 repro 脚本固定为
repro_copod.py,并通过INPUT_FILE = 'repro_inputs/copod_repro_data.csv'读取统一导出的 CSV 副本。附录复现实验说明应明确该算法会把原始输入转写为新的repro_inputs/...csv。 Parameters工作表不会保留block_size和eps等细项参数,这一点在论文复现实验描述里必须补充说明;完整参数口径需要结合repro_copod.py一并给出。
9.3 全量强化补充
- 本轮按真实磁盘再次核对,算法目录为
具体的算法3/异常检测/COPOD-Copula 异常,代表性结果目录更新为具体的算法3/异常检测/COPOD-Copula 异常/results/COPOD-Copula 异常分析结果_20260329_170057。 - 该目录主工作簿为
COPOD_results_20260329_170057.xlsx,真实工作表为Parameters、Preprocess、Threshold、Scores、Top_Anomalies、Tail_Prob、Metrics、Charts。 - 当前图目录为
COPOD_results_20260329_170057_charts/,实体图为scores_hist.png与tail_prob_scatter.png。这说明当前论文图证应围绕“异常分数分布”和“尾部概率结构”展开。 - 当前 repro 脚本固定为
repro_copod.py,并通过INPUT_FILE = 'repro_inputs/copod_repro_data.csv'读取统一转写后的 CSV 输入副本;该文件在repro_inputs/中实际存在。 - 需要区分单轮目录与累积目录。
COPOD-Copula 异常分析结果_20260329_170057是单轮主结果目录;而COPOD-Copula 异常分析结果_20260329_170057_01则累积保留了170057与170059两轮工作簿及多套图目录,不应在正文中混作一次实验。 - 因此,这篇最稳妥的写法是锚定单轮目录
20260329_170057,把_01目录仅作为“多次再次导出存在”的工程事实,而不是论文主证据。
10. 软件实现核查补充(2026-07)
- 当前主结果目录应写作
具体的算法3/异常检测/COPOD-Copula 异常/results/COPOD-Copula 异常分析结果_20260329_170057。 - 正文应围绕
Parameters、Preprocess、Threshold、Scores、Top_Anomalies、Tail_Prob、Metrics、Charts来写。 - 图证应对应
scores_hist.png与tail_prob_scatter.png,并把主结果目录与_01累积目录区分开。 - 复现脚本应按
repro_copod.py + repro_inputs/copod_repro_data.csv的口径说明。