|
2 | 2 |
|
3 | 3 | ## Set-up |
4 | 4 |
|
5 | | -Created template from Python DES RAP Template v1.2.0 |
| 5 | +Created repository from Python DES RAP Template v1.2.0 |
6 | 6 |
|
7 | 7 | Created `docs/log.md` to keep a record of all changes necessary to adapt the template. |
8 | 8 |
|
9 | 9 | Used the template at the end of a README to make a first draft README. |
10 | 10 |
|
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). |
12 | 18 |
|
13 | 19 | 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`. |
14 | 20 |
|
15 | | -> **Reflections:** |
16 | | -> |
| 21 | +Renamed and created environment. |
| 22 | + |
| 23 | +> 💡 **Reflections...** |
| 24 | +> |
17 | 25 | > * 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). |
18 | 26 | > * Template README could have space for orcid. |
| 27 | +> * Would have been handy for template README to give an example of giving credit to template. |
19 | 28 | > * 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. |
0 commit comments