Episia Package#

Episia - Epidemiology Toolbox for Python Based on OpenEpi algorithms, extended for the African public health context.

Quick start:

from episia import epi

# SEIR model
model  = epi.seir(N=1_000_000, I0=10, E0=50, beta=0.35,
                  sigma=1/5.2, gamma=1/14)
result = model.run()
result.plot().show()

# Biostatistics
rr = epi.risk_ratio(a=40, b=10, c=20, d=30)
print(rr)

# Report
report = epi.report(result, title="SEIR — Burkina Faso 2024")
report.save_html("report.html")

Advanced usage (direct imports):

# Models
from episia.models import SIRModel, SEIRModel, SEIRDModel
from episia.models import SensitivityAnalysis, ModelCalibrator

# Stats
from episia.stats import risk_ratio, odds_ratio, diagnostic_test_2x2
from episia.stats import roc_analysis, sample_size_risk_ratio

# Reporting
from episia import EpiReport
report = EpiReport(title="My report", author="Dr. Ouedraogo")
report.add_metrics({"R0": 3.2, "Peak": "42,500"})
report.save_html("report.html")

# Visualization
from episia.viz import plot_epicurve, plot_roc, plot_forest, set_theme
set_theme("dark")

# Surveillance data
from episia.data import SurveillanceDataset, AlertEngine
class episia.AlertEngine(dataset)[source]#

Bases: object

Threshold-based and statistical alert detection for surveillance data.

Example:

engine = AlertEngine(dataset)
alerts = engine.run(
    threshold=10,
    zscore_threshold=2.0,
    use_endemic_channel=True,
)
for a in alerts:
    print(a.period, a.severity, a.message)
Parameters:

dataset (SurveillanceDataset)

__init__(dataset)[source]#
Parameters:

dataset (SurveillanceDataset)

alert_summary(alerts)[source]#

Summarise a list of alerts.

Parameters:

alerts (List[Alert])

Return type:

Dict[str, Any]

run(threshold=None, zscore_threshold=2.0, use_endemic_channel=False, historical_years=None, freq='W')[source]#

Run all enabled alert detectors.

Parameters:
  • threshold (float | None) – Absolute case count threshold.

  • zscore_threshold (float) – Z-score threshold for statistical alert.

  • use_endemic_channel (bool) – Use endemic channel (requires ≥3 historical years).

  • historical_years (List[int] | None) – Years to use for endemic channel baseline.

  • freq (str) – Aggregation frequency (‘D’, ‘W’, ‘ME’).

Returns:

List of Alert objects, sorted by period.

Return type:

List[Alert]

class episia.EpiReport(title='Epidemiological report', author=None, institution=None, date=None, description=None)[source]#

Bases: object

Structured epidemiological report builder.

Example:

report = EpiReport(title="Analyse SEIR", author="Dr. Ouedraogo")
report.add_text("Introduction...", title="Introduction")
report.add_metrics({"R0": 3.2, "Peak": 42500})
report.add_table(df, title="Results")
report.save_html("rapport.html")
report.save_markdown("rapport.md")
Parameters:
  • title (str)

  • author (Optional[str])

  • institution (Optional[str])

  • date (Optional[str])

  • description (Optional[str])

__init__(title='Epidemiological report', author=None, institution=None, date=None, description=None)[source]#
Parameters:
  • title (str)

  • author (str | None)

  • institution (str | None)

  • date (str | None)

  • description (str | None)

add_divider()[source]#
Return type:

EpiReport

add_figure(figure, title=None, caption=None, width='100%')[source]#
Parameters:
  • figure (Any)

  • title (str | None)

  • caption (str | None)

  • width (str)

Return type:

EpiReport

add_metrics(metrics, title='Key indicators')[source]#
Parameters:
Return type:

EpiReport

add_result(result, title=None)[source]#
Parameters:
  • result (Any)

  • title (str | None)

Return type:

EpiReport

add_table(data, title=None, caption=None, max_rows=100)[source]#
Parameters:
  • data (Any)

  • title (str | None)

  • caption (str | None)

  • max_rows (int)

Return type:

EpiReport

add_text(text, title=None, level=2)[source]#
Parameters:
Return type:

