sensitivity Module#

Monte Carlo sensitivity analysis for compartmental models.

This module provides tools to sample parameter distributions, run thousands of model instances, and aggregate results into percentile envelopes for uncertainty quantification.

Classes#

class episia.models.sensitivity.SensitivityAnalysis(model_class, param_class, fixed, distributions, n_samples=200, seed=42, n_jobs=1, t_eval_points=500)[source]#

Bases: object

Monte Carlo sensitivity analysis for compartmental epidemic models.

Example:

from episia.models.sensitivity import SensitivityAnalysis
from episia.models import SEIRModel
from episia.models.parameters import SEIRParameters

sa = SensitivityAnalysis(
    model_class=SEIRModel,
    param_class=SEIRParameters,
    fixed=dict(N=1_000_000, I0=10, E0=50, t_span=(0, 365)),
    distributions={
        'beta':  ('uniform', 0.25, 0.50),
        'sigma': ('normal',  1/5.2, 0.02),
        'gamma': ('uniform', 1/21,  1/7),
    },
    n_samples=500,
    seed=42,
)

result = sa.run()
print(result)
result.plot(compartment='I').show()
result.plot_metric_distribution('r0').show()
result.to_dataframe()
Parameters:
  • fixed (Dict[str, Any])

  • distributions (Dict[str, Tuple])

  • n_samples (int)

  • seed (Optional[int])

  • n_jobs (int)

  • t_eval_points (int)

__init__(model_class, param_class, fixed, distributions, n_samples=200, seed=42, n_jobs=1, t_eval_points=500)[source]#
Parameters:
  • model_class – CompartmentalModel subclass.

  • param_class – Matching parameters class.

  • fixed (Dict[str, Any]) – Parameters held constant across all runs.

  • distributions (Dict[str, Tuple]) – Parameters to sample; values are distribution specs. E.g. {‘beta’: (‘uniform’, 0.2, 0.5)}.

  • n_samples (int) – Number of Monte Carlo draws.

  • seed (int | None) – Random seed for reproducibility.

  • n_jobs (int) – Parallel workers (1 = sequential, -1 = all CPUs).

  • t_eval_points (int) – Number of time points per trajectory.

run(verbose=True)[source]#

Draw samples, run all models, and return aggregated SensitivityResult.

Parameters:

verbose (bool) – Print progress summary.

Returns:

SensitivityResult.

Return type:

SensitivityResult

class episia.models.sensitivity.SensitivityResult(t, envelopes, metrics, n_samples, n_failed, param_samples, compartment_names)[source]#

Bases: object

Aggregated result of a Monte Carlo sensitivity analysis.

Parameters:
t#

Common time array.

Type:

numpy.ndarray

envelopes#

Dict compartment → {‘p5’,’p25’,’p50’,’p75’,’p95’} arrays.

Type:

Dict[str, Dict[str, numpy.ndarray]]

metrics#

DataFrame-ready summary of scalar metrics across runs.

Type:

Dict[str, numpy.ndarray]

n_samples#

Number of successful runs.

Type:

int

n_failed#

Number of failed runs.

Type:

int

param_samples#

List of sampled parameter dicts.

Type:

List[Dict[str, float]]

compartment_names#

Compartments present in results.

Type:

List[str]

__repr__()[source]#

Return repr(self).

Return type:

str

compartment_names: List[str]#
envelopes: Dict[str, Dict[str, ndarray]]#
metrics: Dict[str, ndarray]#
n_failed: int#
n_samples: int#
param_samples: List[Dict[str, float]]#
plot(compartment='I', show_samples=False, n_sample_traces=50, backend='plotly', theme='scientific', title=None)[source]#

Plot percentile envelope for a compartment.

Parameters:
  • compartment (str) – Compartment name (default ‘I’).

  • show_samples (bool) – Overlay individual sample trajectories.

  • n_sample_traces (int) – How many individual traces to show (max).

  • backend (str) – ‘plotly’ or ‘matplotlib’.

  • theme (str) – Theme name.

  • title (str | None) – Figure title (auto if None).

Returns:

Figure object.

Return type:

Any

plot_metric_distribution(metric='r0', backend='plotly', theme='scientific')[source]#

Histogram of a scalar metric across all runs.

Parameters:
  • metric (str) – One of ‘r0’, ‘peak_infected’, ‘peak_time’, ‘final_size’.

  • backend (str) – ‘plotly’ or ‘matplotlib’.

  • theme (str) – Theme name.

Returns:

Figure object.

Return type:

Any

summary()[source]#

Return a dict of summary statistics for each scalar metric.

Returns:

Dict with keys like ‘r0_median’, ‘r0_p5’, ‘peak_infected_p95’, etc.

Return type:

Dict[str, Any]

t: ndarray#
to_dataframe()[source]#

Return a pandas DataFrame with one row per successful run.

Columns: sampled parameters + r0, peak_infected, peak_time, final_size.

Supported Distributions#

  • ('uniform', low, high)

  • ('normal', mean, std)

  • ('lognormal', mean, sigma) # mean/std of underlying normal

  • ('triangular', low, mode, high)

  • ('beta_dist', alpha, beta)

  • ('fixed', value) # Pin a parameter

Examples#

Basic sensitivity analysis:

from episia.models import SEIRModel
from episia.models.parameters import SEIRParameters
from episia.models.sensitivity import SensitivityAnalysis

sa = SensitivityAnalysis(
    model_class=SEIRModel,
    param_class=SEIRParameters,
    fixed={
        'N': 1_000_000,
        'I0': 10,
        'E0': 50,
        't_span': (0, 365)
    },
    distributions={
        'beta': ('uniform', 0.25, 0.50),
        'sigma': ('normal', 1/5.2, 0.02),
        'gamma': ('uniform', 1/21, 1/7),
    },
    n_samples=500,
    seed=42,
    n_jobs=-1  # Use all CPU cores
)

result = sa.run(verbose=True)

print(result)  # Summary statistics
result.plot(compartment='I').show()  # Trajectory envelope
result.plot_metric_distribution('r0').show()  # R₀ distribution

# Export to DataFrame
df = result.to_dataframe()
print(df.describe())

Interpreting results:

# Get summary statistics
summary = result.summary()
print(f"R₀ median: {summary['r0_median']:.2f} [{summary['r0_p5']:.2f}-{summary['r0_p95']:.2f}]")
print(f"Peak infections: {summary['peak_infected_median']:.0f} [{summary['peak_infected_p5']:.0f}-{summary['peak_infected_p95']:.0f}]")

# Access envelope arrays
t = result.t
i_low = result.envelopes['I']['p5']
i_high = result.envelopes['I']['p95']
i_median = result.envelopes['I']['p50']

Comparing compartments:

fig = result.plot(compartment='I', title="Infectious")
fig = result.plot(compartment='R', title="Recovered")
fig = result.plot(compartment='D', title="Deaths")