阈值网格上的均值超额曲线
Mean-Excess Curve Across a Threshold Grid
开始编码某风控团队在 P&L 损失序列上做峰超阈值(POT)诊断,想把均值超额曲线画在一组规则的候选阈值网格上。均值超额函数
是极值理论里标准的阈值选择诊断:当 u_0 之上的超额服从形参为 xi 的广义 Pareto 分布(GPD)时,对 u >= u_0 而言 e(u) 近似为 u 的线性函数,斜率为 xi / (1 - xi)。视觉做法是在网格上评估 e(u),找到曲线开始线性的最小 u,作为 GPD 拟合的推荐阈值。
实现 solution(losses: list[float], threshold_grid: list[float]) -> list[float]:对 threshold_grid 中的每个 u(独立计算,按输入顺序),如果至少有一个损失严格大于 u 就返回经验均值超额 e(u),否则返回 float('nan')。
Example
solution([1.0, 3.0, 5.0, 8.0, 12.0], [0.0, 4.0, 10.0]) 返回 [5.8, 4.333..., 2.0]。
- u = 0:所有损失都超过 0;超额量为
[1, 3, 5, 8, 12],和为 29,均值29 / 5 = 5.8。 - u = 4:超额观测为
[5, 8, 12];超额量为[1, 4, 8],和为 13,均值13 / 3 ≈ 4.3333333...。 - u = 10:只有
12超过;单一超额量为2,均值2.0。
网格项高于最大损失时返回 nan:solution([1.0, 3.0, 5.0], [10.0]) 等于 [float('nan')],因为没有损失严格大于 10。网格项与某个损失相等时不计该损失:solution([1.0, 3.0, 5.0], [3.0]) 只对 {5} 求平均(超额量为 2),得到 [2.0]——严格的 loss > u 排除了 3.0 本身。
参考解为 O((N + K) log N):把 losses 排序一次,构造长度 N + 1 的后缀和(tail_sum[j] = s[j] + ... + s[N-1]),然后对每个网格阈值 u 用 bisect_right(s, u) 找首个超额下标 j。令 m = N - j,超额量之和为 tail_sum[j] - u * m,e(u) = (tail_sum[j] - u * m) / m;m == 0 时输出 nan。在题目的规模下,朴素的 O(N * K) 逐阈值重扫也能在时限内完成。
实现细节由 stubs/stub.py 提供。
Edge cases
N == 0(空 losses):每个网格项都是nan;返回[float('nan')] * K。K == 0(空网格):返回[]。空网格优先于空 losses。u等于max(losses):严格的>排除最大值,超额集合为空,输出nan。u < min(losses):所有损失都计入;e(u) = mean(losses) - u。u等于某个内部观测v:等于v的项被排除(严格>),只有严格大于v的损失构成超额集合。threshold_grid中有重复值:每个重复项独立计算,在它各自的位置上各自产生一个输出。- 负阈值与有正有负的损失(既有亏损也有盈利):没有特别处理,严格
>直接作用于带符号数值。
实践背景
阈值选择是 GPD 拟合中最脆弱的一步:u 选得太低会破坏 GPD 假设,u 选得太高则超额样本太少、xi 估计精度差。均值超额图——网格上的 e(u) 对 u 的曲线——给出一个视觉化健康检查:一段稳定的线性区表示从该最小 u 起 GPD 假设可信。风控桌面每天在新损失样本上跑这条曲线,配合 Hill 估计选定阈值,再喂到下游的尾部 VaR 与期望损失外推中。对超过经验最大值的网格项返回 nan 是刻意为之:让看板能区分「该 u 之上没有尾部数据」与「尾部平坦」,避免自动告警把空集静默吞掉。
约束条件
- 0 <= N <= 4000,其中 N = len(losses)
- 0 <= K <= 1000,其中 K = len(threshold_grid)
- 每个 `losses[i]` 有限且 |losses[i]| <= 1e9(允许有符号,正值表示亏损、负值表示盈利)
- 每个 `threshold_grid[k]` 有限且 |threshold_grid[k]| <= 1e9;网格无需有序;允许重复
- 输出是 Python `list[float]`,长度为 K;元素是有符号的有限浮点数或 `float('nan')`;输出顺序与 `threshold_grid` 输入顺序一致
- 浮点比较容差:rel_tol = 1e-9、abs_tol = 1e-9;本题 `nan` 与 `nan` 视为相等
样例
Case 1 · typical: small example with three thresholds
输入: [[1,3,5,8,12],[0,4,10]]
期望: [5.8,4.333333333333333,2]
u=0:所有 5 个损失都超过;超额和 1+3+5+8+12=29,均值 29/5=5.8。u=4:超额为 {5,8,12},超额和 1+4+8=13,均值 13/3。u=10:只有 12 超过,单一超额量 2。
Case 2 · boundary: threshold equal to max returns nan (strict >)
输入: [[2,5,7,9],[9]]
期望: ["NaN"]
u 等于 max(losses)=9;严格 > 排除最大值,超额集合为空,输出 nan。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · typical: small example with three thresholds
输入: [[1,3,5,8,12],[0,4,10]]
期望: [5.8,4.333333333333333,2]
u=0:所有 5 个损失都超过;超额和 1+3+5+8+12=29,均值 29/5=5.8。u=4:超额为 {5,8,12},超额和 1+4+8=13,均值 13/3。u=10:只有 12 超过,单一超额量 2。
Case 2 · boundary: threshold equal to max returns nan (strict >)
输入: [[2,5,7,9],[9]]
期望: ["NaN"]
u 等于 max(losses)=9;严格 > 排除最大值,超额集合为空,输出 nan。