Skip to main content

Healthcare — patient flow

The problem. Patients move through a hospital in a chain — emergency department → assessment → ward → discharge — and the whole thing jams if any link backs up. A slow discharge process leaves no free beds, which backs up the ward, which strands patients in the ED corridor. Operations needs to see where the flow is actually stuck — which stage's wait is growing — so they fix the real bottleneck, not the loudest one.

What we solve here is finding the bottleneck stage in the patient journey, using the flow data the hospital already has.

Set up demo data

New workspace? Run this once (Python) to create the ED time-to-bed series with a day of readings whose wait grows through the afternoon. Safe to re-run.

import datahub_sdk, numpy as np, pandas as pd

client = datahub_sdk.DataHubClient.from_env()

client.timeseries.create([datahub_sdk.TimeSeries(external_id="ed_time_to_bed_minutes", name="ED time to bed (min)", unit="min", value_type="float")])
idx = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=24, freq="1h")
ttb = 90 + np.linspace(0, 120, 24) + np.random.normal(0, 10, 24)
client.timeseries.insert_from_lists(timestamps=idx, values=ttb, ts="ed_time_to_bed_minutes")

1. Roll up the wait at each stage

ED wait, time-to-bed, and length-of-stay are series. Hourly and daily roll-ups show which stage is slow right now versus its normal — the bottleneck. See Query & aggregate.

var filter = new RetrieveFilter();
filter.setExternalId("ed_time_to_bed_minutes");
filter.setStart(ZonedDateTime.now().minusDays(1));
filter.setEnd(ZonedDateTime.now());
filter.setAggregates(List.of("avg", "max"));
filter.setGranularity("1h");

var request = new DataRetriever<RetrieveFilter>();
request.setItems(List.of(filter));

client.timeseries().retrieve(request).getItems().get(0).getDatapoints()
.forEach(p -> trackStage("time_to_bed", p.getTimestamp(), p.getValue()));

2. Alert when the ED breaches its target

When the ED's four-hour-target breach rate climbs past a threshold, raise a flow_breach event so the patient-flow coordinator can pull the right lever — expedite discharges, open an escalation area. See Turn readings into events.

EventModel breach = new EventModel();
breach.setExternalId("flow_breach_ed_" + System.currentTimeMillis());
breach.setType("flow_breach");
breach.setStatus("open");
breach.setMetadata(Map.of("stage", "emergency_department", "breach_rate_pct", "22"));
breach.setEventTime(ZonedDateTime.now());
client.events().create(List.of(breach));

3. Forecast the surge

Admissions follow strong daily and seasonal patterns — forecast them to staff ahead of the surge instead of reacting to it.

See the result

Chart the hourly wait — the climb is the bottleneck the coordinator acts on:

import matplotlib.pyplot as plt

rf = datahub_sdk.RetrieveFilter(ts="ed_time_to_bed_minutes",
start=pd.Timestamp.now(tz="UTC") - pd.Timedelta(days=1), end=pd.Timestamp.now(tz="UTC"),
aggregates=["avg"], granularity="1h")
v = [dp.average for dp in client.timeseries.retrieve_datapoints(rf)[0].get_datapoints()]
plt.plot(v); plt.title("ED time-to-bed (min), hourly average"); plt.show()

See also