Skip to content

Commit 588213e

Browse files
authored
Merge pull request #5 from pythonhealthdatascience/img
Removal of pharmacy example
2 parents 815b05f + 9afb448 commit 588213e

8 files changed

+758
-606
lines changed

README.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,36 @@ This tutorial has been set up to run on your browser without the need to install
3838
3939
### 2.2 Running notebooks locally on your machine
4040

41-
You can also run the notebooks in `content/` locally on your machine. You'll need to install the provided conda environment `environment.yml`.
41+
#### 2.2.1 Downloading the code
42+
43+
Either clone the repository using git or click on the green "code" button and select "Download Zip".
44+
45+
```bash
46+
git clone https://github.com/pythonhealthdatascience/intro-open-sim.git
47+
```
48+
49+
#### 2.2.2 Installing dependencies and running JupyterLab
50+
51+
All dependencies can be found in [`binder/environment.yml`]() and are pulled from conda-forge. To run the code locally, we recommend installing [miniforge](https://github.com/conda-forge/miniforge);
52+
53+
> miniforge is FOSS alternative to Anaconda and miniconda that uses conda-forge as the default channel for packages. It installs both conda and mamba (a drop in replacement for conda) package managers. We recommend mamba for faster resolving of dependencies and installation of packages.
54+
55+
navigating your terminal (or cmd prompt) to the directory containing the repo and issuing the following command:
56+
57+
```
58+
mamba env create -f binder/environment.yml
59+
```
60+
61+
Activate the mamba environment using the following command:
62+
63+
```
64+
mamba activate simpy_tutorial
65+
```
66+
67+
You can then run the notebooks in `content/` locally on your machine using JupyterLab. Issue the following command and JupyterLab will open in your browser. Notebooks are in the `content/` directory.
4268

4369
```
44-
conda env create --name xeus-python-kernel --file environment.yml
45-
conda activate xeus-python-kernel
70+
jupyter-lab
4671
```
4772

4873
## 📝 3. Citation

binder/environment.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ name: simpy_tutorial
22
channels:
33
- conda-forge
44
dependencies:
5-
- python=3.11
6-
- simpy=4.1.1
5+
- jupyterlab=4.2.4
6+
- matplotlib=3.8.4
77
- numpy<2
88
- pandas=1.5.3
9-
- matplotlib=3.8.4
9+
- python=3.11
10+
- simpy=4.1.1

content/01_sampling.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@
470470
"name": "python",
471471
"nbconvert_exporter": "python",
472472
"pygments_lexer": "ipython3",
473-
"version": "3.1.-1"
473+
"version": "3.11.10"
474474
}
475475
},
476476
"nbformat": 4,

