Skip to content

Commit 251e212

Browse files
authored
Merge pull request #90 from pythonhealthdatascience/dev
Dev
2 parents 86bffdc + bab02f4 commit 251e212

File tree

12 files changed

+2322
-151
lines changed

12 files changed

+2322
-151
lines changed

DESCRIPTION

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ Imports:
44
diffobj,
55
dplyr,
66
fitdistrplus,
7+
future,
8+
future.apply,
79
ggplot2,
810
jsonlite,
11+
kableExtra,
912
lintr,
1013
lubridate,
1114
plotly,

_quarto.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ website:
5050
- pages/output_analysis/warmup.qmd
5151
- pages/output_analysis/outputs.qmd
5252
- pages/output_analysis/replications.qmd
53-
- pages/output_analysis/n_reps.qmd
5453
- pages/output_analysis/parallel.qmd
54+
- pages/output_analysis/n_reps.qmd
5555
- section: "Experimentation"
5656
contents:
5757
- pages/experiments/scenarios.qmd

environment.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ channels:
44
dependencies:
55
- black=25.1.0
66
- flake8=7.3.0
7+
- itables=2.5.2
78
- jupyter=1.1.1
89
- pandas=2.3.1
910
- plotly=6.3.0

pages/model/patients.qmd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ There are two arguments required by the initialiser: `param` and `run_number`.
250250

251251
The `param` argument accepts an instance of the `Parameters` object, which groups all necessary simulation values in one place. This minimises the number of arguments required by `Model`, as it only needs one input for parameters.
252252

253-
The `run_number` will be used when generating the random seeds. This is important when performing multiple replications, as it ensures each run gets a unique seed (see the [Replications](../output_analysis/n_reps.qmd) page for more details).
253+
The `run_number` will be used when generating the random seeds. This is important when performing multiple replications, as it ensures each run gets a unique seed (see the [Replications](../output_analysis/replications.qmd) page for more details).
254254

255255
<br>
256256

@@ -455,7 +455,7 @@ There are two arguments required by the function: `param` and `run_number`.
455455

456456
The `param` argument requires the named list of parameters from the `create_params()` function, which groups all necessary simulation values in one list. This minimise the number of arguments required by `model()`, as it only needs one input for parameters.
457457

458-
The `run_number` will be used when generating the random seeds. This is important when performing multiple replications, as it ensures each run gets a unique seed (see the [Replications](../output_analysis/n_reps.qmd) page for more details).
458+
The `run_number` will be used when generating the random seeds. This is important when performing multiple replications, as it ensures each run gets a unique seed (see the [Replications](../output_analysis/replications.qmd) page for more details).
459459

460460
<br>
461461

pages/output_analysis/outputs.qmd

Lines changed: 103 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ from sim_tools.distributions import Exponential
4545
```{r}
4646
#| output: false
4747
library(dplyr)
48+
library(knitr)
4849
library(simmer)
4950
library(tidyr)
5051
```
@@ -147,7 +148,7 @@ We need to update the `Model` class so its `run` method returns a list of patien
147148

148149
### Runner class
149150

150-
We're also introducing a new `Runner` class, responsible for running simulations and performing results calculations. This keeps calculation logic separate from the core modelling code. Also, this class will be modified to perform multiple model runs on the [Replications](n_reps.qmd) page.
151+
We're also introducing a new `Runner` class, responsible for running simulations and performing results calculations. This keeps calculation logic separate from the core modelling code. Also, this class will be modified to perform multiple model runs on the [Replications](replications.qmd) page.
151152

152153
The basic structure of `Runner` is as follows. It initialises with simulation parameters, runs the model for a single replication, and organises results into separate patient-level and run-level outputs.
153154

@@ -175,7 +176,36 @@ No changes required.
175176

176177
```{r}
177178
#| echo: false
178-
{{< include outputs_resources/create_params.R >}}
179+
#' Generate parameter list.
180+
#'
181+
#' @param interarrival_time Numeric. Time between arrivals (minutes).
182+
#' @param consultation_time Numeric. Mean length of doctor's
183+
#' consultation (minutes).
184+
#' @param number_of_doctors Numeric. Number of doctors.
185+
#' @param warm_up_period Numeric. Duration of the warm-up period (minutes).
186+
#' @param data_collection_period Numeric. Duration of the data collection
187+
#' period (minutes).
188+
#' @param verbose Boolean. Whether to print messages as simulation runs.
189+
#'
190+
#' @return A named list of parameters.
191+
192+
create_params <- function(
193+
interarrival_time = 5L,
194+
consultation_time = 10L,
195+
number_of_doctors = 3L,
196+
warm_up_period = 30L,
197+
data_collection_period = 40L,
198+
verbose = FALSE
199+
) {
200+
list(
201+
interarrival_time = interarrival_time,
202+
consultation_time = consultation_time,
203+
number_of_doctors = number_of_doctors,
204+
warm_up_period = warm_up_period,
205+
data_collection_period = data_collection_period,
206+
verbose = verbose
207+
)
208+
}
179209
```
180210

