Skip to content

Commit dcb9e8b

Browse files
committed
refactor(occupancy)/feat(replications): create Runner to run multiple replications, and move creation of occupancy table to Runner
1 parent 3ef0d72 commit dcb9e8b

File tree

3 files changed

+248
-81
lines changed

3 files changed

+248
-81
lines changed

notebooks/analysis.ipynb

Lines changed: 112 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"import plotly.express as px\n",
1818
"\n",
1919
"from simulation.parameters import Param\n",
20-
"from simulation.model import Model"
20+
"from simulation.model import Model\n",
21+
"from simulation.runner import Runner"
2122
]
2223
},
2324
{
@@ -43,9 +44,8 @@
4344
"metadata": {},
4445
"outputs": [],
4546
"source": [
46-
"# Run the model\n",
47-
"model = Model(param=Param(), run_number=0)\n",
48-
"model.run()"
47+
"# Set up runner\n",
48+
"runner = Runner(param=Param(cores=8))"
4949
]
5050
},
5151
{
@@ -55,12 +55,84 @@
5555
"outputs": [
5656
{
5757
"data": {
58+
"text/html": [
59+
"<div>\n",
60+
"<style scoped>\n",
61+
" .dataframe tbody tr th:only-of-type {\n",
62+
" vertical-align: middle;\n",
63+
" }\n",
64+
"\n",
65+
" .dataframe tbody tr th {\n",
66+
" vertical-align: top;\n",
67+
" }\n",
68+
"\n",
69+
" .dataframe thead th {\n",
70+
" text-align: right;\n",
71+
" }\n",
72+
"</style>\n",
73+
"<table border=\"1\" class=\"dataframe\">\n",
74+
" <thead>\n",
75+
" <tr style=\"text-align: right;\">\n",
76+
" <th></th>\n",
77+
" <th>beds</th>\n",
78+
" <th>freq</th>\n",
79+
" <th>pct</th>\n",
80+
" <th>c_pct</th>\n",
81+
" <th>prob_delay</th>\n",
82+
" </tr>\n",
83+
" </thead>\n",
84+
" <tbody>\n",
85+
" <tr>\n",
86+
" <th>0</th>\n",
87+
" <td>1</td>\n",
88+
" <td>1</td>\n",
89+
" <td>0.000548</td>\n",
90+
" <td>0.000548</td>\n",
91+
" <td>1.000000</td>\n",
92+
" </tr>\n",
93+
" <tr>\n",
94+
" <th>1</th>\n",
95+
" <td>2</td>\n",
96+
" <td>12</td>\n",
97+
" <td>0.006575</td>\n",
98+
" <td>0.007123</td>\n",
99+
" <td>0.923077</td>\n",
100+
" </tr>\n",
101+
" <tr>\n",
102+
" <th>2</th>\n",
103+
" <td>3</td>\n",
104+
" <td>39</td>\n",
105+
" <td>0.021370</td>\n",
106+
" <td>0.028493</td>\n",
107+
" <td>0.750000</td>\n",
108+
" </tr>\n",
109+
" <tr>\n",
110+
" <th>3</th>\n",
111+
" <td>4</td>\n",
112+
" <td>76</td>\n",
113+
" <td>0.041644</td>\n",
114+
" <td>0.070137</td>\n",
115+
" <td>0.593750</td>\n",
116+
" </tr>\n",
117+
" <tr>\n",
118+
" <th>4</th>\n",
119+
" <td>5</td>\n",
120+
" <td>134</td>\n",
121+
" <td>0.073425</td>\n",
122+
" <td>0.143562</td>\n",
123+
" <td>0.511450</td>\n",
124+
" </tr>\n",
125+
" </tbody>\n",
126+
"</table>\n",
127+
"</div>"
128+
],
58129
"text/plain": [
59-
"[{'time': 1095, 'asu_occupancy': 7, 'rehab_occupancy': 12},\n",
60-
" {'time': 1096, 'asu_occupancy': 5, 'rehab_occupancy': 11},\n",
61-
" {'time': 1097, 'asu_occupancy': 5, 'rehab_occupancy': 10},\n",
62-
" {'time': 1098, 'asu_occupancy': 4, 'rehab_occupancy': 10},\n",
63-
" {'time': 1099, 'asu_occupancy': 5, 'rehab_occupancy': 10}]"
130+
" beds freq pct c_pct prob_delay\n",
131+
"0 1 1 0.000548 0.000548 1.000000\n",
132+
"1 2 12 0.006575 0.007123 0.923077\n",
133+
"2 3 39 0.021370 0.028493 0.750000\n",
134+
"3 4 76 0.041644 0.070137 0.593750\n",
135+
"4 5 134 0.073425 0.143562 0.511450"
64136
]
65137
},
66138
"execution_count": 4,
@@ -69,67 +141,28 @@
69141
}
70142
],
71143
"source": [
72-
"# Preview some of the results\n",
73-
"model.audit_list[0:5]"
144+
"# Run once and preview results\n",
145+
"single_results = runner.run_single(run=0)\n",
146+
"single_results[\"asu\"].head()"
74147
]
75148
},
76149
{
77-
"cell_type": "markdown",
150+
"cell_type": "code",
151+
"execution_count": 5,
78152
"metadata": {},
153+
"outputs": [],
79154
"source": [
80-
"## Figure 1\n",
81-
"\n",
82-
"**Figure 1.** Simulation probability density function for occupancy of an acute stroke unit.\n",
83-
"\n",
84-
"**Relevant functions...**"
155+
"# Run replications\n",
156+
"rep_results = runner.run_reps()"
85157
]
86158
},
87159
{
88-
"cell_type": "code",
89-
"execution_count": 5,
160+
"cell_type": "markdown",
90161
"metadata": {},
91-
"outputs": [],
92162
"source": [
93-
"def get_occupancy_freq(unit):\n",
94-
" \"\"\"\n",
95-
" Count the frequency each occupancy level was observed at within the\n",
96-
" interval audit for the specified unit.\n",
97-
"\n",
98-
" Parameters\n",
99-
" ----------\n",
100-
" unit: str\n",
101-
" Name of unit to plot (\"asu\", \"rehab\").\n",
102-
"\n",
103-
" Returns\n",
104-
" -------\n",
105-
" df: pd.DataFrame\n",
106-
" Dataframe with two columns: the number of beds (\"beds\") and the\n",
107-
" frequency this was observed in the audit (\"freq\").\n",
108-
" \"\"\"\n",
109-
" # Get occupancy audit results\n",
110-
" occ_audit = pd.DataFrame(model.audit_list)\n",
111-
"\n",
112-
" # Get the frequency of each number of beds in the specified unit\n",
113-
" frequency = Counter(occ_audit[f\"{unit}_occupancy\"])\n",
114-
"\n",
115-
" # Fill in any gaps - find the maximum bed count, then create complete\n",
116-
" # dictionary include bed counts which were never observed\n",
117-
" min_bed_count = min(frequency.keys())\n",
118-
" max_bed_count = max(frequency.keys())\n",
119-
" complete_frequency = {i: frequency.get(i, 0)\n",
120-
" for i in range(min_bed_count, max_bed_count + 1)}\n",
121-
"\n",
122-
" # Convert into a dataframe\n",
123-
" df = pd.DataFrame(\n",
124-
" complete_frequency.items(), columns=[\"beds\", \"freq\"])\n",
125-
"\n",
126-
" # Add column with frequencies converted to proportions\n",
127-
" df[\"pct\"] = df[\"freq\"] / df[\"freq\"].sum()\n",
128-
"\n",
129-
" # Add column with the cumulative percentage\n",
130-
" df[\"c_pct\"] = df[\"pct\"].cumsum()\n",
163+
"## Figure 1\n",
131164
"\n",
132-
" return df"
165+
"**Figure 1.** Simulation probability density function for occupancy of an acute stroke unit."
133166
]
134167
},
135168
{
@@ -1900,13 +1933,11 @@
19001933
],
19011934
"source": [
19021935
"# Acute stroke unit\n",
1903-
"asu_occupancy = get_occupancy_freq(\"asu\")\n",
1904-
"plot_occupancy_freq(asu_occupancy, unit=\"asu\",\n",
1936+
"plot_occupancy_freq(single_results[\"asu\"], unit=\"asu\",\n",
19051937
" file=\"occupancy_freq_asu.png\")\n",
19061938
"\n",
19071939
"# Rehabilitation unit\n",
1908-
"rehab_occupancy = get_occupancy_freq(\"rehab\")\n",
1909-
"plot_occupancy_freq(rehab_occupancy, unit=\"rehab\",\n",
1940+
"plot_occupancy_freq(single_results[\"rehab\"], unit=\"rehab\",\n",
19101941
" file=\"occupancy_freq_rehab.png\")"
19111942
]
19121943
},
@@ -1918,11 +1949,9 @@
19181949
"\n",
19191950
"**Figure 3**. Simulated trade-off between the probability that a patient is delayed and the no. of acute beds available.\n",
19201951
"\n",
1921-
"### Explanation of the method for calculating blocking probability\n",
1922-
"\n",
19231952
"We can use our frequency and cumulative frequency of occupied beds from the simulation to calculate blocking probability.\n",
19241953
"\n",
1925-
"Above, we created the tables..."
1954+
"Our model output these tables..."
19261955
]
19271956
},
19281957
{
@@ -1955,6 +1984,7 @@
19551984
" <th>freq</th>\n",
19561985
" <th>pct</th>\n",
19571986
" <th>c_pct</th>\n",
1987+
" <th>prob_delay</th>\n",
19581988
" </tr>\n",
19591989
" </thead>\n",
19601990
" <tbody>\n",
@@ -1964,46 +1994,51 @@
19641994
" <td>1</td>\n",
19651995
" <td>0.000548</td>\n",
19661996
" <td>0.000548</td>\n",
1997+
" <td>1.000000</td>\n",
19671998
" </tr>\n",
19681999
" <tr>\n",
19692000
" <th>1</th>\n",
19702001
" <td>2</td>\n",
19712002
" <td>12</td>\n",
19722003
" <td>0.006575</td>\n",
19732004
" <td>0.007123</td>\n",
2005+
" <td>0.923077</td>\n",
19742006
" </tr>\n",
19752007
" <tr>\n",
19762008
" <th>2</th>\n",
19772009
" <td>3</td>\n",
19782010
" <td>39</td>\n",
19792011
" <td>0.021370</td>\n",
19802012
" <td>0.028493</td>\n",
2013+
" <td>0.750000</td>\n",
19812014
" </tr>\n",
19822015
" <tr>\n",
19832016
" <th>3</th>\n",
19842017
" <td>4</td>\n",
19852018
" <td>76</td>\n",
19862019
" <td>0.041644</td>\n",
19872020
" <td>0.070137</td>\n",
2021+
" <td>0.593750</td>\n",
19882022
" </tr>\n",
19892023
" <tr>\n",
19902024
" <th>4</th>\n",
19912025
" <td>5</td>\n",
19922026
" <td>134</td>\n",
19932027
" <td>0.073425</td>\n",
19942028
" <td>0.143562</td>\n",
2029+
" <td>0.511450</td>\n",
19952030
" </tr>\n",
19962031
" </tbody>\n",
19972032
"</table>\n",
19982033
"</div>"
19992034
],
20002035
"text/plain": [
2001-
" beds freq pct c_pct\n",
2002-
"0 1 1 0.000548 0.000548\n",
2003-
"1 2 12 0.006575 0.007123\n",
2004-
"2 3 39 0.021370 0.028493\n",
2005-
"3 4 76 0.041644 0.070137\n",
2006-
"4 5 134 0.073425 0.143562"
2036+
" beds freq pct c_pct prob_delay\n",
2037+
"0 1 1 0.000548 0.000548 1.000000\n",
2038+
"1 2 12 0.006575 0.007123 0.923077\n",
2039+
"2 3 39 0.021370 0.028493 0.750000\n",
2040+
"3 4 76 0.041644 0.070137 0.593750\n",
2041+
"4 5 134 0.073425 0.143562 0.511450"
20072042
]
20082043
},
20092044
"execution_count": 8,
@@ -2012,7 +2047,7 @@
20122047
}
20132048
],
20142049
"source": [
2015-
"asu_occupancy.head()"
2050+
"single_results[\"asu\"].head()"
20162051
]
20172052
},
20182053
{
@@ -2045,9 +2080,7 @@
20452080
"* Any new patients arriving when 8 beds are occupied would experience a delay.\n",
20462081
"* So 0.6 represents the probability that, given we're at or below capacity (7 or 8 beds), we're actually at full capacity (8 beds)\n",
20472082
"\n",
2048-
"In other words, `pct/c_pct` is the probability that a new arrival will experience a delay when the system has exactly x beds occupied, given that the capacity of the system is x beds.\n",
2049-
"\n",
2050-
"### Implementing this method"
2083+
"In other words, `pct/c_pct` is the probability that a new arrival will experience a delay when the system has exactly x beds occupied, given that the capacity of the system is x beds."
20512084
]
20522085
},
20532086
{
@@ -2073,9 +2106,6 @@
20732106
" path: str\n",
20742107
" Path to save file to (excluding filename).\n",
20752108
" \"\"\"\n",
2076-
" # Calculate the probability of delay\n",
2077-
" df[\"prob_delay\"] = df[\"pct\"] / df[\"c_pct\"]\n",
2078-
"\n",
20792109
" # Create the step plot\n",
20802110
" fig = px.line(df, x=\"beds\", y=\"prob_delay\",\n",
20812111
" color_discrete_sequence=[\"black\"])\n",
@@ -3902,8 +3932,9 @@
39023932
}
39033933
],
39043934
"source": [
3905-
"plot_delay_prob(asu_occupancy, unit=\"asu\", file=\"delay_prob_asu.png\")\n",
3906-
"plot_delay_prob(rehab_occupancy, unit=\"rehab\", file=\"delay_prob_rehab.png\")"
3935+
"plot_delay_prob(single_results[\"asu\"], unit=\"asu\", file=\"delay_prob_asu.png\")\n",
3936+
"plot_delay_prob(single_results[\"rehab\"], unit=\"rehab\",\n",
3937+
" file=\"delay_prob_rehab.png\")"
39073938
]
39083939
}
39093940
],