EpiReport

save_html(path)[source]#
Parameters:

path (str | Path)

Return type:

Path

save_json(path)[source]#
Parameters:

path (str | Path)

Return type:

Path

save_markdown(path)[source]#
Parameters:

path (str | Path)

Return type:

Path

to_html()[source]#
Return type:

str

to_json(indent=2)[source]#
Parameters:

indent (int)

Return type:

str

to_markdown()[source]#
Return type:

str

class episia.EpisiaAPI[source]#

Bases: object

Unified Episia API single entry point for all functionality. Instantiated as the module-level epi singleton.

static cumulative_incidence(*args, **kwargs)[source]#
static diagnostic(*args, **kwargs)[source]#
static get_available_themes()[source]#
static incidence_rate(*args, **kwargs)[source]#
static mean_ci(*args, **kwargs)[source]#
static odds_ratio(*args, **kwargs)[source]#
static plot_epicurve(*args, **kwargs)[source]#
static plot_forest(*args, **kwargs)[source]#
static plot_roc(*args, **kwargs)[source]#
static prevalence(*args, **kwargs)[source]#
static proportion_ci(*args, **kwargs)[source]#
static read_csv(path, **kwargs)[source]#
static report(result, title=None, **kwargs)[source]#

Build a report from any EpiResult or ModelResult.

Parameters:
  • result (Any)

  • title (str | None)

static risk_ratio(*args, **kwargs)[source]#
static sample_size(*args, **kwargs)[source]#
static seir(N, I0, E0, beta, sigma, gamma, t_end=365, **kwargs)[source]#

Convenience factory for SEIRModel.

Parameters:
static seird(N, I0, E0, beta, sigma, gamma, mu, t_end=365, **kwargs)[source]#

Convenience factory for SEIRDModel.

Parameters:
static set_theme(theme)[source]#
Parameters:

theme (str)

Return type:

None

static sir(N, I0, beta, gamma, t_end=160, **kwargs)[source]#

Convenience factory for SIRModel.

Parameters:
static surveillance_from_csv(path, **kwargs)[source]#
class episia.SurveillanceDataset(df, *, date_col='date', cases_col='cases', deaths_col=None, district_col=None, disease_col=None, population_col=None)[source]#

Bases: object

Structured surveillance case count dataset.

Wraps a pandas DataFrame with columns:

date / week / period time axis district / site spatial unit (optional) disease disease or syndrome name cases integer case count deaths integer death count (optional) population population at risk (optional)

Built from CSV, DHIS2 exports, or a plain DataFrame.

Example:

from episia.data.surveillance import SurveillanceDataset

ds = SurveillanceDataset.from_csv("meningite_2024.csv",
                                   date_col="semaine",
                                   cases_col="cas")
print(ds.summary())
ds.epicurve().plot().show()
alerts = ds.alert_engine().run()
Parameters:
  • date_col (str)

  • cases_col (str)

  • deaths_col (Optional[str])

  • district_col (Optional[str])

  • disease_col (Optional[str])

  • population_col (Optional[str])

__init__(df, *, date_col='date', cases_col='cases', deaths_col=None, district_col=None, disease_col=None, population_col=None)[source]#
Parameters:
  • date_col (str)

  • cases_col (str)

  • deaths_col (str | None)

  • district_col (str | None)

  • disease_col (str | None)

  • population_col (str | None)

aggregate(freq='W', group_by=None)[source]#

Aggregate cases by time frequency and optional grouping columns.

Parameters:
  • freq (str) – Pandas offset alias (‘D’=daily, ‘W’=weekly, ‘ME’=monthly).

  • group_by (List[str] | None) – Additional columns to group by (district, disease…).

Returns:

pandas DataFrame with aggregated counts.

attack_rate(population=None, per=100000)[source]#

Compute overall attack rate.

Parameters:
  • population (int | None) – Population denominator (uses population_col if None).

  • per (int) – Rate denominator (default 100,000).

Returns:

Attack rate per per population.

Return type:

float

property cfr: float | None#

Case fatality rate = total_deaths / total_cases.

property date_range: Tuple[Any, Any]#
property df#
property diseases: List[str]#
property districts: List[str]#
endemic_channel(historical_years=None, percentiles=(25, 50, 75))[source]#

