Skip to main content

nsevt

nsevt — non-stationary extreme-value tail inference

CI PyPI Downloads/month License: MIT DOI

The current release is nsevt 1.2.0. Install the exact version with pip install nsevt==1.2.0. Every release is archived under the Zenodo concept DOI 10.5281/zenodo.21858232, which resolves to the latest archived version; the immutable version-specific DOI for 1.2.0 is recorded here once Zenodo mints it.

nsevt is a dependency-light Python package for seven connected tasks:

  1. peaks-over-threshold generalized Pareto (GPD) estimation with an adaptive profile-likelihood interval for the shape and a bootstrap of the finite endpoint;
  2. an interval-censored (grouped) GPD fit for discretised data, with profile-likelihood intervals for the shape and the endpoint, which removes the bias that rounding induces in both;
  3. a likelihood-ratio trend test calibrated by complete-block label permutation, with an interpolated minimum-detectable effect reported together with its underlying effect grid;
  4. Monte Carlo power and signed minimum-detectable-effect (MDE) analysis;
  5. a sequential Monte Carlo precision protocol that reports the simulation error of every Monte Carlo estimate and grows a run until its MCSE, estimate and qualitative decision have all stabilised;
  6. a finite-sample calibration suite that measures the empirical type-I error, power, interval coverage and bias/RMSE of any estimator or test on your own data generating process; and
  7. a pre-specified multi-source robustness analysis that distinguishes non-reproduction with adequate power from an unresolved comparison.

The package uses deliberately measured terminology. A negative GPD shape point estimate gives a finite model-conditional statistical endpoint. nsevt reports support for a negative shape only when the entire 95% profile interval lies below zero; neither result is called a physical ceiling.

Install

From PyPI:

pip install nsevt

For development or the optional Streamlit demonstration:

git clone https://github.com/GaiskaSalomon/nsevt.git
cd nsevt
pip install -e ".[dev,demo]"

Quick start

import numpy as np
import nsevt

rng = np.random.default_rng(7)
threshold, xi, sigma = 40.0, -0.25, 10.0
years = np.repeat(np.arange(1980, 2030), 12)
uniform = rng.uniform(size=years.size)
excess = sigma / xi * ((1 - uniform) ** (-xi) - 1)
values = threshold + excess

fit = nsevt.gpd_pot(values, threshold=threshold, n_boot=300)
print(fit.summary())
print("negative-shape estimate:", fit.bounded_estimate)
print("negative shape supported by 95% CI:", fit.bounded_supported)

trend = nsevt.trend_permutation(excess, years, n_perm=999)
print("LR permutation p:", trend["p_permutation"],
      "+/-", trend["p_permutation_mcse"])

mde = nsevt.min_detectable_effect(
    excess, years, direction="both", n_rep=200, n_perm_calibration=499
)
print("positive MDE:", mde["mde_positive"])
print("negative MDE:", mde["mde_negative"])
print("interpolated EMD:", mde["emd_positive"], mde["emd_positive_ci95"])

Discretised (grouped) data

When values are recorded on a grid (for example wind speeds in 5 kt steps), fitting the continuous GPD to the rounded values biases the shape and the finite endpoint. gpd_pot_grouped fits the interval-censored likelihood instead and reports a profile-likelihood interval for both the shape and the endpoint (the endpoint interval profiles the reparameterised endpoint, not a percentile bootstrap).

fit = nsevt.gpd_pot_grouped(values, threshold=40, grid=5.0)
print(fit.summary())
print("shape 95% CI:", fit.xi_ci95)
print("endpoint 95% CI:", fit.endpoint_ci95)

Multi-source robustness

Sources must be genuinely distinct products with comparable temporal support; early and late halves of one record are not substitutes.

result = nsevt.multisource_robustness(
    [("product A", values_a, years_a),
     ("product B", values_b, years_b)],
    threshold=40,
    reference="product A",
)
print(result.trend_status)
print(result.table())