content/02_basic_simpy.ipynb

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,9 @@
4949
"id": "968d7a3d-763d-48a0-9915-421631d1f650",
5050
"metadata": {},
5151
"source": [
52-
"## 3. An example: a hospital pharmacy\n",
52+
"# 3. An example: a urgent care call sample\n",
5353
"\n",
54-
"In this first example, let's assume (unrealistically) that prescriptions arrive **exactly** 5 minutes apart.\n",
55-
"\n",
56-
"![Pharmacy with prescriptions every 5 minutes](./img/pharmacy.png)\n",
54+
"This case study uses a simple model of an urgent care telephone call centre, similar to the NHS 111 service in the UK. To learn `simpy` we will first build a very simple model. In our first iteration of this model, calls to the centre arrive **deterministically**. For now we will ignore resources and activities in the model and just model a deterministic arrival process. The simulation time units are in minutes. Let's assume there are 60 new callers per hour (an fixed inter-arrival time of 1.0 per minute).\n",
5755
"\n",
5856
"## 4. The model building blocks\n",
5957
"\n",
@@ -72,19 +70,19 @@
7270
"We can introduce **delays** or **activities** into a process. For example these might be the duration of a stay on a ward, or the duration of a operation - or, in this case, a **delay between arrivals (inter-arrival time)**. In `simpy` you control this with the following method:\n",
7371
"\n",
7472
"```python\n",
75-
"env.timeout(5.0)\n",
73+
"env.timeout(1.0)\n",
7674
"```\n",
7775
"\n",
7876
"### 4.3 Generators\n",
7977
"\n",
8078
"The events in the DES are modelled and scheduled in `simpy` using python **generators** (i.e. they are the \"event-processing mechanism\"). A generator is a function that behaves like an iterator, meaning it can yield a **sequence of values** when iterated over.\n",
8179
"\n",
82-
"For example, below is a basic generator function that yields a new arrival every 5 minutes. It takes the **environment** as a parameter. It then internally calls the `env.timeout()` method in an infinite loop.\n",
80+
"For example, below is a basic generator function that yields a new arrival every 1 minute. It takes the **environment** as a parameter. It then internally calls the `env.timeout()` method in an infinite loop.\n",
8381
"\n",
8482
"```python\n",
85-
"def prescription_arrival_generator(env):\n",
83+
"def arrivals_generator(env):\n",
8684
" while True:\n",
87-
" yield env.timeout(5.0)\n",
85+
" yield env.timeout(1.0)\n",
8886
"```\n",
8987
"\n",
9088
"### 4.4 SimPy process and run\n",
@@ -94,7 +92,7 @@
9492
"1. Set the generator up as a **SimPy process** using `env.process()`\n",
9593
"\n",
9694
"```python\n",
97-
"env.process(prescription_arrival_generator(env))\n",
95+
"env.process(arrivals_generator(env))\n",
9896
"```\n",
9997
"\n",
10098
"2. Run the environment for a user specified **run length** using `env.run()`\n",
@@ -103,23 +101,23 @@
103101
"env.run(until=25)\n",
104102
"```\n",
105103
"\n",
106-
"The run method handle the infinite loop we set up in `prescription_arrival_generator`. The simulation model has an internal concept of time. It will end execution when its internal clock reaches 25 time units.\n",
104+
"The run method handle the infinite loop we set up in `arrivals_generator`. The simulation model has an internal concept of time. It will end execution when its internal clock reaches 25 time units.\n",
107105
"\n",
108106
"## 5. Create the model\n",
109107
"\n",
110-
"**Now that we have covered the basic building blocks, let's code the actual model.** It makes sense to create our model logic first. The code below will generate arrivals every 5 minutes. Note that the function takes an environment object as a parameter."
108+
"**Now that we have covered the basic building blocks, let's code the actual model.** It makes sense to create our model logic first. The code below will generate arrivals every 60.0 / 100.0 minutes. Note that the function takes an environment object as a parameter."
111109
]
112110
},
113111
{
114112
"cell_type": "code",
115-
"execution_count": 2,
113+
"execution_count": 22,
116114
"id": "a6fd524c-7dc4-41c0-876d-3507ce480dfb",
117115
"metadata": {},
118116
"outputs": [],
119117
"source": [
120-
"def prescription_arrival_generator(env):\n",
118+
"def arrivals_generator(env):\n",
121119
" '''\n",
122-
" Prescriptions arrive with a fixed duration of 5 minutes.\n",
120+
" Callers arrive with a fixed inter-arrival time of 1.0 minutes.\n",
123121
"\n",
124122
" Parameters:\n",
125123
" ------\n",
@@ -131,13 +129,13 @@
131129
" while True:\n",
132130
" \n",
133131
" # sample an inter-arrival time.\n",
134-
" inter_arrival_time = 5.0\n",
132+
" inter_arrival_time = 1.0\n",
135133
" \n",
136134
" # we use the yield keyword instead of return\n",
137135
" yield env.timeout(inter_arrival_time)\n",
138136
" \n",
139137
" # print out the time of the arrival\n",
140-
" print(f'Prescription arrives at: {env.now}')"
138+
" print(f'Call arrives at: {env.now}')"
141139
]
142140
},
143141
{
@@ -152,18 +150,38 @@
152150
},
153151
{
154152
"cell_type": "code",
155-
"execution_count": 3,
153+
"execution_count": 23,
156154
"id": "f6f74ff5-4c95-400e-8494-42e438b18b90",
157155
"metadata": {},
158156
"outputs": [
159157
{
160158
"name": "stdout",
161159
"output_type": "stream",
162160
"text": [
163-
"Prescription arrives at: 5.0\n",
164-
"Prescription arrives at: 10.0\n",
165-
"Prescription arrives at: 15.0\n",
166-
"Prescription arrives at: 20.0\n",
161+
"Call arrives at: 1.0\n",
162+
"Call arrives at: 2.0\n",
163+
"Call arrives at: 3.0\n",
164+
"Call arrives at: 4.0\n",
165+
"Call arrives at: 5.0\n",
166+
"Call arrives at: 6.0\n",
167+
"Call arrives at: 7.0\n",
168+
"Call arrives at: 8.0\n",
169+
"Call arrives at: 9.0\n",
170+
"Call arrives at: 10.0\n",
171+
"Call arrives at: 11.0\n",
172+
"Call arrives at: 12.0\n",
173+
"Call arrives at: 13.0\n",
174+
"Call arrives at: 14.0\n",
175+
"Call arrives at: 15.0\n",
176+
"Call arrives at: 16.0\n",
177+
"Call arrives at: 17.0\n",
178+
"Call arrives at: 18.0\n",
179+
"Call arrives at: 19.0\n",
180+
"Call arrives at: 20.0\n",
181+
"Call arrives at: 21.0\n",
182+
"Call arrives at: 22.0\n",
183+
"Call arrives at: 23.0\n",
184+
"Call arrives at: 24.0\n",
167185
"end of run. simulation clock time = 25\n"
168186
]
169187
}
@@ -175,8 +193,8 @@
175193
"# create the simpy environment object\n",
176194
"env = simpy.Environment()\n",
177195
"\n",
178-
"# tell simpy that the `prescription_arrival_generator` is a process\n",
179-
"env.process(prescription_arrival_generator(env))\n",
196+
"# tell simpy that the `arrivals_generator` is a process\n",
197+
"env.process(arrivals_generator(env))\n",
180198
"\n",
181199
"# run the simulation model\n",
182200
"env.run(until=RUN_LENGTH)\n",
@@ -192,7 +210,7 @@
192210
"\n",
193211
"Before we learn anything more about `simpy`, have a go at the [generators exercise](./03a_exercise1.ipynb).\n",
194212
"\n",
195-
"In the exercise you will need to modify the `prescription_arrival_generator` so that it has random arrivals. This exercise tests that you have understood the basics of `simpy` and random sampling in `numpy`\n"
213+
"In the exercise you will need to modify the `arrivals_generator` so that it has random arrivals. This exercise tests that you have understood the basics of `simpy` and random sampling in `numpy`\n"
196214
]
197215
}
198216
],
@@ -212,7 +230,7 @@
212230
"name": "python",
213231
"nbconvert_exporter": "python",
214232
"pygments_lexer": "ipython3",
215-
"version": "3.12.6"
233+
"version": "3.11.10"
216234
}
217235
},
218236
"nbformat": 4,