Compute the endemic channel (historical percentile envelope).

Groups by ISO week number across historical years. Returns the percentile bands used for alert zone classification.

Parameters:
  • historical_years (List[int] | None) – Years to include (all years if None).

  • percentiles (Tuple[float, float, float]) – (low, median, high) percentiles.

Returns:

‘weeks’, ‘p_low’, ‘p_mid’, ‘p_high’.

Return type:

Dict with keys

filter_date(start=None, end=None)[source]#

Filter to a date range (inclusive).

Parameters:
  • start (Any | None)

  • end (Any | None)

Return type:

SurveillanceDataset

filter_disease(disease)[source]#

Return a new dataset filtered to a single disease.

Parameters:

disease (str)

Return type:

SurveillanceDataset

filter_district(district)[source]#

Return a new dataset filtered to a single district.

Parameters:

district (str)

Return type:

SurveillanceDataset

classmethod from_csv(path, date_col='date', cases_col='cases', deaths_col=None, district_col=None, disease_col=None, population_col=None, **read_kwargs)[source]#

Load from CSV file.

Parameters:
  • path (str | Path) – Path to CSV file.

  • date_col (str) – Column name for date / week.

  • cases_col (str) – Column name for case counts.

  • deaths_col (str | None) – Column name for deaths (optional).

  • district_col (str | None) – Column name for district / site (optional).

  • disease_col (str | None) – Column name for disease / syndrome (optional).

  • population_col (str | None) – Column for population at risk (optional).

  • **read_kwargs – Passed to pd.read_csv.

Returns:

SurveillanceDataset.

Return type:

SurveillanceDataset

classmethod from_dataframe(df, **kwargs)[source]#

Wrap an existing DataFrame.

Return type:

SurveillanceDataset

classmethod from_dict(data, **kwargs)[source]#

Create from a plain dict of lists.

Parameters:

data (Dict[str, List])

Return type:

SurveillanceDataset

property n_records: int#
summary()[source]#

Return a summary statistics dict.

Return type:

Dict[str, Any]

to_timeseries_result()[source]#

Convert to api.results.TimeSeriesResult for viz integration.

Returns:

TimeSeriesResult ready for plot_epicurve().

property total_cases: int#
property total_deaths: int | None#
weekly_attack_rates(population, per=100000)[source]#

Compute weekly attack rates.

Parameters:
  • population (int) – Population at risk.

  • per (int) – Rate denominator.

Returns:

period, cases, attack_rate.

Return type:

pandas DataFrame with columns

episia.report_from_model(model_result, title=None, author=None, institution=None, sensitivity_result=None, backend='plotly', theme='scientific')[source]#

Full model simulation report (SIR / SEIR / SEIRD).

Parameters:
  • model_result (Any)

  • title (str | None)

  • author (str | None)

  • institution (str | None)

  • sensitivity_result (Any | None)

  • backend (str)

  • theme (str)

Return type:

EpiReport

episia.report_from_result(result, title=None, author=None, backend='plotly', theme='scientific')[source]#

One-line report from any EpiResult.

Parameters:
  • result (Any)

  • title (str | None)

  • author (str | None)

  • backend (str)

  • theme (str)

Return type:

EpiReport

Package Overview#

The episia package provides a comprehensive toolbox for epidemiological analysis. The recommended entry point is the unified epi interface:

from episia import epi

# Unified API
model = epi.seir(N=1_000_000, I0=10, E0=50, beta=0.35, sigma=1/5.2, gamma=1/14)
rr = epi.risk_ratio(a=40, b=10, c=20, d=30)
report = epi.report(result)

For advanced usage, individual modules can be imported directly.

Version Information#

episia.__version__: str = "0.1.0a1"#

Current version of Episia.

episia.__author__: str = "Fidlouindé Ariel Shadrac Ouedraogo"#

Package author.

episia.__email__: str = "arielshadrac@gmail.com"#

Author’s email address.

episia.__organization__: str = "Xcept-Health"#

Organization name.

episia.__license__: str = "MIT"#

License type.

Lazy Loading#

