Skip to main content

PyHEOR

Python tools for health economic modeling and cost-effectiveness analysis.

中文 · Français

Example results

Figures generated by the synthetic examples in this repository. Click an image to view it at full size.

Survival modeling Parameter uncertainty
PFS and OS survival curves for two strategies PSA incremental cost and QALY scatter plot
Compare fitted PFS and OS curves. Explore incremental costs and QALYs across PSA draws.
Cost-effectiveness acceptability (CEAC) Multi-strategy decision analysis (CEAF)
Two-strategy cost-effectiveness acceptability curves Cost-effectiveness acceptability frontier with strategy switches
See how cost-effectiveness probabilities vary with WTP. Track the recommended strategy and its cost-effectiveness probability.

Reproduce these figures with the PSM oncology and multi-strategy comparison examples. CEAC/CEAF show labeled display smoothing; analysis and exports retain raw probabilities.

Install

pip install pyheor

Requires Python 3.9+, NumPy, SciPy, pandas, matplotlib and openpyxl.

Models

Model Time Rewards
MarkovModel Explicit fixed cycles Cohort state and transition rewards
PSMModel Explicit fixed cycles Survival-based occupancy, optional Terminal bookkeeping
MicroSimModel Explicit fixed cycles Individual state and transition rewards, heterogeneity and common random numbers
DESModel Continuous, declared time_unit State reward rates integrated over time; event rewards at actual times

Base-case analysis, sensitivity analysis, strategy comparisons, CE planes, CEACs, traces and report exports are available. DES retains base-case and PSA support.

Quick start

State costs and QALYs are per cycle. Discount rates are effective rates per cycle, and the first cycle is undiscounted. Utility weights require an explicit conversion to QALYs.

import pyheor as ph

cycle = ph.Cycle(1, "month")
model = ph.MarkovModel(
    states=["Alive", "Dead"], strategies=["SOC", "TRT"],
    n_cycles=120, cycle=cycle, method="life-table",
    dr_cost=ph.rescale_discount_rate(.03, ph.Cycle(1, "year"), cycle),
    dr_qaly=ph.rescale_discount_rate(.03, ph.Cycle(1, "year"), cycle),
)
model.add_param("u_alive", base=.8, dist=ph.Beta(mean=.8, sd=.05))
model.set_transitions("SOC", [[.98, .02], [0, 1]])
model.set_transitions("TRT", [[.985, .015], [0, 1]])
model.set_state_cost("care", {
    "SOC": {"Alive": 1000}, "TRT": {"Alive": 1500},
})
model.set_state_qaly("health", {
    "Alive": lambda p, k: ph.qaly(p["u_alive"], cycle),
})
model.set_starting_cost("test", 200)
model.set_state_cost("loading", {"Alive": 500}, cycles=1)
model.set_transition_cost("terminal", "Alive", "Dead", 10000)

base = model.run_base_case()
print(base.summary())
print(base.icer())
psa = model.run_psa(n_sim=100, seed=42)

Reward APIs

Costs and QALYs have parallel state, starting, entry, transition and custom interfaces. State rewards accept cycles=1 or a list of cycle numbers. Callbacks receive parameters and optionally a 1-based cycle index and patient attributes. Starting rewards are unweighted time-zero amounts; first-cycle state rewards are weighted by occupancy.

method is beginning, end, or life-table (default). State counts and event flows follow heemod conventions. PSM cannot identify pairwise transition flows, so entry/transition reward registration is unavailable there; use an explicit Terminal state for the documented end-of-life accounting rule.

PSM PSA resamples draws when ordered survival endpoints cross within a strategy, and warns with the rejected-draw count. n_sim counts valid simulations; max_attempts defaults to 10 * n_sim. Base-case runs remain strict. Resampling conditions the parameter distribution on valid curve ordering.

Practical tools

Use these tools through import pyheor as ph. Model engines require explicit units; the helpers perform the conversions you request.

Tool Purpose and input convention
Cycle(length, unit) Define a period; .years, .in_unit(unit) and .time(k, unit=...) convert durations and cycle-boundary times
qaly(utility, duration, unit=None) Utility × duration in years; accepts a Cycle or numeric duration with an explicit unit, utility arrays and decrements
rescale_discount_rate(rate, from_period, to_period) Effective-rate conversion: (1 + rate) ** (target duration / source duration) - 1; use two Cycle objects or two numeric periods in the same unit
rescale_survival(curve, from_unit=..., to_period=...) Convert fitted survival time to model periods, including hazards and quantiles
from_flexsurv(distribution, **parameters) Construct a survival distribution from natural-scale R/flexsurv parameters; does not accept optimizer coefficients or convert time units
ScaledSurvival(curve, factor) Direct time scaling: S_new(t) = S_old(t * factor); prefer rescale_survival() when units are known
ProportionalHazards(curve, hr) Apply S_new(t) = S_old(t) ** hr; HR does not change time units
AcceleratedFailureTime(curve, af) Apply S_new(t) = S_old(t / af); af=1.2 extends survival times by 20%
Beta, Gamma, LogNormal with mean/sd Derive sampling parameters from natural-scale moments; LogNormal also accepts meanlog/sdlog
C Fill a transition row's remaining probability; at most one per row, e.g. [ph.C, .02] gives C=.98
import pyheor as ph