181211
### Warm-up function
@@ -184,7 +214,55 @@ No changes required.
184214

185215
```{r}
186216
#| echo: false
187-
{{< include outputs_resources/filter_warmup.R >}}
217+
#' Filters arrivals and resources to remove warm-up results.
218+
#'
219+
#' @param result Named list with two tables: monitored arrivals & resources.
220+
#' @param warm_up_period Length of warm-up period.
221+
#'
222+
#' @importFrom dplyr ungroup arrange slice n filter
223+
#'
224+
#' @return The name list `result`, but with the tables (`arrivals` and
225+
#' `resources`) filtered to remove warm-up results.
226+
#' @export
227+
228+
filter_warmup <- function(result, warm_up_period) {
229+
# Skip filtering if the warm-up period is zero
230+
if (warm_up_period == 0L) return(result)
231+
232+
# Arrivals: Keep only patients who came during the data collection period
233+
result[["arrivals"]] <- result[["arrivals"]] |>
234+
group_by(.data[["name"]]) |>
235+
filter(all(.data[["start_time"]] >= warm_up_period)) |>
236+
ungroup()
237+
238+
# Resources: Filter to resource events in the data collection period
239+
dc_resources <- filter(result[["resources"]],
240+
.data[["time"]] >= warm_up_period)
241+
242+
if (nrow(dc_resources) > 0L) {
243+
244+
# For each resource, get the last even during warm-up, and replace time
245+
# for that event with the start time of the data collection period
246+
last_usage <- result[["resources"]] |>
247+
filter(.data[["time"]] < warm_up_period) |>
248+
arrange(.data[["time"]]) |>
249+
group_by(.data[["resource"]]) |>
250+
slice(n()) |>
251+
mutate(time = warm_up_period) |>
252+
ungroup()
253+
254+
# Set that last event of the resource/s as the first row of the dataframe
255+
# else calculations would assume all resources were idle at the start of
256+
# the data collection period
257+
result[["resources"]] <- rbind(last_usage, dc_resources)
258+
259+
} else {
260+
# No events after warm-up; use filtered resource table as is
261+
result[["resources"]] <- dc_resources
262+
}
263+
264+
result
265+
}
188266
```
189267