Episia uses lazy loading to improve startup performance. Heavy modules (scipy, sklearn, plotly, matplotlib) are only imported when first used. This means:

  • from episia import epi is fast

  • from episia import risk_ratio triggers the import of required dependencies

The lazy loading is implemented via PEP 562 (__getattr__).

Plotly Renderer Configuration#

Episia automatically configures the Plotly renderer based on the environment:

  • In Jupyter notebooks: Renderer is left unchanged (inline display works normally)

  • In scripts/terminals: Renderer is set to "browser" to open figures in your web browser

This prevents raw JSON from being dumped in the terminal when calling fig.show().

Unified Interface#

episia.epi = EpisiaAPI()#

Unified Episia API single entry point for all functionality. Instantiated as the module-level epi singleton.

The main entry point for Episia functionality. Provides a unified interface to all modules through a single object.

class episia.api.unified.EpisiaAPI[source]

Bases: object

Unified Episia API single entry point for all functionality. Instantiated as the module-level epi singleton.

static cumulative_incidence(*args, **kwargs)[source]
static diagnostic(*args, **kwargs)[source]
static get_available_themes()[source]
static incidence_rate(*args, **kwargs)[source]
static mean_ci(*args, **kwargs)[source]
static odds_ratio(*args, **kwargs)[source]
static plot_epicurve(*args, **kwargs)[source]
static plot_forest(*args, **kwargs)[source]
static plot_roc(*args, **kwargs)[source]
static prevalence(*args, **kwargs)[source]
static proportion_ci(*args, **kwargs)[source]
static read_csv(path, **kwargs)[source]
static report(result, title=None, **kwargs)[source]

Build a report from any EpiResult or ModelResult.

Parameters:
  • result (Any)

  • title (str | None)

static risk_ratio(*args, **kwargs)[source]
static sample_size(*args, **kwargs)[source]
static seir(N, I0, E0, beta, sigma, gamma, t_end=365, **kwargs)[source]

Convenience factory for SEIRModel.

Parameters:
static seird(N, I0, E0, beta, sigma, gamma, mu, t_end=365, **kwargs)[source]

Convenience factory for SEIRDModel.

Parameters:
static set_theme(theme)[source]
Parameters:

theme (str)

Return type:

None

static sir(N, I0, beta, gamma, t_end=160, **kwargs)[source]

Convenience factory for SIRModel.

Parameters:
static surveillance_from_csv(path, **kwargs)[source]

Reporting#

class episia.api.reporting.EpiReport(title='Epidemiological report', author=None, institution=None, date=None, description=None)[source]

Bases: object

Structured epidemiological report builder.

Example:

report = EpiReport(title="Analyse SEIR", author="Dr. Ouedraogo")
report.add_text("Introduction...", title="Introduction")
report.add_metrics({"R0": 3.2, "Peak": 42500})
report.add_table(df, title="Results")
report.save_html("rapport.html")
report.save_markdown("rapport.md")
Parameters:
  • title (str)

  • author (Optional[str])

  • institution (Optional[str])

  • date (Optional[str])

  • description (Optional[str])

__init__(title='Epidemiological report', author=None, institution=None, date=None, description=None)[source]
Parameters:
  • title (str)

  • author (str | None)

  • institution (str | None)

  • date (str | None)

  • description (str | None)

add_divider()[source]
Return type:

EpiReport

add_figure(figure, title=None, caption=None, width='100%')[source]
Parameters:
  • figure (Any)

  • title (str | None)

  • caption (str | None)

  • width (str)

Return type:

EpiReport

add_metrics(metrics, title='Key indicators')[source]
Parameters:
Return type:

EpiReport

add_result(result, title=None)[source]
Parameters:
  • result (Any)

  • title (str | None)

Return type:

EpiReport

add_table(data, title=None, caption=None, max_rows=100)[source]
Parameters:
  • data (Any)

  • title (str | None)

  • caption (str | None)

  • max_rows (int)

Return type:

EpiReport

add_text(text, title=None, level=2)[source]
Parameters:
Return type:

EpiReport

save_html(path)[source]
Parameters:

path (str | Path)

Return type:

Path

save_json(path)[source]
Parameters:

path (str | Path)

Return type:

Path

