Source code for extremeloss.analytics.diagnostics
from __future__ import annotations
import numpy as np
from ..estimation.metrics import empirical_tvar, empirical_var, exceedance_curve
from ..utils.validation import as_1d_float_array
def var_tvar_diagnostic_table(losses, quantiles=(0.95, 0.99, 0.995)) -> dict[str, object]:
arr = as_1d_float_array(losses, name="losses")
rows = []
for q in quantiles:
var_q = empirical_var(arr, q)
tvar_q = empirical_tvar(arr, q)
rows.append(
{
"quantile": float(q),
"var": float(var_q),
"tvar": float(tvar_q),
"tail_ratio": float(tvar_q / var_q) if var_q > 0.0 else np.nan,
}
)
return {"n": int(arr.size), "rows": rows}
[docs]
def extreme_loss_summary(
losses,
*,
thresholds=None,
quantiles=(0.95, 0.99, 0.995),
) -> dict[str, object]:
"""One-call tail summary of a loss sample: moments, VaR/TVaR table, exceedances.
Returns ``n`` / ``mean`` / ``std`` / ``min`` / ``max`` plus a
``var_tvar`` row per quantile -- empirical VaR, TVaR, and their tail
ratio, under the ecosystem estimators; pass ``thresholds`` to add the
empirical ``exceedance_curve``. This is the summary
:func:`tail_summary_from_risksim` produces for a simulation result.
Parameters
----------
losses : array-like
The loss sample.
thresholds : array-like, optional
Grid for the exceedance curve; omitted when ``None``.
quantiles : tuple of float, optional
Levels for the VaR/TVaR rows (default ``(0.95, 0.99, 0.995)``).
Returns
-------
dict
Summary statistics as above.
"""
arr = as_1d_float_array(losses, name="losses")
out: dict[str, object] = {
"n": int(arr.size),
"mean": float(np.mean(arr)),
"std": float(np.std(arr, ddof=0)),
"min": float(np.min(arr)),
"max": float(np.max(arr)),
"var_tvar": var_tvar_diagnostic_table(arr, quantiles=quantiles)["rows"],
}
if thresholds is not None:
out["exceedance_curve"] = exceedance_curve(arr, thresholds)
return out