190268
### Run results function
@@ -226,9 +304,9 @@ We also need to correct the "replication" column. This is generated by `get_mon_
226304
```{r}
227305
param <- create_params()
228306
result <- model(param = param, run_number = 1L)
229-
knitr::kable(result[["arrivals"]] |> arrange(start_time))
230-
knitr::kable(result[["resources"]])
231-
knitr::kable(result[["run_results"]])
307+
kable(result[["arrivals"]] |> arrange(start_time))
308+
kable(result[["resources"]])
309+
kable(result[["run_results"]])
232310
```
233311

234312
At the moment, `result[["run_results"]]` is pretty empty (just has the replication number). Let's add some performance measures!
@@ -390,8 +468,8 @@ In `arrivals` we can see six patients listed, which aligns with our results tabl
390468
```{r}
391469
param <- create_params()
392470
result <- model(param = param, run_number = 1L)
393-
knitr::kable(result[["arrivals"]] |> arrange(start_time))
394-
knitr::kable(result[["run_results"]])
471+
kable(result[["arrivals"]] |> arrange(start_time))
472+
kable(result[["run_results"]])
395473
```
396474

397475
:::
@@ -778,7 +856,7 @@ model <- function(param, run_number) {
778856
779857
# Get the extra arrivals attributes#<<
780858
extra_attributes <- get_mon_attributes(env) |>#<<
781-
select("name", "key", "value") |>#<<
859+
dplyr::select("name", "key", "value") |>#<<
782860
# Add column with resource name, and remove that from key#<<
783861
mutate(resource = gsub("_.+", "", .data[["key"]]),#<<
784862
key = gsub("^[^_]+_", "", .data[["key"]])) |>#<<
@@ -814,8 +892,8 @@ model <- function(param, run_number) {
814892
```{r}
815893
param <- create_params()
816894
result <- model(param = param, run_number = 1L)
817-
knitr::kable(result[["arrivals"]] |> arrange(start_time))
818-
knitr::kable(result[["run_results"]])
895+
kable(result[["arrivals"]] |> arrange(start_time))
896+
kable(result[["run_results"]])
819897
```
820898

821899
:::
@@ -1031,7 +1109,7 @@ model <- function(param, run_number) {
10311109
10321110
# Get the extra arrivals attributes#<<
10331111
extra_attributes <- get_mon_attributes(env) |>#<<
1034-
select("name", "key", "value") |>#<<
1112+
dplyr::select("name", "key", "value") |>#<<
10351113
# Add column with resource name, and remove that from key#<<
10361114
mutate(resource = gsub("_.+", "", .data[["key"]]),#<<
10371115
key = gsub("^[^_]+_", "", .data[["key"]])) |>#<<
@@ -1065,8 +1143,8 @@ model <- function(param, run_number) {
10651143
```{r}
10661144
param <- create_params()
10671145
result <- model(param = param, run_number = 1L)
1068-
knitr::kable(result[["arrivals"]] |> arrange(start_time))
1069-
knitr::kable(result[["run_results"]])
1146+
kable(result[["arrivals"]] |> arrange(start_time))
1147+
kable(result[["run_results"]])
10701148
```
10711149

10721150
:::
@@ -1930,7 +2008,7 @@ No changes required.
19302008
```{r}
19312009
param <- create_params()
19322010
result <- model(param = param, run_number = 1L)
1933-
knitr::kable(result[["run_results"]])
2011+
kable(result[["run_results"]])
19342012
```
19352013

19362014
<br>
@@ -1940,7 +2018,7 @@ We can also view the result with `summarise = FALSE`:
19402018
```{r}
19412019
util_df <- calc_utilisation(resources = result[["resources"]],
19422020
summarise = FALSE)
1943-
knitr::kable(util_df)
2021+
kable(util_df)
19442022
```
19452023

19462024
:::
@@ -2320,8 +2398,8 @@ model <- function(param, run_number) {
23202398
```{r}
23212399
param <- create_params()
23222400
result <- model(param = param, run_number = 1L)
2323-
knitr::kable(result[["arrivals"]] |> arrange(start_time))
2324-
knitr::kable(result[["run_results"]])
2401+
kable(result[["arrivals"]] |> arrange(start_time))
2402+
kable(result[["run_results"]])
23252403
```
23262404

23272405
:::
@@ -2717,8 +2795,8 @@ model <- function(param, run_number) {
27172795
```{r}
27182796
param <- create_params()
27192797
result <- model(param = param, run_number = 1L)
2720-
knitr::kable(result[["arrivals"]] |> arrange(start_time))
2721-
knitr::kable(result[["run_results"]])
2798+
kable(result[["arrivals"]] |> arrange(start_time))
2799+
kable(result[["run_results"]])
27222800
```
27232801

27242802
:::
@@ -3175,8 +3253,8 @@ Here, we see the new `result[["patient_in_system"]]` dataframe, as well as the r
31753253
```{r}
31763254
param <- create_params()
31773255
result <- model(param = param, run_number = 1L)
3178-
knitr::kable(result[["patients_in_system"]])
3179-
knitr::kable(result[["run_results"]])
3256+
kable(result[["patients_in_system"]])
3257+
kable(result[["run_results"]])
31803258
```
31813259

31823260
:::
@@ -3688,7 +3766,7 @@ model <- function(param, run_number) {
36883766
36893767
# Get the extra arrivals attributes#<<
36903768
extra_attributes <- get_mon_attributes(env) |>#<<
3691-
select("name", "key", "value") |>#<<
3769+
dplyr::select("name", "key", "value") |>#<<
36923770
# Add column with resource name, and remove that from key#<<
36933771
mutate(resource = gsub("_.+", "", .data[["key"]]),#<<
36943772
key = gsub("^[^_]+_", "", .data[["key"]])) |>#<<
@@ -3729,8 +3807,8 @@ model <- function(param, run_number) {
37293807
```{r}
37303808
param <- create_params()
37313809
result <- model(param = param, run_number = 1L)
3732-
knitr::kable(result[["arrivals"]])
3733-
knitr::kable(result[["run_results"]])
3810+
kable(result[["arrivals"]])
3811+
kable(result[["run_results"]])
37343812
```
37353813

37363814
:::

pages/output_analysis/outputs_resources/create_params.R

Lines changed: 0 additions & 30 deletions
This file was deleted.

pages/output_analysis/outputs_resources/filter_warmup.R

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)