import simpy
from vidigi.logging import EventLogger
from vidigi.animation import animate_activity_log
from examples.feat_gauge_only_animations.simple_triage_assess_treat_model import Model
from vidigi.utils import EventPosition, create_event_position_df
import plotly.io as pio
= "notebook" pio.renderers.default
Feature Breakdown: Comparing gauge, hybrid and gaugeless animations for simulations with high entity volumes
= Model(run_number=1)
model = model.run() logs
logs.summary()
{'total_events': 12928,
'event_types': {'queue': 9055, 'arrival_departure': 3873},
'time_range': (0.0, 363.99308604394935),
'unique_entities': 3756}
= logs.to_dataframe()
logs_df logs_df
entity_id | event_type | event | time | run_number | |
---|---|---|---|---|---|
0 | 1 | arrival_departure | arrival | 0.000000 | 1 |
1 | 1 | queue | queue_initial_review | 0.000000 | 1 |
2 | 1 | queue | start_initial_review | 0.000000 | 1 |
3 | 2 | arrival_departure | arrival | 0.190817 | 1 |
4 | 2 | queue | queue_initial_review | 0.190817 | 1 |
... | ... | ... | ... | ... | ... |
12923 | 3754 | queue | queue_initial_review | 363.946793 | 1 |
12924 | 3755 | arrival_departure | arrival | 363.949331 | 1 |
12925 | 3755 | queue | queue_initial_review | 363.949331 | 1 |
12926 | 3756 | arrival_departure | arrival | 363.993086 | 1 |
12927 | 3756 | queue | queue_initial_review | 363.993086 | 1 |
12928 rows × 5 columns
logs_df.event.unique()
array(['arrival', 'queue_initial_review', 'start_initial_review',
'end_initial_review', 'queue_assessment', 'start_assessment',
'end_assessment', 'queue_treatment', 'start_treatment',
'end_treatment', 'depart'], dtype=object)
= create_event_position_df(
event_position_df ='queue_initial_review', x=200, y=600, label="Waiting for <br> Initial Review"),
[EventPosition(event='queue_assessment', x=350, y=400, label="Waiting for <br> Assessment"),
EventPosition(event='queue_treatment', x=500, y=200, label="Waiting for <br> Treatment")]
EventPosition(event
)
event_position_df
event | x | y | label | resource | |
---|---|---|---|---|---|
0 | queue_initial_review | 200 | 600 | Waiting for <br> Initial Review | None |
1 | queue_assessment | 350 | 400 | Waiting for <br> Assessment | None |
2 | queue_treatment | 500 | 200 | Waiting for <br> Treatment | None |
animate_activity_log(=logs_df,
event_log=event_position_df,
event_position_df="days",
simulation_time_unit=7,
every_x_time_units=True,
step_snapshot_limit_gauges=0,
step_snapshot_max=365,
limit_duration=600,
override_x_max=700
override_y_max )
For comparison, let’s explore including some icons for individuals to try to demonstrate flow between steps.
However, as many of the individuals moving between the steps are not shown in the queue in the frame before they move, we observe a confusing ‘flying in’ effect from the top left of the animation, which is a plotly limitation/default which has not yet been overcome in the package.
animate_activity_log(=logs_df,
event_log=event_position_df,
event_position_df="days",
simulation_time_unit=7,
every_x_time_units=True,
step_snapshot_limit_gauges=10,
step_snapshot_max=365,
limit_duration=600,
override_x_max=700,
override_y_max=10,
wrap_queues_at=["⚫"],
custom_entity_icon_list=1000
frame_duration )
Changing to a daily level still doesn’t really represent the rate of flow between steps.
animate_activity_log(=logs_df,
event_log=event_position_df,
event_position_df="days",
simulation_time_unit=1,
every_x_time_units=True,
step_snapshot_limit_gauges=10,
step_snapshot_max=365,
limit_duration=600,
override_x_max=700,
override_y_max=10,
wrap_queues_at=["⚫"],
custom_entity_icon_list=400,
frame_duration=600
frame_transition_duration )
Finally, let’s try including all of our individuals in the plot - we’ll just make them very small and adjust our event position dataframe slightly to provide more space.
However - you may notice that the animation starts to struggle significantly, with slowdowns occurring, despite it generating very quickly. This is due to the sheer amount of data per frame that needs to be stored.
However, this does give us more of a sense of flow between steps, which can complement the gauge animation. The gauge version of the animation can be utilised to better track the scale of queues, while the non-gauge animation may better serve as a demonstration of how the underlying model works in terms of potential paths for people to move between steps.
= create_event_position_df(
event_position_df ='queue_initial_review', x=200, y=900, label="Waiting for <br> Initial Review"),
[EventPosition(event='queue_assessment', x=350, y=500, label="Waiting for <br> Assessment"),
EventPosition(event='queue_treatment', x=500, y=200, label="Waiting for <br> Treatment")]
EventPosition(event
)
event_position_df
event | x | y | label | resource | |
---|---|---|---|---|---|
0 | queue_initial_review | 200 | 900 | Waiting for <br> Initial Review | None |
1 | queue_assessment | 350 | 500 | Waiting for <br> Assessment | None |
2 | queue_treatment | 500 | 200 | Waiting for <br> Treatment | None |
= animate_activity_log(
fig =logs_df,
event_log=event_position_df,
event_position_df="days",
simulation_time_unit=7,
every_x_time_units=True,
step_snapshot_limit_gauges=9999,
step_snapshot_max=True,
debug_mode=365,
limit_duration=600,
override_x_max=1800,
override_y_max=75,
wrap_queues_at=["⚫"],
custom_entity_icon_list=6,
entity_icon_size=2,
gap_between_entities=15,
gap_between_queue_rows=1000,
frame_duration=2000,
frame_transition_duration
)
fig
Animation function called at 18:25:44
Iteration through time-unit-by-time-unit logs complete 18:25:44
Snapshot df concatenation complete at 18:25:44
Reshaped animation dataframe finished construction at 18:25:44
C:\simviz\vidigi\vidigi\animation.py:1191: UserWarning:
`step_snapshot_max` is not a multiple of `wrap_queues_at`.The animation will display better if this is resolved.
Placement dataframe finished construction at 18:25:45
Output animation generation complete at 18:25:46
Total Time Elapsed: 1.82 seconds