Skip to content

Commit b07f656

Browse files
committed
feat(scenarios): simplifying python version of page
1 parent d640359 commit b07f656

File tree

1 file changed

+26
-143
lines changed

1 file changed

+26
-143
lines changed

pages/experiments/scenarios.qmd

Lines changed: 26 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,6 @@ If you are running several scenarios, it can be useful to define a helper functi
193193

194194
> "[HSMA - little book of DES](https://des.hsma.co.uk/)" from Sammi Rosser, Dan Chalk and Amy Heather 2025 (MIT Licence).
195195
196-
We walk through it step-by-step below.
197-
198196
```{python}
199197
def run_scenarios(scenarios, param_factory=None):
200198
"""
@@ -262,146 +260,6 @@ def run_scenarios(scenarios, param_factory=None):
262260
return pd.concat(results)
263261
```
264262

265-
### Explaining the `run_scenarios` function
266-
267-
```{.python}
268-
def run_scenarios(scenarios, param_factory=None):
269-
"""
270-
Execute a set of scenarios and return the results from each run.
271-
272-
Parameters
273-
----------
274-
scenarios : dict
275-
Dictionary where key is name of parameter and value is a list with
276-
different values to run in scenarios.
277-
param_factory : callable or None, optional
278-
A callable that returns a new Parameters object for each scenario run.
279-
This can be a class (e.g., `Parameters`) or a factory function/lambda
280-
with preset arguments (e.g., `lambda: Parameters(number_of_runs=4)`).
281-
If not provided, defaults to using `Parameters()` with no arguments.
282-
283-
Returns
284-
-------
285-
pandas.DataFrame
286-
DataFrame with results from each run of each scenario.
287-
288-
Notes
289-
-----
290-
Function adapted from Rosser, Chalk and Heather 2025.
291-
"""
292-
# If none provided, use Parameters
293-
if param_factory is None:
294-
param_factory = Parameters
295-
```
296-
297-
The first argument, `scenarios`, is a dictionary. The keys should be parameter names, and the values should be lists of values to run. For example:
298-
299-
```
300-
{"interarrival_time": [4, 5, 6],
301-
"number_of_doctors": [2, 3, 4]}
302-
```
303-
304-
The second argument, `param_factory`, is a callable that returns a fresh Parameters instance for each scenario run. This ensures every scenario gets an independent set of parameters and avoids carrying state from one scenario to another.
305-
306-
* For a default base case, supply `param_factory = Parameters`.
307-
308-
* To use a specific preset (e.g., altering the number of runs for all scenarios), supply a lambda: `param_factory = lambda: Parameters(number_of_runs=4)`
309-
310-
<br>
311-
312-
```{.python}
313-
# Find every possible permutation of the scenarios
314-
all_scenarios_tuples = list(itertools.product(*scenarios.values()))
315-
```
316-
317-
This line generates all possible combinations of values for each parameter.
318-
319-
<br>
320-
321-
```{.python}
322-
# Convert back into dictionaries
323-
all_scenarios_dicts = [
324-
dict(zip(scenarios.keys(), p)) for p in all_scenarios_tuples
325-
]
326-
327-
# Preview some of the scenarios
328-
print(f"There are {len(all_scenarios_dicts)} scenarios. Running:")
329-
```
330-
331-
Each scenario is converted from a tuple of values into a dictionary, making it easier to update parameters by name when running scenarios.
332-
333-
<br>
334-
335-
```{.python}
336-
# Run the scenarios...
337-
results = []
338-
for index, scenario_to_run in enumerate(all_scenarios_dicts):
339-
print(scenario_to_run)
340-
```
341-
342-
We loop over each scenario dictionary and run it. Printing the scenario shows which parameter values are being used for each run.
343-
344-
<br>
345-
346-
```{.python}
347-
# Create fresh instance of parameter class for each scenario
348-
param = param_factory()
349-
350-
# Update parameter list with the scenario parameters
351-
param.scenario_name = index
352-
for key in scenario_to_run:
353-
setattr(param, key, scenario_to_run[key])
354-
```
355-
356-
For each scenario, we create a new (independent) parameter object, then update its attributes to match the scenario values. This ensures each run starts with a fresh configuration and avoids parameter sharing between scenarios.
357-
358-
<br>
359-
360-
```{.python}
361-
# Perform replications
362-
scenario_exp = Runner(param)
363-
scenario_res = scenario_exp.run_reps()["run"]
364-
365-
# Add scenario number and values to the results dataframe
366-
scenario_res["scenario"] = index
367-
for key in scenario_to_run:
368-
scenario_res[key] = scenario_to_run[key]
369-
370-
# Add results from scenario to list
371-
results.append(scenario_res)
372-
```
373-
374-
Run the model for each scenario and collect results, adding the scenario numbers and settings to the output.
375-
376-
<br>
377-
378-
```{.python}
379-
return pd.concat(results)
380-
```
381-
382-
Combine results from all scenarios into a single dataframe.
383-
384-
### Run scenarios
385-
386-
We can now use `run_scenarios()` to execute every possible combination of the specified parameters.
387-
388-
```{python}
389-
# Run scenarios
390-
scenario_results = run_scenarios(
391-
scenarios = {"interarrival_time": [4, 5, 6, 7, 8],
392-
"number_of_doctors": [3, 4, 5]},
393-
param_factory = lambda: Parameters(number_of_runs = 100)
394-
)
395-
396-
# Save to CSV
397-
scenario_results.to_csv(
398-
"scenarios_resources/python_scenario_results.csv", index=False
399-
)
400-
401-
# View scenario results
402-
HTML(to_html_datatable(scenario_results.head(200)))
403-
```
404-
405263
:::
406264

407265
::: {.r-content}
@@ -476,14 +334,39 @@ run_scenarios <- function(scenarios, base_list, verbose = TRUE) {
476334
}
477335
```
478336

479-
This function generates all possible scenario combinations from the values supplied in `scenarios`.
337+
:::
338+
339+
The function generates all possible scenario combinations from the values supplied in `scenarios`.
480340

481341
It then iterates through these, creating a fresh set of parameters for each scenario run. This ensures every scenario gets an independent set of parameters and avoids carrying state from one scenario to another.
482342

483343
The replication results from each scenario are saved in a list and finally combined into a single table.
484344

485345
Let's run it!
486346

347+
::: {.python-content}
348+
349+
```{python}
350+
# Run scenarios
351+
scenario_results = run_scenarios(
352+
scenarios = {"interarrival_time": [4, 5, 6, 7, 8],
353+
"number_of_doctors": [3, 4, 5]},
354+
param_factory = lambda: Parameters(number_of_runs = 100)
355+
)
356+
357+
# Save to CSV
358+
scenario_results.to_csv(
359+
"scenarios_resources/python_scenario_results.csv", index=False
360+
)
361+
362+
# View scenario results
363+
HTML(to_html_datatable(scenario_results.head(200)))
364+
```
365+
366+
:::
367+
368+
::: {.r-content}
369+
487370
```{r}
488371
# Run scenarios
489372
scenarios <- list(

0 commit comments

Comments
 (0)