aggregate builds essentially exact compound (aggregate) probability
distributions quickly and accurately. It can be used to solve insurance, risk
management, and actuarial problems using realistic models that reflect
underlying frequency and severity. It delivers the speed and accuracy of
parametric distributions to situations that usually require simulation, making
it as easy to work with an aggregate (compound) probability distribution as the
lognormal. aggregate includes an expressive language called DecL to describe
aggregate distributions and is implemented in Python under an open source
BSD-license.
Version 1.0 represents a substantial extension over the prior 0.30.1 release. It was written in collaboration with Claude Code and adds:
- The ability to model positive and negative amounts, opening the way for a PnL profit-and-loss class supporting price quoting and evaluation.
- Automated generation of incremental gross–ceded–net views across multi-layer occurrence and aggregate programs.
- FFT calculations performed in a support window that need not include the origin, providing more efficient discretization.
- Bivariate distributions, including bivariate severity, clash, ceded–net, gross-cede, and gross–net models.
- Standard reinsurance variable features: swings, slides, profit commissions, reinstatements, and loss corridors, as well as loss-sensitive retro rating used in large accounts.
Aggregate: fast, accurate, and flexible approximation of compound probability
distributions
describes the Aggregate class within aggregate. This paper has been
published in the peer reviewed journal Annals of Actuarial
Science's
Actuarial Software series. The paper describes the purpose, implementation, and
use Aggregate, showing how it can be used to create and manipulate compound
frequency-severity distributions.
See CHANGELOG.md for the full version history.
Almost everything is stable. Aggregate, Portfolio, PnL, Severity,
Frequency, Distortion, BivariateAggregate, Underwriter, build, qd
and the DecL grammar carry the usual promise: from 1.0 onward a documented name
keeps its meaning, and a breaking change waits for a major release after a
deprecation period.
Two modules are provisional, in the sense of PEP
411: aggregate.charts and
aggregate.exhibits. They are not part of the 1.0 API contract and may change
in a minor release with no deprecation period. They are additive side projects
to the release, they import from the core and the core does not import them, so
nothing in them can reach the stable surface. They are public on purpose: use
them and report what does not fit, which is how a provisional module graduates
to stable.
The full statement, including exactly what "provisional" covers in each, is in the API Stability page of the documentation.
https://aggregate.readthedocs.io/
https://github.com/mynl/aggregate
aggregate requires Python 3.12 or later. The strongly recommended way to
install and manage it is with uv, Astral's fast
Python package and project manager — follow the
uv installation guide
to get it.
Once uv is installed, add aggregate to a uv-managed project:
uv init myproject # or cd into an existing uv project
cd myproject
uv add aggregate # resolves, locks, and installs into .venvuv add records the dependency in your pyproject.toml and syncs the project
environment; from then on uv sync recreates that exact, locked environment on
any machine. Optional extras add capabilities:
uv add "aggregate[numba]" # numba-compiled TVaR paths
uv add "aggregate[viz]" # interactive bivariate exploration: holoviews, datashader, bokeh
uv add "aggregate[massive]" # disk-backed bivariate grids via zarr
uv add "aggregate[notebook]" # JupyterLab, widgets
uv add "aggregate[dev]" # documentation build and test tooling
uv add "aggregate[all]" # all of the above except devRun anything inside the managed environment with uv run, for example
uv run python or uv run jupyter lab. All the code examples have been tested
in such an environment and the documentation builds in it.
If you already have an environment and simply want the package dropped into it, install it directly — with uv:
uv pip install aggregateor with plain pip:
pip install aggregateTo get started, import build. It provides easy access to all functionality.
The function qd is a quick display helper, printing germane information.
Here is a model of the sum of three dice rolls. Running qd(a) prints the mean,
SD, CV, skewness, and 1st, 50th and 99th percentiles for the frequency,
severity, and aggregate components. Common statistical functions like the cdf
and quantile function are built-in. The whole probability distribution is
available in a.density_df.
from aggregate import build, qd
a = build('agg Dice dfreq [3] dsev [1:6]')
qd(a)
>>> Aggregate object: Dice. Frequency distribution empirical. Severity
dhistogram, [1, 6], bounded; atoms [1 2 3 4 5 6]. Updated with bucket
size 1 and log2 = 5. Validation: not unreasonable.
>>> Mean SD CV Skew P01 Median P99
>>> X
>>> Freq 3 0 0
>>> Sev 3.5 1.7078 0.48795 0 1 3 6
>>> Agg 10.5 2.958 0.28172 0 4 10 17
print(f'\nProbability sum < 12 = {a.cdf(12):.3f}\nMedian = {a.q(0.5):.0f}')
>>> Probability sum < 12 = 0.741
>>> Median = 10
aggregate can use any scipy.stats continuous random variable as a severity, and
supports all common frequency distributions. Here is a compound-Poisson with lognormal
severity, mean 50 and cv 2.
a = build('agg Example 10 claims sev lognorm 50 cv 2 poisson')
qd(a)
>>> Aggregate object: Example. Frequency distribution poisson. Severity lognorm,
[0, inf), subexponential right tail. Updated with bucket size 2 and log2 =
16. Validation: not unreasonable.
>>> Mean SD CV Skew P01 Median P99
>>> X
>>> Freq 10 3.1623 0.31623 0.31623
>>> Sev 50 100 2 13.981 2 22 428
>>> Agg 500 353.56 0.70711 3.5312 68 422 1736
See the documentation for more examples.
In a notebook the DecL does not have to live inside a Python string. Load the magic once per kernel:
%load_ext aggregate.magics
and write the program as the cell:
%%agg
agg Dice dfreq [3] dsev [1:6]
which is exactly a = build('agg Dice dfreq [3] dsev [1:6]') followed by
qd(a), with two conveniences: the program is not in quotes, so your editor
still highlights it as DecL, and the object is bound to its declared name as
well as to a, giving both a and Dice.
Full descriptions are in the documentation.
See pyproject.toml.
The pytest suite lives in tests/:
uv run pytest # fast suite (multi-minute cases deselected)
uv run pytest -m "slow or not slow" # everything, including the slow bivariate cases
uv run pytest tests/test_decl_parser.py # one file; add -k "pattern" to filter by name
aggregate was used to create all of the examples, figures, and tables in
Pricing Insurance
Risk. Those
exhibits can still be re-created using version 0.30.1 (the
Baseline
release). However, much of the needed functionality was removed in version 1.0,
because it was not core to the go-forward purpose of the package: it compared recommended
(spectral) methods with (not recommended) legacy methods. The blog post describes how to create PIR exhibits for a custom portfolio, again using the Baseline release.
BSD 3 license.
All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. Create a pull request on github and/or email me.
Social media: https://www.reddit.com/r/AggregateDistribution/.
Blog: https://blog.mynl.com