month = ph.Cycle(1, "month")
year = ph.Cycle(1, "year")
monthly_qaly = ph.qaly(.8, month)                    # 0.066667 QALY
monthly_cost = 12000 * month.years                  # Annual cost → monthly: 1000
monthly_dr = ph.rescale_discount_rate(.03, year, month)
three_month_qaly = ph.qaly(.8, 3, unit="month")      # 0.2 QALY
month_boundaries = month.time([0, 1, 12], unit="year")

# Fitted in years; after conversion t=1 means one month.
curve = ph.from_flexsurv("weibull", shape=1.3, scale=1.5)
monthly_curve = ph.rescale_survival(
    curve, from_unit="year", to_period=month,
)
treated_curve = ph.ProportionalHazards(monthly_curve, hr=.75)

Supported from_flexsurv() inputs:

Distribution Parameters
exp rate
weibull, weibullPH, llogis shape, scale
lnorm meanlog, sdlog
gompertz shape, rate
gengamma mu, sigma, Q
gengamma.orig shape, scale, k

Parameters follow the respective R distribution definitions; names are not interchangeable across distributions. In particular, weibullPH uses a PH coefficient named scale, which the adapter converts to PyHEOR's Weibull scale.

Duration conventions are 12 months, 52 weeks or 365 days per year, rather than calendar date arithmetic. Multiply annual state cost rates by cycle years; enter one-time costs at their occurrence amounts. Put parameter-dependent QALY, discount-rate and survival conversions inside PSA/OWSA callbacks so they are recomputed for each draw.

Plots

Plot Purpose Entry point
State traces State occupancy over time; Markov also supports stacked areas Markov, PSM, MicroSim: base.plot_trace()
Survival curves Compare survival across strategies PSM, MicroSim, DES: base.plot_survival()
Partitioned survival area PFS, progressed and dead occupancy PSM: base.plot_state_area()
Individual outcome histograms Cost, QALY or life-year distributions MicroSim, DES: base.plot_outcomes_histogram()
Model / transition diagrams State structure and transitions Markov: base.plot_model_diagram(), base.plot_transition_diagram()
Tornado Rank parameter impacts owsa.plot_tornado()
PSA cost-effectiveness scatter Incremental costs and QALYs psa.plot_scatter()
CEAC Cost-effectiveness probability by strategy psa.plot_ceac()
PSA convergence Inspect simulation stability Markov, PSM: psa.plot_convergence()
Efficiency frontier / NMB curves Compare strategies and WTP thresholds cea.plot_frontier(), cea.plot_nmb_curve()
CEAF / EVPI curves Recommendation uncertainty and value of perfect information PSA-backed cea.plot_ceaf(), cea.plot_evpi()

Plots return Matplotlib Figure objects for customization and export:

fig = psa.plot_ceac(wtp_range=(0, 100000))
fig.savefig("ceac.png", dpi=150, bbox_inches="tight")

cea = ph.CEAnalysis.from_psa(psa)
cea.plot_ceaf(wtp_range=(0, 100000))

Display smoothing does not change analysis or exported values; empirical survival curves retain steps.

Results and export

base.summary() and base.icer() report totals and incremental analysis. reward_components provides cost/QALY components; cycle_rewards and state_occupancy provide cycle detail. DES also provides event logs and time in state; metadata records units and calculation conventions.

Use ph.export_to_excel(base, "results.xlsx") for result tables, ph.export_excel_model(base, "model.xlsx") for Markov/PSM formula workbooks, and ph.generate_report(model, "report.md") for an analysis report.

Examples

Runnable examples cover Markov, PSM, MicroSim and strategy comparisons. See CHANGELOG for version history.

Development

See development and versioning rules.

pip install -e '.[dev]'
pytest

License: AGPL-3.0-or-later.

Metadata

Release files for pyheor 0.4.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 pyheor 0.4.0
File Size Uploaded Provenance Actions
pyheor-0.4.0.tar.gz 142.9 kB With provenance Details for pyheor-0.4.0.tar.gz

Built distribution (wheel)

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

Total release size: 257.8 kB

Release files / pyheor-0.4.0.tar.gz

Download URL pyheor-0.4.0.tar.gz
Size 142.9 kB
Tags Source
SHA-256 checksum
How to use checksums
5ac70fe75b3a6737f1c2789cff0b34ddb0f411a051b24441ae740f7c65c832be
BLAKE2b-256 checksum
How to use checksums
ee3486e8766c46df4a373e96224200b0865e86dd958ee4b4d245f3e0799e34a2
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 Oct 5, 2026.

Transparency log

Release files / pyheor-0.4.0-py3-none-any.whl

Download URL pyheor-0.4.0-py3-none-any.whl
Size 114.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3c0b76ebe2d19a2b4c9a65625aa813361705d95b689517b7d48204be17604081
BLAKE2b-256 checksum
How to use checksums
ba33b38d812754ce72b25fab7bca08910fe7b9ab71519d31ac50770a6dd98860
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 Oct 5, 2026.

Transparency log

Security for pyheor 0.4.0

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pyheor-0.4.0.tar.gz

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 Oct 5, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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