Skip to content

Commit 187c5fe

Browse files
committed
Add several more complex animation examples
1 parent a1c3e0e commit 187c5fe

65 files changed

Lines changed: 31716 additions & 1775 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
/.quarto/
2+
*.pyc
3+
*output_generate_animation_df*.csv
4+
*output_reshape_for_animations*.csv

animation_examples/complex_community_booking/__init__.py

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
,clinic_1,clinic_2,clinic_3,clinic_4,clinic_5,clinic_6,clinic_7,clinic_8,clinic_9,clinic_10,clinic_11
2+
clinic_1,1,1,1,0,0,0,0,0,0,0,0
3+
clinic_2,1,1,1,0,0,0,0,0,0,0,0
4+
clinic_3,1,1,1,1,1,0,0,0,1,0,0
5+
clinic_4,0,0,0,1,1,1,0,0,0,0,0
6+
clinic_5,0,0,0,1,1,1,0,0,0,0,0
7+
clinic_6,0,0,0,1,1,1,1,0,0,0,0
8+
clinic_7,0,0,0,0,0,1,1,0,0,0,0
9+
clinic_8,0,0,0,0,0,0,0,1,1,1,1
10+
clinic_9,0,0,1,0,0,0,0,1,1,1,0
11+
clinic_10,0,0,0,0,0,0,0,0,1,1,0
12+
clinic_11,0,0,0,0,0,0,0,1,0,0,1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
clinic,prop,referred_out,dna
2+
1,0.111491288098121,0.456,0.2
3+
2,0.026346265199768,0.428,0.25
4+
3,0.196004632310365,0.489,0.25
5+
3,0.062220350581671,0.296,0.2
6+
4,0.025451387061115,0.275,0.23
7+
5,0.081302310891193,0.091,0.21
8+
6,0.05714060114755,0.162,0.24
9+
7,0.04927093751645,0.129,0.17
10+
8,0.125625098699795,0.41,0.21
11+
9,0.255356108859294,0.361,0.3
12+
10,0.009791019634679,0.123,0.2
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
clinic_1,clinic_2,clinic_3,clinic_4,clinic_5,clinic_6,clinic_7,clinic_8,clinic_9,clinic_10,clinic_11
2+
4,1,7,3,1,5,3,3,5,11,1
3+
4,1,6,3,1,5,3,3,5,10,0
4+
4,1,7,3,2,5,4,3,5,11,1
5+
4,1,6,3,1,5,3,3,5,10,1
6+
4,1,6,2,1,4,3,2,4,10,0
7+
0,0,0,0,0,0,0,0,0,0,0
8+
0,0,0,0,0,0,0,0,0,0,0
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import pandas as pd
2+
import math
3+
from animation_examples.complex_community_booking.model_classes import Scenario, generate_seed_vector
4+
from animation_examples.complex_community_booking.simulation_execution_functions import single_run
5+
from vidigi.prep import reshape_for_animations, generate_animation_df
6+
from vidigi.animation import generate_animation
7+
import plotly.io as pio
8+
pio.renderers.default = "notebook"
9+
10+
shifts = pd.read_csv("animation_examples/complex_community_booking/data/shifts.csv")
11+
# if scenario_choice == "As-is" or scenario_choice == "With Pooling":
12+
# prop_carve_out = [0.0, 0.9, 0.15, 0.01]
13+
prop_carve_out = 0.15
14+
15+
#depending on settings and CPU this model takes around 15-20 seconds to run
16+
RESULTS_COLLECTION = 90 * 1
17+
18+
# We use a warm-up period
19+
# because the model starts up empty which doesn't reflect reality
20+
WARM_UP = 60 * 1
21+
RUN_LENGTH = RESULTS_COLLECTION + WARM_UP
22+
23+
# Set up the scenario for the model to run.
24+
scenarios = {}
25+
26+
scenarios['as-is'] = Scenario(
27+
RUN_LENGTH,
28+
WARM_UP,
29+
prop_carve_out=prop_carve_out,
30+
seeds=generate_seed_vector(),
31+
slots_file=shifts
32+
)
33+
34+
scenarios['pooled'] = Scenario(
35+
RUN_LENGTH,
36+
WARM_UP,
37+
prop_carve_out=prop_carve_out,
38+
pooling=True,
39+
seeds=generate_seed_vector(),
40+
slots_file=shifts
41+
)
42+
43+
scenarios['no_carve_out'] = Scenario(
44+
RUN_LENGTH,
45+
WARM_UP,
46+
pooling=True,
47+
prop_carve_out=0.0,
48+
seeds=generate_seed_vector(),
49+
slots_file=shifts
50+
)
51+
52+
clinic_lkup_df = pd.DataFrame([
53+
{'clinic': 0, 'icon': "🟠"},
54+
{'clinic': 1, 'icon': "🟡"},
55+
{'clinic': 2, 'icon': "🟢"},
56+
{'clinic': 3, 'icon': "🔵"},
57+
{'clinic': 4, 'icon': "🟣"},
58+
{'clinic': 5, 'icon': "🟤"},
59+
{'clinic': 6, 'icon': "⚫"},
60+
{'clinic': 7, 'icon': "⚪"},
61+
{'clinic': 8, 'icon': "🔶"},
62+
{'clinic': 9, 'icon': "🔷"},
63+
{'clinic': 10, 'icon': "🟩"}
64+
])
65+
66+
67+
def show_home_clinic(row):
68+
if "more" not in row["icon"]:
69+
if row["home_clinic"] == 0:
70+
return "🟠"
71+
if row["home_clinic"] == 1:
72+
return "🟡"
73+
if row["home_clinic"] == 2:
74+
return "🟢"
75+
if row["home_clinic"] == 3:
76+
return "🔵"
77+
if row["home_clinic"] == 4:
78+
return "🟣"
79+
if row["home_clinic"] == 5:
80+
return "🟤"
81+
if row["home_clinic"] == 6:
82+
return "⚫"
83+
if row["home_clinic"] == 7:
84+
return "⚪"
85+
if row["home_clinic"] == 8:
86+
return "🔶"
87+
if row["home_clinic"] == 9:
88+
return "🔷"
89+
if row["home_clinic"] == 10:
90+
return "🟩"
91+
else:
92+
return row["icon"]
93+
else:
94+
return row["icon"]
95+
96+
def show_priority_icon(row):
97+
if "more" not in row["icon"]:
98+
if row["pathway"] == 2:
99+
return "🚨"
100+
else:
101+
return row["icon"]
102+
else:
103+
return row["icon"]
104+
105+
def add_los_to_icon(row):
106+
if row["event_original"] == "have_appointment":
107+
return f'{row["icon"]}<br>{int(row["wait"])}'
108+
else:
109+
return row["icon"]
110+
111+
def generate_scenario_results(scenario):
112+
results_all, results_low, results_high, event_log = single_run(scenarios[scenario])
113+
event_log_df = pd.DataFrame(event_log)
114+
event_log_df['event_original'] = event_log_df['event']
115+
event_log_df['event'] = event_log_df.apply(lambda x: f"{x['event']}{f'_{int(x.booked_clinic)}' if pd.notna(x['booked_clinic']) else ''}", axis=1)
116+
117+
full_patient_df = reshape_for_animations(
118+
event_log_df,
119+
entity_col_name="patient",
120+
limit_duration=WARM_UP+180,
121+
every_x_time_units=1,
122+
step_snapshot_max=50,
123+
)
124+
125+
# Remove the warm-up period from the event log
126+
full_patient_df = full_patient_df[full_patient_df["snapshot_time"] >= WARM_UP]
127+
128+
clinics = [x for x in event_log_df['booked_clinic'].sort_values().unique().tolist() if not math.isnan(x)]
129+
130+
clinic_waits = [{'event': f'appointment_booked_waiting_{int(clinic)}',
131+
'y': 950-(clinic+1)*80,
132+
'x': 625,
133+
'label': f"Booked into<br>clinic {int(clinic)}",
134+
'clinic': int(clinic)}
135+
for clinic in clinics]
136+
137+
clinic_attends = [{'event': f'have_appointment_{int(clinic)}',
138+
'y': 950-(clinic+1)*80,
139+
'x': 850,
140+
'label': f"Attending appointment<br>at clinic {int(clinic)}"}
141+
for clinic in clinics]
142+
143+
event_position_df = pd.concat([pd.DataFrame(clinic_waits),(pd.DataFrame(clinic_attends))])
144+
145+
referred_out = [{'event': f'referred_out_{int(clinic)}',
146+
'y': 950-(clinic+1)*80,
147+
'x': 125,
148+
'label': f"Referred Out From <br>clinic {int(clinic)}"}
149+
for clinic in clinics]
150+
151+
event_position_df = pd.concat([event_position_df,(pd.DataFrame(referred_out))])
152+
153+
# if scenario == "pooled" or "no_carve_out":
154+
# event_position_df = event_position_df.merge(clinic_lkup_df, how="left")
155+
# event_position_df["label"] = event_position_df.apply(
156+
# lambda x: f"{x['label']} {x['icon']}" if pd.notna(x['icon']) else x['label'],
157+
# axis=1
158+
# )
159+
# event_position_df = event_position_df.drop(columns="icon")
160+
161+
event_position_df.drop(columns="clinic")
162+
163+
full_patient_df_plus_pos = generate_animation_df(
164+
full_entity_df=full_patient_df,
165+
entity_col_name="patient",
166+
event_position_df=event_position_df,
167+
wrap_queues_at=25,
168+
step_snapshot_max=50,
169+
gap_between_entities=15,
170+
gap_between_queue_rows=15,
171+
debug_mode=False
172+
)
173+
174+
return full_patient_df, full_patient_df_plus_pos, event_position_df
175+
176+
177+
full_patient_df, full_patient_df_plus_pos, event_position_df = generate_scenario_results(
178+
'as-is'
179+
)
180+
181+
def generate_clinic_animation(final_df):
182+
fig = generate_animation(
183+
full_entity_df_plus_pos=final_df,
184+
event_position_df=event_position_df,
185+
scenario=None,
186+
entity_col_name="patient",
187+
plotly_height=800,
188+
plotly_width=1000,
189+
override_x_max=1000,
190+
override_y_max=1000,
191+
entity_icon_size=10,
192+
text_size=10,
193+
include_play_button=True,
194+
add_background_image=None,
195+
display_stage_labels=True,
196+
time_display_units="d",
197+
simulation_time_unit="days",
198+
start_date="2022-06-27",
199+
setup_mode=False,
200+
frame_duration=1500, #milliseconds
201+
frame_transition_duration=1000, #milliseconds
202+
debug_mode=False
203+
)
204+
205+
return fig
206+
207+
#TODO
208+
# Add in additional trace that shows the number of available slots per day
209+
# using the slot df
210+
211+
#TODO
212+
# Pooled booking version where being in non-home clinic makes you one colour
213+
# and home clinic makes you another
214+
215+
#TODO
216+
# Investigate adding a priority attribute to event log
217+
# that can be considered when ranking queues if present
218+
219+
generate_clinic_animation(full_patient_df_plus_pos)

0 commit comments

Comments
 (0)