diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 830855e..962e4f0 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -23,12 +23,6 @@ jobs:
cache: 'pip'
- run: pip install -r requirements.txt
- - name: Lint model code
+ - name: Lint
run: |
- flake8 ./simulation
- pylint ./simulation
-
- - name: Lint tests
- run: |
- flake8 ./tests
- pylint ./tests
+ bash lint.sh
\ No newline at end of file
diff --git a/README.md b/README.md
index 6aa5a16..a36968e 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ This repository provides a template for building discrete-event simulation (DES)
For clarity, changes from the DES book in this template are explained in `docs/hsma_changes.md`.
-✨ **Style:** The coding style is based on the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html). Linting is implemented using `flake8` and `pylint` for `.py` files, and `pycodestyle` for `.ipynb` files.
+✨ **Style:** The coding style is based on the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html). Linting is implemented using `pylint` (with `nbqa` to enable it to run on jupyter notebooks).
🧱 **Package structure:** In Python, a package can simply be defined as a directory with an `__init__.py` file in it. In this repository, the scripts for model (within `simulation/`) are treated as a little local package. This keeps the code model code isolated from our experiments and analysis. It is installed as an editable (`-e`) local import - with `-e` meaning it will update with changes to the local files in `simulation/`. As it is installed in our environment, it can then easily be used anywhere else in the directory - here, in `notebooks/` and `tests/` - without needing any additional code (e.g. no need to modify `sys.path`, or have additional `__init__.py` files).
@@ -162,18 +162,22 @@ If you have changed the model behaviour, you may wish to amend, remove or write
🔎 **Linting**
-You can lint the `.py` files by running either of this commands from the terminal:
+You can lint the `.py` files by running:
```
-flake8 simulation/model.py
pylint simulation/model.py
```
-The first commands in the `.ipynb` files will lint the notebooks using `pycodestyle` when executed:
+You can lint the `.ipynb` by adding `nbqa` to the start of the command - e.g.:
```
-%load_ext pycodestyle_magic
-%pycodestyle_on
+nbqa pylint notebooks/analysis.ipynb
+```
+
+A bash script has been provided which can be used to lint all files in succession by running:
+
+```
+bash lint.sh
```
@@ -225,6 +229,7 @@ repo/
├── CONTRIBUTING.md # Contribution instructions
├── environment.yaml # Conda environment (includes Python version)
├── LICENSE # Licence file
+├── lint.sh # Bash script to lint all .py and .ipynb files at once
├── pyproject.toml # Metadata for local `simulation/` package
├── README.md # This file! Describes the repository
└── requirements.txt # Virtual environment (used by GitHub actions)
diff --git a/docs/time_weighted_averages.ipynb b/docs/time_weighted_averages.ipynb
index 9894ae1..cbac3af 100644
--- a/docs/time_weighted_averages.ipynb
+++ b/docs/time_weighted_averages.ipynb
@@ -15,9 +15,7 @@
"metadata": {},
"outputs": [],
"source": [
- "%load_ext pycodestyle_magic\n",
- "%pycodestyle_on\n",
- "\n",
+ "# pylint: disable=missing-module-docstring\n",
"import plotly.express as px\n",
"import plotly.io as pio\n",
"\n",
@@ -57,7 +55,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -171,6 +169,7 @@
" queue[i] * (time[i+1] - time[i]) for i in range(len(time)-1))\n",
"\n",
"# Total time duration\n",
+ "# pylint:disable=invalid-name\n",
"total_time = time[-1] - time[0]\n",
"\n",
"# Compute time-weighted average\n",
@@ -227,7 +226,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -351,7 +350,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "template-des",
+ "display_name": "Python 3",
"language": "python",
"name": "python3"
},
@@ -365,7 +364,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.13.0"
+ "version": "3.13.1"
}
},
"nbformat": 4,
diff --git a/environment.yaml b/environment.yaml
index f34ad17..7f3fe20 100644
--- a/environment.yaml
+++ b/environment.yaml
@@ -2,24 +2,20 @@ name: template-des
channels:
- conda-forge
dependencies:
- - black
- - flake8=7.1.1
- ipykernel=6.29.5
- jinja2=3.1.5
- joblib=1.4.2
- nbformat=5.10.4
- - nbqa
- - numpy=2.1.3
+ - nbqa=1.9.0
+ - numpy=2.2.2
- pandas=2.2.3
- - pip=24.3.1
+ - pip=25.0
- plotly_express=0.4.1
- - pycodestyle=2.12.1
- - pylint=3.3.3
+ - pylint=3.3.4
- pytest=8.3.4
- pytest-xdist=3.6.1
- - python=3.13.0
+ - python=3.13.1
- simpy=4.1.1
- pip:
- kaleido==0.2.1
- - pycodestyle_magic==0.5
- -e .[dev]
diff --git a/lint.sh b/lint.sh
new file mode 100644
index 0000000..e558a8e
--- /dev/null
+++ b/lint.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+echo "Linting model code..."
+pylint ./simulation
+
+echo "Linting tests..."
+pylint ./tests
+
+echo "Linting notebooks..."
+nbqa pylint ./notebooks
+
+echo "Linting time-weighted averages notebook..."
+nbqa pylint ./docs/time_weighted_averages.ipynb
\ No newline at end of file
diff --git a/notebooks/analysis.ipynb b/notebooks/analysis.ipynb
index d7a2e7f..46ef9c0 100644
--- a/notebooks/analysis.ipynb
+++ b/notebooks/analysis.ipynb
@@ -16,178 +16,15 @@
"\n",
"Credit:\n",
"\n",
- "* Analysis of the spread of replication results was adapted from Tom Monks (2024) HPDM097 - Making a difference with health data (https://github.com/health-data-science-OR/stochastic_systems) (MIT Licence)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "jp-MarkdownHeadingCollapsed": true
- },
- "source": [
- "## Plotly \"allow a user to select KPI\" histogram\n",
- "\n",
- "The function `create_user_controlled_hist` creates a plotly chart that allows a user to choose which of the KPIs to view. It has optional parameter to map the simulation result variable names to a display \"friendly name\" and \"units\". Users pick a KPI from a drop down list. An optional parameter can be used to exclude certain results if they are not relevant to the modeller. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "import plotly.graph_objects as go"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "def create_user_controlled_hist(\n",
- " results, exclude_columns=None, name_mappings=None, include_instruct=True\n",
- "):\n",
- " \"\"\"\n",
- " Create a plotly histogram that includes a drop down list that allows a user\n",
- " to select which KPI is displayed as a histogram\n",
- "\n",
- " Params:\n",
- " -------\n",
- " results: pd.DataFrame\n",
- " rows = replications, cols = KPIs\n",
- " exclude_columns: list, optional\n",
- " List of column numbers to exclude from the dropdown list\n",
- " name_mappings: dict, optional\n",
- " Nested dictionary mapping column names to friendly names and units\n",
- " Format: {column_name: {'friendly_name': str, 'units': str}}#\n",
- " include_instruct: bool, optional\n",
- " Including the instruction \"Select KPI from drop down list\" above\n",
- " plot. Useful for interactive applications.\n",
- "\n",
- " Returns:\n",
- " -------\n",
- " plotly.figure\n",
- "\n",
- " Source:\n",
- " ------\n",
- " The code in this function was adapted from:\n",
- " https://stackoverflow.com/questions/59406167/plotly-how-to-filter-a-pandas-\n",
- " dataframe-using-a-dropdown-menu\n",
- "\n",
- " and\n",
- "\n",
- " Monks T and Harper A. Improving the usability of open health service \n",
- " delivery simulation models using Python and web apps \n",
- " [version 2; peer review: 3 approved]. NIHR Open Res 2023, 3:48 #\n",
- " https://doi.org/10.3310/nihropenres.13467.2\n",
- " \"\"\"\n",
- "\n",
- " # create a figure\n",
- " fig = go.Figure()\n",
- "\n",
- " # Filter out excluded columns\n",
- " if exclude_columns is None:\n",
- " exclude_columns = []\n",
- " included_columns = [\n",
- " col\n",
- " for i, col in enumerate(results.columns)\n",
- " if i not in exclude_columns\n",
- " ]\n",
- "\n",
- " # set up ONE trace\n",
- " first_col = included_columns[0]\n",
- " first_friendly_name = (\n",
- " name_mappings[first_col]['friendly_name']\n",
- " if name_mappings and first_col in name_mappings\n",
- " else first_col\n",
- " )\n",
- " first_units = (\n",
- " name_mappings[first_col]['units']\n",
- " if name_mappings and first_col in name_mappings\n",
- " else ''\n",
- " )\n",
- " first_x_title = (\n",
- " f'{first_friendly_name} ({first_units})'\n",
- " if first_units\n",
- " else first_friendly_name\n",
- " )\n",
- "\n",
- " fig.add_trace(go.Histogram(x=results[first_col]))\n",
- " fig.update_xaxes(title_text=first_x_title)\n",
- "\n",
- " buttons = []\n",
- "\n",
- " # create list of drop down items - KPIs\n",
- " for col in included_columns:\n",
- " if name_mappings and col in name_mappings:\n",
- " friendly_name = name_mappings[col]['friendly_name']\n",
- " units = name_mappings[col]['units']\n",
- " x_title = f'{friendly_name} ({units})' if units else friendly_name\n",
- " else:\n",
- " friendly_name = col\n",
- " x_title = col\n",
- "\n",
- " buttons.append(\n",
- " dict(\n",
- " method='update',\n",
- " label=friendly_name,\n",
- " args=[{'x': [results[col]]}, {'xaxis': {'title': x_title}}],\n",
- " )\n",
- " )\n",
- "\n",
- " # create update menu and parameters\n",
- " updatemenu = []\n",
- " your_menu = dict()\n",
- " updatemenu.append(your_menu)\n",
- "\n",
- " updatemenu[0]['buttons'] = buttons\n",
- " updatemenu[0]['direction'] = 'down'\n",
- " updatemenu[0]['showactive'] = True\n",
- "\n",
- " # add dropdown menus to the figure\n",
- " fig.update_layout(showlegend=False, updatemenus=updatemenu)\n",
- "\n",
- " # Add annotation as instruction\n",
- " if include_instruct:\n",
- " fig.add_annotation(\n",
- " text='Select a KPI from the drop down list',\n",
- " xref='paper',\n",
- " yref='paper',\n",
- " x=0.0,\n",
- " y=1.1, # Positions the text above the plot\n",
- " showarrow=False,\n",
- " font=dict(size=12),\n",
- " )\n",
- "\n",
- " return fig"
+ "* Analysis of the spread of replication results was adapted from Tom Monks (2024) HPDM097 - Making a difference with health data (https://github.com/health-data-science-OR/stochastic_systems) (MIT Licence).\n",
+ "* The function `create_user_controlled_hist()` was adapted from [stack overflow](https://stackoverflow.com/questions/59406167/plotly-how-to-filter-a-pandas-dataframe-using-a-dropdown-menu) and Monks T and Harper A. [Improving the usability of open health service delivery simulation models using Python and web apps](https://doi.org/10.3310/nihropenres.13467.2) [version 2; peer review: 3 approved]. NIHR Open Res 2023, 3:48."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Set-up\n",
- "\n",
- "Load notebook linters."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "# %load_ext pycodestyle_magic"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "# %pycodestyle_on"
+ "## Set-up"
]
},
{
@@ -199,10 +36,11 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
+ "# pylint: disable=missing-module-docstring\n",
"# To ensure any updates to `simulation/` are fetched without needing to restart\n",
"# the notebook environment, reload `simulation/` before execution of each cell\n",
"%load_ext autoreload\n",
@@ -212,15 +50,18 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
+ "# pylint: disable=wrong-import-position\n",
"import os\n",
+ "import time\n",
+ "from IPython.display import display\n",
"import pandas as pd\n",
"import plotly.express as px\n",
+ "import plotly.graph_objects as go\n",
"import plotly.io as pio\n",
- "import time\n",
"\n",
"from simulation.logging import SimLogger\n",
"from simulation.model import (\n",
@@ -231,48 +72,58 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Display plotly express figures as non-interactive figures. This means they will be visible when browsing the notebooks on GitHub. To switch these back to interactive figures, simply remove this line."
+ "Start timer."
]
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
- "pio.renderers.default = 'svg'"
+ "start_time = time.time()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Start timer."
+ "Define path to outputs folder."
]
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
- "start_time = time.time()"
+ "OUTPUT_DIR = '../outputs/'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Define path to outputs folder."
+ "Define labels for variables in the dataset."
]
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
- "output_dir = '../outputs/'"
+ "LABELS = {\n",
+ " 'arrivals': 'Patient arrivals (n)',\n",
+ " 'mean_q_time_nurse': 'Mean wait time for nurse (minutes)',\n",
+ " 'mean_n_consult_time': 'Mean consultation time with nurse (minutes)',\n",
+ " 'mean_time_with_nurse': 'Mean consultation time with nurse (minutes)',\n",
+ " 'mean_nurse_utilisation': 'Mean nurse utilisation',\n",
+ " 'mean_nurse_utilisation_tw': 'Time-weighted mean nurse utilisation',\n",
+ " 'mean_nurse_q_length': 'Time-weighted mean queue length for nurse (n)',\n",
+ " 'patient_inter': 'Patient inter-arrival time',\n",
+ " 'number_of_nurses': 'Number of nurses'\n",
+ "}"
]
},
{
@@ -291,7 +142,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -311,7 +162,7 @@
"```python\n",
"# Save file\n",
"experiment.patient_results_df.to_csv(\n",
- " os.path.join(output_dir, 'example_patient.csv.gz'),\n",
+ " os.path.join(OUTPUT_DIR, 'example_patient.csv.gz'),\n",
" index=False, compression='gzip')\n",
"\n",
"# Load file\n",
@@ -323,7 +174,7 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
@@ -418,7 +269,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 8,
"metadata": {},
"outputs": [
{
@@ -535,12 +386,12 @@
"source": [
"display(experiment.run_results_df.head())\n",
"experiment.run_results_df.to_csv(\n",
- " os.path.join(output_dir, 'example_run.csv'), index=False)"
+ " os.path.join(OUTPUT_DIR, 'example_run.csv'), index=False)"
]
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 9,
"metadata": {},
"outputs": [
{
@@ -645,12 +496,12 @@
"source": [
"display(experiment.interval_audit_df.head())\n",
"experiment.interval_audit_df.to_csv(\n",
- " os.path.join(output_dir, 'example_interval_audit.csv'), index=False)"
+ " os.path.join(OUTPUT_DIR, 'example_interval_audit.csv'), index=False)"
]
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
@@ -750,83 +601,1374 @@
"source": [
"display(experiment.overall_results_df.head())\n",
"experiment.overall_results_df.to_csv(\n",
- " os.path.join(output_dir, 'example_overall.csv'), index=False)"
+ " os.path.join(OUTPUT_DIR, 'example_overall.csv'), index=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## View spread of results across replications\n",
+ "## Interactive plots that show the spread of results across replications\n",
"\n",
- "> TM Note: I have kept code as before, but added the additional user controlled histogram plot to select KPIs"
+ "In this plot, modeller select the desired metric to view from a drop down list.\n",
+ "\n",
+ "Note: These will not be rendered if viewing the notebook on GitHub - instead, scroll down to view non-interactive version of plots."
]
},
{
"cell_type": "code",
- "execution_count": 15,
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pio.renderers.default = 'plotly_mimetype'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def create_user_controlled_hist(\n",
+ " results, exclude_columns=None, name_mappings=None, include_instruct=True\n",
+ "):\n",
+ " \"\"\"\n",
+ " Create a plotly histogram that includes a drop down list that allows a user\n",
+ " to select which key performance indicator (KPIs) are displayed.\n",
+ "\n",
+ " It has the optional parameter to map the simulation result variable names\n",
+ " to display a \"friendly name\" and \"units\". Users pick a KPI from a drop\n",
+ " down list. An optional parameter can be used to exclude certain results if\n",
+ " they are not relevant to the modeller.\n",
+ "\n",
+ " Arguments:\n",
+ " results (pd.DataFrame)\n",
+ " Dataframe where rows are replications and columns are KPIs.\n",
+ " exclude_columns (list, optional)\n",
+ " List of column numbers to exclude from the dropdown list.\n",
+ " name_mappings (dict, optional)\n",
+ " Dictionary mapping column names to labels. If not provided,\n",
+ " function will default to variable names.\n",
+ " include_instruct (bool, optional)\n",
+ " Including the instruction \"Select KPI from drop down list\" above\n",
+ " plot. Useful for interactive applications.\n",
+ "\n",
+ " Returns:\n",
+ " plotly.figure\n",
+ " \"\"\"\n",
+ " # Set as empty dictionary if mappings not provided. The function will later\n",
+ " # search for a label and find no match, so use the variable name.\n",
+ " if name_mappings is None:\n",
+ " name_mappings = {}\n",
+ "\n",
+ " # Initialise figure\n",
+ " fig = go.Figure()\n",
+ "\n",
+ " # Determine included columns (filter out excluded ones)\n",
+ " exclude_columns = exclude_columns or []\n",
+ " included_columns = [col for i, col in enumerate(results.columns)\n",
+ " if i not in exclude_columns]\n",
+ "\n",
+ " # Add initial histogram trace, with first column, for initial display\n",
+ " initial_column = included_columns[0]\n",
+ " fig.add_trace(go.Histogram(x=results[initial_column]))\n",
+ " fig.update_layout(\n",
+ " # If name_mappings is provided, use it; otherwise, use the column name\n",
+ " xaxis_title=name_mappings.get(initial_column, initial_column),\n",
+ " yaxis_title='Replications')\n",
+ "\n",
+ " # Generate dropdown buttons for KPI selection\n",
+ " buttons = [\n",
+ " {\n",
+ " 'method': 'update',\n",
+ " 'label': name_mappings.get(col, col),\n",
+ " 'args': [\n",
+ " {'x': [results[col]]},\n",
+ " {'xaxis': {'title': name_mappings.get(col, col)}}\n",
+ " ],\n",
+ " }\n",
+ " for col in included_columns\n",
+ " ]\n",
+ "\n",
+ " # Configure dropdown menu\n",
+ " fig.update_layout(\n",
+ " showlegend=False,\n",
+ " updatemenus=[{'buttons': buttons,\n",
+ " 'direction': 'down',\n",
+ " 'showactive': True}]\n",
+ " )\n",
+ "\n",
+ " # Optionally add an instruction annotation\n",
+ " if include_instruct:\n",
+ " fig.add_annotation(\n",
+ " text='Select a KPI from the drop down list',\n",
+ " xref='paper',\n",
+ " yref='paper',\n",
+ " x=0.0,\n",
+ " y=1.1, # Position text above the plot\n",
+ " showarrow=False,\n",
+ " font={'size': 12},\n",
+ " )\n",
+ "\n",
+ " return fig"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
- "image/svg+xml": [
- ""
- ]
+ "application/vnd.plotly.v1+json": {
+ "config": {
+ "plotlyServerURL": "https://plot.ly"
+ },
+ "data": [
+ {
+ "type": "histogram",
+ "x": [
+ 10972,
+ 10784,
+ 10854,
+ 10831,
+ 10720,
+ 10772,
+ 10831,
+ 10781,
+ 10772,
+ 10705,
+ 10927,
+ 10688,
+ 11092,
+ 10640,
+ 10904,
+ 10849,
+ 10719,
+ 10713,
+ 10568,
+ 10707,
+ 10845,
+ 10726,
+ 10618,
+ 10914,
+ 10660,
+ 10685,
+ 10806,
+ 10748,
+ 10589,
+ 10863,
+ 10796
+ ]
+ }
+ ],
+ "layout": {
+ "annotations": [
+ {
+ "font": {
+ "size": 12
+ },
+ "showarrow": false,
+ "text": "Select a KPI from the drop down list",
+ "x": 0,
+ "xref": "paper",
+ "y": 1.1,
+ "yref": "paper"
+ }
+ ],
+ "showlegend": false,
+ "template": {
+ "data": {
+ "bar": [
+ {
+ "error_x": {
+ "color": "#2a3f5f"
+ },
+ "error_y": {
+ "color": "#2a3f5f"
+ },
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "bar"
+ }
+ ],
+ "barpolar": [
+ {
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "barpolar"
+ }
+ ],
+ "carpet": [
+ {
+ "aaxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "baxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "type": "carpet"
+ }
+ ],
+ "choropleth": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "choropleth"
+ }
+ ],
+ "contour": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "contour"
+ }
+ ],
+ "contourcarpet": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "contourcarpet"
+ }
+ ],
+ "heatmap": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "heatmap"
+ }
+ ],
+ "heatmapgl": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "heatmapgl"
+ }
+ ],
+ "histogram": [
+ {
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "histogram"
+ }
+ ],
+ "histogram2d": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "histogram2d"
+ }
+ ],
+ "histogram2dcontour": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "histogram2dcontour"
+ }
+ ],
+ "mesh3d": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "mesh3d"
+ }
+ ],
+ "parcoords": [
+ {
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "parcoords"
+ }
+ ],
+ "pie": [
+ {
+ "automargin": true,
+ "type": "pie"
+ }
+ ],
+ "scatter": [
+ {
+ "fillpattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ },
+ "type": "scatter"
+ }
+ ],
+ "scatter3d": [
+ {
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatter3d"
+ }
+ ],
+ "scattercarpet": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattercarpet"
+ }
+ ],
+ "scattergeo": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattergeo"
+ }
+ ],
+ "scattergl": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattergl"
+ }
+ ],
+ "scattermapbox": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattermapbox"
+ }
+ ],
+ "scatterpolar": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterpolar"
+ }
+ ],
+ "scatterpolargl": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterpolargl"
+ }
+ ],
+ "scatterternary": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterternary"
+ }
+ ],
+ "surface": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "surface"
+ }
+ ],
+ "table": [
+ {
+ "cells": {
+ "fill": {
+ "color": "#EBF0F8"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "header": {
+ "fill": {
+ "color": "#C8D4E3"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "type": "table"
+ }
+ ]
+ },
+ "layout": {
+ "annotationdefaults": {
+ "arrowcolor": "#2a3f5f",
+ "arrowhead": 0,
+ "arrowwidth": 1
+ },
+ "autotypenumbers": "strict",
+ "coloraxis": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "colorscale": {
+ "diverging": [
+ [
+ 0,
+ "#8e0152"
+ ],
+ [
+ 0.1,
+ "#c51b7d"
+ ],
+ [
+ 0.2,
+ "#de77ae"
+ ],
+ [
+ 0.3,
+ "#f1b6da"
+ ],
+ [
+ 0.4,
+ "#fde0ef"
+ ],
+ [
+ 0.5,
+ "#f7f7f7"
+ ],
+ [
+ 0.6,
+ "#e6f5d0"
+ ],
+ [
+ 0.7,
+ "#b8e186"
+ ],
+ [
+ 0.8,
+ "#7fbc41"
+ ],
+ [
+ 0.9,
+ "#4d9221"
+ ],
+ [
+ 1,
+ "#276419"
+ ]
+ ],
+ "sequential": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "sequentialminus": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ]
+ },
+ "colorway": [
+ "#636efa",
+ "#EF553B",
+ "#00cc96",
+ "#ab63fa",
+ "#FFA15A",
+ "#19d3f3",
+ "#FF6692",
+ "#B6E880",
+ "#FF97FF",
+ "#FECB52"
+ ],
+ "font": {
+ "color": "#2a3f5f"
+ },
+ "geo": {
+ "bgcolor": "white",
+ "lakecolor": "white",
+ "landcolor": "#E5ECF6",
+ "showlakes": true,
+ "showland": true,
+ "subunitcolor": "white"
+ },
+ "hoverlabel": {
+ "align": "left"
+ },
+ "hovermode": "closest",
+ "mapbox": {
+ "style": "light"
+ },
+ "paper_bgcolor": "white",
+ "plot_bgcolor": "#E5ECF6",
+ "polar": {
+ "angularaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "bgcolor": "#E5ECF6",
+ "radialaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "scene": {
+ "xaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ },
+ "yaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ },
+ "zaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ }
+ },
+ "shapedefaults": {
+ "line": {
+ "color": "#2a3f5f"
+ }
+ },
+ "ternary": {
+ "aaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "baxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "bgcolor": "#E5ECF6",
+ "caxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "title": {
+ "x": 0.05
+ },
+ "xaxis": {
+ "automargin": true,
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "zerolinewidth": 2
+ },
+ "yaxis": {
+ "automargin": true,
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "zerolinewidth": 2
+ }
+ }
+ },
+ "updatemenus": [
+ {
+ "buttons": [
+ {
+ "args": [
+ {
+ "x": [
+ [
+ 10972,
+ 10784,
+ 10854,
+ 10831,
+ 10720,
+ 10772,
+ 10831,
+ 10781,
+ 10772,
+ 10705,
+ 10927,
+ 10688,
+ 11092,
+ 10640,
+ 10904,
+ 10849,
+ 10719,
+ 10713,
+ 10568,
+ 10707,
+ 10845,
+ 10726,
+ 10618,
+ 10914,
+ 10660,
+ 10685,
+ 10806,
+ 10748,
+ 10589,
+ 10863,
+ 10796
+ ]
+ ]
+ },
+ {
+ "xaxis": {
+ "title": "Patient arrivals (n)"
+ }
+ }
+ ],
+ "label": "Patient arrivals (n)",
+ "method": "update"
+ },
+ {
+ "args": [
+ {
+ "x": [
+ [
+ 0.504541081338615,
+ 0.514150649003393,
+ 0.5232349226016817,
+ 0.4791488631810612,
+ 0.46145745726579823,
+ 0.3882646868128185,
+ 0.4669381081669874,
+ 0.625888360901447,
+ 0.4684971326393017,
+ 0.5634349862001105,
+ 0.562585969190057,
+ 0.39781182403365434,
+ 0.44530362984867583,
+ 0.4397605749351992,
+ 0.6107822771635987,
+ 0.47670998312351787,
+ 0.428336230071242,
+ 0.505574486075843,
+ 0.3615821082986753,
+ 0.4191589720392036,
+ 0.6120142008002236,
+ 0.5173212645251098,
+ 0.5316566599495316,
+ 0.5270546197660896,
+ 0.5029697553047842,
+ 0.6270646900660448,
+ 0.5061794260886525,
+ 0.4925537687951073,
+ 0.4577189216542841,
+ 0.5422405738480466,
+ 0.5102078888697092
+ ]
+ ]
+ },
+ {
+ "xaxis": {
+ "title": "Mean wait time for nurse (minutes)"
+ }
+ }
+ ],
+ "label": "Mean wait time for nurse (minutes)",
+ "method": "update"
+ },
+ {
+ "args": [
+ {
+ "x": [
+ [
+ 9.84226781662332,
+ 10.060480983450425,
+ 9.925024519746302,
+ 9.9370571543943,
+ 10.015904147971671,
+ 9.884995942861282,
+ 10.041799654744745,
+ 10.086979128063648,
+ 10.202270228377186,
+ 10.09260227901333,
+ 10.083553615426284,
+ 9.893334037740962,
+ 9.810905063859671,
+ 9.933879963761742,
+ 10.167683587926042,
+ 9.975581276412813,
+ 9.809804967118032,
+ 9.989154604862323,
+ 9.800182515281657,
+ 9.9193906381964,
+ 9.813774247048583,
+ 9.858186468091487,
+ 9.969891296475984,
+ 9.925291785145637,
+ 9.96454608027881,
+ 10.236338445072258,
+ 10.039653295082593,
+ 10.050997027592315,
+ 10.044180668559221,
+ 10.034383892923174,
+ 9.922063713004379
+ ]
+ ]
+ },
+ {
+ "xaxis": {
+ "title": "Mean consultation time with nurse (minutes)"
+ }
+ }
+ ],
+ "label": "Mean consultation time with nurse (minutes)",
+ "method": "update"
+ },
+ {
+ "args": [
+ {
+ "x": [
+ [
+ 0.4996386466177992,
+ 0.5019905433327594,
+ 0.4981297032213408,
+ 0.49822049332297624,
+ 0.4968703372055205,
+ 0.492904233058074,
+ 0.503356251584998,
+ 0.5034489381047406,
+ 0.508790994907774,
+ 0.49997178325917613,
+ 0.5098373896503802,
+ 0.4895233535105571,
+ 0.5034276977164731,
+ 0.48925457544062534,
+ 0.513130373051619,
+ 0.5009629889699926,
+ 0.4866947365248423,
+ 0.4954087210793937,
+ 0.4793715775153063,
+ 0.4915291568180775,
+ 0.49261944375555833,
+ 0.48940978932654106,
+ 0.4892835610032433,
+ 0.5013818263178943,
+ 0.49160360886911897,
+ 0.5060501169430693,
+ 0.5022615440123254,
+ 0.4999546777219452,
+ 0.4923573694687436,
+ 0.5046064549261675,
+ 0.49578999982774674
+ ]
+ ]
+ },
+ {
+ "xaxis": {
+ "title": "Mean nurse utilisation"
+ }
+ }
+ ],
+ "label": "Mean nurse utilisation",
+ "method": "update"
+ },
+ {
+ "args": [
+ {
+ "x": [
+ [
+ 0.49974045251278504,
+ 0.5020690100987935,
+ 0.49826045142699366,
+ 0.49826895028941315,
+ 0.4971055007588991,
+ 0.4930559758823056,
+ 0.5034005361957363,
+ 0.5037422085180931,
+ 0.5089130015618397,
+ 0.5002278075946733,
+ 0.5099889270180521,
+ 0.489627107280519,
+ 0.503459064398387,
+ 0.48926772880832675,
+ 0.5135294499218969,
+ 0.5010455334069701,
+ 0.48682744992243954,
+ 0.4957697619501464,
+ 0.4795652084277076,
+ 0.49176351271863106,
+ 0.4926720149388125,
+ 0.4895612849605331,
+ 0.4893719186906069,
+ 0.5014338001561723,
+ 0.49162164437257705,
+ 0.5061407672693301,
+ 0.5023039700989324,
+ 0.5001183558252174,
+ 0.49236726450892265,
+ 0.5047331518468752,
+ 0.49597325153194544
+ ]
+ ]
+ },
+ {
+ "xaxis": {
+ "title": "Time-weighted mean nurse utilisation"
+ }
+ }
+ ],
+ "label": "Time-weighted mean nurse utilisation",
+ "method": "update"
+ },
+ {
+ "args": [
+ {
+ "x": [
+ [
+ 0.12814409130665008,
+ 0.1283472360845507,
+ 0.1326692082859329,
+ 0.12013104947023319,
+ 0.11450981346966105,
+ 0.09681451866545558,
+ 0.11706959836936667,
+ 0.15619681525181714,
+ 0.11682062761089254,
+ 0.1399966447950717,
+ 0.14230039086434612,
+ 0.09842159202017818,
+ 0.11437196007956935,
+ 0.10831140086366944,
+ 0.1541659710692565,
+ 0.11971820849321864,
+ 0.10628092708642692,
+ 0.1253754506789469,
+ 0.08845369723380557,
+ 0.10388738688943873,
+ 0.15364106499255614,
+ 0.1284441639651928,
+ 0.13067431517000294,
+ 0.1331544935214607,
+ 0.12411244424881943,
+ 0.15515572552368928,
+ 0.126615159220231,
+ 0.12254555340300495,
+ 0.11219411253234293,
+ 0.13635091096554006,
+ 0.12767905794112022
+ ]
+ ]
+ },
+ {
+ "xaxis": {
+ "title": "Time-weighted mean queue length for nurse (n)"
+ }
+ }
+ ],
+ "label": "Time-weighted mean queue length for nurse (n)",
+ "method": "update"
+ }
+ ],
+ "direction": "down",
+ "showactive": true
+ }
+ ],
+ "xaxis": {
+ "title": {
+ "text": "Patient arrivals (n)"
+ }
+ },
+ "yaxis": {
+ "title": {
+ "text": "Replications"
+ }
+ }
+ }
+ }
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
- "# optional name mappings to help user. if excluded then column names used.\n",
- "name_mappings = {\n",
- " 'arrivals': {'friendly_name': 'Patient Arrivals', 'units': 'n'},\n",
- " 'mean_q_time_nurse': {\n",
- " 'friendly_name': 'Nurse waiting time',\n",
- " 'units': 'minutes',\n",
- " },\n",
- " 'mean_time_with_nurse': {\n",
- " 'friendly_name': 'Patient contact time',\n",
- " 'units': 'minutes',\n",
- " },\n",
- " 'mean_nurse_utilisation': {\n",
- " 'friendly_name': 'Nurse Utilization',\n",
- " 'units': '%',\n",
- " },\n",
- "}\n",
- "\n",
"create_user_controlled_hist(\n",
" experiment.run_results_df,\n",
" exclude_columns=[0, 1],\n",
- " name_mappings=name_mappings,\n",
+ " name_mappings=LABELS,\n",
" include_instruct=True,\n",
")"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Static plots that show the spread of results across replications\n",
+ "\n",
+ "Display subsequent plotly express figures as non-interactive figures. This means they will be visible when browsing the notebooks on GitHub. To switch these back to interactive figures, simply remove this line."
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 16,
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pio.renderers.default = 'svg'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
- "def plot_results_spread(rep_results, column, x_label, file):\n",
+ "def plot_results_spread(\n",
+ " rep_results, column, file, name_mappings=None, path=OUTPUT_DIR\n",
+ "):\n",
" \"\"\"\n",
" Plot spread of results from across replications, for chosen column.\n",
" Show figure and save under specified file name.\n",
"\n",
" Arguments:\n",
" rep_results (pandas.DataFrame)\n",
- " The dataframe of replication results. \n",
+ " The dataframe of replication results.\n",
" column (str):\n",
" Name of column to plot.\n",
- " x_label (str):\n",
- " X axis label.\n",
" file (str):\n",
" Filename to save figure to.\n",
+ " name_mappings (dict, optional)\n",
+ " Dictionary mapping column names to labels. If not provided,\n",
+ " function will default to variable names.\n",
+ " path (str):\n",
+ " Path to save the file to (excluding filename).\n",
" \"\"\"\n",
+ " # Set as empty dictionary if mappings not provided. The function will later\n",
+ " # search for a label and find no match, so use the variable name.\n",
+ " if name_mappings is None:\n",
+ " name_mappings = {}\n",
+ "\n",
+ " # Create plot\n",
" fig = px.histogram(rep_results[column])\n",
" fig.update_layout(\n",
- " xaxis_title=x_label,\n",
+ " xaxis_title=name_mappings.get(column, column),\n",
" yaxis_title='Frequency',\n",
" template='plotly_white',\n",
" showlegend=False)\n",
@@ -835,18 +1977,18 @@
" fig.show()\n",
"\n",
" # Save figure\n",
- " fig.write_image(os.path.join(output_dir, file))"
+ " fig.write_image(os.path.join(path, file))"
]
},
{
"cell_type": "code",
- "execution_count": 17,
+ "execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -855,7 +1997,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -864,7 +2006,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -873,7 +2015,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -883,30 +2025,30 @@
"source": [
"plot_results_spread(\n",
" rep_results=experiment.run_results_df,\n",
- " column='arrivals', \n",
- " x_label='Arrivals', \n",
- " file='spread_arrivals.png'\n",
+ " column='arrivals',\n",
+ " file='spread_arrivals.png',\n",
+ " name_mappings=LABELS\n",
")\n",
"\n",
"plot_results_spread(\n",
" rep_results=experiment.run_results_df,\n",
" column='mean_q_time_nurse',\n",
- " x_label='Mean wait time for nurse',\n",
" file='spread_nurse_wait.png',\n",
+ " name_mappings=LABELS\n",
")\n",
"\n",
"plot_results_spread(\n",
" rep_results=experiment.run_results_df,\n",
" column='mean_time_with_nurse',\n",
- " x_label='Mean length of nurse consultation',\n",
" file='spread_nurse_time.png',\n",
+ " name_mappings=LABELS\n",
")\n",
"\n",
"plot_results_spread(\n",
" rep_results=experiment.run_results_df,\n",
" column='mean_nurse_utilisation',\n",
- " x_label='Mean nurse utilisation',\n",
" file='spread_nurse_util.png',\n",
+ " name_mappings=LABELS\n",
")"
]
},
@@ -921,7 +2063,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 17,
"metadata": {},
"outputs": [
{
@@ -961,7 +2103,7 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 18,
"metadata": {},
"outputs": [
{
@@ -1090,7 +2232,7 @@
"4 3 5 "
]
},
- "execution_count": 19,
+ "execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@@ -1108,12 +2250,11 @@
},
{
"cell_type": "code",
- "execution_count": 20,
+ "execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
- "def plot_scenario(results, x_var, result_var, colour_var, xaxis_title,\n",
- " yaxis_title, legend_title):\n",
+ "def plot_scenario(results, x_var, result_var, colour_var, name_mappings):\n",
" \"\"\"\n",
" Plot results from different model scenarios.\n",
"\n",
@@ -1126,12 +2267,9 @@
" Name of variable with results, to plot on Y axis.\n",
" colour_var (str|None):\n",
" Name of variable to colour lines with (or set to None).\n",
- " xaxis_title (str):\n",
- " Title for X axis.\n",
- " yaxis_title (str):\n",
- " Title for Y axis.\n",
- " legend_title (str):\n",
- " Title for figure legend.\n",
+ " name_mappings (dict, optional)\n",
+ " Dictionary mapping column names to labels. If not provided,\n",
+ " function will default to variable names.\n",
" \"\"\"\n",
" # If x_var and colour_var are provided, combine both in a list to use\n",
" # as grouping variables when calculating average results\n",
@@ -1153,9 +2291,9 @@
" # Plot mean line\n",
" fig = px.line(df, x=x_var, y='mean', color=colour_var)\n",
" fig.update_layout(\n",
- " xaxis_title=xaxis_title,\n",
- " yaxis_title=yaxis_title,\n",
- " legend_title_text=legend_title,\n",
+ " xaxis_title=name_mappings.get(x_var, x_var),\n",
+ " yaxis_title=name_mappings.get(result_var, result_var),\n",
+ " legend_title_text=name_mappings.get(colour_var, colour_var),\n",
" template='plotly_white'\n",
" )\n",
"\n",
@@ -1179,13 +2317,13 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -1193,18 +2331,16 @@
}
],
"source": [
- "result, fig = plot_scenario(\n",
+ "result, plot = plot_scenario(\n",
" results=scenario_results,\n",
" x_var='patient_inter',\n",
" result_var='mean_q_time_nurse',\n",
" colour_var='number_of_nurses',\n",
- " xaxis_title='Patient inter-arrival time',\n",
- " yaxis_title='Mean wait time for nurse (minutes)',\n",
- " legend_title='Nurses')\n",
+ " name_mappings=LABELS)\n",
"\n",
- "fig.show()\n",
+ "plot.show()\n",
"\n",
- "fig.write_image(os.path.join(output_dir, 'scenario_nurse_wait.png'))"
+ "plot.write_image(os.path.join(OUTPUT_DIR, 'scenario_nurse_wait.png'))"
]
},
{
@@ -1216,13 +2352,13 @@
},
{
"cell_type": "code",
- "execution_count": 22,
+ "execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -1230,18 +2366,16 @@
}
],
"source": [
- "result, fig = plot_scenario(\n",
+ "result, plot = plot_scenario(\n",
" results=scenario_results,\n",
" x_var='patient_inter',\n",
" result_var='mean_nurse_utilisation',\n",
" colour_var='number_of_nurses',\n",
- " xaxis_title='Patient inter-arrival time',\n",
- " yaxis_title='Mean nurse utilisation',\n",
- " legend_title='Nurses')\n",
+ " name_mappings=LABELS)\n",
"\n",
- "fig.show()\n",
+ "plot.show()\n",
"\n",
- "fig.write_image(os.path.join(output_dir, 'scenario_nurse_util.png'))"
+ "plot.write_image(os.path.join(OUTPUT_DIR, 'scenario_nurse_util.png'))"
]
},
{
@@ -1253,7 +2387,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 22,
"metadata": {},
"outputs": [
{
@@ -1296,7 +2430,8 @@
"# Convert to latex, display and save\n",
"table_latex = table.to_latex()\n",
"print(table_latex)\n",
- "with open(os.path.join(output_dir, 'scenario_nurse_util.tex'), 'w') as tf:\n",
+ "with open(os.path.join(OUTPUT_DIR, 'scenario_nurse_util.tex'),\n",
+ " 'w', encoding='utf-8') as tf:\n",
" tf.write(table_latex)"
]
},
@@ -1316,7 +2451,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 23,
"metadata": {},
"outputs": [
{
@@ -1344,13 +2479,13 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -1358,24 +2493,22 @@
}
],
"source": [
- "result, fig = plot_scenario(\n",
+ "result, plot = plot_scenario(\n",
" results=sensitivity_consult,\n",
" x_var='mean_n_consult_time',\n",
" result_var='mean_q_time_nurse',\n",
" colour_var=None,\n",
- " xaxis_title='Mean nurse consultation time (minutes)',\n",
- " yaxis_title='Mean wait time for nurse (minutes)',\n",
- " legend_title='Nurses',\n",
+ " name_mappings=LABELS\n",
")\n",
"\n",
- "fig.show()\n",
+ "plot.show()\n",
"\n",
- "fig.write_image(os.path.join(output_dir, 'sensitivity_consult_time.png'))"
+ "plot.write_image(os.path.join(OUTPUT_DIR, 'sensitivity_consult_time.png'))"
]
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 25,
"metadata": {},
"outputs": [
{
@@ -1419,7 +2552,8 @@
"# Convert to latex, display and save\n",
"table_latex = table.to_latex()\n",
"print(table_latex)\n",
- "with open(os.path.join(output_dir, 'sensitivity_consult_time.tex'), 'w') as tf:\n",
+ "with open(os.path.join(OUTPUT_DIR, 'sensitivity_consult_time.tex'),\n",
+ " 'w', encoding='utf-8') as tf:\n",
" tf.write(table_latex)"
]
},
@@ -1434,7 +2568,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 26,
"metadata": {},
"outputs": [
{
@@ -1519,7 +2653,7 @@
"667690 21590 61919.845349 NaN NaN 30"
]
},
- "execution_count": 27,
+ "execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
@@ -1547,80 +2681,80 @@
},
{
"cell_type": "code",
- "execution_count": 28,
+ "execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "2025-01-29 13:39:29,576 - INFO - logging.py:log():128 - Initialised model: {'param': , 'run_number': 0, 'env': , 'nurse': , 'patients': [], 'nurse_time_used': 0, 'nurse_consult_count': 0, 'running_mean_nurse_wait': 0, 'audit_list': [], 'results_list': [], 'patient_inter_arrival_dist': , 'nurse_consult_time_dist': }\n",
- "2025-01-29 13:39:29,577 - INFO - logging.py:log():128 - Parameters: {'_initialising': False, 'patient_inter': 4, 'mean_n_consult_time': 10, 'number_of_nurses': 5, 'warm_up_period': 50, 'data_collection_period': 100, 'number_of_runs': 1, 'audit_interval': 120, 'scenario_name': 0, 'cores': 0, 'logger': }\n",
- "2025-01-29 13:39:29,577 - INFO - logging.py:log():128 - Patient 1 arrives at: 0.000\n",
- "2025-01-29 13:39:29,577 - INFO - logging.py:log():128 - Patient 1 is seen by nurse after 0.000 minutes. Consultation length: 8.031 minutes.\n",
- "2025-01-29 13:39:29,578 - INFO - logging.py:log():128 - Patient 2 arrives at: 13.174\n",
- "2025-01-29 13:39:29,578 - INFO - logging.py:log():128 - Patient 2 is seen by nurse after 13.174 minutes. Consultation length: 3.820 minutes.\n",
- "2025-01-29 13:39:29,578 - INFO - logging.py:log():128 - Patient 3 arrives at: 16.227\n",
- "2025-01-29 13:39:29,578 - INFO - logging.py:log():128 - Patient 3 is seen by nurse after 16.227 minutes. Consultation length: 3.642 minutes.\n",
- "2025-01-29 13:39:29,578 - INFO - logging.py:log():128 - Patient 4 arrives at: 21.236\n",
- "2025-01-29 13:39:29,579 - INFO - logging.py:log():128 - Patient 4 is seen by nurse after 21.236 minutes. Consultation length: 5.295 minutes.\n",
- "2025-01-29 13:39:29,579 - INFO - logging.py:log():128 - Patient 5 arrives at: 22.140\n",
- "2025-01-29 13:39:29,579 - INFO - logging.py:log():128 - Patient 5 is seen by nurse after 22.140 minutes. Consultation length: 27.884 minutes.\n",
- "2025-01-29 13:39:29,579 - INFO - logging.py:log():128 - Patient 6 arrives at: 23.023\n",
- "2025-01-29 13:39:29,579 - INFO - logging.py:log():128 - Patient 6 is seen by nurse after 23.023 minutes. Consultation length: 19.610 minutes.\n",
- "2025-01-29 13:39:29,579 - INFO - logging.py:log():128 - Patient 7 arrives at: 30.223\n",
- "2025-01-29 13:39:29,580 - INFO - logging.py:log():128 - Patient 7 is seen by nurse after 30.223 minutes. Consultation length: 9.490 minutes.\n",
- "2025-01-29 13:39:29,580 - INFO - logging.py:log():128 - Patient 8 arrives at: 30.487\n",
- "2025-01-29 13:39:29,580 - INFO - logging.py:log():128 - Patient 8 is seen by nurse after 30.487 minutes. Consultation length: 41.665 minutes.\n",
- "2025-01-29 13:39:29,580 - INFO - logging.py:log():128 - Patient 9 arrives at: 34.089\n",
- "2025-01-29 13:39:29,580 - INFO - logging.py:log():128 - Patient 9 is seen by nurse after 34.089 minutes. Consultation length: 5.874 minutes.\n",
- "2025-01-29 13:39:29,581 - INFO - logging.py:log():128 - Patient 10 arrives at: 35.270\n",
- "2025-01-29 13:39:29,581 - INFO - logging.py:log():128 - Patient 10 is seen by nurse after 35.270 minutes. Consultation length: 27.882 minutes.\n",
- "2025-01-29 13:39:29,581 - INFO - logging.py:log():128 - Patient 11 arrives at: 44.470\n",
- "2025-01-29 13:39:29,581 - INFO - logging.py:log():128 - Patient 11 is seen by nurse after 44.470 minutes. Consultation length: 24.915 minutes.\n",
- "2025-01-29 13:39:29,582 - INFO - logging.py:log():128 - ──────────\n",
- "2025-01-29 13:39:29,582 - INFO - logging.py:log():128 - 50.00: Warm up complete.\n",
- "2025-01-29 13:39:29,582 - INFO - logging.py:log():128 - ──────────\n",
- "2025-01-29 13:39:29,582 - INFO - logging.py:log():128 - Patient 1 arrives at: 51.904\n",
- "2025-01-29 13:39:29,582 - INFO - logging.py:log():128 - Patient 1 is seen by nurse after 51.904 minutes. Consultation length: 18.079 minutes.\n",
- "2025-01-29 13:39:29,583 - INFO - logging.py:log():128 - Patient 2 arrives at: 51.963\n",
- "2025-01-29 13:39:29,583 - INFO - logging.py:log():128 - Patient 2 is seen by nurse after 51.963 minutes. Consultation length: 3.102 minutes.\n",
- "2025-01-29 13:39:29,583 - INFO - logging.py:log():128 - Patient 3 arrives at: 74.349\n",
- "2025-01-29 13:39:29,583 - INFO - logging.py:log():128 - Patient 3 is seen by nurse after 74.349 minutes. Consultation length: 26.745 minutes.\n",
- "2025-01-29 13:39:29,583 - INFO - logging.py:log():128 - Patient 4 arrives at: 77.534\n",
- "2025-01-29 13:39:29,584 - INFO - logging.py:log():128 - Patient 4 is seen by nurse after 77.534 minutes. Consultation length: 0.748 minutes.\n",
- "2025-01-29 13:39:29,584 - INFO - logging.py:log():128 - Patient 5 arrives at: 78.932\n",
- "2025-01-29 13:39:29,584 - INFO - logging.py:log():128 - Patient 5 is seen by nurse after 78.932 minutes. Consultation length: 0.528 minutes.\n",
- "2025-01-29 13:39:29,584 - INFO - logging.py:log():128 - Patient 6 arrives at: 86.815\n",
- "2025-01-29 13:39:29,584 - INFO - logging.py:log():128 - Patient 6 is seen by nurse after 86.815 minutes. Consultation length: 2.435 minutes.\n",
- "2025-01-29 13:39:29,585 - INFO - logging.py:log():128 - Patient 7 arrives at: 89.783\n",
- "2025-01-29 13:39:29,585 - INFO - logging.py:log():128 - Patient 7 is seen by nurse after 89.783 minutes. Consultation length: 9.666 minutes.\n",
- "2025-01-29 13:39:29,585 - INFO - logging.py:log():128 - Patient 8 arrives at: 89.807\n",
- "2025-01-29 13:39:29,585 - INFO - logging.py:log():128 - Patient 8 is seen by nurse after 89.807 minutes. Consultation length: 7.005 minutes.\n",
- "2025-01-29 13:39:29,585 - INFO - logging.py:log():128 - Patient 9 arrives at: 93.118\n",
- "2025-01-29 13:39:29,586 - INFO - logging.py:log():128 - Patient 9 is seen by nurse after 93.118 minutes. Consultation length: 20.185 minutes.\n",
- "2025-01-29 13:39:29,586 - INFO - logging.py:log():128 - Patient 10 arrives at: 95.598\n",
- "2025-01-29 13:39:29,586 - INFO - logging.py:log():128 - Patient 10 is seen by nurse after 95.598 minutes. Consultation length: 7.651 minutes.\n",
- "2025-01-29 13:39:29,587 - INFO - logging.py:log():128 - Patient 11 arrives at: 98.019\n",
- "2025-01-29 13:39:29,587 - INFO - logging.py:log():128 - Patient 11 is seen by nurse after 98.019 minutes. Consultation length: 27.908 minutes.\n",
- "2025-01-29 13:39:29,587 - INFO - logging.py:log():128 - Patient 12 arrives at: 109.379\n",
- "2025-01-29 13:39:29,587 - INFO - logging.py:log():128 - Patient 12 is seen by nurse after 109.379 minutes. Consultation length: 16.811 minutes.\n",
- "2025-01-29 13:39:29,587 - INFO - logging.py:log():128 - Patient 13 arrives at: 110.322\n",
- "2025-01-29 13:39:29,588 - INFO - logging.py:log():128 - Patient 13 is seen by nurse after 110.322 minutes. Consultation length: 24.157 minutes.\n",
- "2025-01-29 13:39:29,588 - INFO - logging.py:log():128 - Patient 14 arrives at: 120.342\n",
- "2025-01-29 13:39:29,588 - INFO - logging.py:log():128 - Patient 14 is seen by nurse after 120.342 minutes. Consultation length: 1.451 minutes.\n",
- "2025-01-29 13:39:29,588 - INFO - logging.py:log():128 - Patient 15 arrives at: 121.643\n",
- "2025-01-29 13:39:29,588 - INFO - logging.py:log():128 - Patient 15 is seen by nurse after 121.643 minutes. Consultation length: 1.343 minutes.\n",
- "2025-01-29 13:39:29,589 - INFO - logging.py:log():128 - Patient 16 arrives at: 127.827\n",
- "2025-01-29 13:39:29,589 - INFO - logging.py:log():128 - Patient 16 is seen by nurse after 127.827 minutes. Consultation length: 1.554 minutes.\n",
- "2025-01-29 13:39:29,589 - INFO - logging.py:log():128 - Patient 17 arrives at: 132.055\n",
- "2025-01-29 13:39:29,589 - INFO - logging.py:log():128 - Patient 17 is seen by nurse after 132.055 minutes. Consultation length: 15.623 minutes.\n",
- "2025-01-29 13:39:29,589 - INFO - logging.py:log():128 - Patient 18 arrives at: 133.617\n",
- "2025-01-29 13:39:29,590 - INFO - logging.py:log():128 - Patient 18 is seen by nurse after 133.617 minutes. Consultation length: 4.688 minutes.\n",
- "2025-01-29 13:39:29,590 - INFO - logging.py:log():128 - Patient 19 arrives at: 136.346\n",
- "2025-01-29 13:39:29,590 - INFO - logging.py:log():128 - Patient 19 is seen by nurse after 136.346 minutes. Consultation length: 2.612 minutes.\n",
- "2025-01-29 13:39:29,590 - INFO - logging.py:log():128 - Patient 20 arrives at: 144.142\n",
- "2025-01-29 13:39:29,590 - INFO - logging.py:log():128 - Patient 20 is seen by nurse after 144.142 minutes. Consultation length: 6.348 minutes.\n"
+ "2025-01-30 13:21:27,913 - INFO - logging.py:log():128 - Initialised model: {'param': , 'run_number': 0, 'env': , 'nurse': , 'patients': [], 'nurse_time_used': 0, 'nurse_consult_count': 0, 'running_mean_nurse_wait': 0, 'audit_list': [], 'results_list': [], 'patient_inter_arrival_dist': , 'nurse_consult_time_dist': }\n",
+ "2025-01-30 13:21:27,913 - INFO - logging.py:log():128 - Parameters: {'_initialising': False, 'patient_inter': 4, 'mean_n_consult_time': 10, 'number_of_nurses': 5, 'warm_up_period': 50, 'data_collection_period': 100, 'number_of_runs': 1, 'audit_interval': 120, 'scenario_name': 0, 'cores': 0, 'logger': }\n",
+ "2025-01-30 13:21:27,914 - INFO - logging.py:log():128 - Patient 1 arrives at: 0.000\n",
+ "2025-01-30 13:21:27,914 - INFO - logging.py:log():128 - Patient 1 is seen by nurse after 0.000 minutes. Consultation length: 8.031 minutes.\n",
+ "2025-01-30 13:21:27,914 - INFO - logging.py:log():128 - Patient 2 arrives at: 13.174\n",
+ "2025-01-30 13:21:27,914 - INFO - logging.py:log():128 - Patient 2 is seen by nurse after 0.000 minutes. Consultation length: 3.820 minutes.\n",
+ "2025-01-30 13:21:27,915 - INFO - logging.py:log():128 - Patient 3 arrives at: 16.227\n",
+ "2025-01-30 13:21:27,915 - INFO - logging.py:log():128 - Patient 3 is seen by nurse after 0.000 minutes. Consultation length: 3.642 minutes.\n",
+ "2025-01-30 13:21:27,915 - INFO - logging.py:log():128 - Patient 4 arrives at: 21.236\n",
+ "2025-01-30 13:21:27,915 - INFO - logging.py:log():128 - Patient 4 is seen by nurse after 0.000 minutes. Consultation length: 5.295 minutes.\n",
+ "2025-01-30 13:21:27,916 - INFO - logging.py:log():128 - Patient 5 arrives at: 22.140\n",
+ "2025-01-30 13:21:27,916 - INFO - logging.py:log():128 - Patient 5 is seen by nurse after 0.000 minutes. Consultation length: 27.884 minutes.\n",
+ "2025-01-30 13:21:27,916 - INFO - logging.py:log():128 - Patient 6 arrives at: 23.023\n",
+ "2025-01-30 13:21:27,916 - INFO - logging.py:log():128 - Patient 6 is seen by nurse after 0.000 minutes. Consultation length: 19.610 minutes.\n",
+ "2025-01-30 13:21:27,916 - INFO - logging.py:log():128 - Patient 7 arrives at: 30.223\n",
+ "2025-01-30 13:21:27,917 - INFO - logging.py:log():128 - Patient 7 is seen by nurse after 0.000 minutes. Consultation length: 9.490 minutes.\n",
+ "2025-01-30 13:21:27,917 - INFO - logging.py:log():128 - Patient 8 arrives at: 30.487\n",
+ "2025-01-30 13:21:27,917 - INFO - logging.py:log():128 - Patient 8 is seen by nurse after 0.000 minutes. Consultation length: 41.665 minutes.\n",
+ "2025-01-30 13:21:27,917 - INFO - logging.py:log():128 - Patient 9 arrives at: 34.089\n",
+ "2025-01-30 13:21:27,917 - INFO - logging.py:log():128 - Patient 9 is seen by nurse after 0.000 minutes. Consultation length: 5.874 minutes.\n",
+ "2025-01-30 13:21:27,918 - INFO - logging.py:log():128 - Patient 10 arrives at: 35.270\n",
+ "2025-01-30 13:21:27,918 - INFO - logging.py:log():128 - Patient 10 is seen by nurse after 4.442 minutes. Consultation length: 27.882 minutes.\n",
+ "2025-01-30 13:21:27,918 - INFO - logging.py:log():128 - Patient 11 arrives at: 44.470\n",
+ "2025-01-30 13:21:27,918 - INFO - logging.py:log():128 - Patient 11 is seen by nurse after 0.000 minutes. Consultation length: 24.915 minutes.\n",
+ "2025-01-30 13:21:27,919 - INFO - logging.py:log():128 - ──────────\n",
+ "2025-01-30 13:21:27,919 - INFO - logging.py:log():128 - 50.00: Warm up complete.\n",
+ "2025-01-30 13:21:27,919 - INFO - logging.py:log():128 - ──────────\n",
+ "2025-01-30 13:21:27,919 - INFO - logging.py:log():128 - Patient 1 arrives at: 51.904\n",
+ "2025-01-30 13:21:27,920 - INFO - logging.py:log():128 - Patient 1 is seen by nurse after 0.000 minutes. Consultation length: 18.079 minutes.\n",
+ "2025-01-30 13:21:27,920 - INFO - logging.py:log():128 - Patient 2 arrives at: 51.963\n",
+ "2025-01-30 13:21:27,920 - INFO - logging.py:log():128 - Patient 2 is seen by nurse after 0.000 minutes. Consultation length: 3.102 minutes.\n",
+ "2025-01-30 13:21:27,920 - INFO - logging.py:log():128 - Patient 3 arrives at: 74.349\n",
+ "2025-01-30 13:21:27,920 - INFO - logging.py:log():128 - Patient 3 is seen by nurse after 0.000 minutes. Consultation length: 26.745 minutes.\n",
+ "2025-01-30 13:21:27,921 - INFO - logging.py:log():128 - Patient 4 arrives at: 77.534\n",
+ "2025-01-30 13:21:27,921 - INFO - logging.py:log():128 - Patient 4 is seen by nurse after 0.000 minutes. Consultation length: 0.748 minutes.\n",
+ "2025-01-30 13:21:27,921 - INFO - logging.py:log():128 - Patient 5 arrives at: 78.932\n",
+ "2025-01-30 13:21:27,921 - INFO - logging.py:log():128 - Patient 5 is seen by nurse after 0.000 minutes. Consultation length: 0.528 minutes.\n",
+ "2025-01-30 13:21:27,921 - INFO - logging.py:log():128 - Patient 6 arrives at: 86.815\n",
+ "2025-01-30 13:21:27,922 - INFO - logging.py:log():128 - Patient 6 is seen by nurse after 0.000 minutes. Consultation length: 2.435 minutes.\n",
+ "2025-01-30 13:21:27,922 - INFO - logging.py:log():128 - Patient 7 arrives at: 89.783\n",
+ "2025-01-30 13:21:27,922 - INFO - logging.py:log():128 - Patient 7 is seen by nurse after 0.000 minutes. Consultation length: 9.666 minutes.\n",
+ "2025-01-30 13:21:27,923 - INFO - logging.py:log():128 - Patient 8 arrives at: 89.807\n",
+ "2025-01-30 13:21:27,923 - INFO - logging.py:log():128 - Patient 8 is seen by nurse after 0.000 minutes. Consultation length: 7.005 minutes.\n",
+ "2025-01-30 13:21:27,923 - INFO - logging.py:log():128 - Patient 9 arrives at: 93.118\n",
+ "2025-01-30 13:21:27,923 - INFO - logging.py:log():128 - Patient 9 is seen by nurse after 0.000 minutes. Consultation length: 20.185 minutes.\n",
+ "2025-01-30 13:21:27,924 - INFO - logging.py:log():128 - Patient 10 arrives at: 95.598\n",
+ "2025-01-30 13:21:27,924 - INFO - logging.py:log():128 - Patient 10 is seen by nurse after 0.000 minutes. Consultation length: 7.651 minutes.\n",
+ "2025-01-30 13:21:27,924 - INFO - logging.py:log():128 - Patient 11 arrives at: 98.019\n",
+ "2025-01-30 13:21:27,924 - INFO - logging.py:log():128 - Patient 11 is seen by nurse after 0.000 minutes. Consultation length: 27.908 minutes.\n",
+ "2025-01-30 13:21:27,925 - INFO - logging.py:log():128 - Patient 12 arrives at: 109.379\n",
+ "2025-01-30 13:21:27,925 - INFO - logging.py:log():128 - Patient 12 is seen by nurse after 0.000 minutes. Consultation length: 16.811 minutes.\n",
+ "2025-01-30 13:21:27,925 - INFO - logging.py:log():128 - Patient 13 arrives at: 110.322\n",
+ "2025-01-30 13:21:27,925 - INFO - logging.py:log():128 - Patient 13 is seen by nurse after 0.000 minutes. Consultation length: 24.157 minutes.\n",
+ "2025-01-30 13:21:27,925 - INFO - logging.py:log():128 - Patient 14 arrives at: 120.342\n",
+ "2025-01-30 13:21:27,926 - INFO - logging.py:log():128 - Patient 14 is seen by nurse after 0.000 minutes. Consultation length: 1.451 minutes.\n",
+ "2025-01-30 13:21:27,926 - INFO - logging.py:log():128 - Patient 15 arrives at: 121.643\n",
+ "2025-01-30 13:21:27,926 - INFO - logging.py:log():128 - Patient 15 is seen by nurse after 0.000 minutes. Consultation length: 1.343 minutes.\n",
+ "2025-01-30 13:21:27,926 - INFO - logging.py:log():128 - Patient 16 arrives at: 127.827\n",
+ "2025-01-30 13:21:27,927 - INFO - logging.py:log():128 - Patient 16 is seen by nurse after 0.000 minutes. Consultation length: 1.554 minutes.\n",
+ "2025-01-30 13:21:27,927 - INFO - logging.py:log():128 - Patient 17 arrives at: 132.055\n",
+ "2025-01-30 13:21:27,927 - INFO - logging.py:log():128 - Patient 17 is seen by nurse after 0.000 minutes. Consultation length: 15.623 minutes.\n",
+ "2025-01-30 13:21:27,927 - INFO - logging.py:log():128 - Patient 18 arrives at: 133.617\n",
+ "2025-01-30 13:21:27,927 - INFO - logging.py:log():128 - Patient 18 is seen by nurse after 0.000 minutes. Consultation length: 4.688 minutes.\n",
+ "2025-01-30 13:21:27,927 - INFO - logging.py:log():128 - Patient 19 arrives at: 136.346\n",
+ "2025-01-30 13:21:27,928 - INFO - logging.py:log():128 - Patient 19 is seen by nurse after 0.000 minutes. Consultation length: 2.612 minutes.\n",
+ "2025-01-30 13:21:27,928 - INFO - logging.py:log():128 - Patient 20 arrives at: 144.142\n",
+ "2025-01-30 13:21:27,928 - INFO - logging.py:log():128 - Patient 20 is seen by nurse after 0.000 minutes. Consultation length: 6.348 minutes.\n"
]
}
],
@@ -1646,7 +2780,7 @@
},
{
"cell_type": "code",
- "execution_count": 29,
+ "execution_count": 28,
"metadata": {},
"outputs": [
{
@@ -1734,14 +2868,13 @@
" 'time_with_nurse': 6.348067933102151}]"
]
},
- "execution_count": 29,
"metadata": {},
- "output_type": "execute_result"
+ "output_type": "display_data"
}
],
"source": [
"# Compare to patient-level results\n",
- "model.results_list"
+ "display(model.results_list)"
]
},
{
@@ -1753,14 +2886,14 @@
},
{
"cell_type": "code",
- "execution_count": 30,
+ "execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Notebook run time: 0m 27s\n"
+ "Notebook run time: 0m 28s\n"
]
}
],
@@ -1790,7 +2923,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.13.0"
+ "version": "3.13.1"
}
},
"nbformat": 4,
diff --git a/notebooks/choosing_parameters.ipynb b/notebooks/choosing_parameters.ipynb
index 23e89c6..0f912de 100644
--- a/notebooks/choosing_parameters.ipynb
+++ b/notebooks/choosing_parameters.ipynb
@@ -31,27 +31,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Set-up\n",
- "\n",
- "Load notebook linters."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "%load_ext pycodestyle_magic"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "%pycodestyle_on"
+ "## Set-up"
]
},
{
@@ -63,10 +43,11 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
+ "# pylint: disable=missing-module-docstring\n",
"# To ensure any updates to `simulation/` are fetched without needing to restart\n",
"# the notebook environment, reload `simulation/` before execution of each cell\n",
"%load_ext autoreload\n",
@@ -76,21 +57,23 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
- "from simulation.model import Defaults, Runner, summary_stats\n",
+ "# pylint: disable=wrong-import-position\n",
+ "import os\n",
+ "import time\n",
+ "import warnings\n",
"\n",
"from IPython.display import display\n",
- "import os\n",
"import pandas as pd\n",
"import plotly.express as px\n",
"import plotly.io as pio\n",
"import plotly.graph_objects as go\n",
"import plotly.subplots as sp\n",
- "import time\n",
- "import warnings"
+ "\n",
+ "from simulation.model import Defaults, Runner, summary_stats"
]
},
{
@@ -102,7 +85,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@@ -118,7 +101,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@@ -134,11 +117,41 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "OUTPUT_DIR = '../outputs/'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Define labels for variables in the dataset."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
- "output_dir = '../outputs/'"
+ "LABELS = {\n",
+ " 'arrivals': 'Patient arrivals (n)',\n",
+ " 'mean_q_time_nurse': 'Mean wait time for nurse (minutes)',\n",
+ " 'mean_n_consult_time': 'Mean consultation time with nurse (minutes)',\n",
+ " 'mean_time_with_nurse': 'Mean consultation time with nurse (minutes)',\n",
+ " 'mean_nurse_utilisation': 'Mean nurse utilisation',\n",
+ " 'adj_mean_nurse_utilisation': 'Mean nurse utilisation (*100 - %)',\n",
+ " 'adj_mean_q_time_nurse': 'Mean wait time for nurse (*100) (minutes)',\n",
+ " 'mean_nurse_utilisation_tw': 'Time-weighted mean nurse utilisation',\n",
+ " 'mean_nurse_q_length': 'Time-weighted mean queue length for nurse (n)',\n",
+ " 'patient_inter': 'Patient inter-arrival time',\n",
+ " 'number_of_nurses': 'Number of nurses',\n",
+ " 'utilisation': 'Utilisation',\n",
+ " 'running_mean_wait_time': 'Running mean nurse wait time (minutes)'\n",
+ "}"
]
},
{
@@ -156,11 +169,13 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
- "def time_series_inspection(file, data_collection_period, warm_up=None):\n",
+ "def time_series_inspection(\n",
+ " file, data_collection_period, warm_up=None, path=OUTPUT_DIR, labels=None\n",
+ "):\n",
" \"\"\"\n",
" Time series inspection method for determining length of warm-up.\n",
"\n",
@@ -172,6 +187,11 @@
" warm_up (float, optional):\n",
" Location on X axis to plot vertical red line indicating the chosen\n",
" warm-up period. Defaults to None, which will not plot a line.\n",
+ " path (str):\n",
+ " Path to save file to (exc. filename)\n",
+ " labels (dict):\n",
+ " Contains mappings from variable names to full labels. If none\n",
+ " provided, will default to using variable names.\n",
" \"\"\"\n",
" # Use default parameters, but with no warm-up and specified run length,\n",
" # and with no replications\n",
@@ -190,16 +210,13 @@
" choose_warmup.interval_audit_df['resource_name'] == 'nurse']\n",
"\n",
" # Define columns to analyse\n",
- " plot = {\n",
- " 'utilisation': 'Cumulative mean nurse utilisation',\n",
- " 'running_mean_wait_time': 'Running mean nurse wait time'\n",
- " }\n",
+ " plot = ['utilisation', 'running_mean_wait_time']\n",
"\n",
" # Create 1x2 subplot\n",
" full_figure = sp.make_subplots(rows=2, cols=1, shared_xaxes=True)\n",
"\n",
- " i = 1\n",
- " for var, label in plot.items():\n",
+ " counter = 1\n",
+ " for var in plot:\n",
" # Reformat so index is simulation time and columns are each run\n",
" reformat = (\n",
" nurse[['simulation_time', var, 'run']]\n",
@@ -213,6 +230,9 @@
" cumulative = reformat.expanding().mean()\n",
" elif var == 'running_mean_wait_time':\n",
" cumulative = reformat.copy()\n",
+ " else:\n",
+ " print('Expected var to be utilisation or running_mean_wait_time.')\n",
+ " break\n",
"\n",
" # Create plot (using go.Scatter instead of px.express, for sub-plots)\n",
" full_figure.add_trace(\n",
@@ -220,13 +240,14 @@
" x=cumulative.index,\n",
" y=cumulative[0],\n",
" mode='lines',\n",
- " line=dict(color='royalblue')\n",
+ " line={'color': 'royalblue'}\n",
" ),\n",
- " row=i, col=1\n",
+ " row=counter, col=1\n",
" )\n",
"\n",
" # Add y axis label\n",
- " full_figure.update_yaxes(title_text=label, row=i, col=1)\n",
+ " full_figure.update_yaxes(title_text=labels.get(var, var),\n",
+ " row=counter, col=1)\n",
"\n",
" # Add vertical line for warm-up period specified\n",
" if warm_up is not None:\n",
@@ -235,7 +256,7 @@
" annotation_text='Suggested warm-up length',\n",
" annotation_position='top left',\n",
" annotation_font_color='red')\n",
- " i += 1\n",
+ " counter += 1\n",
"\n",
" # Add x axis title\n",
" full_figure.update_xaxes(title_text='Run time (minutes)')\n",
@@ -252,7 +273,7 @@
" full_figure.show()\n",
"\n",
" # Save figure\n",
- " full_figure.write_image(os.path.join(output_dir, file))"
+ " full_figure.write_image(os.path.join(path, file))"
]
},
{
@@ -264,13 +285,13 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -278,8 +299,10 @@
}
],
"source": [
- "time_series_inspection(file='choose_param_time_series_1.png',\n",
- " data_collection_period=1440*3, warm_up=2520)"
+ "time_series_inspection(\n",
+ " file='choose_param_time_series_1.png',\n",
+ " data_collection_period=1440*3, warm_up=2520,\n",
+ " labels=LABELS)"
]
},
{
@@ -291,13 +314,13 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -305,8 +328,10 @@
}
],
"source": [
- "time_series_inspection(file='choose_param_time_series_2.png',\n",
- " data_collection_period=1440*40, warm_up=1440*13)"
+ "time_series_inspection(\n",
+ " file='choose_param_time_series_2.png',\n",
+ " data_collection_period=1440*40, warm_up=1440*13,\n",
+ " labels=LABELS)"
]
},
{
@@ -326,12 +351,15 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
- "def confidence_interval_method(file, replications, metric, desired_precision,\n",
- " yaxis_title, min_rep=None):\n",
+ "# pylint: disable=too-many-arguments,too-many-positional-arguments\n",
+ "def confidence_interval_method(\n",
+ " file, replications, metric, desired_precision,\n",
+ " min_rep=None, path=OUTPUT_DIR, labels=None\n",
+ "):\n",
" \"\"\"\n",
" Use the confidence interval method to select the number of replications.\n",
"\n",
@@ -344,10 +372,14 @@
" Name of performance metric to assess.\n",
" desired_precision (float):\n",
" Desired mean deviation from confidence interval.\n",
- " yaxis_title (str):\n",
- " Label for y axis.\n",
" min_rep (int):\n",
- " A suggested minimum number of replications.\n",
+ " A suggested minimum number of replications, using to draw vertical\n",
+ " line on plot. If none provided, will not add a line.\n",
+ " path (str):\n",
+ " Path to save file to (exc. filename).\n",
+ " labels (dict):\n",
+ " Contains mappings from variable names to full labels. If none\n",
+ " provided, will default to using variable names.\n",
" \"\"\"\n",
" param = Defaults()\n",
" param.number_of_runs = replications\n",
@@ -355,29 +387,26 @@
" choose_rep.run_reps()\n",
"\n",
" # If mean of metric is less than 1, multiply by 100\n",
- " if choose_rep.run_results_df[metric].mean() < 1:\n",
- " choose_rep.run_results_df[f'adj_{metric}'] = (\n",
- " choose_rep.run_results_df[metric]*100)\n",
+ " df = choose_rep.run_results_df\n",
+ " if df[metric].mean() < 1:\n",
+ " df[f'adj_{metric}'] = df[metric] * 100\n",
" metric = f'adj_{metric}'\n",
"\n",
- " # Initialise list to store the results\n",
- " cumulative_list = []\n",
- "\n",
- " # For each row in the dataframe, filter to rows up to the i-th replication\n",
- " # then perform calculations\n",
- " for i in range(1, replications+1):\n",
- " mean, std_dev, ci_lower, ci_upper = summary_stats(\n",
- " choose_rep.run_results_df[metric].iloc[:i])\n",
- " deviation = ((ci_upper-mean)/mean)*100\n",
- " cumulative_list.append({\n",
+ " # Compute cumulative statistics\n",
+ " cumulative = pd.DataFrame([\n",
+ " {\n",
" 'replications': i,\n",
- " 'cumulative_mean': mean,\n",
- " 'cumulative_std': std_dev,\n",
- " 'lower_ci': ci_lower,\n",
- " 'upper_ci': ci_upper,\n",
- " 'perc_deviation': deviation\n",
- " })\n",
- " cumulative = pd.DataFrame(cumulative_list)\n",
+ " 'cumulative_mean': stats[0],\n",
+ " 'cumulative_std': stats[1],\n",
+ " 'lower_ci': stats[2],\n",
+ " 'upper_ci': stats[3],\n",
+ " 'perc_deviation': ((stats[3] - stats[0]) / stats[0]) * 100\n",
+ " }\n",
+ " for i, stats in enumerate(\n",
+ " (summary_stats(df[metric].iloc[:i])\n",
+ " for i in range(1, replications + 1))\n",
+ " )\n",
+ " ])\n",
" display(cumulative)\n",
"\n",
" # Get minimum number of replications where deviation is less than target\n",
@@ -391,27 +420,27 @@
" f'desired precision ({desired_precision}).')\n",
"\n",
" # Plot the cumulative mean and confidence interval\n",
- " fig = px.line(cumulative,\n",
- " x='replications',\n",
- " y=['cumulative_mean', 'lower_ci', 'upper_ci'])\n",
- " fig.update_layout(\n",
+ " figure = px.line(cumulative,\n",
+ " x='replications',\n",
+ " y=['cumulative_mean', 'lower_ci', 'upper_ci'])\n",
+ " figure.update_layout(\n",
" xaxis_title='Number of replications',\n",
- " yaxis_title=yaxis_title,\n",
+ " yaxis_title=labels.get(metric, metric),\n",
" template='plotly_white'\n",
" )\n",
" if min_rep is not None:\n",
- " fig.add_vline(x=min_rep, line_color='red', line_dash='dash')\n",
+ " figure.add_vline(x=min_rep, line_color='red', line_dash='dash')\n",
"\n",
" # Show figure\n",
- " fig.show()\n",
+ " figure.show()\n",
"\n",
" # Save figure\n",
- " fig.write_image(os.path.join(output_dir, file))"
+ " figure.write_image(os.path.join(path, file))"
]
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 11,
"metadata": {},
"outputs": [
{
@@ -446,7 +475,7 @@
" \n",
" \n",
" | 0 | \n",
- " 1 | \n",
+ " 0 | \n",
" 9.842268 | \n",
" NaN | \n",
" NaN | \n",
@@ -455,7 +484,7 @@
"
\n",
" \n",
" | 1 | \n",
- " 2 | \n",
+ " 1 | \n",
" 9.951374 | \n",
" 0.154300 | \n",
" 8.565044 | \n",
@@ -464,7 +493,7 @@
"
\n",
" \n",
" | 2 | \n",
- " 3 | \n",
+ " 2 | \n",
" 9.942591 | \n",
" 0.110162 | \n",
" 9.668933 | \n",
@@ -473,7 +502,7 @@
"
\n",
" \n",
" | 3 | \n",
- " 4 | \n",
+ " 3 | \n",
" 9.941208 | \n",
" 0.089990 | \n",
" 9.798014 | \n",
@@ -482,7 +511,7 @@
"
\n",
" \n",
" | 4 | \n",
- " 5 | \n",
+ " 4 | \n",
" 9.956147 | \n",
" 0.084791 | \n",
" 9.850865 | \n",
@@ -491,7 +520,7 @@
"
\n",
" \n",
" | 5 | \n",
- " 6 | \n",
+ " 5 | \n",
" 9.944288 | \n",
" 0.081212 | \n",
" 9.859062 | \n",
@@ -500,7 +529,7 @@
"
\n",
" \n",
" | 6 | \n",
- " 7 | \n",
+ " 6 | \n",
" 9.958219 | \n",
" 0.082792 | \n",
" 9.881649 | \n",
@@ -509,7 +538,7 @@
"
\n",
" \n",
" | 7 | \n",
- " 8 | \n",
+ " 7 | \n",
" 9.974314 | \n",
" 0.089150 | \n",
" 9.899783 | \n",
@@ -518,7 +547,7 @@
"
\n",
" \n",
" | 8 | \n",
- " 9 | \n",
+ " 8 | \n",
" 9.999642 | \n",
" 0.112818 | \n",
" 9.912922 | \n",
@@ -527,7 +556,7 @@
"
\n",
" \n",
" | 9 | \n",
- " 10 | \n",
+ " 9 | \n",
" 10.008938 | \n",
" 0.110354 | \n",
" 9.929996 | \n",
@@ -536,7 +565,7 @@
"
\n",
" \n",
" | 10 | \n",
- " 11 | \n",
+ " 10 | \n",
" 10.015721 | \n",
" 0.107081 | \n",
" 9.943784 | \n",
@@ -545,7 +574,7 @@
"
\n",
" \n",
" | 11 | \n",
- " 12 | \n",
+ " 11 | \n",
" 10.005522 | \n",
" 0.108038 | \n",
" 9.936879 | \n",
@@ -554,7 +583,7 @@
"
\n",
" \n",
" | 12 | \n",
- " 13 | \n",
+ " 12 | \n",
" 9.990552 | \n",
" 0.116675 | \n",
" 9.920046 | \n",
@@ -563,7 +592,7 @@
"
\n",
" \n",
" | 13 | \n",
- " 14 | \n",
+ " 13 | \n",
" 9.986504 | \n",
" 0.113116 | \n",
" 9.921193 | \n",
@@ -572,7 +601,7 @@
"
\n",
" \n",
" | 14 | \n",
- " 15 | \n",
+ " 14 | \n",
" 9.998583 | \n",
" 0.118616 | \n",
" 9.932895 | \n",
@@ -581,7 +610,7 @@
"
\n",
" \n",
" | 15 | \n",
- " 16 | \n",
+ " 15 | \n",
" 9.997145 | \n",
" 0.114738 | \n",
" 9.936005 | \n",
@@ -590,7 +619,7 @@
"
\n",
" \n",
" | 16 | \n",
- " 17 | \n",
+ " 16 | \n",
" 9.986125 | \n",
" 0.120027 | \n",
" 9.924413 | \n",
@@ -599,7 +628,7 @@
"
\n",
" \n",
" | 17 | \n",
- " 18 | \n",
+ " 17 | \n",
" 9.986293 | \n",
" 0.116445 | \n",
" 9.928386 | \n",
@@ -608,7 +637,7 @@
"
\n",
" \n",
" | 18 | \n",
- " 19 | \n",
+ " 18 | \n",
" 9.976498 | \n",
" 0.120951 | \n",
" 9.918201 | \n",
@@ -617,7 +646,7 @@
"
\n",
" \n",
" | 19 | \n",
- " 20 | \n",
+ " 19 | \n",
" 9.973643 | \n",
" 0.118416 | \n",
" 9.918222 | \n",
@@ -630,26 +659,26 @@
],
"text/plain": [
" replications cumulative_mean cumulative_std lower_ci upper_ci \\\n",
- "0 1 9.842268 NaN NaN NaN \n",
- "1 2 9.951374 0.154300 8.565044 11.337705 \n",
- "2 3 9.942591 0.110162 9.668933 10.216249 \n",
- "3 4 9.941208 0.089990 9.798014 10.084401 \n",
- "4 5 9.956147 0.084791 9.850865 10.061429 \n",
- "5 6 9.944288 0.081212 9.859062 10.029515 \n",
- "6 7 9.958219 0.082792 9.881649 10.034788 \n",
- "7 8 9.974314 0.089150 9.899783 10.048845 \n",
- "8 9 9.999642 0.112818 9.912922 10.086362 \n",
- "9 10 10.008938 0.110354 9.929996 10.087880 \n",
- "10 11 10.015721 0.107081 9.943784 10.087659 \n",
- "11 12 10.005522 0.108038 9.936879 10.074166 \n",
- "12 13 9.990552 0.116675 9.920046 10.061058 \n",
- "13 14 9.986504 0.113116 9.921193 10.051815 \n",
- "14 15 9.998583 0.118616 9.932895 10.064270 \n",
- "15 16 9.997145 0.114738 9.936005 10.058284 \n",
- "16 17 9.986125 0.120027 9.924413 10.047837 \n",
- "17 18 9.986293 0.116445 9.928386 10.044200 \n",
- "18 19 9.976498 0.120951 9.918201 10.034795 \n",
- "19 20 9.973643 0.118416 9.918222 10.029063 \n",
+ "0 0 9.842268 NaN NaN NaN \n",
+ "1 1 9.951374 0.154300 8.565044 11.337705 \n",
+ "2 2 9.942591 0.110162 9.668933 10.216249 \n",
+ "3 3 9.941208 0.089990 9.798014 10.084401 \n",
+ "4 4 9.956147 0.084791 9.850865 10.061429 \n",
+ "5 5 9.944288 0.081212 9.859062 10.029515 \n",
+ "6 6 9.958219 0.082792 9.881649 10.034788 \n",
+ "7 7 9.974314 0.089150 9.899783 10.048845 \n",
+ "8 8 9.999642 0.112818 9.912922 10.086362 \n",
+ "9 9 10.008938 0.110354 9.929996 10.087880 \n",
+ "10 10 10.015721 0.107081 9.943784 10.087659 \n",
+ "11 11 10.005522 0.108038 9.936879 10.074166 \n",
+ "12 12 9.990552 0.116675 9.920046 10.061058 \n",
+ "13 13 9.986504 0.113116 9.921193 10.051815 \n",
+ "14 14 9.998583 0.118616 9.932895 10.064270 \n",
+ "15 15 9.997145 0.114738 9.936005 10.058284 \n",
+ "16 16 9.986125 0.120027 9.924413 10.047837 \n",
+ "17 17 9.986293 0.116445 9.928386 10.044200 \n",
+ "18 18 9.976498 0.120951 9.918201 10.034795 \n",
+ "19 19 9.973643 0.118416 9.918222 10.029063 \n",
"\n",
" perc_deviation \n",
"0 NaN \n",
@@ -687,7 +716,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -700,8 +729,8 @@
" replications=20,\n",
" metric='mean_time_with_nurse',\n",
" desired_precision=0.05,\n",
- " yaxis_title='Mean time with nurse',\n",
- " min_rep=3\n",
+ " min_rep=3,\n",
+ " labels=LABELS\n",
")"
]
},
@@ -714,7 +743,7 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 12,
"metadata": {},
"outputs": [
{
@@ -749,7 +778,7 @@
"
\n",
" \n",
" | 0 | \n",
- " 1 | \n",
+ " 0 | \n",
" 50.454108 | \n",
" NaN | \n",
" NaN | \n",
@@ -758,7 +787,7 @@
"
\n",
" \n",
" | 1 | \n",
- " 2 | \n",
+ " 1 | \n",
" 50.934587 | \n",
" 0.679499 | \n",
" 44.829530 | \n",
@@ -767,7 +796,7 @@
"
\n",
" \n",
" | 2 | \n",
- " 3 | \n",
+ " 2 | \n",
" 51.397555 | \n",
" 0.934815 | \n",
" 49.075346 | \n",
@@ -776,7 +805,7 @@
"
\n",
" \n",
" | 3 | \n",
- " 4 | \n",
+ " 3 | \n",
" 50.526888 | \n",
" 1.901271 | \n",
" 47.501541 | \n",
@@ -785,7 +814,7 @@
"
\n",
" \n",
" | 4 | \n",
- " 5 | \n",
+ " 4 | \n",
" 49.650659 | \n",
" 2.559298 | \n",
" 46.472872 | \n",
@@ -794,7 +823,7 @@
"
\n",
" \n",
" | 5 | \n",
- " 6 | \n",
+ " 5 | \n",
" 47.846628 | \n",
" 4.976664 | \n",
" 42.623939 | \n",
@@ -803,7 +832,7 @@
"
\n",
" \n",
" | 6 | \n",
- " 7 | \n",
+ " 6 | \n",
" 47.681940 | \n",
" 4.563900 | \n",
" 43.461036 | \n",
@@ -812,7 +841,7 @@
"
\n",
" \n",
" | 7 | \n",
- " 8 | \n",
+ " 7 | \n",
" 49.545302 | \n",
" 6.755037 | \n",
" 43.897949 | \n",
@@ -821,7 +850,7 @@
"
\n",
" \n",
" | 8 | \n",
- " 9 | \n",
+ " 8 | \n",
" 49.245792 | \n",
" 6.382325 | \n",
" 44.339903 | \n",
@@ -830,7 +859,7 @@
"
\n",
" \n",
" | 9 | \n",
- " 10 | \n",
+ " 9 | \n",
" 49.955562 | \n",
" 6.422290 | \n",
" 45.361333 | \n",
@@ -839,7 +868,7 @@
"
\n",
" \n",
" | 10 | \n",
- " 11 | \n",
+ " 10 | \n",
" 50.528566 | \n",
" 6.382232 | \n",
" 46.240924 | \n",
@@ -848,7 +877,7 @@
"
\n",
" \n",
" | 11 | \n",
- " 12 | \n",
+ " 11 | \n",
" 49.632950 | \n",
" 6.830477 | \n",
" 45.293072 | \n",
@@ -857,7 +886,7 @@
"
\n",
" \n",
" | 12 | \n",
- " 13 | \n",
+ " 12 | \n",
" 49.240444 | \n",
" 6.691058 | \n",
" 45.197078 | \n",
@@ -866,7 +895,7 @@
"
\n",
" \n",
" | 13 | \n",
- " 14 | \n",
+ " 13 | \n",
" 48.864416 | \n",
" 6.580726 | \n",
" 45.064818 | \n",
@@ -875,7 +904,7 @@
"
\n",
" \n",
" | 14 | \n",
- " 15 | \n",
+ " 14 | \n",
" 49.678670 | \n",
" 7.082218 | \n",
" 45.756668 | \n",
@@ -884,7 +913,7 @@
"
\n",
" \n",
" | 15 | \n",
- " 16 | \n",
+ " 15 | \n",
" 49.553191 | \n",
" 6.860458 | \n",
" 45.897511 | \n",
@@ -893,7 +922,7 @@
"
\n",
" \n",
" | 16 | \n",
- " 17 | \n",
+ " 16 | \n",
" 49.157922 | \n",
" 6.839612 | \n",
" 45.641318 | \n",
@@ -902,7 +931,7 @@
"
\n",
" \n",
" | 17 | \n",
- " 18 | \n",
+ " 17 | \n",
" 49.235673 | \n",
" 6.643593 | \n",
" 45.931892 | \n",
@@ -911,7 +940,7 @@
"
\n",
" \n",
" | 18 | \n",
- " 19 | \n",
+ " 18 | \n",
" 48.547386 | \n",
" 7.119432 | \n",
" 45.115930 | \n",
@@ -920,7 +949,7 @@
"
\n",
" \n",
" | 19 | \n",
- " 20 | \n",
+ " 19 | \n",
" 48.215812 | \n",
" 7.086427 | \n",
" 44.899262 | \n",
@@ -929,7 +958,7 @@
"
\n",
" \n",
" | 20 | \n",
- " 21 | \n",
+ " 20 | \n",
" 48.834174 | \n",
" 7.465680 | \n",
" 45.435837 | \n",
@@ -938,7 +967,7 @@
"
\n",
" \n",
" | 21 | \n",
- " 22 | \n",
+ " 21 | \n",
" 48.965899 | \n",
" 7.311908 | \n",
" 45.723980 | \n",
@@ -947,7 +976,7 @@
"
\n",
" \n",
" | 22 | \n",
- " 23 | \n",
+ " 22 | \n",
" 49.148498 | \n",
" 7.197270 | \n",
" 46.036165 | \n",
@@ -956,7 +985,7 @@
"
\n",
" \n",
" | 23 | \n",
- " 24 | \n",
+ " 23 | \n",
" 49.296704 | \n",
" 7.076415 | \n",
" 46.308596 | \n",
@@ -965,7 +994,7 @@
"
\n",
" \n",
" | 24 | \n",
- " 25 | \n",
+ " 24 | \n",
" 49.336715 | \n",
" 6.930310 | \n",
" 46.476024 | \n",
@@ -974,7 +1003,7 @@
"
\n",
" \n",
" | 25 | \n",
- " 26 | \n",
+ " 25 | \n",
" 49.850937 | \n",
" 7.278945 | \n",
" 46.910907 | \n",
@@ -983,7 +1012,7 @@
"
\n",
" \n",
" | 26 | \n",
- " 27 | \n",
+ " 26 | \n",
" 49.879344 | \n",
" 7.139119 | \n",
" 47.055203 | \n",
@@ -992,7 +1021,7 @@
"
\n",
" \n",
" | 27 | \n",
- " 28 | \n",
+ " 27 | \n",
" 49.857060 | \n",
" 7.006658 | \n",
" 47.140161 | \n",
@@ -1001,7 +1030,7 @@
"
\n",
" \n",
" | 28 | \n",
- " 29 | \n",
+ " 28 | \n",
" 49.716192 | \n",
" 6.922094 | \n",
" 47.083168 | \n",
@@ -1010,7 +1039,7 @@
"
\n",
" \n",
" | 29 | \n",
- " 30 | \n",
+ " 29 | \n",
" 49.866454 | \n",
" 6.851314 | \n",
" 47.308131 | \n",
@@ -1019,7 +1048,7 @@
"
\n",
" \n",
" | 30 | \n",
- " 31 | \n",
+ " 30 | \n",
" 49.903691 | \n",
" 6.739347 | \n",
" 47.431678 | \n",
@@ -1028,7 +1057,7 @@
"
\n",
" \n",
" | 31 | \n",
- " 32 | \n",
+ " 31 | \n",
" 49.850411 | \n",
" 6.636604 | \n",
" 47.457660 | \n",
@@ -1037,7 +1066,7 @@
"
\n",
" \n",
" | 32 | \n",
- " 33 | \n",
+ " 32 | \n",
" 49.955695 | \n",
" 6.560024 | \n",
" 47.629612 | \n",
@@ -1046,7 +1075,7 @@
"
\n",
" \n",
" | 33 | \n",
- " 34 | \n",
+ " 33 | \n",
" 50.078240 | \n",
" 6.499265 | \n",
" 47.810540 | \n",
@@ -1055,7 +1084,7 @@
"
\n",
" \n",
" | 34 | \n",
- " 35 | \n",
+ " 34 | \n",
" 50.002865 | \n",
" 6.418484 | \n",
" 47.798038 | \n",
@@ -1064,7 +1093,7 @@
"
\n",
" \n",
" | 35 | \n",
- " 36 | \n",
+ " 35 | \n",
" 49.888926 | \n",
" 6.362958 | \n",
" 47.736010 | \n",
@@ -1073,7 +1102,7 @@
"
\n",
" \n",
" | 36 | \n",
- " 37 | \n",
+ " 36 | \n",
" 50.308907 | \n",
" 6.774128 | \n",
" 48.050300 | \n",
@@ -1082,7 +1111,7 @@
"
\n",
" \n",
" | 37 | \n",
- " 38 | \n",
+ " 37 | \n",
" 50.617933 | \n",
" 6.948198 | \n",
" 48.334117 | \n",
@@ -1091,7 +1120,7 @@
"
\n",
" \n",
" | 38 | \n",
- " 39 | \n",
+ " 38 | \n",
" 50.726526 | \n",
" 6.889623 | \n",
" 48.493168 | \n",
@@ -1100,7 +1129,7 @@
"
\n",
" \n",
" | 39 | \n",
- " 40 | \n",
+ " 39 | \n",
" 51.107307 | \n",
" 7.214539 | \n",
" 48.799985 | \n",
@@ -1109,7 +1138,7 @@
"
\n",
" \n",
" | 40 | \n",
- " 41 | \n",
+ " 40 | \n",
" 51.253305 | \n",
" 7.184864 | \n",
" 48.985482 | \n",
@@ -1118,7 +1147,7 @@
"
\n",
" \n",
" | 41 | \n",
- " 42 | \n",
+ " 41 | \n",
" 51.158101 | \n",
" 7.123473 | \n",
" 48.938271 | \n",
@@ -1127,7 +1156,7 @@
"
\n",
" \n",
" | 42 | \n",
- " 43 | \n",
+ " 42 | \n",
" 51.309010 | \n",
" 7.107386 | \n",
" 49.121680 | \n",
@@ -1136,7 +1165,7 @@
"
\n",
" \n",
" | 43 | \n",
- " 44 | \n",
+ " 43 | \n",
" 51.432981 | \n",
" 7.072227 | \n",
" 49.282828 | \n",
@@ -1145,7 +1174,7 @@
"
\n",
" \n",
" | 44 | \n",
- " 45 | \n",
+ " 44 | \n",
" 51.762158 | \n",
" 7.331830 | \n",
" 49.559432 | \n",
@@ -1154,7 +1183,7 @@
"
\n",
" \n",
" | 45 | \n",
- " 46 | \n",
+ " 45 | \n",
" 52.120721 | \n",
" 7.646912 | \n",
" 49.849868 | \n",
@@ -1163,7 +1192,7 @@
"
\n",
" \n",
" | 46 | \n",
- " 47 | \n",
+ " 46 | \n",
" 51.999456 | \n",
" 7.608890 | \n",
" 49.765402 | \n",
@@ -1172,7 +1201,7 @@
"
\n",
" \n",
" | 47 | \n",
- " 48 | \n",
+ " 47 | \n",
" 52.091068 | \n",
" 7.554220 | \n",
" 49.897551 | \n",
@@ -1181,7 +1210,7 @@
"
\n",
" \n",
" | 48 | \n",
- " 49 | \n",
+ " 48 | \n",
" 52.011852 | \n",
" 7.495655 | \n",
" 49.858849 | \n",
@@ -1190,7 +1219,7 @@
"
\n",
" \n",
" | 49 | \n",
- " 50 | \n",
+ " 49 | \n",
" 51.955719 | \n",
" 7.429385 | \n",
" 49.844311 | \n",
@@ -1203,56 +1232,56 @@
],
"text/plain": [
" replications cumulative_mean cumulative_std lower_ci upper_ci \\\n",
- "0 1 50.454108 NaN NaN NaN \n",
- "1 2 50.934587 0.679499 44.829530 57.039643 \n",
- "2 3 51.397555 0.934815 49.075346 53.719764 \n",
- "3 4 50.526888 1.901271 47.501541 53.552234 \n",
- "4 5 49.650659 2.559298 46.472872 52.828447 \n",
- "5 6 47.846628 4.976664 42.623939 53.069317 \n",
- "6 7 47.681940 4.563900 43.461036 51.902843 \n",
- "7 8 49.545302 6.755037 43.897949 55.192654 \n",
- "8 9 49.245792 6.382325 44.339903 54.151681 \n",
- "9 10 49.955562 6.422290 45.361333 54.549792 \n",
- "10 11 50.528566 6.382232 46.240924 54.816207 \n",
- "11 12 49.632950 6.830477 45.293072 53.972828 \n",
- "12 13 49.240444 6.691058 45.197078 53.283810 \n",
- "13 14 48.864416 6.580726 45.064818 52.664014 \n",
- "14 15 49.678670 7.082218 45.756668 53.600672 \n",
- "15 16 49.553191 6.860458 45.897511 53.208871 \n",
- "16 17 49.157922 6.839612 45.641318 52.674526 \n",
- "17 18 49.235673 6.643593 45.931892 52.539455 \n",
- "18 19 48.547386 7.119432 45.115930 51.978842 \n",
- "19 20 48.215812 7.086427 44.899262 51.532361 \n",
- "20 21 48.834174 7.465680 45.435837 52.232511 \n",
- "21 22 48.965899 7.311908 45.723980 52.207817 \n",
- "22 23 49.148498 7.197270 46.036165 52.260830 \n",
- "23 24 49.296704 7.076415 46.308596 52.284812 \n",
- "24 25 49.336715 6.930310 46.476024 52.197406 \n",
- "25 26 49.850937 7.278945 46.910907 52.790966 \n",
- "26 27 49.879344 7.139119 47.055203 52.703485 \n",
- "27 28 49.857060 7.006658 47.140161 52.573958 \n",
- "28 29 49.716192 6.922094 47.083168 52.349216 \n",
- "29 30 49.866454 6.851314 47.308131 52.424776 \n",
- "30 31 49.903691 6.739347 47.431678 52.375703 \n",
- "31 32 49.850411 6.636604 47.457660 52.243162 \n",
- "32 33 49.955695 6.560024 47.629612 52.281778 \n",
- "33 34 50.078240 6.499265 47.810540 52.345941 \n",
- "34 35 50.002865 6.418484 47.798038 52.207691 \n",
- "35 36 49.888926 6.362958 47.736010 52.041841 \n",
- "36 37 50.308907 6.774128 48.050300 52.567514 \n",
- "37 38 50.617933 6.948198 48.334117 52.901749 \n",
- "38 39 50.726526 6.889623 48.493168 52.959883 \n",
- "39 40 51.107307 7.214539 48.799985 53.414629 \n",
- "40 41 51.253305 7.184864 48.985482 53.521127 \n",
- "41 42 51.158101 7.123473 48.938271 53.377932 \n",
- "42 43 51.309010 7.107386 49.121680 53.496341 \n",
- "43 44 51.432981 7.072227 49.282828 53.583135 \n",
- "44 45 51.762158 7.331830 49.559432 53.964883 \n",
- "45 46 52.120721 7.646912 49.849868 54.391573 \n",
- "46 47 51.999456 7.608890 49.765402 54.233510 \n",
- "47 48 52.091068 7.554220 49.897551 54.284585 \n",
- "48 49 52.011852 7.495655 49.858849 54.164856 \n",
- "49 50 51.955719 7.429385 49.844311 54.067127 \n",
+ "0 0 50.454108 NaN NaN NaN \n",
+ "1 1 50.934587 0.679499 44.829530 57.039643 \n",
+ "2 2 51.397555 0.934815 49.075346 53.719764 \n",
+ "3 3 50.526888 1.901271 47.501541 53.552234 \n",
+ "4 4 49.650659 2.559298 46.472872 52.828447 \n",
+ "5 5 47.846628 4.976664 42.623939 53.069317 \n",
+ "6 6 47.681940 4.563900 43.461036 51.902843 \n",
+ "7 7 49.545302 6.755037 43.897949 55.192654 \n",
+ "8 8 49.245792 6.382325 44.339903 54.151681 \n",
+ "9 9 49.955562 6.422290 45.361333 54.549792 \n",
+ "10 10 50.528566 6.382232 46.240924 54.816207 \n",
+ "11 11 49.632950 6.830477 45.293072 53.972828 \n",
+ "12 12 49.240444 6.691058 45.197078 53.283810 \n",
+ "13 13 48.864416 6.580726 45.064818 52.664014 \n",
+ "14 14 49.678670 7.082218 45.756668 53.600672 \n",
+ "15 15 49.553191 6.860458 45.897511 53.208871 \n",
+ "16 16 49.157922 6.839612 45.641318 52.674526 \n",
+ "17 17 49.235673 6.643593 45.931892 52.539455 \n",
+ "18 18 48.547386 7.119432 45.115930 51.978842 \n",
+ "19 19 48.215812 7.086427 44.899262 51.532361 \n",
+ "20 20 48.834174 7.465680 45.435837 52.232511 \n",
+ "21 21 48.965899 7.311908 45.723980 52.207817 \n",
+ "22 22 49.148498 7.197270 46.036165 52.260830 \n",
+ "23 23 49.296704 7.076415 46.308596 52.284812 \n",
+ "24 24 49.336715 6.930310 46.476024 52.197406 \n",
+ "25 25 49.850937 7.278945 46.910907 52.790966 \n",
+ "26 26 49.879344 7.139119 47.055203 52.703485 \n",
+ "27 27 49.857060 7.006658 47.140161 52.573958 \n",
+ "28 28 49.716192 6.922094 47.083168 52.349216 \n",
+ "29 29 49.866454 6.851314 47.308131 52.424776 \n",
+ "30 30 49.903691 6.739347 47.431678 52.375703 \n",
+ "31 31 49.850411 6.636604 47.457660 52.243162 \n",
+ "32 32 49.955695 6.560024 47.629612 52.281778 \n",
+ "33 33 50.078240 6.499265 47.810540 52.345941 \n",
+ "34 34 50.002865 6.418484 47.798038 52.207691 \n",
+ "35 35 49.888926 6.362958 47.736010 52.041841 \n",
+ "36 36 50.308907 6.774128 48.050300 52.567514 \n",
+ "37 37 50.617933 6.948198 48.334117 52.901749 \n",
+ "38 38 50.726526 6.889623 48.493168 52.959883 \n",
+ "39 39 51.107307 7.214539 48.799985 53.414629 \n",
+ "40 40 51.253305 7.184864 48.985482 53.521127 \n",
+ "41 41 51.158101 7.123473 48.938271 53.377932 \n",
+ "42 42 51.309010 7.107386 49.121680 53.496341 \n",
+ "43 43 51.432981 7.072227 49.282828 53.583135 \n",
+ "44 44 51.762158 7.331830 49.559432 53.964883 \n",
+ "45 45 52.120721 7.646912 49.849868 54.391573 \n",
+ "46 46 51.999456 7.608890 49.765402 54.233510 \n",
+ "47 47 52.091068 7.554220 49.897551 54.284585 \n",
+ "48 48 52.011852 7.495655 49.858849 54.164856 \n",
+ "49 49 51.955719 7.429385 49.844311 54.067127 \n",
"\n",
" perc_deviation \n",
"0 NaN \n",
@@ -1320,7 +1349,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -1333,14 +1362,14 @@
" replications=50,\n",
" metric='mean_q_time_nurse',\n",
" desired_precision=0.05,\n",
- " yaxis_title='Mean wait time for nurse',\n",
- " min_rep=31\n",
+ " min_rep=31,\n",
+ " labels=LABELS\n",
")"
]
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 13,
"metadata": {},
"outputs": [
{
@@ -1375,7 +1404,7 @@
"
\n",
" \n",
" | 0 | \n",
- " 1 | \n",
+ " 0 | \n",
" 49.963865 | \n",
" NaN | \n",
" NaN | \n",
@@ -1384,7 +1413,7 @@
"
\n",
" \n",
" | 1 | \n",
- " 2 | \n",
+ " 1 | \n",
" 50.081459 | \n",
" 0.166304 | \n",
" 48.587275 | \n",
@@ -1393,7 +1422,7 @@
"
\n",
" \n",
" | 2 | \n",
- " 3 | \n",
+ " 2 | \n",
" 49.991963 | \n",
" 0.194570 | \n",
" 49.508625 | \n",
@@ -1402,7 +1431,7 @@
"
\n",
" \n",
" | 3 | \n",
- " 4 | \n",
+ " 3 | \n",
" 49.949485 | \n",
" 0.180155 | \n",
" 49.662817 | \n",
@@ -1411,7 +1440,7 @@
"
\n",
" \n",
" | 4 | \n",
- " 5 | \n",
+ " 4 | \n",
" 49.896994 | \n",
" 0.195238 | \n",
" 49.654574 | \n",
@@ -1420,7 +1449,7 @@
"
\n",
" \n",
" | 5 | \n",
- " 6 | \n",
+ " 5 | \n",
" 49.795899 | \n",
" 0.303011 | \n",
" 49.477908 | \n",
@@ -1429,7 +1458,7 @@
"
\n",
" \n",
" | 6 | \n",
- " 7 | \n",
+ " 6 | \n",
" 49.873003 | \n",
" 0.343698 | \n",
" 49.555136 | \n",
@@ -1438,7 +1467,7 @@
"
\n",
" \n",
" | 7 | \n",
- " 8 | \n",
+ " 7 | \n",
" 49.931989 | \n",
" 0.359288 | \n",
" 49.631617 | \n",
@@ -1447,7 +1476,7 @@
"
\n",
" \n",
" | 8 | \n",
- " 9 | \n",
+ " 8 | \n",
" 50.037224 | \n",
" 0.461108 | \n",
" 49.682785 | \n",
@@ -1456,7 +1485,7 @@
"
\n",
" \n",
" | 9 | \n",
- " 10 | \n",
+ " 9 | \n",
" 50.033219 | \n",
" 0.434921 | \n",
" 49.722095 | \n",
@@ -1465,7 +1494,7 @@
"
\n",
" \n",
" | 10 | \n",
- " 11 | \n",
+ " 10 | \n",
" 50.119630 | \n",
" 0.502370 | \n",
" 49.782133 | \n",
@@ -1474,7 +1503,7 @@
"
\n",
" \n",
" | 11 | \n",
- " 12 | \n",
+ " 11 | \n",
" 50.022356 | \n",
" 0.585646 | \n",
" 49.650254 | \n",
@@ -1483,7 +1512,7 @@
"
\n",
" \n",
" | 12 | \n",
- " 13 | \n",
+ " 12 | \n",
" 50.047003 | \n",
" 0.567711 | \n",
" 49.703938 | \n",
@@ -1492,7 +1521,7 @@
"
\n",
" \n",
" | 13 | \n",
- " 14 | \n",
+ " 13 | \n",
" 49.966892 | \n",
" 0.622376 | \n",
" 49.607543 | \n",
@@ -1501,7 +1530,7 @@
"
\n",
" \n",
" | 14 | \n",
- " 15 | \n",
+ " 14 | \n",
" 50.056635 | \n",
" 0.693174 | \n",
" 49.672768 | \n",
@@ -1510,7 +1539,7 @@
"
\n",
" \n",
" | 15 | \n",
- " 16 | \n",
+ " 15 | \n",
" 50.059114 | \n",
" 0.669744 | \n",
" 49.702233 | \n",
@@ -1519,7 +1548,7 @@
"
\n",
" \n",
" | 16 | \n",
- " 17 | \n",
+ " 16 | \n",
" 49.977371 | \n",
" 0.730832 | \n",
" 49.601612 | \n",
@@ -1528,7 +1557,7 @@
"
\n",
" \n",
" | 17 | \n",
- " 18 | \n",
+ " 17 | \n",
" 49.953121 | \n",
" 0.716437 | \n",
" 49.596845 | \n",
@@ -1537,7 +1566,7 @@
"
\n",
" \n",
" | 18 | \n",
- " 19 | \n",
+ " 18 | \n",
" 49.847018 | \n",
" 0.835863 | \n",
" 49.444144 | \n",
@@ -1546,7 +1575,7 @@
"
\n",
" \n",
" | 19 | \n",
- " 20 | \n",
+ " 19 | \n",
" 49.812312 | \n",
" 0.828241 | \n",
" 49.424684 | \n",
@@ -1559,26 +1588,26 @@
],
"text/plain": [
" replications cumulative_mean cumulative_std lower_ci upper_ci \\\n",
- "0 1 49.963865 NaN NaN NaN \n",
- "1 2 50.081459 0.166304 48.587275 51.575644 \n",
- "2 3 49.991963 0.194570 49.508625 50.475301 \n",
- "3 4 49.949485 0.180155 49.662817 50.236152 \n",
- "4 5 49.896994 0.195238 49.654574 50.139415 \n",
- "5 6 49.795899 0.303011 49.477908 50.113890 \n",
- "6 7 49.873003 0.343698 49.555136 50.190870 \n",
- "7 8 49.931989 0.359288 49.631617 50.232361 \n",
- "8 9 50.037224 0.461108 49.682785 50.391663 \n",
- "9 10 50.033219 0.434921 49.722095 50.344343 \n",
- "10 11 50.119630 0.502370 49.782133 50.457127 \n",
- "11 12 50.022356 0.585646 49.650254 50.394457 \n",
- "12 13 50.047003 0.567711 49.703938 50.390067 \n",
- "13 14 49.966892 0.622376 49.607543 50.326242 \n",
- "14 15 50.056635 0.693174 49.672768 50.440503 \n",
- "15 16 50.059114 0.669744 49.702233 50.415996 \n",
- "16 17 49.977371 0.730832 49.601612 50.353130 \n",
- "17 18 49.953121 0.716437 49.596845 50.309397 \n",
- "18 19 49.847018 0.835863 49.444144 50.249891 \n",
- "19 20 49.812312 0.828241 49.424684 50.199941 \n",
+ "0 0 49.963865 NaN NaN NaN \n",
+ "1 1 50.081459 0.166304 48.587275 51.575644 \n",
+ "2 2 49.991963 0.194570 49.508625 50.475301 \n",
+ "3 3 49.949485 0.180155 49.662817 50.236152 \n",
+ "4 4 49.896994 0.195238 49.654574 50.139415 \n",
+ "5 5 49.795899 0.303011 49.477908 50.113890 \n",
+ "6 6 49.873003 0.343698 49.555136 50.190870 \n",
+ "7 7 49.931989 0.359288 49.631617 50.232361 \n",
+ "8 8 50.037224 0.461108 49.682785 50.391663 \n",
+ "9 9 50.033219 0.434921 49.722095 50.344343 \n",
+ "10 10 50.119630 0.502370 49.782133 50.457127 \n",
+ "11 11 50.022356 0.585646 49.650254 50.394457 \n",
+ "12 12 50.047003 0.567711 49.703938 50.390067 \n",
+ "13 13 49.966892 0.622376 49.607543 50.326242 \n",
+ "14 14 50.056635 0.693174 49.672768 50.440503 \n",
+ "15 15 50.059114 0.669744 49.702233 50.415996 \n",
+ "16 16 49.977371 0.730832 49.601612 50.353130 \n",
+ "17 17 49.953121 0.716437 49.596845 50.309397 \n",
+ "18 18 49.847018 0.835863 49.444144 50.249891 \n",
+ "19 19 49.812312 0.828241 49.424684 50.199941 \n",
"\n",
" perc_deviation \n",
"0 NaN \n",
@@ -1616,7 +1645,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -1629,8 +1658,8 @@
" replications=20,\n",
" metric='mean_nurse_utilisation',\n",
" desired_precision=0.05,\n",
- " yaxis_title='Mean nurse utilisation',\n",
- " min_rep=3\n",
+ " min_rep=3,\n",
+ " labels=LABELS\n",
")"
]
},
@@ -1645,7 +1674,7 @@
},
{
"cell_type": "code",
- "execution_count": 15,
+ "execution_count": 14,
"metadata": {},
"outputs": [
{
@@ -1674,7 +1703,7 @@
{
"data": {
"image/svg+xml": [
- ""
+ ""
]
},
"metadata": {},
@@ -1688,9 +1717,9 @@
" print(f'Running with cores: {i}.')\n",
" start_time = time.time()\n",
"\n",
- " param = Defaults()\n",
- " param.cores = i\n",
- " experiment = Runner(param)\n",
+ " run_param = Defaults()\n",
+ " run_param.cores = i\n",
+ " experiment = Runner(run_param)\n",
" experiment.run_reps()\n",
"\n",
" # Round time to nearest .5 seconds\n",
@@ -1700,17 +1729,17 @@
"# Plot time by number of cores\n",
"timing_results = pd.DataFrame(speed)\n",
"print(timing_results)\n",
- "fig = px.line(timing_results, x='cores', y='run_time')\n",
- "fig.update_layout(\n",
+ "cores_fig = px.line(timing_results, x='cores', y='run_time')\n",
+ "cores_fig.update_layout(\n",
" xaxis_title='Number of cores',\n",
" yaxis_title='Run time (rounded to nearest .5 seconds)',\n",
" template='plotly_white'\n",
")\n",
"\n",
"# Display and save figure\n",
- "fig.show()\n",
- "fig.write_image(os.path.join(output_dir, 'choose_param_cores.png'),\n",
- " width=800, height=600)"
+ "cores_fig.show()\n",
+ "cores_fig.write_image(os.path.join(OUTPUT_DIR, 'choose_param_cores.png'),\n",
+ " width=800, height=600)"
]
},
{
@@ -1722,7 +1751,7 @@
},
{
"cell_type": "code",
- "execution_count": 16,
+ "execution_count": 15,
"metadata": {},
"outputs": [
{
@@ -1759,7 +1788,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.13.0"
+ "version": "3.13.1"
}
},
"nbformat": 4,
diff --git a/notebooks/generate_exp_results.ipynb b/notebooks/generate_exp_results.ipynb
index 1ae6846..1c1a4aa 100644
--- a/notebooks/generate_exp_results.ipynb
+++ b/notebooks/generate_exp_results.ipynb
@@ -17,27 +17,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Set-up\n",
- "\n",
- "Load notebook linters."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "%load_ext pycodestyle_magic"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "%pycodestyle_on"
+ "## Set-up"
]
},
{
@@ -49,10 +29,11 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
+ "# pylint: disable=missing-module-docstring\n",
"# To ensure any updates to `simulation/` are fetched without needing to restart\n",
"# the notebook environment, reload `simulation/` before execution of each cell\n",
"%load_ext autoreload\n",
@@ -62,14 +43,17 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
+ "# pylint: disable=wrong-import-position\n",
"# Import required packages\n",
- "from simulation.model import Defaults, Runner\n",
"import os\n",
- "import time"
+ "import time\n",
+ "from IPython.display import display\n",
+ "\n",
+ "from simulation.model import Defaults, Runner"
]
},
{
@@ -81,7 +65,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@@ -98,12 +82,12 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Define path to folder for expected results for tests\n",
- "tests = '../tests/exp_results/'"
+ "TESTS = '../tests/exp_results/'"
]
},
{
@@ -115,7 +99,7 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -138,7 +122,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 6,
"metadata": {},
"outputs": [
{
@@ -288,12 +272,12 @@
"# Patient-level results\n",
"display(experiment.patient_results_df)\n",
"experiment.patient_results_df.to_csv(\n",
- " os.path.join(tests, 'patient.csv'), index=False)"
+ " os.path.join(TESTS, 'patient.csv'), index=False)"
]
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
@@ -411,12 +395,12 @@
"# Run results\n",
"display(experiment.run_results_df)\n",
"experiment.run_results_df.to_csv(\n",
- " os.path.join(tests, 'run.csv'), index=False)"
+ " os.path.join(TESTS, 'run.csv'), index=False)"
]
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 8,
"metadata": {},
"outputs": [
{
@@ -591,12 +575,12 @@
"# Interval audit results\n",
"display(experiment.interval_audit_df)\n",
"experiment.interval_audit_df.to_csv(\n",
- " os.path.join(tests, 'interval.csv'), index=False)"
+ " os.path.join(TESTS, 'interval.csv'), index=False)"
]
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 9,
"metadata": {},
"outputs": [
{
@@ -697,12 +681,12 @@
"# Overall results\n",
"display(experiment.overall_results_df)\n",
"experiment.overall_results_df.to_csv(\n",
- " os.path.join(tests, 'overall.csv'), index=True)"
+ " os.path.join(TESTS, 'overall.csv'), index=True)"
]
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
@@ -739,7 +723,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.13.0"
+ "version": "3.13.1"
}
},
"nbformat": 4,
diff --git a/outputs/choose_param_conf_int_1.png b/outputs/choose_param_conf_int_1.png
index 1df7b26..fadfa6a 100644
Binary files a/outputs/choose_param_conf_int_1.png and b/outputs/choose_param_conf_int_1.png differ
diff --git a/outputs/choose_param_conf_int_2.png b/outputs/choose_param_conf_int_2.png
index 1aae52e..7e248d7 100644
Binary files a/outputs/choose_param_conf_int_2.png and b/outputs/choose_param_conf_int_2.png differ
diff --git a/outputs/choose_param_conf_int_3.png b/outputs/choose_param_conf_int_3.png
index 26c64e3..18e1ceb 100644
Binary files a/outputs/choose_param_conf_int_3.png and b/outputs/choose_param_conf_int_3.png differ
diff --git a/outputs/choose_param_time_series_1.png b/outputs/choose_param_time_series_1.png
index cae4d17..ae39346 100644
Binary files a/outputs/choose_param_time_series_1.png and b/outputs/choose_param_time_series_1.png differ
diff --git a/outputs/choose_param_time_series_2.png b/outputs/choose_param_time_series_2.png
index 8eba128..8b5c23f 100644
Binary files a/outputs/choose_param_time_series_2.png and b/outputs/choose_param_time_series_2.png differ
diff --git a/outputs/example_overall.csv b/outputs/example_overall.csv
index 8deedfd..c7bf801 100644
--- a/outputs/example_overall.csv
+++ b/outputs/example_overall.csv
@@ -1,5 +1,5 @@
arrivals,mean_q_time_nurse,mean_time_with_nurse,mean_nurse_utilisation,mean_nurse_utilisation_tw,mean_nurse_q_length
-10776.741935483871,0.499036905566402,9.978456743390534,0.49767035119563796,0.497804034286856,0.12459850290556283
-115.80327218806197,0.06739346957542013,0.11513823236530764,0.007524465631261531,0.0075405891452702024,0.017212719807337778
-10734.264952313013,0.47431678135150124,9.936223698796068,0.4949103549196282,0.4950381238581607,0.11828482630760263
-10819.21891865473,0.5237570297813028,10.020689787985,0.5004303474716477,0.5005699447155513,0.13091217950352302
+10776.741935483871,0.499036905566402,9.978456743390534,0.49767035119563796,0.49780403428685593,0.12459850290556283
+115.80327218806197,0.06739346957542013,0.11513823236530764,0.007524465631261531,0.007540589145270748,0.017212719807337778
+10734.264952313013,0.47431678135150124,9.936223698796068,0.4949103549196282,0.4950381238581605,0.11828482630760263
+10819.21891865473,0.5237570297813028,10.020689787985,0.5004303474716477,0.5005699447155514,0.13091217950352302
diff --git a/outputs/example_run.csv b/outputs/example_run.csv
index cdec45f..fb80c78 100644
--- a/outputs/example_run.csv
+++ b/outputs/example_run.csv
@@ -1,32 +1,32 @@
run_number,scenario,arrivals,mean_q_time_nurse,mean_time_with_nurse,mean_nurse_utilisation,mean_nurse_utilisation_tw,mean_nurse_q_length
-0,0,10972,0.504541081338615,9.84226781662332,0.4996386466177992,0.4997404525127849,0.12814409130665008
-1,0,10784,0.514150649003393,10.060480983450425,0.5019905433327594,0.502069010098797,0.1283472360845507
-2,0,10854,0.5232349226016817,9.925024519746302,0.4981297032213408,0.4982604514269931,0.1326692082859329
-3,0,10831,0.4791488631810612,9.9370571543943,0.49822049332297624,0.4982689502894135,0.12013104947023319
-4,0,10720,0.46145745726579823,10.015904147971671,0.4968703372055205,0.4971055007588973,0.11450981346966105
-5,0,10772,0.3882646868128185,9.884995942861282,0.492904233058074,0.4930559758823053,0.09681451866545558
-6,0,10831,0.4669381081669874,10.041799654744745,0.503356251584998,0.5034005361957391,0.11706959836936667
+0,0,10972,0.504541081338615,9.84226781662332,0.4996386466177992,0.49974045251278504,0.12814409130665008
+1,0,10784,0.514150649003393,10.060480983450425,0.5019905433327594,0.5020690100987935,0.1283472360845507
+2,0,10854,0.5232349226016817,9.925024519746302,0.4981297032213408,0.49826045142699366,0.1326692082859329
+3,0,10831,0.4791488631810612,9.9370571543943,0.49822049332297624,0.49826895028941315,0.12013104947023319
+4,0,10720,0.46145745726579823,10.015904147971671,0.4968703372055205,0.4971055007588991,0.11450981346966105
+5,0,10772,0.3882646868128185,9.884995942861282,0.492904233058074,0.4930559758823056,0.09681451866545558
+6,0,10831,0.4669381081669874,10.041799654744745,0.503356251584998,0.5034005361957363,0.11706959836936667
7,0,10781,0.625888360901447,10.086979128063648,0.5034489381047406,0.5037422085180931,0.15619681525181714
-8,0,10772,0.4684971326393017,10.202270228377186,0.508790994907774,0.5089130015618383,0.11682062761089254
-9,0,10705,0.5634349862001105,10.09260227901333,0.49997178325917613,0.5002278075946729,0.1399966447950717
-10,0,10927,0.562585969190057,10.083553615426284,0.5098373896503802,0.5099889270180503,0.14230039086434612
-11,0,10688,0.39781182403365434,9.893334037740962,0.4895233535105571,0.48962710728052056,0.09842159202017818
-12,0,11092,0.44530362984867583,9.810905063859671,0.5034276977164731,0.5034590643983867,0.11437196007956935
-13,0,10640,0.4397605749351992,9.933879963761742,0.48925457544062534,0.4892677288083271,0.10831140086366944
-14,0,10904,0.6107822771635987,10.167683587926042,0.513130373051619,0.5135294499218972,0.1541659710692565
-15,0,10849,0.47670998312351787,9.975581276412813,0.5009629889699926,0.5010455334069686,0.11971820849321864
-16,0,10719,0.428336230071242,9.809804967118032,0.4866947365248423,0.4868274499224406,0.10628092708642692
-17,0,10713,0.505574486075843,9.989154604862323,0.4954087210793937,0.4957697619501451,0.1253754506789469
-18,0,10568,0.3615821082986753,9.800182515281657,0.4793715775153063,0.4795652084277097,0.08845369723380557
-19,0,10707,0.4191589720392036,9.9193906381964,0.4915291568180775,0.4917635127186296,0.10388738688943873
-20,0,10845,0.6120142008002236,9.813774247048583,0.49261944375555833,0.49267201493881163,0.15364106499255614
-21,0,10726,0.5173212645251098,9.858186468091487,0.48940978932654106,0.4895612849605359,0.1284441639651928
-22,0,10618,0.5316566599495316,9.969891296475984,0.4892835610032433,0.4893719186906082,0.13067431517000294
-23,0,10914,0.5270546197660896,9.925291785145637,0.5013818263178943,0.501433800156172,0.1331544935214607
-24,0,10660,0.5029697553047842,9.96454608027881,0.49160360886911897,0.49162164437257694,0.12411244424881943
-25,0,10685,0.6270646900660448,10.236338445072258,0.5060501169430693,0.5061407672693311,0.15515572552368928
-26,0,10806,0.5061794260886525,10.039653295082593,0.5022615440123254,0.5023039700989318,0.126615159220231
-27,0,10748,0.4925537687951073,10.050997027592315,0.4999546777219452,0.5001183558252156,0.12254555340300495
-28,0,10589,0.4577189216542841,10.044180668559221,0.4923573694687436,0.4923672645089264,0.11219411253234293
-29,0,10863,0.5422405738480466,10.034383892923174,0.5046064549261675,0.5047331518468727,0.13635091096554006
-30,0,10796,0.5102078888697092,9.922063713004379,0.49578999982774674,0.49597325153194355,0.12767905794112022
+8,0,10772,0.4684971326393017,10.202270228377186,0.508790994907774,0.5089130015618397,0.11682062761089254
+9,0,10705,0.5634349862001105,10.09260227901333,0.49997178325917613,0.5002278075946733,0.1399966447950717
+10,0,10927,0.562585969190057,10.083553615426284,0.5098373896503802,0.5099889270180521,0.14230039086434612
+11,0,10688,0.39781182403365434,9.893334037740962,0.4895233535105571,0.489627107280519,0.09842159202017818
+12,0,11092,0.44530362984867583,9.810905063859671,0.5034276977164731,0.503459064398387,0.11437196007956935
+13,0,10640,0.4397605749351992,9.933879963761742,0.48925457544062534,0.48926772880832675,0.10831140086366944
+14,0,10904,0.6107822771635987,10.167683587926042,0.513130373051619,0.5135294499218969,0.1541659710692565
+15,0,10849,0.47670998312351787,9.975581276412813,0.5009629889699926,0.5010455334069701,0.11971820849321864
+16,0,10719,0.428336230071242,9.809804967118032,0.4866947365248423,0.48682744992243954,0.10628092708642692
+17,0,10713,0.505574486075843,9.989154604862323,0.4954087210793937,0.4957697619501464,0.1253754506789469
+18,0,10568,0.3615821082986753,9.800182515281657,0.4793715775153063,0.4795652084277076,0.08845369723380557
+19,0,10707,0.4191589720392036,9.9193906381964,0.4915291568180775,0.49176351271863106,0.10388738688943873
+20,0,10845,0.6120142008002236,9.813774247048583,0.49261944375555833,0.4926720149388125,0.15364106499255614
+21,0,10726,0.5173212645251098,9.858186468091487,0.48940978932654106,0.4895612849605331,0.1284441639651928
+22,0,10618,0.5316566599495316,9.969891296475984,0.4892835610032433,0.4893719186906069,0.13067431517000294
+23,0,10914,0.5270546197660896,9.925291785145637,0.5013818263178943,0.5014338001561723,0.1331544935214607
+24,0,10660,0.5029697553047842,9.96454608027881,0.49160360886911897,0.49162164437257705,0.12411244424881943
+25,0,10685,0.6270646900660448,10.236338445072258,0.5060501169430693,0.5061407672693301,0.15515572552368928
+26,0,10806,0.5061794260886525,10.039653295082593,0.5022615440123254,0.5023039700989324,0.126615159220231
+27,0,10748,0.4925537687951073,10.050997027592315,0.4999546777219452,0.5001183558252174,0.12254555340300495
+28,0,10589,0.4577189216542841,10.044180668559221,0.4923573694687436,0.49236726450892265,0.11219411253234293
+29,0,10863,0.5422405738480466,10.034383892923174,0.5046064549261675,0.5047331518468752,0.13635091096554006
+30,0,10796,0.5102078888697092,9.922063713004379,0.49578999982774674,0.49597325153194544,0.12767905794112022
diff --git a/outputs/scenario_nurse_util.png b/outputs/scenario_nurse_util.png
index cc3822e..0e449ea 100644
Binary files a/outputs/scenario_nurse_util.png and b/outputs/scenario_nurse_util.png differ
diff --git a/outputs/scenario_nurse_wait.png b/outputs/scenario_nurse_wait.png
index f768431..17fadfb 100644
Binary files a/outputs/scenario_nurse_wait.png and b/outputs/scenario_nurse_wait.png differ
diff --git a/outputs/sensitivity_consult_time.png b/outputs/sensitivity_consult_time.png
index 241803b..00de371 100644
Binary files a/outputs/sensitivity_consult_time.png and b/outputs/sensitivity_consult_time.png differ
diff --git a/outputs/spread_arrivals.png b/outputs/spread_arrivals.png
index 4e08329..8bc1a42 100644
Binary files a/outputs/spread_arrivals.png and b/outputs/spread_arrivals.png differ
diff --git a/outputs/spread_nurse_time.png b/outputs/spread_nurse_time.png
index bc6a988..5ad4f50 100644
Binary files a/outputs/spread_nurse_time.png and b/outputs/spread_nurse_time.png differ
diff --git a/outputs/spread_nurse_wait.png b/outputs/spread_nurse_wait.png
index d864b5f..9c291a4 100644
Binary files a/outputs/spread_nurse_wait.png and b/outputs/spread_nurse_wait.png differ
diff --git a/requirements.txt b/requirements.txt
index 2a6123d..2d6ec97 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,16 +1,14 @@
-flake8==7.1.1
ipykernel==6.29.5
-jinja2==3.1.4
+jinja2==3.1.5
joblib==1.4.2
kaleido==0.2.1
nbformat==5.10.4
-numpy==2.1.3
+nbqa==1.9.0
+numpy==2.2.2
pandas==2.2.3
-pip==24.3.1
+pip==25.0
plotly_express==0.4.1
-pycodestyle==2.12.1
-pycodestyle_magic==0.5
-pylint==3.3.3
+pylint==3.3.4
pytest==8.3.4
pytest-xdist==3.6.1
simpy==4.1.1
diff --git a/simulation/model.py b/simulation/model.py
index 2a37de8..68a7b86 100644
--- a/simulation/model.py
+++ b/simulation/model.py
@@ -198,14 +198,14 @@ class MonitoredResource(simpy.Resource):
simpy.Resource, which is referred to as the superclass or parent class.
Attributes:
- time_last_event (float):
+ time_last_event (list):
Time of last resource request or release.
- area_n_in_queue (float):
- Total time that patients have spent queueing for the resource
+ area_n_in_queue (list):
+ Time that patients have spent queueing for the resource
(i.e. sum of the times each patient spent waiting). Used to
calculate the average queue length.
- area_resource_busy (float):
- Total time that resources have been in use during the simulation
+ area_resource_busy (list):
+ Time that resources have been in use during the simulation
(i.e. sum of the times each individual resource was busy). Used
to calculated utilisation.
"""
@@ -229,9 +229,9 @@ def init_results(self):
"""
Resets monitoring attributes to initial values.
"""
- self.time_last_event = self._env.now
- self.area_n_in_queue = 0.0
- self.area_resource_busy = 0.0
+ self.time_last_event = [self._env.now]
+ self.area_n_in_queue = [0.0]
+ self.area_resource_busy = [0.0]
def request(self, *args, **kwargs):
"""
@@ -289,16 +289,18 @@ def update_time_weighted_stats(self):
persist over time.
"""
# Calculate time since last event
- time_since_last_event = self._env.now - self.time_last_event
+ time_since_last_event = self._env.now - self.time_last_event[-1]
- # Update self.time_last_event to current time
- self.time_last_event = self._env.now
+ # Add record of current time
+ self.time_last_event.append(self._env.now)
- # Update the statistics
+ # Add "area under curve" of people in queue
# len(self.queue) is the number of requests queued
- self.area_n_in_queue += len(self.queue) * time_since_last_event
+ self.area_n_in_queue.append(len(self.queue) * time_since_last_event)
+
+ # Add "area under curve" of resources in use
# self.count is the number of resources in use
- self.area_resource_busy += self.count * time_since_last_event
+ self.area_resource_busy.append(self.count * time_since_last_event)
class Exponential:
@@ -499,7 +501,7 @@ def attend_clinic(self, patient):
# Log wait time and time spent with nurse
self.param.logger.log(
f'Patient {patient.patient_id} is seen by nurse after ' +
- f'{patient.arrival_time:.3f} minutes. Consultation length: ' +
+ f'{patient.q_time_nurse:.3f} minutes. Consultation length: ' +
f'{patient.time_with_nurse:.3f} minutes.'
)
@@ -552,20 +554,22 @@ def init_results_variables(self):
def warm_up_complete(self):
"""
- Resets all results collection variables once warm-up period has passed.
+ If there is a warm-up period, then reset all results collection
+ variables once warm-up period has passed.
"""
- # Delay process until warm-up period has completed
- yield self.env.timeout(self.param.warm_up_period)
+ if self.param.warm_up_period > 0:
+ # Delay process until warm-up period has completed
+ yield self.env.timeout(self.param.warm_up_period)
- # Reset results collection variables
- self.init_results_variables()
+ # Reset results collection variables
+ self.init_results_variables()
- # If there was a warm-up period, log that this time has passed so we
- # can distinguish between patients before and after warm-up in logs
- if self.param.warm_up_period > 0:
- self.param.logger.log('─' * 10)
- self.param.logger.log(f'{self.env.now:.2f}: Warm up complete.')
- self.param.logger.log('─' * 10)
+ # If there was a warm-up period, log that this time has passed so
+ # can distinguish between patients before and after warm-up in logs
+ if self.param.warm_up_period > 0:
+ self.param.logger.log('─' * 10)
+ self.param.logger.log(f'{self.env.now:.2f}: Warm up complete.')
+ self.param.logger.log('─' * 10)
def run(self):
"""
@@ -576,6 +580,7 @@ def run(self):
self.param.data_collection_period)
# Schedule process which will reset results when warm-up period ends
+ # (or does nothing if these is no warm-up)
self.env.process(self.warm_up_complete())
# Schedule patient generator to run during simulation
@@ -590,7 +595,7 @@ def run(self):
# If the simulation ends while resources are still in use or requests
# are still in the queue, the time between the last recorded event and
- # the simulation end will not have been accounted for. Hence, we call
+ # the simulation end will not have been accounted for. Hence, we call
# update_time_weighted_stats() to run for last event --> end.
self.nurse.update_time_weighted_stats()
@@ -672,10 +677,10 @@ def run_single(self, run):
'mean_nurse_utilisation': (model.nurse_time_used /
(self.param.number_of_nurses *
self.param.data_collection_period)),
- 'mean_nurse_utilisation_tw': (model.nurse.area_resource_busy /
+ 'mean_nurse_utilisation_tw': (sum(model.nurse.area_resource_busy) /
(self.param.number_of_nurses *
self.param.data_collection_period)),
- 'mean_nurse_q_length': (model.nurse.area_n_in_queue /
+ 'mean_nurse_q_length': (sum(model.nurse.area_n_in_queue) /
self.param.data_collection_period)
}
diff --git a/tests/exp_results/overall.csv b/tests/exp_results/overall.csv
index dbf57c7..4a48725 100644
--- a/tests/exp_results/overall.csv
+++ b/tests/exp_results/overall.csv
@@ -1,5 +1,5 @@
,arrivals,mean_q_time_nurse,mean_time_with_nurse,mean_nurse_utilisation,mean_nurse_utilisation_tw,mean_nurse_q_length
mean,378.0,2.1667325673589564,10.159320371752361,0.6362147508884318,0.6397764054245421,0.5445692746656905
-std_dev,15.198684153570664,0.7844583147114635,0.4786676748781607,0.02383218108751965,0.024277980144559095,0.19009766156834382
-lower_95_ci,359.12834106644124,1.192698919890132,9.564975952752244,0.606623189633386,0.6096313115299372,0.3085318521535542
-upper_95_ci,396.87165893355876,3.1407662148277806,10.75366479075248,0.6658063121434776,0.6699214993191469,0.7806066971778267
+std_dev,15.198684153570664,0.7844583147114635,0.4786676748781607,0.02383218108751965,0.024277980144559032,0.19009766156834385
+lower_95_ci,359.12834106644124,1.192698919890132,9.564975952752244,0.606623189633386,0.6096313115299372,0.30853185215355416
+upper_95_ci,396.87165893355876,3.1407662148277806,10.75366479075248,0.6658063121434776,0.6699214993191469,0.7806066971778268
diff --git a/tests/exp_results/run.csv b/tests/exp_results/run.csv
index b7c840d..531d0e5 100644
--- a/tests/exp_results/run.csv
+++ b/tests/exp_results/run.csv
@@ -1,6 +1,6 @@
run_number,scenario,arrivals,mean_q_time_nurse,mean_time_with_nurse,mean_nurse_utilisation,mean_nurse_utilisation_tw,mean_nurse_q_length
-0,0,403,2.065395937787463,9.622970542788952,0.6430060048077357,0.6430060048077361,0.5549030419522317
-1,0,382,1.3967545762785822,10.18623477209793,0.6456396558371599,0.6476900423541169,0.35570683209227894
-2,0,369,3.4842840540548505,10.733822459914006,0.6607899582685668,0.6683185188812463,0.8582185938875108
-3,0,367,2.0505219928782124,10.511446608041043,0.6345411517895263,0.6384068134475295,0.5021881615506957
-4,0,369,1.8367062757956718,9.742127475919874,0.5970969837391709,0.6014606476320816,0.45182974384573527
+0,0,403,2.065395937787463,9.622970542788952,0.6430060048077357,0.643006004807736,0.5549030419522317
+1,0,382,1.3967545762785822,10.18623477209793,0.6456396558371599,0.6476900423541168,0.35570683209227894
+2,0,369,3.4842840540548505,10.733822459914006,0.6607899582685668,0.6683185188812463,0.8582185938875109
+3,0,367,2.0505219928782124,10.511446608041043,0.6345411517895263,0.6384068134475298,0.5021881615506957
+4,0,369,1.8367062757956718,9.742127475919874,0.5970969837391709,0.6014606476320817,0.45182974384573527
diff --git a/tests/test_unittest_model.py b/tests/test_unittest_model.py
index 3e64e51..22783c8 100644
--- a/tests/test_unittest_model.py
+++ b/tests/test_unittest_model.py
@@ -210,12 +210,38 @@ def helper_warmup(warm_up_period):
f'high arrival rate, but got {first_warmup["q_time_nurse"]}.'
)
- # Check that, in model without warm-up, first patient has 0 queue time
+ # Check that model without warm-up has arrival and queue time of 0
+ assert first_none['arrival_time'] == 0, (
+ 'Expect first patient to arrive at time 0 when model is run ' +
+ f'without warm-up, but got {first_none["arrival_time"]}.'
+ )
assert first_none['q_time_nurse'] == 0, (
'Expect first patient to have no wait time in model without warm-up ' +
f'but got {first_none["q_time_nurse"]}.'
)
+ # Check that the first interval audit entry with no warm-up has time and
+ # results of 0
+ first_interval = results_none['interval_audit'].iloc[0]
+ assert first_interval['simulation_time'] == 0, (
+ 'With no warm-up, expect first entry in interval audit to be ' +
+ f'at time 0, but it was at time {first_interval['simulation_time']}.'
+ )
+ assert first_interval['utilisation'] == 0, (
+ 'With no warm-up, expect first entry in interval audit to ' +
+ f'have 0 utilisation, but it was {first_interval['utilisation']}.'
+ )
+ assert first_interval['queue_length'] == 0, (
+ 'With no warm-up, expect first entry in interval audit to ' +
+ 'have no queue, but there was queue length of ' +
+ f'{first_interval['queue_length']}.'
+ )
+ assert first_interval['running_mean_wait_time'] == 0, (
+ 'With no warm-up, expect first entry in interval audit to ' +
+ 'have running mean wait time of 0 but it was ' +
+ f'{first_interval['running_mean_wait_time']}.'
+ )
+
def test_arrivals():
"""
@@ -497,11 +523,11 @@ def process_task(env, resource, duration):
expected_busy_time = 12.0
# Run assertions
- assert resource.area_n_in_queue == expected_queue_time, (
+ assert sum(resource.area_n_in_queue) == expected_queue_time, (
f'Expected queue time {expected_queue_time} but ' +
f'observed {resource.area_n_in_queue}.'
)
- assert resource.area_resource_busy == expected_busy_time, (
+ assert sum(resource.area_resource_busy) == expected_busy_time, (
f'Expected queue time {expected_busy_time} but ' +
f'observed {resource.area_resource_busy}.'
)