Skip to content

Commit

Permalink
Merge pull request #2 from rolling-stock-scheduling/feature/efficienc…
Browse files Browse the repository at this point in the history
…y-pie-chart

Add efficiency pie chart, still need to define the service day, which…
  • Loading branch information
munterfi authored May 6, 2024
2 parents a06f254 + ec3e9a4 commit b35d126
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 3 deletions.
3 changes: 2 additions & 1 deletion rssched/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import typer
from typer import Argument, echo

import time
from rssched.io.reader import import_response
from rssched.visualization.plot import generate_plots

Expand All @@ -16,6 +16,7 @@ def main(source: Annotated[Path, Argument()]):
response = import_response(source)
for fig in generate_plots(response, source.stem):
fig.show()
time.sleep(0.5)


if __name__ == "__main__":
Expand Down
9 changes: 9 additions & 0 deletions rssched/visualization/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@
"ServiceTrip": "rgb(183, 183, 183)",
"MaintenanceSlot": "rgb(45, 50, 125)",
"DeadHeadTrip": "rgb(235, 0, 0)",
"Idle": "rgb(252, 187, 0)",
}

COLORS = [
"rgb(20, 58, 133)",
"rgb(111, 34, 130)",
"rgb(232, 78, 16)",
"rgb(255, 222, 21)",
"rgb(0, 151, 59)",
]
89 changes: 89 additions & 0 deletions rssched/visualization/fleet_efficiency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pandas as pd
import plotly.graph_objects as go

from rssched.model.response import Response
from rssched.visualization.colors import EVENT_TYPES


def summarize_vehicle_activities(response):
activities = []

for fleet in response.schedule.fleet:
vehicle_type = fleet.vehicle_type

for vehicle in fleet.vehicles:
vehicle_id = vehicle.id
times = []

for segment in vehicle.departure_segments:
times.append(("ServiceTrip", segment.departure, segment.arrival))
for slot in vehicle.maintenance_slots:
times.append(("MaintenanceSlot", slot.start, slot.end))
for trip in vehicle.dead_head_trips:
times.append(("DeadHeadTrip", trip.departure, trip.arrival))

times.sort(key=lambda x: x[1])

last_end_time = None

for activity_type, start, end in times:
duration = (end - start).total_seconds() / 3600
activities.append(
{
"vehicle_id": vehicle_id,
"vehicle_type": vehicle_type,
"activity_type": activity_type,
"duration": duration,
"start_time": start,
"end_time": end,
}
)

if last_end_time is not None and start > last_end_time:
idle_duration = (start - last_end_time).total_seconds() / 3600
activities.append(
{
"vehicle_id": vehicle_id,
"vehicle_type": vehicle_type,
"activity_type": "Idle",
"duration": idle_duration,
"start_time": last_end_time,
"end_time": start,
}
)

last_end_time = end

df_activities = pd.DataFrame(activities)
return df_activities


def plot_fleet_efficiency(response: Response, instance_name: str):
df = summarize_vehicle_activities(response)

vehicle_types = df["vehicle_type"].unique()

figs = []

for v_type in vehicle_types:
filtered_df = df[df["vehicle_type"] == v_type]
grouped = filtered_df.groupby("activity_type")["duration"].sum().reset_index()
fig = go.Figure(
data=[
go.Pie(
labels=grouped["activity_type"],
values=grouped["duration"],
textinfo="label+percent",
insidetextorientation="radial",
marker_colors=[
EVENT_TYPES[atype] for atype in grouped["activity_type"]
],
)
]
)
fig.update_layout(
title=f"Activity Distribution: {v_type} (instance: {instance_name})"
)
figs.append(fig)

return figs
2 changes: 2 additions & 0 deletions rssched/visualization/plot.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from rssched.model.response import Response
from rssched.visualization.active_events import plot_active_events_over_time
from rssched.visualization.fleet_efficiency import plot_fleet_efficiency
from rssched.visualization.vehicle_type_gantt import plot_gantt_per_vehicle_type
from rssched.visualization.vehicle_utilization import plot_vehicle_utilization

Expand All @@ -8,4 +9,5 @@ def generate_plots(response: Response, instance_name: str):
figures = plot_gantt_per_vehicle_type(response, instance_name)
figures.append(plot_active_events_over_time(response, instance_name))
figures.append(plot_vehicle_utilization(response, instance_name))
figures.extend(plot_fleet_efficiency(response, instance_name))
return figures
4 changes: 3 additions & 1 deletion rssched/visualization/vehicle_utilization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import plotly.express as px

from rssched.model.response import Response
from rssched.visualization.colors import COLORS


def plot_vehicle_utilization(response: Response, instance_name: str):
Expand All @@ -28,11 +29,12 @@ def plot_vehicle_utilization(response: Response, instance_name: str):
y="Duration",
color="Vehicle Type",
title=f"Vehicle Utilization (instance: {instance_name})",
color_discrete_sequence=COLORS,
labels={"Duration": "Total Hours"},
)
fig.update_layout(
xaxis_title="Vehicle ID",
yaxis_title="Total Utilization [h]",
yaxis_title="Service Trip Time [h]",
xaxis={"categoryorder": "total descending"},
)
fig.update_layout(hovermode="x", hoverdistance=50)
Expand Down
2 changes: 1 addition & 1 deletion tests/visualization/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
def test_generate_plots():
response = import_response(PkgDataAccess.locate_response())
figs = generate_plots(response, "test_instance")
assert len(figs) == 4
assert len(figs) == 6

0 comments on commit b35d126

Please sign in to comment.