Skip to content

Commit e61389a

Browse files
authored
Merge pull request #1 from dom-muston/main
Small updates to readme, and add pkgdown site
2 parents d32a279 + 691694a commit e61389a

File tree

13 files changed

+464
-272
lines changed

13 files changed

+464
-272
lines changed

.github/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.html

.github/workflows/R-CMD-check.yaml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2+
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
8+
name: R-CMD-check.yaml
9+
10+
permissions: read-all
11+
12+
jobs:
13+
R-CMD-check:
14+
runs-on: ${{ matrix.config.os }}
15+
16+
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
17+
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
config:
22+
- {os: macos-latest, r: 'release'}
23+
- {os: windows-latest, r: 'release'}
24+
- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
25+
- {os: ubuntu-latest, r: 'release'}
26+
- {os: ubuntu-latest, r: 'oldrel-1'}
27+
28+
env:
29+
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
30+
R_KEEP_PKG_SOURCE: yes
31+
32+
steps:
33+
- uses: actions/checkout@v4
34+
35+
- uses: r-lib/actions/setup-pandoc@v2
36+
37+
- uses: r-lib/actions/setup-r@v2
38+
with:
39+
r-version: ${{ matrix.config.r }}
40+
http-user-agent: ${{ matrix.config.http-user-agent }}
41+
use-public-rspm: true
42+
43+
- uses: r-lib/actions/setup-r-dependencies@v2
44+
with:
45+
extra-packages: any::rcmdcheck
46+
needs: check
47+
48+
- uses: r-lib/actions/check-r-package@v2
49+
with:
50+
upload-snapshots: true
51+
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'

.github/workflows/pkgdown.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2+
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
release:
8+
types: [published]
9+
workflow_dispatch:
10+
11+
name: pkgdown.yaml
12+
13+
permissions: read-all
14+
15+
jobs:
16+
pkgdown:
17+
runs-on: ubuntu-latest
18+
# Only restrict concurrency for non-PR jobs
19+
concurrency:
20+
group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
21+
env:
22+
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
23+
permissions:
24+
contents: write
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: r-lib/actions/setup-pandoc@v2
29+
30+
- uses: r-lib/actions/setup-r@v2
31+
with:
32+
use-public-rspm: true
33+
34+
- uses: r-lib/actions/setup-r-dependencies@v2
35+
with:
36+
extra-packages: any::pkgdown, local::.
37+
needs: website
38+
39+
- name: Build site
40+
run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
41+
shell: Rscript {0}
42+
43+
- name: Deploy to GitHub pages 🚀
44+
if: github.event_name != 'pull_request'
45+
uses: JamesIves/github-pages-deploy-action@v4.5.0
46+
with:
47+
clean: false
48+
branch: gh-pages
49+
folder: docs
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2+
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
8+
name: test-coverage.yaml
9+
10+
permissions: read-all
11+
12+
jobs:
13+
test-coverage:
14+
runs-on: ubuntu-latest
15+
env:
16+
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: r-lib/actions/setup-r@v2
22+
with:
23+
use-public-rspm: true
24+
25+
- uses: r-lib/actions/setup-r-dependencies@v2
26+
with:
27+
extra-packages: any::covr, any::xml2
28+
needs: coverage
29+
30+
- name: Test coverage
31+
run: |
32+
cov <- covr::package_coverage(
33+
quiet = FALSE,
34+
clean = FALSE,
35+
install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
36+
)
37+
print(cov)
38+
covr::to_cobertura(cov)
39+
shell: Rscript {0}
40+
41+
- uses: codecov/codecov-action@v5
42+
with:
43+
# Fail if error if not on PR, or if on PR and token is given
44+
fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }}
45+
files: ./cobertura.xml
46+
plugins: noop
47+
disable_search: true
48+
token: ${{ secrets.CODECOV_TOKEN }}
49+
50+
- name: Show testthat output
51+
if: always()
52+
run: |
53+
## --------------------------------------------------------------------
54+
find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
55+
shell: bash
56+
57+
- name: Upload test results
58+
if: failure()
59+
uses: actions/upload-artifact@v4
60+
with:
61+
name: coverage-test-failures
62+
path: ${{ runner.temp }}/package

