Emergency room simulation
In healthcare systems, optimizing patient flow and resource allocation is crucial for providing efficient and effective care. Simulation modeling allows us to analyze and evaluate different scenarios to improve the performance of medical facilities. In this simulation, we explore the dynamics of patient arrivals, triage, and treatment in an outpatient setting. By simulating the operations of a medical facility over a specified time period, we can assess key performance metrics, such as waiting time, treatment time, and resource utilization.
Problem: The emergency room (ER) is a critical part of a healthcare facility where patients with varying degrees of illness or injury seek immediate medical attention. However, managing the flow of patients in the ER can be challenging, particularly during peak hours or when resources are limited. It is essential to ensure that patients are efficiently triaged, treated, and allocated appropriate medical resources (doctors and nurses) based on the severity of their condition.
Significance: The project's simulation-based optimization of resource allocation and patient flow in an emergency room has the potential to enhance efficiency, improve patient outcomes, and enable data-driven decision making for healthcare administrators.
The flowchart of the process is shown below:
from IPython.display import Image, display
# Specify the desired width and height
width = 500
height = 300
# Create the Image object with the specified dimensions and display it
display(Image(filename='flowchart.jpg', width=width, height=height))
# Import required libraries
!pip install simpy
import simpy
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
Collecting simpy Downloading simpy-4.0.1-py2.py3-none-any.whl (29 kB) Installing collected packages: simpy Successfully installed simpy-4.0.1
# Constants
MEAN_INTERARRIVAL = 10
CHECKIN_MIN = 6
CHECKIN_MAX = 12
TRIAGE_MIN = 3
TRIAGE_MAX = 15
TRIAGE_MODE = 5
CRITICAL_PROB = 0.4
# Global variables
num_doctors = 2
# Data collection
results = []
utilization_doctors = []
utilization_nurses = []
# Arrival process
def patient_arrival(env, reception, doctor, nurse):
patient_id = 0
while True:
# Create a new patient
patient_id += 1
env.process(patient_process(env, patient_id, reception, doctor, nurse))
# Sample time until next arrival
interarrival_time = random.expovariate(1.0 / MEAN_INTERARRIVAL)
yield env.timeout(interarrival_time)
# Patient process
def patient_process(env, patient_id, reception, doctor, nurse):
global results
# Check-in
checkin_time = random.uniform(CHECKIN_MIN, CHECKIN_MAX)
yield env.timeout(checkin_time)
print(f"Patient {patient_id} checked-in at {env.now}")
arrival_time = env.now
# Triage
triage_time = random.triangular(TRIAGE_MIN, TRIAGE_MAX, TRIAGE_MODE)
yield env.timeout(triage_time)
print(f"Patient {patient_id} triaged at {env.now}")
token_time = env.now
# Check critical condition
critical = random.random() < CRITICAL_PROB
# Request doctor/nurse
if critical:
with doctor.request() as req:
yield req
print(f"Patient {patient_id} sees a doctor at {env.now}")
yield env.timeout(15) # Treatment time for critical patients
print(f"Patient {patient_id} treated by doctor at {env.now}")
treatment_time = 15
else:
with nurse.request() as req:
yield req
print(f"Patient {patient_id} sees a nurse at {env.now}")
yield env.timeout(10) # Treatment time for non-critical patients
print(f"Patient {patient_id} treated by nurse at {env.now}")
treatment_time = 10
# Calculate waiting time and treatment time
waiting_time = env.now - (checkin_time + triage_time)
# Record the results
result = {
'Patient ID': patient_id,
'Service': 'Doctor' if critical else 'Nurse',
'Arrival Time': arrival_time,
'Token Time': token_time,
'Service Start Time': env.now - treatment_time,
'Service Stop Time': env.now,
'Token Time (Secs)': token_time * 60,
'Service Time (Mins)': treatment_time,
'Wait Time (Mins)': waiting_time
}
results.append(result)
# Monitor the number of patients and resource utilization
def monitor(env, doctor, nurse):
global num_doctors, utilization_doctors, utilization_nurses
while True:
if doctor.count == num_doctors and doctor.queue:
num_doctors += 1
print(f"Additional doctor summoned at {env.now}. Total doctors: {num_doctors}")
elif num_doctors > 2 and doctor.count == 0 and not doctor.queue:
num_doctors -= 1
print(f"Doctor dismissed at {env.now}. Total doctors: {num_doctors}")
utilization_doctors.append(doctor.count / num_doctors)
utilization_nurses.append(nurse.count / 2)
yield env.timeout(1)
# Set up simulation
env = simpy.Environment()
# Resources
reception = simpy.Resource(env, capacity=3)
doctor = simpy.Resource(env, capacity=num_doctors)
nurse = simpy.Resource(env, capacity=2)
# Start processes
env.process(patient_arrival(env, reception, doctor, nurse))
env.process(monitor(env, doctor, nurse))
# Run simulation
env.run(until=300) # Run for 5 hours
# Create a DataFrame from the collected results
df = pd.DataFrame(results, columns=['Patient ID', 'Service', 'Arrival Time', 'Token Time',
'Service Start Time', 'Service Stop Time', 'Token Time (Secs)',
'Service Time (Mins)', 'Wait Time (Mins)'])
# Performance metric calculation
avg_waiting_time = df['Wait Time (Mins)'].mean()
avg_treatment_time = df['Service Time (Mins)'].mean()
doctor_utilization = np.mean(utilization_doctors)
nurse_utilization = np.mean(utilization_nurses)
# Print performance metrics
print(f"Average waiting time: {avg_waiting_time:.2f} minutes")
print(f"Average treatment time: {avg_treatment_time:.2f} minutes")
print(f"Doctor utilization: {doctor_utilization:.2%}")
print(f"Nurse utilization: {nurse_utilization:.2%}")
# Data visualization
plt.figure(figsize=(10, 6))
plt.hist(df['Wait Time (Mins)'], bins=20, alpha=0.8, color='blue')
plt.xlabel('Waiting Time (minutes)')
plt.ylabel('Frequency')
plt.title('Distribution of Waiting Times')
plt.grid(True)
plt.show()
Patient 2 checked-in at 7.107477593918198 Patient 1 checked-in at 7.895147091461459 Patient 2 triaged at 12.328061501279926 Patient 2 sees a doctor at 12.328061501279926 Patient 1 triaged at 14.291124885485537 Patient 1 sees a nurse at 14.291124885485537 Patient 3 checked-in at 16.327093193689656 Patient 4 checked-in at 18.381798808270993 Patient 3 triaged at 23.297100795507266 Patient 3 sees a doctor at 23.297100795507266 Patient 5 checked-in at 24.221166986673683 Patient 1 treated by nurse at 24.291124885485537 Patient 4 triaged at 25.08085358281184 Patient 4 sees a nurse at 25.08085358281184 Patient 2 treated by doctor at 27.328061501279926 Patient 5 triaged at 31.660642507921192 Patient 5 sees a nurse at 31.660642507921192 Patient 6 checked-in at 33.93393996390836 Patient 4 treated by nurse at 35.08085358281184 Patient 3 treated by doctor at 38.29710079550726 Patient 7 checked-in at 39.47075696578161 Patient 6 triaged at 41.64965560063597 Patient 6 sees a doctor at 41.64965560063597 Patient 5 treated by nurse at 41.660642507921196 Patient 8 checked-in at 43.001911447847654 Patient 7 triaged at 47.86285884029084 Patient 7 sees a nurse at 47.86285884029084 Patient 8 triaged at 48.62275872529126 Patient 8 sees a nurse at 48.62275872529126 Patient 6 treated by doctor at 56.64965560063597 Patient 9 checked-in at 57.65314072889163 Patient 7 treated by nurse at 57.86285884029084 Patient 8 treated by nurse at 58.62275872529126 Patient 10 checked-in at 62.7980654990089 Patient 9 triaged at 68.97032435432139 Patient 9 sees a nurse at 68.97032435432139 Patient 10 triaged at 69.7675865699513 Patient 10 sees a nurse at 69.7675865699513 Patient 11 checked-in at 77.34633917684049 Patient 9 treated by nurse at 78.97032435432139 Patient 10 treated by nurse at 79.7675865699513 Patient 11 triaged at 83.79251869178242 Patient 11 sees a nurse at 83.79251869178242 Patient 11 treated by nurse at 93.79251869178242 Patient 12 checked-in at 98.12394874707319 Patient 14 checked-in at 99.54428664888336 Patient 13 checked-in at 101.13142987287719 Patient 12 triaged at 102.79492294781436 Patient 12 sees a nurse at 102.79492294781436 Patient 14 triaged at 104.57643957641866 Patient 14 sees a doctor at 104.57643957641866 Patient 13 triaged at 108.02779900389812 Patient 13 sees a nurse at 108.02779900389812 Patient 12 treated by nurse at 112.79492294781436 Patient 15 checked-in at 113.03208952665565 Patient 16 checked-in at 115.0473469831116 Patient 17 checked-in at 115.62509951526948 Patient 13 treated by nurse at 118.02779900389812 Patient 14 treated by doctor at 119.57643957641866 Patient 16 triaged at 121.1359560422893 Patient 16 sees a doctor at 121.1359560422893 Patient 15 triaged at 123.16559217406723 Patient 15 sees a doctor at 123.16559217406723 Patient 17 triaged at 123.38037600444471 Additional doctor summoned at 124. Total doctors: 3 Patient 18 checked-in at 127.84022199962337 Patient 19 checked-in at 129.4365994502287 Patient 19 triaged at 133.3889893635742 Patient 19 sees a nurse at 133.3889893635742 Patient 16 treated by doctor at 136.1359560422893 Patient 17 sees a doctor at 136.1359560422893 Patient 18 triaged at 136.4326605475934 Patient 18 sees a nurse at 136.4326605475934 Patient 20 checked-in at 137.62645870871103 Patient 15 treated by doctor at 138.16559217406723 Patient 19 treated by nurse at 143.3889893635742 Patient 21 checked-in at 143.5626927895542 Patient 20 triaged at 145.94678140944478 Patient 20 sees a nurse at 145.94678140944478 Patient 18 treated by nurse at 146.4326605475934 Patient 17 treated by doctor at 151.1359560422893 Doctor dismissed at 152. Total doctors: 2 Patient 21 triaged at 154.54399024872956 Patient 21 sees a nurse at 154.54399024872956 Patient 20 treated by nurse at 155.94678140944478 Patient 21 treated by nurse at 164.54399024872956 Patient 22 checked-in at 164.58497088388697 Patient 23 checked-in at 172.23028593194744 Patient 22 triaged at 174.33504288495845 Patient 22 sees a doctor at 174.33504288495845 Patient 23 triaged at 178.37657634337842 Patient 23 sees a doctor at 178.37657634337842 Patient 24 checked-in at 179.0559477318653 Patient 24 triaged at 187.10882306616125 Patient 24 sees a nurse at 187.10882306616125 Patient 22 treated by doctor at 189.33504288495845 Patient 25 checked-in at 192.45759488989896 Patient 23 treated by doctor at 193.37657634337842 Patient 24 treated by nurse at 197.10882306616125 Patient 25 triaged at 200.71857446446114 Patient 25 sees a nurse at 200.71857446446114 Patient 25 treated by nurse at 210.71857446446114 Patient 26 checked-in at 229.93573408781594 Patient 27 checked-in at 234.42955411820196 Patient 28 checked-in at 235.62307448290537 Patient 26 triaged at 237.14424567614142 Patient 26 sees a doctor at 237.14424567614142 Patient 28 triaged at 239.1801045062548 Patient 28 sees a doctor at 239.1801045062548 Patient 27 triaged at 242.09418924231676 Patient 29 checked-in at 242.98153249162516 Additional doctor summoned at 243. Total doctors: 3 Patient 30 checked-in at 247.77369677675972 Patient 29 triaged at 248.57567581807191 Patient 31 checked-in at 250.84040882688979 Patient 26 treated by doctor at 252.14424567614142 Patient 27 sees a doctor at 252.14424567614142 Patient 30 triaged at 253.99696054076733 Patient 30 sees a nurse at 253.99696054076733 Patient 28 treated by doctor at 254.1801045062548 Patient 29 sees a doctor at 254.1801045062548 Patient 33 checked-in at 259.0670234646141 Patient 31 triaged at 259.25342280942135 Patient 31 sees a nurse at 259.25342280942135 Patient 32 checked-in at 259.5418923025103 Patient 33 triaged at 263.0407806081464 Patient 32 triaged at 263.6999724860195 Patient 30 treated by nurse at 263.9969605407673 Patient 33 sees a nurse at 263.9969605407673 Patient 27 treated by doctor at 267.1442456761414 Patient 29 treated by doctor at 269.1801045062548 Patient 31 treated by nurse at 269.25342280942135 Patient 32 sees a nurse at 269.25342280942135 Doctor dismissed at 270. Total doctors: 2 Patient 33 treated by nurse at 273.9969605407673 Patient 35 checked-in at 278.13698553806915 Patient 34 checked-in at 278.22120137641843 Patient 32 treated by nurse at 279.25342280942135 Patient 34 triaged at 284.6289752480452 Patient 34 sees a nurse at 284.6289752480452 Patient 35 triaged at 288.11230795838964 Patient 35 sees a doctor at 288.11230795838964 Patient 36 checked-in at 288.15503057556475 Patient 37 checked-in at 290.36716698681164 Patient 34 treated by nurse at 294.6289752480452 Patient 36 triaged at 296.0313144045089 Patient 36 sees a doctor at 296.0313144045089 Patient 37 triaged at 298.2392308110931 Additional doctor summoned at 299. Total doctors: 3 Patient 38 checked-in at 299.77957141132697 Average waiting time: 136.67 minutes Average treatment time: 11.91 minutes Doctor utilization: 29.44% Nurse utilization: 35.00%
# Calculate average waiting time
avg_waiting_time = df['Wait Time (Mins)'].mean()
# Plot waiting time over time
plt.figure(figsize=(10, 6))
plt.plot(df['Wait Time (Mins)'])
plt.xlabel('Patient')
plt.ylabel('Waiting Time (minutes)')
plt.title('Waiting Time for Each Patient')
plt.grid(True)
plt.show()
# Print simulation report
simulation_report = f"Simulation Report:\n\n"
simulation_report += f"Average waiting time: {avg_waiting_time:.2f} minutes\n"
print(simulation_report)
Simulation Report: Average waiting time: 136.67 minutes
The emergency room simulation model was verified during the development process by printing and checking intermediate outputs. By reviewing the generated report for each run of the simulation, it was ensured that the service stop time of one patient did not overlap with the service start time of the next customer. Within the given time frame of the simulation, it was observed that an expected number of patients , approximately 22-33, were being served in each run. Additionally, other variables and values were tested during each run to ensure that there were no unrealistic or absurd results.
Validation of the simulation model for the emergency room scenario presented more challenges since there was no available dataset for comparison. However, the flow of patiants and the reported wait times closely resembled personal experiences in emergency room , which provided a basis for validation. Although the simulation was based on a hypothetical situation, the simulated wait times and the overall behavior of the system were deemed reasonable and aligned with real-world observations. While validation against empirical data would have strengthened the reliability of the simulation, the model's outputs and behaviors were consistent with expectations and personal experiences. The simulation can be considered a useful tool for exploring and analyzing the emergency room scenario, providing insights and supporting decision making in the absence of real-world data.
# Display the DataFrame with improved formatting
styled_df = df.style.set_properties(**{'text-align': 'center'})
styled_df = styled_df.set_table_styles([{'selector': 'th', 'props': [('text-align', 'center')]}])
# Show the styled DataFrame
display(styled_df)
| Patient ID | Service | Arrival Time | Token Time | Service Start Time | Service Stop Time | Token Time (Secs) | Service Time (Mins) | Wait Time (Mins) | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Nurse | 7.895147 | 14.291125 | 14.291125 | 24.291125 | 857.467493 | 10 | 10.000000 |
| 1 | 2 | Doctor | 7.107478 | 12.328062 | 12.328062 | 27.328062 | 739.683690 | 15 | 15.872620 |
| 2 | 4 | Nurse | 18.381799 | 25.080854 | 25.080854 | 35.080854 | 1504.851215 | 10 | 16.952347 |
| 3 | 3 | Doctor | 16.327093 | 23.297101 | 23.297101 | 38.297101 | 1397.826048 | 15 | 20.418614 |
| 4 | 5 | Nurse | 24.221167 | 31.660643 | 31.660643 | 41.660643 | 1899.638550 | 10 | 26.381226 |
| 5 | 6 | Doctor | 33.933940 | 41.649656 | 41.649656 | 56.649656 | 2498.979336 | 15 | 42.062173 |
| 6 | 7 | Nurse | 39.470757 | 47.862859 | 47.862859 | 57.862859 | 2871.771530 | 10 | 42.655131 |
| 7 | 8 | Nurse | 43.001911 | 48.622759 | 48.622759 | 58.622759 | 2917.365524 | 10 | 46.037264 |
| 8 | 9 | Nurse | 57.653141 | 68.970324 | 68.970324 | 78.970324 | 4138.219461 | 10 | 56.653208 |
| 9 | 10 | Nurse | 62.798065 | 69.767587 | 69.767587 | 79.767587 | 4186.055194 | 10 | 64.123082 |
| 10 | 11 | Nurse | 77.346339 | 83.792519 | 83.792519 | 93.792519 | 5027.551122 | 10 | 80.078263 |
| 11 | 12 | Nurse | 98.123949 | 102.794923 | 102.794923 | 112.794923 | 6167.695377 | 10 | 99.086593 |
| 12 | 13 | Nurse | 101.131430 | 108.027799 | 108.027799 | 118.027799 | 6481.667940 | 10 | 102.747919 |
| 13 | 14 | Doctor | 99.544287 | 104.576440 | 104.576440 | 119.576440 | 6274.586375 | 15 | 108.414100 |
| 14 | 16 | Doctor | 115.047347 | 121.135956 | 121.135956 | 136.135956 | 7268.157363 | 15 | 118.600642 |
| 15 | 15 | Doctor | 113.032090 | 123.165592 | 123.165592 | 138.165592 | 7389.935530 | 15 | 117.575891 |
| 16 | 19 | Nurse | 129.436599 | 133.388989 | 133.388989 | 143.388989 | 8003.339362 | 10 | 129.388659 |
| 17 | 18 | Nurse | 127.840222 | 136.432661 | 136.432661 | 146.432661 | 8185.959633 | 10 | 127.613154 |
| 18 | 17 | Doctor | 115.625100 | 123.380376 | 136.135956 | 151.135956 | 7402.822560 | 15 | 134.109272 |
| 19 | 20 | Nurse | 137.626459 | 145.946781 | 145.946781 | 155.946781 | 8756.806885 | 10 | 135.753020 |
| 20 | 21 | Nurse | 143.562693 | 154.543990 | 154.543990 | 164.543990 | 9272.639415 | 10 | 143.336282 |
| 21 | 22 | Doctor | 164.584971 | 174.335043 | 174.335043 | 189.335043 | 10460.102573 | 15 | 173.150985 |
| 22 | 23 | Doctor | 172.230286 | 178.376576 | 178.376576 | 193.376576 | 10702.594581 | 15 | 177.488951 |
| 23 | 24 | Nurse | 179.055948 | 187.108823 | 187.108823 | 197.108823 | 11226.529384 | 10 | 178.595377 |
| 24 | 25 | Nurse | 192.457595 | 200.718574 | 200.718574 | 210.718574 | 12043.114468 | 10 | 194.528510 |
| 25 | 26 | Doctor | 229.935734 | 237.144246 | 237.144246 | 252.144246 | 14228.654741 | 15 | 235.660090 |
| 26 | 28 | Doctor | 235.623074 | 239.180105 | 239.180105 | 254.180105 | 14350.806270 | 15 | 242.494385 |
| 27 | 30 | Nurse | 247.773697 | 253.996961 | 253.996961 | 263.996961 | 15239.817632 | 10 | 247.338318 |
| 28 | 27 | Doctor | 234.429554 | 242.094189 | 252.144246 | 267.144246 | 14525.651355 | 15 | 248.685813 |
| 29 | 29 | Doctor | 242.981532 | 248.575676 | 254.180105 | 269.180105 | 14914.540549 | 15 | 252.118567 |
| 30 | 31 | Nurse | 250.840409 | 259.253423 | 259.253423 | 269.253423 | 15555.205369 | 10 | 254.446699 |
| 31 | 33 | Nurse | 259.067023 | 263.040781 | 263.996961 | 273.996961 | 15782.446836 | 10 | 259.648308 |
| 32 | 32 | Nurse | 259.541892 | 263.699972 | 269.253423 | 279.253423 | 15821.998349 | 10 | 263.819010 |
| 33 | 34 | Nurse | 278.221201 | 284.628975 | 284.628975 | 294.628975 | 17077.738515 | 10 | 280.867469 |
Bar Chart for Service Type
service_counts = df['Service'].value_counts()
plt.figure(figsize=(10, 6))
plt.bar(service_counts.index, service_counts.values)
plt.xlabel('Service Type')
plt.ylabel('Number of Patients')
plt.title('Distribution of Service Types')
plt.grid(True)
plt.show()
# Calculate performance metrics
avg_waiting_time = df['Wait Time (Mins)'].mean()
avg_treatment_time = df['Service Time (Mins)'].mean()
doctor_utilization = np.mean(utilization_doctors)
nurse_utilization = np.mean(utilization_nurses)
# Print performance metrics
print(f"Average waiting time: {avg_waiting_time:.2f} minutes")
print(f"Average treatment time: {avg_treatment_time:.2f} minutes")
print(f"Doctor utilization: {doctor_utilization:.2%}")
print(f"Nurse utilization: {nurse_utilization:.2%}")
Average waiting time: 136.67 minutes Average treatment time: 11.91 minutes Doctor utilization: 29.44% Nurse utilization: 35.00%
Response curves for average waiting time, average treatment time, doctor utilization, and nurse utilization as a function of the number of doctors. Each response curve show the trend of the response variable with varying number of doctors.
# Line Chart of Resource Utilization
plt.figure(figsize=(10, 6))
plt.plot(range(len(utilization_doctors)), utilization_doctors, label='Doctor Utilization')
plt.plot(range(len(utilization_nurses)), utilization_nurses, label='Nurse Utilization')
plt.xlabel('Time')
plt.ylabel('Utilization')
plt.title('Resource Utilization Over Time')
plt.legend()
plt.grid(True)
plt.show()
# Stacked Bar Chart of Service Distribution
time_intervals = range(0, 100, 10) # Customize the time intervals as needed
doctor_counts = df[df['Service'] == 'Doctor'].groupby(pd.cut(df['Service Start Time'], time_intervals)).size()
nurse_counts = df[df['Service'] == 'Nurse'].groupby(pd.cut(df['Service Start Time'], time_intervals)).size()
plt.figure(figsize=(10, 6))
plt.bar(time_intervals[:-1], doctor_counts, label='Doctor')
plt.bar(time_intervals[:-1], nurse_counts, bottom=doctor_counts, label='Nurse')
plt.xlabel('Time Intervals')
plt.ylabel('Number of Patients')
plt.title('Service Distribution Over Time')
plt.legend()
plt.grid(True)
plt.show()
# Box Plot of Waiting Times
plt.figure(figsize=(8, 6))
plt.boxplot(df['Wait Time (Mins)'])
plt.ylabel('Waiting Time (minutes)')
plt.title('Distribution of Waiting Times')
plt.grid(True)
plt.show()
# Histogram of Treatment Times by Service
plt.figure(figsize=(10, 6))
plt.hist(df[df['Service'] == 'Doctor']['Service Time (Mins)'], bins=20, alpha=0.8, color='blue', label='Doctor')
plt.hist(df[df['Service'] == 'Nurse']['Service Time (Mins)'], bins=20, alpha=0.8, color='green', label='Nurse')
plt.xlabel('Treatment Time (minutes)')
plt.ylabel('Frequency')
plt.title('Distribution of Treatment Times by Service')
plt.legend()
plt.grid(True)
plt.show()
# Animate the real-time visualization
ani = animation.FuncAnimation(fig, update, frames=range(0, 100), interval=100)
plt.show()
The simulation based optimization of resource allocation and patient flow in an outpatient setting, particularly in the emergency room, offers a promising approach to improve healthcare system efficiency and patient outcomes. By utilizing simulation modeling techniques, we can analyze and evaluate different scenarios, assess critical performance metrics, and make data-driven decisions. The developed simulation model effectively captures patient dynamics, resource utilization, and key factors affecting patient flow. The results provide valuable insights into waiting times, treatment times, and resource utilization, enabling healthcare administrators to identify areas for improvement and optimize resource allocation. This simulation model holds great potential in enhancing healthcare system efficiency and supporting informed decision-making for improved patient care.