Stage 1 model

import pandas as pd
import plotly.express as px

from ambdes import Model, SimConfig, ambsys, Results, Runner

Run model for 100 minutes with log messages

TODO: Convert this into logging tests which compare patient results with record in the vidigi logger.

ambsys_data = ambsys(
    csv_path="data/AmbSYS-to-Mar-2026-UI7FG.csv",
    org_code="RYF",
    month="3",
    year=2026,
)
ambsys_data
{'mean_iat_min': {'C1': 5.02646098412341,
  'C2': 1.042868823735545,
  'C3': 2.4551754482455177,
  'C4': 135.6838905775076},
 'mean_handover_time_min': 29.916666666666668,
 'p90_handover_time_min': 51.28333333333333,
 'sd_handover_time_min': 11.385314934097805}
config = SimConfig(
    ambsys_data=ambsys_data,
)
config.n_ambulances=1
model = Model(run_number=0, config=config)
model.run()
log = model.logger.to_dataframe()
print(model.patients[0].__dict__)
log[log["entity_id"] == 1]
{'patient_id': 1, 'category': 'C2', 'call_timestamp': 0.83753254853899, 'response_time': 4.073026530173381}
entity_id event_type event time run_number resource_id
0 1 arrival_departure arrival 0.837533 0 NaN
1 1 queue ambulance_wait_begins 0.837533 0 NaN
2 1 resource_use ambulance_arrives 0.837533 0 1.0
271 1 resource_use_end ambulance_available 96.261361 0 1.0
272 1 arrival_departure depart 96.261361 0 NaN
print(model.patients[1].__dict__)
log[log["entity_id"] == 2]
{'patient_id': 2, 'category': 'C2', 'call_timestamp': 1.2359102250485265, 'response_time': None}
entity_id event_type event time run_number resource_id
3 2 arrival_departure arrival 1.235910 0 NaN
4 2 queue ambulance_wait_begins 1.235910 0 NaN
273 2 resource_use ambulance_arrives 96.261361 0 1.0

Run model for longer and inspect patient times

config = SimConfig(
    ambsys_data=ambsys_data,
    run_length=10080,  # One week
)
model = Model(run_number=0, config=config)
model.run()
df = pd.DataFrame(
    {
        "response_time": [p.response_time for p in model.patients],
        "category": [p.category for p in model.patients],
    }
)

fig = px.histogram(
    df,
    x="response_time",
    facet_col="category",
    nbins=50,
    category_orders={"category": ["C1", "C2", "C3", "C4"]},
    labels={
        "response_time": "Response time (minutes)",
        "category": "Category",
    },
    title="Distribution of response times by category",
)

# fig.update_yaxes(
#    matches=None,
#    showticklabels=True,
# )
fig.layout.yaxis.title.text = "Number of patients"

fig.show()
for cat in ["C1", "C2", "C3", "C4"]:
    fig = px.histogram(
        df[df["category"] == cat],
        x="response_time",
        nbins=20,
        title=f"Response times: {cat}",
        labels={"response_time": "Response time (minutes)"},
    )
    fig.update_yaxes(title_text="Number of patients")
    fig.show()

Average results

runner = Runner(config)
results = runner.run_reps()
results["patients"]
run patient_id category call_timestamp response_time
0 0 1 C2 0.837533 4.073027
1 0 2 C2 1.235910 7.424848
2 0 3 C2 1.615698 3.004763
3 0 4 C2 2.167870 1.554013
4 0 5 C3 3.757094 7.215603
... ... ... ... ... ...
79223 4 15974 C2 10076.322889 NaN
79224 4 15975 C2 10076.676822 1.634170
79225 4 15976 C2 10076.729948 NaN
79226 4 15977 C2 10077.762373 0.346001
79227 4 15978 C3 10078.551056 NaN

79228 rows × 5 columns