Possible trend statuses include reproduced, inconsistent_direction, not_reproduced_with_power, not_resolved, and no_reference_signal. Agreement or disagreement across sources is a robustness result; it does not by itself attribute a discrepancy to instruments, homogenization, or physical change.

Monte Carlo precision

Any power, coverage, permutation p-value or bootstrap interval is itself a Monte Carlo estimate with a simulation error. nsevt.mc reports that error and grows a run in blocks until the Monte Carlo standard error, the estimate and every registered qualitative decision have all stabilised, rather than trusting a fixed replicate budget. It depends only on NumPy and is estimator-agnostic: you supply the replicate outcomes.

from nsevt import mc

streams = mc.block_streams(seed=20260814, n_blocks=64)

def draw(k, block_index):                       # k rejection indicators
    rng = streams[block_index]
    return (rng.random(k) < 0.8).astype(float)   # e.g. a power study

run = mc.run_sequential("power", draw, kind="proportion", epsilon=0.0025)
s = run.summary()
print(s["status"], s["R_star"], s["estimate"], s["mcse"])

required_replicates(0.80, 0.0025) reports the 25,600 replicates such a target needs, and permutation_pvalue returns the floor-aware (1 + #exceed) / (B + 1). At observed proportions of exactly zero or one, the MCSE uses a Jeffreys half-count rather than reporting zero simulation error.

Finite-sample calibration

nsevt.calibration checks whether a method's asymptotic guarantees actually hold at your sample size and under your data generating process. You pass a simulate(rng, n) callable and your estimator or test; the suite measures the empirical type-I error or power (rejection_rate), interval coverage (coverage), and bias/RMSE (bias_rmse). Under a misspecified DGP an estimator may converge to a pseudo-true value rather than the generating parameter. pseudo_true provides a large-sample Monte Carlo proxy for that target so coverage and bias can be assessed against an explicit, reproducible reference. It is one large-sample fit, not proof of a limiting value; check increasing sample sizes and independent seeds before treating it as a target.

from nsevt import calibration as cal

def simulate(rng, n):                     # your data generating process
    u = rng.uniform(size=n)
    return 15.0 / -0.2 * ((1 - u) ** 0.2 - 1)      # GPD(xi=-0.2, sigma=15)

def interval(sample):
    _, _, ci = nsevt.profile_ci_xi(sample)
    return ci

cov = cal.coverage(interval, simulate, n=400, target=-0.2, level=0.95,
                   epsilon=0.01, r0=1000, r_min=1000, block=500)
print(cov["coverage"], cov["mcse"], cov["status"])

The proportion analyses run on the sequential protocol above, so each reports its Monte Carlo standard error and whether it stabilised.

Grouped regression and return levels

nsevt.design extends the grouped fit from a single scale to a log-linear scale design, and adds return levels. fit_grouped_design takes a design matrix (a trend column, group indicators, or any combination); profile_ci_coef gives a profile-likelihood interval for any coefficient — the interval counterpart of the permutation trend test. return_level and profile_ci_return_level give the level exceeded once per m observations at an exceedance rate, with a profile interval obtained by profiling the level itself.

from nsevt import design
import numpy as np

X = np.column_stack([np.ones(marks.size), decade])   # intercept + a trend column
fit = design.fit_grouped_design(marks, threshold=40, design=X, grid=5.0)
ci = design.profile_ci_coef(marks, 40, X, coef=1, grid=5.0)   # trend interval
print(fit["coef"][1], ci["ci"])

rl = design.profile_ci_return_level(marks, 40, rate=0.4, m=100, grid=5.0)
print(rl["return_level"], rl["ci"])

Stable and experimental functionality

As of 1.0.0 the public API is stable: the modules listed as stable below — their names, signatures, and documented return schemas — are frozen, and a backward-incompatible change to them will require a 2.0.0. Additions remain minor (1.x) changes. The nsevt.experimental routines are outside this guarantee.

status module purpose
stable nsevt.gpd GPD fit, profile interval, conditional endpoint bootstrap, return levels
stable nsevt.grouped interval-censored (grouped) GPD fit; profile intervals for shape and endpoint
stable nsevt.trend LR block-label permutation, power/MDE, descriptive block-bootstrap interval
stable nsevt.mc sequential Monte Carlo precision: MCSE, stopping rule, traces, reproducible substreams
stable nsevt.calibration finite-sample type-I/power, coverage, bias/RMSE and pseudo-true target
stable nsevt.design grouped GPD regression (covariate scale), coefficient profile CI, return levels
stable nsevt.transportability multi-source robustness and power-aware status
stable with assumptions split_conformal upper tail bound for exchangeable calibration scores
experimental block_conformal block-aggregate dependence sensitivity diagnostic
experimental twoscale_trend residual-bootstrap distribution-valued trend diagnostic
experimental wasserstein_decomposition numerical quantile-grid energy decomposition

The exact assumptions and claim boundaries are documented in docs/assumptions.md. The experimental routines are collected under nsevt.experimental (still importable from the top level for backward compatibility) and are outside the public-API stability guarantee. docs/stability.md is the authoritative per-symbol list and the semantic-versioning rule; it supersedes any other stability statement, including the manuscript, when they disagree.

Reproducibility and tests

The implementation was adapted from research pipelines and then regression- checked; it is not represented as a verbatim copy. Randomized routines accept a seed and report the number of successful replicates. Run the validation suite with:

pip install -e ".[dev]"
python tools/release_check.py
ruff check src tests demo tools
mypy src/nsevt
pytest --cov=nsevt --cov-report=term-missing --cov-fail-under=80
python -m build
python -m twine check dist/*
python tools/write_checksums.py dist
python tools/check_distributions.py dist

See docs/validation.md for what each test establishes and, equally importantly, what it does not establish.

Releases and archived evidence

Stable releases use matching X.Y.Z identifiers in the installed package, Git tag, GitHub release, PyPI distribution, CITATION.cff, and Zenodo metadata. The concept DOI above identifies the software across releases; Zenodo also assigns an immutable DOI to each exact version. Release candidates are exercised on TestPyPI and are not presented as stable archived versions.

Release artifacts are built once, smoke-tested as both a wheel and source distribution, hashed, attached to a draft GitHub release, and then published unchanged to PyPI when that draft is released. See RELEASING.md for the maintained checklist and trust boundaries.

Citation and license

Use CITATION.cff or the Zenodo DOI shown above. nsevt is released under the MIT License; see LICENSE.

Metadata

Release files for nsevt 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nsevt 1.2.0
File Size Uploaded
nsevt-1.2.0.tar.gz 116.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nsevt 1.2.0
File Interpreter ABI Platform
nsevt-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 171.1 kB

Release files / nsevt-1.2.0.tar.gz

Download URL nsevt-1.2.0.tar.gz
Size 116.2 kB
Tags Source
SHA-256 checksum
How to use checksums
1003256c01e46ba06bde1c5fdb4d29c40b6afb43c45fa7a60b67792553d59925
BLAKE2b-256 checksum
How to use checksums
1f7937ff63be7ac4804d26dd58abc90799e117637bab1eb112999a021dd66900
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release files / nsevt-1.2.0-py3-none-any.whl

Download URL nsevt-1.2.0-py3-none-any.whl
Size 54.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e1a7eebf6f651ac07db246326d15383dfe0acea1124e00aa9de6727f24ae96a5
BLAKE2b-256 checksum
How to use checksums
b1533836279ff2c1f016e034c56abfae54f4adf8384044470cc99d9d47d9735f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release history Release notifications | RSS feed

1.2.2

2 release files

1.2.1

2 release files

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page