README.Rmd

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ knitr::opts_chunk$set(
1717

1818
<!-- badges: start -->
1919
[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)
20+
[![R-CMD-check](https://github.com/MSDLLCpapers/dynacem/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/MSDLLCpapers/dynacem/actions/workflows/R-CMD-check.yaml)
21+
[![Codecov test coverage](https://codecov.io/gh/MSDLLCpapers/dynacem/graph/badge.svg)](https://app.codecov.io/gh/MSDLLCpapers/dynacem)
2022
<!-- badges: end -->
2123

2224
The goal of *dynacem* is to evaluates cost-effectiveness models with dynamic pricing and uptake.
@@ -28,18 +30,24 @@ Cost-effectiveness models are conventionally developed for single cohorts withou
2830
You can install the development version of dynacem from [GitHub](https://github.com/) with:
2931

3032
```{r install}
31-
# install.packages("pak")
32-
# pak::pak("MSDLLCpapers/dynacem")
33+
#| eval: false
34+
install.packages("pak")
35+
pak::pak("MSDLLCpapers/dynacem")
3336
```
3437

3538
## Example
3639

3740
Some example present value calculations are provided below for a single payoff. A typical cost-effectiveness model may involve several separate payoffs, for each intevention modeled, each with their own price index and discount rate.
3841

42+
### Obtain payoffs vector
43+
44+
In this case, we call out the drug acquisition cost of the new intervention (*cost_daq_new*), the total cost (*cost_total*) and QALYs (*qaly*) accumulated in each timestep. The *get_dynfields()* function will also calculated 'rolled-up' values as at the start of each timestep rather than discounted to time zero.
45+
3946
```{r getdata}
4047
# Load the dynacem package
4148
library(dynacem)
4249
library(ggplot2)
50+
library(tidyr)
4351
4452
# Review oncpsm model (heemod object)
4553
oncpsm
@@ -51,15 +59,27 @@ democe <- get_dynfields(
5159
discount = "disc"
5260
)
5361
head(democe)
62+
```
63+
64+
For this example, we are just interested in the payoff for drug acquisition costs of the new intervention.
5465

66+
```{r payoffs}
5567
# Obtain a vector of payoffs
5668
payoffs <- democe |>
5769
dplyr::filter(int=="new") |>
58-
dplyr::mutate(cost_oth = cost_total - cost_daq_new)
70+
dplyr::select(model_time, int, cost_daq_new, cost_daq_new_rup)
5971
payoffs
6072
```
6173

62-
Now let us calculate a discounted present value, given dynamic uptake (of one patient per timestep) and dynamic pricing. We assume that there are 52 timesteps per year and a discount rate of 3% (real) per year. The general rate of inflation is 5% per year. The underlying price of the payoff being costed rises with inflation of 5% for the first three years, then drops by 50%, where it rises by 4% per year.
74+
### Define dynamic pricing and uptake
75+
76+
Now let us calculate a discounted present value, given dynamic uptake of one patient per timestep, and dynamic pricing.
77+
78+
- We assume that there are 52 timesteps per year and a discount rate of 3% (real) per year.
79+
- The general rate of inflation is 5% per year.
80+
- The underlying price of the payoff being costed rises with inflation of 5% for the first three years, then drops by 50%, after which it rises by 4% per year.
81+
82+
We create a price index twice as long as we need right now for reasons that should become clear later.
6383

6484
```{r calc1}
6585
# Time horizon
@@ -73,32 +93,65 @@ disc_pt <- (1+disc_py)^(1/52) - 1
7393
prices <- c(1.05^((1:156)/52), 0.5 * (1.05^3) * 1.04^((1:(2 * Nt-156))/52))
7494
7595
# Graphically check the prices index
76-
dsprices <- tibble::tibble(
96+
tibble::tibble(
7797
years = (1:Nt)/52,
7898
prices = prices[1:Nt]
79-
)
80-
ggplot2::ggplot(dsprices, aes(x = years, y = prices)) +
81-
geom_line()
99+
) |>
100+
ggplot2::ggplot(aes(x = years, y = prices)) +
101+
ggplot2::geom_line() +
102+
ylim(0, 1.5)
103+
```
104+
105+
### Calculate current present value
82106

83-
# Calculate total discounted present value, given dynamic uptake and pricing
107+
Now with the payoff, uptake, pricing and discount rate set, we can call the *dynpv()* function and calculate the present value of the payoff.
108+
109+
```{r calc_pv}
110+
# Calculate total discounted present value of drug acquisition costs, given dynamic uptake and pricing
84111
pv1 <- dynpv(
85112
uptakes = rep(1, Nt),
86-
payoffs = payoffs$cost_oth,
113+
payoffs = payoffs$cost_daq_new_rup,
87114
prices = prices[1:Nt],
88115
disc = disc_pt
89116
)
90117
pv1$results
91118
```
92119

93-
We also wish to calculate discounted present values into the future, say every annually for 10 years. We need a price index that lasts 30 years (20 year time horizon + up to 10 years of future evaluations). We would expect this to change over time due to the price index.
120+
### Present values into the future
121+
122+
We also wish to calculate discounted present values (PV) into the future, say every annually for 10 years.
123+
124+
We need a price index that lasts 30 years (20 year time horizon + up to 10 years of future evaluations). Fortunately our price index is 40 years long (2 x 20).
125+
126+
We would expect the PV to change over time. The nominal PV will increase over time due to price inflation of of this payoff of 4\% per year. The real PV will decrease because the rate of price inflation of this particular payoff (4\% per year) is less than the general rate of inflation (5\% per year) factored into the nominal discount rate (8\% per year).
94127

95128
```{r calc2}
96129
# Present value at time 1, 53, 105, ...
97130
pv2 <- futurepv(
98-
l = (1:10)*52 - 51,
99-
payoffs = payoffs$cost_oth,
131+
l = (1:11)*52 - 51,
132+
payoffs = payoffs$cost_daq_new_rup,
100133
prices = prices,
101134
disc = disc_pt
102135
)
103-
pv2$results
104-
```
136+
137+
# Obtain a dataset of the real and nominal ICER over time
138+
ds <- pv2$results$mean |>
139+
dplyr::rename(Nominal = mean) |>
140+
dplyr::mutate(
141+
Years = tzero/52,
142+
pinfl = 1.05^Years,
143+
Real = Nominal / pinfl
144+
) |>
145+
tidyr::pivot_longer(
146+
cols = c("Nominal", "Real"),
147+
names_to = "Type",
148+
values_to = "PV"
149+
)
150+
151+
# Plot real and nominal ICER over time
152+
ggplot2::ggplot(ds,
153+
aes(x = Years, y = PV, color=Type)) +
154+
ggplot2::geom_line() +
155+
xlim(0, 10) +
156+
ylim(0, 150000)
157+
```

0 commit comments

Comments
 (0)