results["run"]
category n_patients mean_response_time run
0 C1 2016 10.043064 0
1 C2 9861 9.874453 0
2 C3 3956 9.786215 0
3 C4 76 9.607152 0
4 C1 2024 10.011695 1
5 C2 9570 9.819834 1
6 C3 4150 10.001758 1
7 C4 69 9.693440 1
8 C1 2027 10.179896 2
9 C2 9662 10.040621 2
10 C3 4122 9.891074 2
11 C4 84 10.730120 2
12 C1 2003 9.810804 3
13 C2 9621 9.895342 3
14 C3 3925 10.219530 3
15 C4 84 10.448439 3
16 C1 1997 9.739551 4
17 C2 9758 9.935218 4
18 C3 4136 10.249755 4
19 C4 87 11.583257 4
results["overall"]
category mean_n_patients mean_response_time
0 C1 2013.4 9.957002
1 C2 9694.4 9.913094
2 C3 4057.8 10.029666
3 C4 80.0 10.412481
config.n_ambulances = 1
config.run_length = 50_000
config.log_to_console = False
runner = Runner(config)
results = runner.run_single(run_number=0)
results["run"]
category n_patients mean_response_time run
0 C1 10122 24882.128638 0
1 C2 47831 24981.944913 0
2 C3 20204 25145.029568 0
3 C4 392 33869.408274 0
results["patients"].head(20)
run patient_id category call_timestamp response_time
0 0 1 C2 0.837533 4.073027
1 0 2 C2 1.235910 102.450299
2 0 3 C2 1.615698 200.196118
3 0 4 C2 2.167870 306.023344
4 0 5 C3 3.757094 405.667583
5 0 6 C3 4.039516 522.338683
6 0 7 C2 5.075784 619.716820
7 0 8 C3 6.931662 745.773423
8 0 9 C2 7.120883 847.157297
9 0 10 C2 8.110538 949.657591
10 0 11 C3 12.243683 1038.956183
11 0 12 C2 12.455700 1166.599226
12 0 13 C2 13.068331 1274.419547
13 0 14 C3 13.186539 1381.522168
14 0 15 C3 14.411677 1514.495141
15 0 16 C3 15.247608 1641.393826
16 0 17 C2 15.976105 1735.483518
17 0 18 C1 16.554789 1870.729585
18 0 19 C3 17.581974 1967.770161
19 0 20 C2 18.574372 2064.439026
results["model"].logger.to_dataframe().head(30)
entity_id event_type event time run_number resource_id
0 1 arrival_departure arrival 0.837533 0 NaN
1 1 queue ambulance_wait_begins 0.837533 0 NaN
2 1 resource_use ambulance_arrives 0.837533 0 1.0
3 2 arrival_departure arrival 1.235910 0 NaN
4 2 queue ambulance_wait_begins 1.235910 0 NaN
5 3 arrival_departure arrival 1.615698 0 NaN
6 3 queue ambulance_wait_begins 1.615698 0 NaN
7 4 arrival_departure arrival 2.167870 0 NaN
8 4 queue ambulance_wait_begins 2.167870 0 NaN
9 5 arrival_departure arrival 3.757094 0 NaN
10 5 queue ambulance_wait_begins 3.757094 0 NaN
11 6 arrival_departure arrival 4.039516 0 NaN
12 6 queue ambulance_wait_begins 4.039516 0 NaN
13 7 arrival_departure arrival 5.075784 0 NaN
14 7 queue ambulance_wait_begins 5.075784 0 NaN
15 8 arrival_departure arrival 6.931662 0 NaN
16 8 queue ambulance_wait_begins 6.931662 0 NaN
17 9 arrival_departure arrival 7.120883 0 NaN
18 9 queue ambulance_wait_begins 7.120883 0 NaN
19 10 arrival_departure arrival 8.110538 0 NaN
20 10 queue ambulance_wait_begins 8.110538 0 NaN
21 11 arrival_departure arrival 12.243683 0 NaN
22 11 queue ambulance_wait_begins 12.243683 0 NaN
23 12 arrival_departure arrival 12.455700 0 NaN
24 12 queue ambulance_wait_begins 12.455700 0 NaN
25 13 arrival_departure arrival 13.068331 0 NaN
26 13 queue ambulance_wait_begins 13.068331 0 NaN
27 14 arrival_departure arrival 13.186539 0 NaN
28 14 queue ambulance_wait_begins 13.186539 0 NaN
29 15 arrival_departure arrival 14.411677 0 NaN