save_markdown(path)[source]
Parameters:

path (str | Path)

Return type:

Path

to_html()[source]
Return type:

str

to_json(indent=2)[source]
Parameters:

indent (int)

Return type:

str

to_markdown()[source]
Return type:

str

episia.api.reporting.report_from_result(result, title=None, author=None, backend='plotly', theme='scientific')[source]

One-line report from any EpiResult.

Parameters:
  • result (Any)

  • title (str | None)

  • author (str | None)

  • backend (str)

  • theme (str)

Return type:

EpiReport

episia.api.reporting.report_from_model(model_result, title=None, author=None, institution=None, sensitivity_result=None, backend='plotly', theme='scientific')[source]

Full model simulation report (SIR / SEIR / SEIRD).

Parameters:
  • model_result (Any)

  • title (str | None)

  • author (str | None)

  • institution (str | None)

  • sensitivity_result (Any | None)

  • backend (str)

  • theme (str)

Return type:

EpiReport

Surveillance Data#

class episia.data.surveillance.SurveillanceDataset(df, *, date_col='date', cases_col='cases', deaths_col=None, district_col=None, disease_col=None, population_col=None)[source]

Bases: object

Structured surveillance case count dataset.

Wraps a pandas DataFrame with columns:

date / week / period time axis district / site spatial unit (optional) disease disease or syndrome name cases integer case count deaths integer death count (optional) population population at risk (optional)

Built from CSV, DHIS2 exports, or a plain DataFrame.

Example:

from episia.data.surveillance import SurveillanceDataset

ds = SurveillanceDataset.from_csv("meningite_2024.csv",
                                   date_col="semaine",
                                   cases_col="cas")
print(ds.summary())
ds.epicurve().plot().show()
alerts = ds.alert_engine().run()
Parameters:
  • date_col (str)

  • cases_col (str)

  • deaths_col (Optional[str])

  • district_col (Optional[str])

  • disease_col (Optional[str])

  • population_col (Optional[str])

__init__(df, *, date_col='date', cases_col='cases', deaths_col=None, district_col=None, disease_col=None, population_col=None)[source]
Parameters:
  • date_col (str)

  • cases_col (str)

  • deaths_col (str | None)

  • district_col (str | None)

  • disease_col (str | None)

  • population_col (str | None)

aggregate(freq='W', group_by=None)[source]

Aggregate cases by time frequency and optional grouping columns.

Parameters:
  • freq (str) – Pandas offset alias (‘D’=daily, ‘W’=weekly, ‘ME’=monthly).

  • group_by (List[str] | None) – Additional columns to group by (district, disease…).

Returns:

pandas DataFrame with aggregated counts.

attack_rate(population=None, per=100000)[source]

Compute overall attack rate.

Parameters:
  • population (int | None) – Population denominator (uses population_col if None).

  • per (int) – Rate denominator (default 100,000).

Returns:

Attack rate per per population.

Return type:

float

property cfr: float | None

Case fatality rate = total_deaths / total_cases.

property date_range: Tuple[Any, Any]
property df
property diseases: List[str]
property districts: List[str]
endemic_channel(historical_years=None, percentiles=(25, 50, 75))[source]

Compute the endemic channel (historical percentile envelope).

Groups by ISO week number across historical years. Returns the percentile bands used for alert zone classification.

Parameters:
  • historical_years (List[int] | None) – Years to include (all years if None).

  • percentiles (Tuple[float, float, float]) – (low, median, high) percentiles.

Returns:

‘weeks’, ‘p_low’, ‘p_mid’, ‘p_high’.

Return type:

Dict with keys

filter_date(start=None, end=None)[source]

Filter to a date range (inclusive).

Parameters:
  • start (Any | None)

  • end (Any | None)

Return type:

SurveillanceDataset

filter_disease(disease)[source]

Return a new dataset filtered to a single disease.

Parameters:

disease (str)

Return type:

SurveillanceDataset

filter_district(district)[source]

Return a new dataset filtered to a single district.

Parameters:

district (str)

Return type:

SurveillanceDataset

classmethod from_csv(path, date_col='date', cases_col='cases', deaths_col=None, district_col=None, disease_col=None, population_col=None, **read_kwargs)[source]

