Metadata-Version: 2.4
Name: pyheor
Version: 0.4.1
Summary: Python Health Economics Research Optimizer - A framework for Markov cohort models and cost-effectiveness analysis
Author: PyHEOR Team
License: AGPL-3.0-or-later
Project-URL: Homepage, https://github.com/lenardar/PyHEOR
Project-URL: Repository, https://github.com/lenardar/PyHEOR
Project-URL: Issues, https://github.com/lenardar/PyHEOR/issues
Project-URL: Changelog, https://github.com/lenardar/PyHEOR/blob/main/CHANGELOG.md
Keywords: health economics,cost-effectiveness,markov model,ICER,PSA,OWSA
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.21
Requires-Dist: pandas>=1.3
Requires-Dist: matplotlib>=3.5
Requires-Dist: scipy>=1.7
Requires-Dist: tabulate>=0.9
Requires-Dist: openpyxl>=3.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: jupyter; extra == "dev"
Requires-Dist: seaborn; extra == "dev"
Dynamic: license-file

# PyHEOR

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

[中文](https://github.com/lenardar/PyHEOR/blob/main/README_zh.md) · [Français](https://github.com/lenardar/PyHEOR/blob/main/README_fr.md)

## Example results

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

| Survival modeling | Parameter uncertainty |
|:---:|:---:|
| [<img src="https://raw.githubusercontent.com/lenardar/PyHEOR/main/examples/psm_oncology/figures/survival_curves.png" width="440" alt="PFS and OS survival curves for two strategies">](https://github.com/lenardar/PyHEOR/blob/main/examples/psm_oncology/figures/survival_curves.png) | [<img src="https://raw.githubusercontent.com/lenardar/PyHEOR/main/examples/psm_oncology/figures/ce_scatter.png" width="440" alt="PSA incremental cost and QALY scatter plot">](https://github.com/lenardar/PyHEOR/blob/main/examples/psm_oncology/figures/ce_scatter.png) |
| Compare fitted PFS and OS curves. | Explore incremental costs and QALYs across PSA draws. |
| **Cost-effectiveness acceptability (CEAC)** | **Multi-strategy decision analysis (CEAF)** |
| [<img src="https://raw.githubusercontent.com/lenardar/PyHEOR/main/examples/psm_oncology/figures/ceac.png" width="440" alt="Two-strategy cost-effectiveness acceptability curves">](https://github.com/lenardar/PyHEOR/blob/main/examples/psm_oncology/figures/ceac.png) | [<img src="https://raw.githubusercontent.com/lenardar/PyHEOR/main/examples/multi_strategy_comparison/figures/ceaf.png" width="440" alt="Cost-effectiveness acceptability frontier with strategy switches">](https://github.com/lenardar/PyHEOR/blob/main/examples/multi_strategy_comparison/figures/ceaf.png) |
| See how cost-effectiveness probabilities vary with WTP. | Track the recommended strategy and its cost-effectiveness probability. |

Reproduce these figures with the [PSM oncology](https://github.com/lenardar/PyHEOR/blob/main/examples/psm_oncology/example.py) and [multi-strategy comparison](https://github.com/lenardar/PyHEOR/blob/main/examples/multi_strategy_comparison/example.py) examples. CEAC/CEAF show labeled display smoothing; analysis and exports retain raw probabilities.

## Install

```bash
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.

```python
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` |

```python
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` |

`GeneralizedGamma` (including `from_flexsurv("gengamma", ...)`) uses the log-normal limit with `meanlog=mu` and `sdlog=sigma` when `abs(Q) < 1e-5`, to avoid numerical precision loss. This is exact at Q=0 and an approximation for nonzero Q. Excel export uses the same threshold; its positive-Q Gamma upper tails use `1-CDF`, which can round very small probabilities to zero.

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:

```python
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](https://github.com/lenardar/PyHEOR/blob/main/examples) cover Markov, PSM, MicroSim and strategy comparisons. See [CHANGELOG](https://github.com/lenardar/PyHEOR/blob/main/CHANGELOG.md) for version history.

## Development

See [development and versioning rules](https://github.com/lenardar/PyHEOR/blob/main/CONTRIBUTING.md).

```bash
pip install -e '.[dev]'
pytest
```

License: [AGPL-3.0-or-later](https://github.com/lenardar/PyHEOR/blob/main/LICENSE).