content/03a_exercise1.ipynb

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"id": "f2147e34-ce39-4d8c-a7db-e8acec2b63e0",
66
"metadata": {},
77
"source": [
8-
"# Generator exercise\n",
8+
"# Solutions for the generator exercise\n",
99
"\n",
10-
"🧐 For the solutions, please see the [generator exercise solutions notebook](./03b_exercise1_solutions.ipynb)\n"
10+
"🧐 For the solutions, please see the [generator exercise solutions notebook](./03b_exercise1_solutions.ipynb)"
1111
]
1212
},
1313
{
@@ -20,7 +20,7 @@
2020
},
2121
{
2222
"cell_type": "code",
23-
"execution_count": 1,
23+
"execution_count": null,
2424
"id": "85fd4a85-bb77-498c-96ee-c14de89994a2",
2525
"metadata": {},
2626
"outputs": [],
@@ -34,22 +34,21 @@
3434
"id": "bfab7f97-9cad-419a-8d75-a2ba190edee8",
3535
"metadata": {},
3636
"source": [
37-
"## 2. Example code\n",
37+
"## Example code\n",
3838
"\n",
39-
"The code below is taken from the simple pharmacy example. In this code arrivals occur with an inter-arrival time (IAT) of exactly 5 minutes."
39+
"The code below is taken from the simple call centre example. In this code arrivals occur with an inter-arrival time (IAT) of exactly 1 minute."
4040
]
4141
},
4242
{
4343
"cell_type": "code",
44-
"execution_count": 2,
44+
"execution_count": null,
4545
"id": "ee2439f2-0d35-41fd-a5e2-4b95954dd5c5",
4646
"metadata": {},
4747
"outputs": [],
4848
"source": [
49-
"def prescription_arrival_generator(env):\n",
49+
"def arrivals_generator(env):\n",
5050
" '''\n",
51-
" Prescriptions arrive with a fixed duration of\n",
52-
" 5 minutes.\n",
51+
" Prescriptions arrive with a fixed duration of 1 minute.\n",
5352
"\n",
5453
" Parameters:\n",
5554
" ------\n",
@@ -61,42 +60,30 @@
6160
" while True:\n",
6261
" \n",
6362
" # sample an inter-arrival time.\n",
64-
" inter_arrival_time = 5.0\n",
63+
" inter_arrival_time = 1.0\n",
6564
" \n",
6665
" # we use the yield keyword instead of return\n",
6766
" yield env.timeout(inter_arrival_time)\n",
6867
" \n",
6968
" # print out the time of the arrival\n",
70-
" print(f'Prescription arrives at: {env.now}')"
69+
" print(f'Call arrives at: {env.now}')"
7170
]
7271
},
7372
{
7473
"cell_type": "code",
75-
"execution_count": 3,
74+
"execution_count": null,
7675
"id": "40c495d5-6f55-4c93-99e3-5bfa6cdff36d",
7776
"metadata": {},
78-
"outputs": [
79-
{
80-
"name": "stdout",
81-
"output_type": "stream",
82-
"text": [
83-
"Prescription arrives at: 5.0\n",
84-
"Prescription arrives at: 10.0\n",
85-
"Prescription arrives at: 15.0\n",
86-
"Prescription arrives at: 20.0\n",
87-
"end of run. simulation clock time = 25\n"
88-
]
89-
}
90-
],
77+
"outputs": [],
9178
"source": [
9279
"# model parameters\n",
9380
"RUN_LENGTH = 25\n",
9481
"\n",
9582
"# create the simpy environment object\n",
9683
"env = simpy.Environment()\n",
9784
"\n",
98-
"# tell simpy that the `prescription_arrival_generator` is a process\n",
99-
"env.process(prescription_arrival_generator(env))\n",
85+
"# tell simpy that the `arrivals_generator` is a process\n",
86+
"env.process(arrivals_generator(env))\n",
10087
"\n",
10188
"# run the simulation model\n",
10289
"env.run(until=RUN_LENGTH)\n",
@@ -112,7 +99,7 @@
11299
"\n",
113100
"**Task:**\n",
114101
"\n",
115-
"Update `prescription_arrival_generator()` so that inter-arrival times follow an **exponential distribution** with a mean of 5.0 minutes between arrivals. Use a run length of 25 minutes.\n",
102+
"Update `arrivals_generator()` so that inter-arrival times follow an **exponential distribution** with a mean inter-arrival time of 60.0 / 100 minutes between arrivals (i.e. 100 arrivals per hour). Use a run length of 25 minutes.\n",
116103
"\n",
117104
"**Bonus challenge:**\n",
118105
"\n",
@@ -130,12 +117,12 @@
130117
},
131118
{
132119
"cell_type": "code",
133-
"execution_count": 4,
134-
"id": "65823eff-8d0f-4eaf-a531-173c8ab6290c",
120+
"execution_count": null,
121+
"id": "49dcec06-cf80-47d0-befd-66bc842f2e6e",
135122
"metadata": {},
136123
"outputs": [],
137124
"source": [
138-
"# your code here."
125+
"# your code here..."
139126
]
140127
}
141128
],

0 commit comments

Comments
 (0)