Skip to content

Commit 02aa96a

Browse files
authored
Merge pull request #1 from pythonhealthdatascience/dev
Dev
2 parents e85eb57 + b7f43a6 commit 02aa96a

File tree

63 files changed

+578
-26205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+578
-26205
lines changed

.pylintrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
[FORMAT]
2-
max-line-length=79
2+
max-line-length=79
3+
4+
[MESSAGES CONTROL]
5+
disable=too-many-arguments,too-many-positional-arguments,too-few-public-methods,too-many-instance-attributes

CHANGELOG.md

Lines changed: 0 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -4,95 +4,3 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Dates formatted as YYYY-MM-DD as per [ISO standard](https://www.iso.org/iso-8601-date-and-time-format.html).
7-
8-
## v1.2.0 - 2025-03-26
9-
10-
Add tests, change from default inputs, rename some variables, and add a method which allows the solution of `ReplicationsAlgorithm` to be less than the `initial_replications` set.
11-
12-
### Added
13-
14-
* Add unit tests for `ReplicationsAlgorithm` when only 2 replications are run, and for the new `find_position()` method.
15-
* Add back test for scenario analysis.
16-
17-
### Changed
18-
19-
* Linting GitHub action no longer triggers on pull requests.
20-
* Renamed `count_unseen` and `q_time_unseen` to be resource-specific (i.e.g `count_unseen_nurse`).
21-
* Set default alpha to 0.05 for `OnlineStatistics`.
22-
* Accept instance of `Param` class as input for `run_scenarios()` rather than a dictionary.
23-
24-
### Fixed
25-
26-
* Add `find_position()` method to `ReplicationsAlgorithm`, allowing us to correct results if the solution was below the `initial_replications` set.
27-
28-
## v1.1.0 - 2025-03-21
29-
30-
Add a bash script for executing notebooks and new tests for warm-up patients and replication consistency. Fixes were made to correct notebook configurations, adjust test parameters, refine the replication algorithm, and ensure accurate calculations, such as correcting nurse time usage and setting appropriate values in the replication algorithm. Other changes include updates to default parameters and documentation.
31-
32-
### Added
33-
34-
* Bash script to execute all notebooks (with `nbconvert` add to environment for this).
35-
* Add test related to inclusion/exclusion of warm-up patients in certain metrics (`test_warmup_high_demand()`).
36-
* Add test for consistent `nreps` between replications methods.
37-
38-
### Changed
39-
40-
* Changed default warm-up length in `Param()`.
41-
* Add pylint line limit so it adheres to PEP-8.
42-
* Allow input of `Param()` to the objects in `replications.py` so we can specify parameter set for back tests (so they don't just change results when we change the model defaults).
43-
* Add specific parameters to `generate_exp_results.ipynb` for back tests.
44-
* Add acknowledgements to README and docstrings.
45-
* Lowered the default `min_rep` for both confidence_interval_method functions.
46-
47-
### Fixed
48-
49-
* Correct `choosing_warmup.ipynb` to use multiple replications and run length at least 5-10x actual.
50-
* Fix `test_waiting_time_utilisation()`
51-
* Correct `test_klimit()` to actually use the parametrize inputs.
52-
* Add correction to `nurse_time_used` for when patients span the warm-up and data collection period.
53-
* Allow `None` for the dashed line in `plotly_confidence_interval_method()`.
54-
* Allow a solution below `initial_replications` in `ReplicationsAlgorithm`.
55-
* Set `target_met` back to 0 in `ReplicationsAlgorithm` if precision is no longer achieved.
56-
* Set `test_consistent_outputs()` to have no lookahead.
57-
58-
## v1.0.0 - 2025-02-27
59-
60-
Lots and lots of changes! Many of these are a result of comments from peer review of code by Tom Monks.
61-
62-
### Added
63-
64-
* Virtual environment alternative to conda.
65-
* Bash script to lint repository.
66-
* Lots of new unit tests and functional tests!
67-
* GitHub actions to run tests and lint repository.
68-
* Add `MonitoredResource` and alternative warm-up results collection.
69-
* Time-weighted statistics - including relevant code (`replications.py`), documentation (`choosing_replications.ipynb`), and tests (`_replications` in tests).
70-
* User-controlled interactive histogram of results in `analysis.ipynb`.
71-
* Add metrics for unseen patients.
72-
73-
### Changed
74-
75-
* Changes to code and environment to accomodate new features (described in 'Added').
76-
* Import simulation as a local package.
77-
* Save all tables and figures.
78-
* Add Tom Monks to author list.
79-
* Expanded README.
80-
* Renaming classes and variables (e.g. `Trial` to `Runner`, `Defaults` to `Param`).
81-
* Improved log formatting.
82-
* Moved methods (e.g. from `analysis.ipynb` to `simulation/`).
83-
* Re-arranged tests into unit tests, back tests and functional tests.
84-
85-
### Fixed
86-
87-
* First arrival no longer at time 0.
88-
* Begin interval audit at start of data collection period (rather than start of warm-up period).
89-
* Correct logging message where wrong time was used.
90-
* Add error handling for invalid cores (in model + test) and error message for attempts to log when in parallel.
91-
* Resolved runtime warning with handling for variance 0 in `summary_stats()`.
92-
* Prevent output of standard deviation or confidence intervals from `summary_stats()` when n<3.
93-
* Add error handling for results processing when there are no arrivals.
94-
* Add error handling for invalid mean in `Exponential`.
95-
96-
## v0.1.0 - 2025-01-09
97-
98-
🌱 First release of the python DES template.

CITATION.cff

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
cff-version: 1.2.0
55
title: >-
6-
Python DES RAP Template
6+
Stroke capacity planning model: python DES RAP
77
message: >-
88
If you use this software, please cite it using the
99
metadata from this file.
@@ -14,16 +14,11 @@ authors:
1414
email: a.heather2@exeter.ac.uk
1515
affiliation: University of Exeter
1616
orcid: 'https://orcid.org/0000-0002-6596-3479'
17-
- given-names: Thomas
18-
family-names: Monks
19-
email: t.m.w.monks@exeter.ac.uk
20-
affiliation: University of Exeter
21-
orcid: 'https://orcid.org/0000-0003-2631-4481'
2217
repository-code: >-
23-
https://github.com/pythonhealthdatascience/rap_template_python_des
18+
https://github.com/pythonhealthdatascience/stroke_rap_python
2419
abstract: >-
25-
A template for creating discrete-event simulation (DES) models in Python
26-
within a reproducible analytical pipeline (RAP).
20+
Applying the Python DES RAP Template to the Stroke Capacity Planning Model
21+
from Monks et al. 2016.
2722
license: MIT
28-
version: '1.2.0'
23+
version: '0.1.0'
2924
date-released: '2025-03-26'

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Stroke capacity planning model: python DES RAP
44

5-
[![python](https://img.shields.io/badge/-Python_Version-blue?logo=python&logoColor=white)](https://www.python.org/)
5+
[![python](https://img.shields.io/badge/-Python_3.13.1-blue?logo=python&logoColor=white)](https://www.python.org/)
66
![licence](https://img.shields.io/badge/Licence-MIT-green.svg?labelColor=gray)
77
[![ORCID: Heather](https://img.shields.io/badge/ORCID_Amy_Heather-0000--0002--6596--3479-brightgreen)](https://orcid.org/0000-0002-6596-3479)
88

@@ -12,6 +12,8 @@ This repository applies the [Python DES RAP Template](https://github.com/pythonh
1212

1313
> Monks T, Worthington D, Allen M, Pitt M, Stein K, James MA. A modelling tool for capacity planning in acute and community stroke services. BMC Health Serv Res. 2016 Sep 29;16(1):530. doi: [10.1186/s12913-016-1789-4](https://doi.org/10.1186/s12913-016-1789-4). PMID: 27688152; PMCID: PMC5043535.
1414
15+
![](images/stroke_rehab_design.png)
16+
1517
<br>
1618

1719
## Installation
@@ -53,8 +55,12 @@ To find this information:
5355

5456
## Citation
5557

56-
If you use this template, please cite:
58+
For this applied example, please cite:
5759

5860
> Heather, A. (2025). Stroke capacity planning model: python DES RAP. GitHub. https://github.com/pythonhealthdatascience/stroke_rap_python.
5961
62+
This example is built using the [Python DES RAP Template](https://github.com/pythonhealthdatascience/rap_template_python_des). Please also cite the original template:
63+
64+
> Heather, A. Monks, T. (2025). Python DES RAP Template. Zenodo. https://doi.org/10.5281/zenodo.14622466. GitHub. https://github.com/pythonhealthdatascience/rap_template_python_des.
65+
6066
A `CITATION.cff` file is also provided.

docs/article_monks_etal_2016.pdf

548 KB
Binary file not shown.

docs/log.md

Lines changed: 209 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,223 @@
22

33
## Set-up
44

5-
Created template from Python DES RAP Template v1.2.0
5+
Created repository from Python DES RAP Template v1.2.0
66

77
Created `docs/log.md` to keep a record of all changes necessary to adapt the template.
88

99
Used the template at the end of a README to make a first draft README.
1010

11-
Emptied `docs/heather_2025.md` and `docs/nhs_rap.md`. Deleted `docs/hsma_changes.md`.
11+
Emptied `docs/heather_2025.md`, `docs/nhs_rap.md` and `CHANGELOG.md`.
12+
13+
Deleted `docs/hsma_changes.md` and `inputs/`.
14+
15+
Redid `CITATION.cff`.
16+
17+
Left `LICENSE` as is (but others would need to change).
1218

1319
Used [llm_simpy/notebooks/03_stroke/00_stress/](https://github.com/pythonhealthdatascience/llm_simpy/tree/main/notebooks/03_stroke/00_stress) to fill out `docs/stress_des.md`.
1420

15-
> **Reflections:**
16-
>
21+
Renamed and created environment.
22+
23+
> 💡 **Reflections...**
24+
>
1725
> * Could encourage / suggestion Allyon 2021 [Keeping modelling notebooks with TRACE: Good for you and good for environmental research and management support](https://www.sciencedirect.com/science/article/pii/S1364815220309890).
1826
> * Template README could have space for orcid.
27+
> * Would have been handy for template README to give an example of giving credit to template.
1928
> * Should template README just be a seperate file? But then would have to rename, so maybe, fine as is.
20-
> * Empty versions of docs/ would have been handy.
29+
> * Empty versions of docs/ and changelog would have been handy.
30+
31+
## Parameters
32+
33+
### Structuring the parameter class
34+
35+
Focused just on `model.py`. I made an empty .py file, then looked at them side-by-side.
36+
37+
I started with my model parameters, and filling a class with all the base parameters.
38+
39+
But this seemed very clunky, with lots of parameters, and figured I ought to find a better way of doing it...
40+
41+
```
42+
class Param:
43+
"""
44+
Default parameters for simulation.
45+
"""
46+
def __init__(
47+
self,
48+
# Acute stroke unit arrivals
49+
asu_arrive_stroke=1.2,
50+
asu_arrive_tia=9.3,
51+
asu_arrive_neuro=3.6,
52+
asu_arrive_other=3.2,
53+
# Rehab arrivals
54+
rehab_arrive_stroke=21.8,
55+
rehab_arrive_neuro=31.7,
56+
rehab_arrive_other=28.6,
57+
# Acute stroke unit length of stay
58+
asu_los_stroke_no_esd_mean=7.4,
59+
asu_los_stroke_no_esd_sd=8.61,
60+
asu_los_stroke_esd_mean=4.6,
61+
asu_los_stroke_esd_sd=4.8,
62+
asu_los_tia_mean=1.8,
63+
asu_los_tia_sd=2.3,
64+
asu_los_neuro_mean=4.0,
65+
asu_los_neuro_sd=5.0,
66+
asu_los_other_mean=3.8,
67+
asu_los_other_sd=5.2,
68+
# Rehab length of stay
69+
rehab_los_stroke_no_esd_mean=28.4,
70+
rehab_los_stroke_no_esd_sd=27.2,
71+
rehab_los_stroke_esd_mean=30.3,
72+
rehab_los_stroke_esd_sd=23.1,
73+
rehab_los_tia_mean=18.7,
74+
rehab_los_tia_sd=23.5,
75+
rehab_los_neuro_mean=27.6,
76+
rehab_los_neuro_sd=28.4,
77+
rehab_los_other_mean=16.1,
78+
rehab_los_other_sd=14.1,
79+
# Routing out of the acute stroke unit
80+
asu_to_rehab_stroke=0.24,
81+
asu_to_rehab_tia=0.01,
82+
asu_to_rehab_neuro=0.11,
83+
asu_to_rehab_other=0.05,
84+
asu_to_esd_stroke=0.13,
85+
asu_to_esd_tia=0.01,
86+
asu_to_esd_neuro=0.05,
87+
asu_to_esd_other=0.10,
88+
asu_to_other_stroke=0.63,
89+
asu_to_other_tia=0.98,
90+
asu_to_other_neuro=0.84,
91+
asu_to_other_other=0.85,
92+
# Routing out of rehab
93+
rehab_to_esd_stroke=0.40,
94+
rehab_to_esd_tia=0,
95+
rehab_to_esd_neuro=0.09,
96+
rehab_to_esd_other=0.13,
97+
rehab_to_other_stroke=0.60,
98+
rehab_to_other_tia=1,
99+
rehab_to_neuro=0.91,
100+
rehab_to_other=0.88
101+
):
102+
"""
103+
Initialise instance of the parameter class.
104+
"""
105+
self.asu_arrive_stroke = asu_arrive_stroke
106+
self.asu_arrive_tia = asu_arrive_tia
107+
self.asu_arrive_neuro = asu_arrive_neuro
108+
self.asu_arrive_other = asu_arrive_other
109+
self.rehab_arrive_stroke = rehab_arrive_stroke
110+
self.rehab_arrive_neuro = rehab_arrive_neuro
111+
self.rehab_arrive_other = rehab_arrive_other
112+
self.asu_los_stroke_no_esd_mean = asu_los_stroke_no_esd_mean
113+
self.asu_los_stroke_no_esd_sd = asu_los_stroke_no_esd_sd
114+
self.asu_los_stroke_esd_mean = asu_los_stroke_esd_mean
115+
self.asu_los_stroke_esd_sd = asu_los_stroke_esd_sd
116+
self.asu_los_tia_mean = asu_los_tia_mean
117+
self.asu_los_tia_sd = asu_los_tia_sd
118+
self.asu_los_neuro_mean = asu_los_neuro_mean
119+
self.asu_los_neuro_sd = asu_los_neuro_sd
120+
self.asu_los_other_mean = asu_los_other_mean
121+
self.asu_los_other_sd = asu_los_other_sd
122+
self.rehab_los_stroke_no_esd_mean = rehab_los_stroke_no_esd_mean
123+
self.rehab_los_stroke_no_esd_sd = rehab_los_stroke_no_esd_sd
124+
self.rehab_los_stroke_esd_mean = rehab_los_stroke_esd_mean
125+
self.rehab_los_stroke_esd_sd = rehab_los_stroke_esd_sd
126+
self.rehab_los_tia_mean = rehab_los_tia_mean
127+
self.rehab_los_tia_sd = rehab_los_tia_sd
128+
self.rehab_los_neuro_mean = rehab_los_neuro_mean
129+
self.rehab_los_neuro_sd = rehab_los_neuro_sd
130+
self.rehab_los_other_mean = rehab_los_other_mean
131+
self.rehab_los_other_sd = rehab_los_other_sd
132+
# etc...
133+
```
134+
135+
Using a single line instead of assigning each parameter individually...
136+
137+
```
138+
vars(self).update(locals())
139+
```
140+
141+
Using classes when populating the classes, and storing groups of parameters in dictionaries, i.e.:
142+
143+
```
144+
@dataclass
145+
class ASUArrivalRates:
146+
stroke=1.2
147+
tia=9.3
148+
neuro=3.6
149+
other=3.2
150+
151+
@dataclass
152+
class RehabArrivalRates:
153+
stroke=21.8
154+
neuro=31.7
155+
other=28.6
156+
157+
class Param:
158+
def __init__(
159+
asu_arrivals=ASUArrivalRates(),
160+
rehab_arrivals=RehabArrivalRates(),
161+
...
162+
)
163+
```
164+
165+
Importance of ArrivalRates() class is that it aids people in altering the default parameters - for example, to just change the stroke arrival rate,...
166+
167+
```
168+
Param(asu_arrivals = ASUArrivalRates(stroke=3))
169+
```
170+
171+
Also, we could import these values from a file as well? For now though, will stick with defining in the code.
172+
173+
Then I ran pylint, and add pylint disablers for too many arguments.
174+
175+
> 💡Handy to consider this way of structuring, for models with lots of parameters (which can be fairly common).
176+
177+
## Clearing out
178+
179+
I removed most files... it was overwhelming and tricky to work with, if I am changing and breaking things, and that breaks all my tests, and so on.
180+
181+
> 💡 I realise the templates are a little daunting - and I wrote them! Getting started with this, I did the strategy of essentially "clearing things away" - and then referring back to them as I got set up again. Perhaps a more useable / accessible version of these templates would be structuring them as step-by-step quarto books. Because that's essentially how am I treating them - AND that is how I learnt best, when I was learning DES, is working through step-by-step with the HSMA book, building it up. So these templates are me having worked out how to implement everything we want - and then the applied examples is stress testing / real life testing, figuring out how we work through, where we make changes - and using all that then to write step-by-step tutorial books. One for Python, one for R. Step-by-step walkthough of RAP DES in XYZ.
182+
183+
## Back to parameters
184+
185+
### Refactoring
186+
187+
With so many parameters, I feel like it maybe makes sense to seperate out the code more than I did in the template, so have changed from `model.py` to `parameters.py`
188+
189+
> 💡 Could the location of functions in the template do with reorganising? What is clearest?
190+
191+
### Playing with them
192+
193+
I wanted to try out using the classes to make sure they work. I created a disposable notebook to play around with them in.
194+
195+
> 💡 Should be clear how we do this early on, so can play about with it.
196+
197+
I realised dataclasses don't allow you to call ASUArrivals(stroke=4), as they are recognised as attributes, but only recognised as parameters when you type hint ie.
198+
199+
```
200+
@dataclass
201+
class ASUArrivals:
202+
stroke: float = 1.2
203+
tia: float = 9.3
204+
neuro: float = 3.6
205+
other: float = 3.2
206+
207+
ASUArrivals(stroke=4)
208+
```
209+
210+
I don't want this weird/hidden-feeling behaviour, especially as people say type hints shouldn't functionally affect your code in Python, and so will stick with normal classes.
211+
212+
### Preventing addition of new attributes
213+
214+
From writing the templates, I know how important it is that we prevent the addition of new attributes.
215+
216+
In python, I do this using a method in the parameter class. This is also possible in R if set up as a R6Class, but I chose to use a function for simplicity, so that instead checks the parameters when they are input to model. The downside of that approach as it will check every time the model is called (i.e. with each replication).
217+
218+
However, an alternative would be for my parameter classes to **inherit** from a main class with that functionality.
219+
220+
> 💡 Emphasise the importance of this, that it's not just something to drop.
221+
222+
> 💡 When using the dataclasses, our provided method for preventing the addition of new attributes no longer works.
223+
224+
I set up the **parent class**, and then add **tests** which check this is functioning properly.
28.3 KB
Binary file not shown.
361 KB
Binary file not shown.

0 commit comments

Comments
 (0)