simulation/parameters.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,9 @@ def __init__(
419419
rehab_routing=RehabRouting(),
420420
warm_up_period=365*3, # 3 years
421421
data_collection_period=365*5, # 5 years
422+
number_of_runs=150,
422423
audit_interval=1,
424+
cores=1,
423425
log_to_console=False,
424426
log_to_file=False
425427
):
@@ -445,8 +447,15 @@ def __init__(
445447
Length of the warm-up period.
446448
data_collection_period: int
447449
Length of the data collection period.
450+
number_of_runs: int
451+
The number of runs (i.e. replications), defining how many times to
452+
re-run the simulation (with different random numbers).
448453
audit_interval: float
449454
Frequency of simulation audits in days.
455+
cores: int
456+
Number of CPU cores to use for parallel execution. Set to desired
457+
number, or to -1 to use all available cores. For sequential
458+
execution, set to 1.
450459
log_to_console: boolean
451460
Whether to print log messages to the console.
452461
log_to_file: boolean
@@ -461,7 +470,9 @@ def __init__(
461470
self.rehab_routing = rehab_routing
462471
self.warm_up_period = warm_up_period
463472
self.data_collection_period = data_collection_period
473+
self.number_of_runs = number_of_runs
464474
self.audit_interval = audit_interval
475+
self.cores = cores
465476

466477
# Set up logger
467478
self.logger = SimLogger(log_to_console=log_to_console,

0 commit comments

Comments
 (0)