Skip to content

Commit ce57c2a

Browse files
committed
single madcap plot fix error
1 parent 11e888c commit ce57c2a

2 files changed

Lines changed: 77 additions & 17 deletions

File tree

README.md

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,59 @@
3131

3232
## Summary
3333

34-
patientflow, a Python package, converts patient-level predictions into output that is useful for bed managers in hospitals. If you have a predictive model of some outcome for a patient, like admission or discharge from hospital, you can use patientflow to create bed count distributions for a cohort of patients. The package was developed for University College London Hospitals (UCLH) NHS Trust to predict the number of emergency admissions within the next eight hours. The methods generalise to any problem where it is useful to convert patient-level predictions into outcomes for a whole cohort of patients at a point in time. The repository includes a synthetic dataset and a series of notebooks demonstrating the use of the package.
34+
patientflow, a Python package, converts patient-level predictions into output that is useful for bed managers in hospitals. If you have a predictive model of some outcome for a patient, like admission or discharge from hospital, you can use patientflow to create bed count distributions for a cohort of patients.
35+
36+
The package was developed for University College London Hospitals (UCLH) NHS Trust to predict the number of emergency admissions within the next eight hours. The methods generalise to any problem where it is useful to convert patient-level predictions into outcomes for a whole cohort of patients at a point in time. The repository includes a synthetic dataset and a series of notebooks demonstrating the use of the package.
3537

3638
## Background
3739

3840
I'm [Zella King](https://github.com/zmek/), a health data scientist in the Clinical Operational Research Unit (CORU) at University College London. Since 2020, I have worked with University College London Hospital (UCLH) on practical tools to improve patient flow through the hospital.
3941

40-
Hospital bed managers constantly monitor whether they have sufficient beds to meet demand. At specific points during the day they count numbers of inpatients likely to leave, and numbers of new admissions. Their projections about short-term changes are vital because if they anticipate a shortage of bedsd, bed managers must take swift action to mitigate the situation.
42+
Hospital bed managers constantly monitor whether they have sufficient beds to meet demand. At specific points during the day they count numbers of inpatients likely to leave, and numbers of new admissions. Their projections about short-term changes are vital because if they anticipate a shortage of beds, bed managers must take swift action to mitigate the situation.
4143

4244
With a team from UCLH, I developed a predictive tool that is now in daily use by bed managers at the hospital.
4345

4446
The tool we built for UCLH takes a 'snapshot' of patients in the hospital at a point in time, and using data from the hospital's electronic record system, predicts the number of emergency admissions in the next 8 or 12 hours. We are working on predicting discharges in the same way.
4547

4648
The key principle is that we take data on hospital visits that are unfinished, and predict whether some outcome (admission from A&E, discharge from hospital, or transfer to another clinical specialty) will happen to each of those patients in a window of time. What the outcome is doesn't really matter; the same methods can be used.
4749

48-
But the true utility of our approach - and the thing that makes it very generalisable - is that we build up from the patient-level predictions into a predictions for a whole cohort of patients at a point in time. That step is what creates useful information for bed managers. They are less interested in whether any individual will need a bed and more interested in the overall number of beds needed, and in which parts of the hospital. They trade in cohort-level data - numbers of beds needed for patients in A&E, number of transfers out of the acute medical unit to other wards, number of patients leaving a certain ward. And they are always only looking a few hours ahead.
50+
The utility of our approach - and the thing that makes it very generalisable - is that we build up from the patient-level predictions into a predictions for a whole cohort of patients at a point in time. That step is what creates useful information for bed managers. They are less interested in whether any individual will need a bed and more interested in the overall number of beds needed, and in which parts of the hospital. They trade in cohort-level data - numbers of beds needed for patients in A&E, number of transfers out of the acute medical unit to other wards, number of patients leaving a certain ward. And they are always only looking a few hours ahead.
4951

5052
The methods that we developed for UCLH can be used in any hospitals setting where point-in-time predictions about cohorts of patients are useful. We are sharing these methods because we want to make it easier for researchers and analysts in healthcare to create information products that are useful for site and operations managers in hospitals.
5153

5254
We provide a Python package to make this convenient. The repository includes a set of notebooks with code written in Python and commentary on how to use the package.
5355

5456
We also show a fully worked example of how to predict emergency demand for beds, and demonstrate how we tailored the approach, using the package, to the specific demands of bed managers at UCLH.
5557