Load from CSV file.

Parameters:
  • path (str | Path) – Path to CSV file.

  • date_col (str) – Column name for date / week.

  • cases_col (str) – Column name for case counts.

  • deaths_col (str | None) – Column name for deaths (optional).

  • district_col (str | None) – Column name for district / site (optional).

  • disease_col (str | None) – Column name for disease / syndrome (optional).

  • population_col (str | None) – Column for population at risk (optional).

  • **read_kwargs – Passed to pd.read_csv.

Returns:

SurveillanceDataset.

Return type:

SurveillanceDataset

classmethod from_dataframe(df, **kwargs)[source]

Wrap an existing DataFrame.

Return type:

SurveillanceDataset

classmethod from_dict(data, **kwargs)[source]

Create from a plain dict of lists.

Parameters:

data (Dict[str, List])

Return type:

SurveillanceDataset

property n_records: int
summary()[source]

Return a summary statistics dict.

Return type:

Dict[str, Any]

to_timeseries_result()[source]

Convert to api.results.TimeSeriesResult for viz integration.

Returns:

TimeSeriesResult ready for plot_epicurve().

property total_cases: int
property total_deaths: int | None
weekly_attack_rates(population, per=100000)[source]

Compute weekly attack rates.

Parameters:
  • population (int) – Population at risk.

  • per (int) – Rate denominator.

Returns:

period, cases, attack_rate.

Return type:

pandas DataFrame with columns

class episia.data.surveillance.AlertEngine(dataset)[source]

Bases: object

Threshold-based and statistical alert detection for surveillance data.

Example:

engine = AlertEngine(dataset)
alerts = engine.run(
    threshold=10,
    zscore_threshold=2.0,
    use_endemic_channel=True,
)
for a in alerts:
    print(a.period, a.severity, a.message)
Parameters:

dataset (SurveillanceDataset)

__init__(dataset)[source]
Parameters:

dataset (SurveillanceDataset)

alert_summary(alerts)[source]

Summarise a list of alerts.

Parameters:

alerts (List[Alert])

Return type:

Dict[str, Any]

run(threshold=None, zscore_threshold=2.0, use_endemic_channel=False, historical_years=None, freq='W')[source]

Run all enabled alert detectors.

Parameters:
  • threshold (float | None) – Absolute case count threshold.

  • zscore_threshold (float) – Z-score threshold for statistical alert.

  • use_endemic_channel (bool) – Use endemic channel (requires ≥3 historical years).

  • historical_years (List[int] | None) – Years to use for endemic channel baseline.

  • freq (str) – Aggregation frequency (‘D’, ‘W’, ‘ME’).

Returns:

List of Alert objects, sorted by period.

Return type:

List[Alert]

Available Lazy Imports#

The following names are lazily loaded from their respective modules:

Statistics: - risk_ratio - odds_ratio - proportion_ci - mean_ci - diagnostic_test_2x2 - roc_analysis - sample_size_risk_ratio - sample_size_single_proportion

Visualization: - set_theme - get_available_themes - plot_epicurve - plot_roc - plot_forest

Examples#

Basic usage with unified interface:

from episia import epi

# SEIR model
model = epi.seir(
    N=1_000_000,
    I0=10,
    E0=50,
    beta=0.35,
    sigma=1/5.2,
    gamma=1/14
)
result = model.run()
result.plot().show()

# Risk ratio
rr = epi.risk_ratio(a=40, b=10, c=20, d=30)
print(rr)  # Risk Ratio: 2.667 (1.514-4.696)

# Generate report
report = epi.report(result, title="SEIR Analysis")
report.save_html("report.html")

Direct imports for advanced usage:

from episia.models import SEIRModel, SensitivityAnalysis
from episia.stats import roc_analysis, sample_size_risk_ratio
from episia.viz import plot_epicurve, set_theme
from episia.data import SurveillanceDataset

# Set theme globally
set_theme("dark")

# Load surveillance data
ds = SurveillanceDataset.from_csv("cases.csv")

# ROC analysis
roc_result = roc_analysis(y_true, y_scores)

Version check:

import episia
print(f"Episia version: {episia.__version__}")