Published in

Analytics Vidhya

# Overview

Being a data scientist in the healthcare domain and a true mathematician at heart, I have always been fascinated with how complex systems such as that of hospitals efficiently utilize optimization algorithms to make the maximum use of their skilled manpower ensuring highest standards of patient care. This article is based on a case study taken from Cedars-Sinai Hospital in Los Angeles, CA and how they enabled their organization in determining an efficient weekly schedule to assign operating rooms (ORs) to different departments in a hospital with limited resources.

# Challenges

In most hospitals around the country, surgeons are paid based the number of surgeries they perform i.e. ‘fee-for-service’ basis. Unplanned alteration of the OR schedule can directly affect their income. Thus creating an efficient schedule eventually becomes a highly political process within hospitals. Further, each department sets their own targets in terms of number of surgery hours per week which is likely to create internal clashes if the management fails to create an efficient schedule. This calls for the need to apply a mathematical acumen to present an optimized plan that strikes a healthy balance between all departments in the hospital without jeopardizing the quality of treatment. Let us now have a closer look at the sample problem.

# Problem Definition

Cedars-Sinai hospital has 10 operating rooms staffed from Monday to Friday for 8 hours each day. That is,

10 ORs * 5 days * 8 hours = 400 surgery hours to be efficiently divided among 5 major departments in the hospital namely:

1. Department of Gynecology
2. Department of Cardiology
3. Department of Neurosurgery
4. Department of Orthopedics, and
5. Department of General Surgery

We will be solving the above case study by formulating an LPP in Python. All codes and data used in this article can be accessed via my Git repo (https://github.com/mohiteprathamesh1996/Hospital-OR-Scheduling).

Importing the necessary dependencies,

import pandas as pd
import numpy as np
import itertools
from pulp import *
from IPython.core.display import display, HTMLdef display_side_by_side(dfs:list, captions:list):
"""Display tables side by side to save vertical space
Input:
dfs: list of pandas.DataFrame
captions: list of table captions
"""
output = ""
combined = dict(zip(captions, dfs))
for caption, df in combined.items():
output += df.style.set_table_attributes("style='display:inline'").set_caption(caption)._repr_html_()
output += "\xa0\xa0\xa0"
display(HTML(output))

import warnings
warnings.filterwarnings("ignore")

As discussed earlier, each department has set the target for number surgery hours for the week as follows:

target_hours_per_week = pd.read_excel("OR Scheduling.xlsx",         sheet_name="Target Hours per Week")target_hours_per_week.set_index(["Department"], inplace=True)

Now, each department has a limited number of surgery teams available each day as:

teams_available_per_week = pd.read_excel("OR Scheduling.xlsx", sheet_name="Available teams per week")teams_available_per_week.set_index(["Department"], inplace=True)

Further, every department can have a maximum daily requirement on the number of ORs for the week as:

max_daily_OR_requirement = pd.read_excel("OR Scheduling.xlsx", sheet_name="Max Daily OR requirements")max_daily_OR_requirement.set_index(["Department"], inplace=True)

# Deciding Factors

Based on the above available information, the hospital management has to decide how many ORs should each department be assigned on each day for the entire week?

Let the decision variable in this case be:

X (j, k) = Number of ORs allocated to department ‘j’ on day ‘k’ (WILL BE AN INTEGER!)

where j ∈ (‘Genecology’, ‘Cardiology’, ‘Neurosurgery’, ‘Orthopedics’,
‘General Surgery’)

and k ∈ (‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Thursday’, ‘Friday’)

teams = teams_available_per_week.index.to_list()
day_of_week = teams_available_per_week.columns.to_list()
var_dict = LpVariable.dicts(
"Allotment",
[(dept, day) for dept in teams for day in day_of_week],
lowBound = 0,
cat = "Integer")

# Objective Function

The objective of the hospital management is to assign ORs so as to maximize the proportion of target number of surgery hours per week for each department. That is, if department ‘j’ is aiming for ‘t(j)’ hours of surgery for that week then we have to Maximize: Σ(8 * X(j, k))/t(j) summed over all departments for the week. This ensures maximum utilization!

model = LpProblem(
"Optimized Allocation of Hospital Operating Rooms",
LpMaximize)

model += lpSum(
[8*var_dict[(team,days)]/target_hours_per_week.loc[team, "Target_Hours"] for team in teams for days in day_of_week])

# Constraints

At any given day, the hospital has at the most 10 ORs available which should be scheduled for surgery across these 5 departments.

Thus ,

0 ≤ Σ X (j, k) ≤ 10

where ‘j’ ∈ (‘Genecology’, ‘Cardiology’, ‘Neurosurgery’, ‘Orthopedics’,
‘General Surgery’) and ‘k’ is held constant for that day of the week.

for day in day_of_week:
model += lpSum([var_dict[(team, day)] for team in teams]) <= 10

Next, the number of allocated ORs for a department cannot exceed the number of available teams that day for that department.

That is,

0 ≤ X (j, k) ≤ Number of teams available for department ‘j’ on day ‘k’

for team in teams:
for day in day_of_week:
model += var_dict[(team, day)] <= teams_available_per_week.loc[team, day]

Lastly, we must satisfy the daily maximum requirement of each department.

Therefore,

0 ≤ X (j, k) ≤ Maximum ORs required by department ‘j’ on day ‘k’

for team in teams:
for day in day_of_week:
model += var_dict[(team, day)] <= max_daily_OR_requirement.loc[team, day]

# Optimal Solution

Now that we have defined the decision variables, formulated the objective function along with the constraints, we can obtain the optimal solution as follows. The code below outputs the most mathematically efficient OR schedule!

model.solve()
if LpStatus[model.status]=="Optimal":
optimal_soln = pd.DataFrame(
[(v.name,
int(v.varValue)) for v in model.variables() if v.varValue!=0],
columns=["Decisions", "# ORs Allocated"])

display_side_by_side(dfs = [optimal_soln[optimal_soln["Decisions"].str.contains(d)].reset_index(drop=True) for d in day_of_week],
captions = ["Operating Room Schedule for "+i for i in day_of_week])

--

--

## More from Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com