58+
## What patientflow is for:
59+
60+
* Converting individual patient predictions to cohort-level insights: The core purpose is transforming patient-level predictions into aggregate bed count distributions for groups of patients.
61+
* Short-term operational planning: The package is designed for bed managers who need to make decisions within an 4-16 hour timeframe.
62+
* Use with real-time data: The modelling is intended to be used with data streamed from an electronic health record in near to real-time
63+
* Point-in-time analyses: It works by taking "snapshots" of hospital populations and making projections from those specific moments.
64+
* Various patient flow outcomes: While developed for emergency admissions, it generalises to other outcomes like discharges or transfers between units.
65+
* Hospital resource management: It helps operational staff anticipate bed needs across different hospital areas.
66+
* Working with unfinished patient journeys: It is designed for making predictions when outcomes are still pending as as yet unknown.
67+
* Demonstrating predictive model development: The package includes examples that show how to create the predictive models for patient outcomes.
68+
69+
## What patientflow is NOT for:
70+
71+
* Long-term capacity planning: The package focuses on immediate operational needs (hours ahead), not strategic planning over weeks or months.
72+
* Individual patient management: It's not designed for clinical decision-making about specific patients.
73+
* Detailed clinical pathway analysis: It doesn't model complex clinical pathways or detailed patient journeys.
74+
* General hospital analytics: It's specifically focused on bed management, not broader hospital analytics like financial planning or clinical quality metrics.
75+
* Finished/historical patient analysis: While historical data might train underlying models, the package itself focuses on active cases and future projections.
76+
* Replacing human judgment: It provides decision support but isn't meant to automate bed management decisions completely.
77+
78+
## Mathematical assumptions underlying the conversion from individual to cohort predictions:
79+
80+
* Independence of patient outcomes: The package assumes that individual patient outcomes are conditionally independent given the features used in prediction.
81+
* Symbolic probability generation: The conversion uses symbolic mathematics (via SymPy) to construct a probability generating function that represents the exact distribution of possible cohort outcomes.
82+
* Bernoulli outcome model: Each patient outcome is modeled as a Bernoulli trial with its own probability, and the package computes the exact probability distribution for the sum of these independent trials.
83+
* Coefficient extraction approach: The method works by expanding a symbolic expression and extracting coefficients corresponding to each possible cohort outcome count.
84+
* Optional weighted aggregation: When converting individual probabilities to cohort-level predictions, the package allows for weighted importance of individual predictions, modifying the contribution of each patient to the overall distribution in specific contexts (eg admissions to different specialties).
85+
* Discrete outcome space: The package assumes outcomes can be represented as discrete counts (e.g., number of admissions) rather than continuous values.
86+
5687
## Getting started
5788

5889
- Exploration: Start with the [notebooks README](notebooks/README.md) to get an outline of what is included in the notebooks, and read the [patientflow README](src/patientflow/README.md) for an overview of the Python package

src/patientflow/viz/madcap_plot.py

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,11 @@ def generate_madcap_plots(
122122

123123
fig, axes = plt.subplots(num_rows, num_cols, figsize=(num_plots * 5, 4))
124124

125-
# Ensure axes is always a 2D array
126-
if num_rows == 1:
127-
axes = axes.reshape(1, -1)
128-
129-
for i, trained_model in enumerate(trained_models_sorted):
125+
# Handle the case of a single plot differently
126+
if num_plots == 1:
127+
# When there's only one plot, axes is a single Axes object, not an array
128+
trained_model = trained_models_sorted[0]
129+
130130
# Use calibrated pipeline if available, otherwise use regular pipeline
131131
if (
132132
hasattr(trained_model, "calibrated_pipeline")
@@ -148,16 +148,46 @@ def generate_madcap_plots(
148148

149149
X_test = add_missing_columns(pipeline, X_test)
150150
predict_proba = pipeline.predict_proba(X_test)[:, 1]
151+
152+
# Plot directly on the single axes
153+
plot_madcap_subplot(predict_proba, y_test, prediction_time, axes)
154+
else:
155+
# For multiple plots, ensure axes is always a 2D array
156+
if num_rows == 1:
157+
axes = axes.reshape(1, -1)
158+
159+
for i, trained_model in enumerate(trained_models_sorted):
160+
# Use calibrated pipeline if available, otherwise use regular pipeline
161+
if (
162+
hasattr(trained_model, "calibrated_pipeline")
163+
and trained_model.calibrated_pipeline is not None
164+
):
165+
pipeline = trained_model.calibrated_pipeline
166+
else:
167+
pipeline = trained_model.pipeline
168+
169+
prediction_time = trained_model.training_results.prediction_time
170+
171+
# Get test data for this prediction time
172+
X_test, y_test = get_snapshots_at_prediction_time(
173+
df=test_visits,
174+
prediction_time=prediction_time,
175+
exclude_columns=exclude_from_training_data,
176+
single_snapshot_per_visit=False,
177+
)
151178

152-
row = i // num_cols
153-
col = i % num_cols
154-
plot_madcap_subplot(predict_proba, y_test, prediction_time, axes[row, col])
179+
X_test = add_missing_columns(pipeline, X_test)
180+
predict_proba = pipeline.predict_proba(X_test)[:, 1]
155181

156-
# Hide any unused subplots
157-
for j in range(i + 1, num_rows * num_cols):
158-
row = j // num_cols
159-
col = j % num_cols
160-
axes[row, col].axis("off")
182+
row = i // num_cols
183+
col = i % num_cols
184+
plot_madcap_subplot(predict_proba, y_test, prediction_time, axes[row, col])
185+
186+
# Hide any unused subplots
187+
for j in range(i + 1, num_rows * num_cols):
188+
row = j // num_cols
189+
col = j % num_cols
190+
axes[row, col].axis("off")
161191

162192
plt.tight_layout()
163193

@@ -175,7 +205,6 @@ def generate_madcap_plots(
175205
plt.show()
176206
plt.close(fig)
177207

178-
179208
def plot_madcap_subplot(predict_proba, label, _prediction_time, ax):
180209
"""
181210
Plots a single MADCAP subplot showing cumulative predicted and observed admissions.

0 commit comments

Comments
 (0)