eflips-eval 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,284 @@
1
+ from datetime import datetime
2
+ from typing import Dict, List, Optional, Tuple
3
+
4
+ import pandas as pd
5
+ import plotly.express as px # type: ignore
6
+ import plotly.graph_objs as go # type: ignore
7
+ from plotly.subplots import make_subplots # type: ignore
8
+
9
+
10
+ def get_color_scheme(
11
+ color_scheme: str,
12
+ ) -> Dict[str, str | Dict[str, str] | str | px.colors.sequential]:
13
+ """
14
+ This function returns a dictionary with the color scheme to be used in the gantt chart
15
+ :param color_scheme: A string representing the color scheme to be used in the gantt chart. It can be one of the following:
16
+ - "event_type"
17
+ - "soc"
18
+ - "location"
19
+ :return: A dictionary with the color scheme
20
+ """
21
+ color = None
22
+ color_discrete_map = None
23
+ color_continuous_scale = None
24
+
25
+ match color_scheme:
26
+ case "event_type":
27
+ color = "event_type"
28
+ color_discrete_map = {
29
+ "Charging Depot": "forestgreen",
30
+ "Charging Opportunity": "lightgreen",
31
+ "Driving": "skyblue",
32
+ "Service": "salmon",
33
+ "Standby Departure": "orange",
34
+ "Standby": "lightcoral",
35
+ "Precondition": "lightgrey",
36
+ }
37
+ color_continuous_scale = ""
38
+ case "soc":
39
+ color = "soc_end"
40
+ color_discrete_map = None
41
+ color_continuous_scale = px.colors.sequential.Viridis
42
+ case "location":
43
+ color = "location"
44
+ color_discrete_map = {
45
+ "Depot": "salmon",
46
+ "Trip": "forestgreen",
47
+ "Station": "steelblue",
48
+ }
49
+ color_continuous_scale = ""
50
+ case _:
51
+ raise ValueError("Invalid color scheme")
52
+
53
+ color_scheme_dict = {
54
+ "color": color,
55
+ "color_discrete_map": color_discrete_map,
56
+ "color_continuous_scale": color_continuous_scale,
57
+ }
58
+
59
+ return color_scheme_dict
60
+
61
+
62
+ def departure_arrival_soc(prepared_data: pd.DataFrame) -> go.Figure:
63
+ """
64
+ This function visualizes the departure and arrival SoC using plotly
65
+ :param prepared_data: The result of the departure_arrival_soc function, a dataframe with the following columns:
66
+
67
+ - event_id: the associated event id
68
+ - rotation_id: the associated rotation id (for the trip the vehicle is going on or returning from)
69
+ - rotation_name: the name of the rotation
70
+ - vehicle_type_id: the vehicle type id
71
+ - vehicle_type_name: the name of the vehicle type
72
+ - vehicle_id: the vehicle id
73
+ - vehicle_name: the name of the vehicle
74
+ - time: the time at which this SoC was recorded (for departure, this is the departure time from the depot, for
75
+ arrival, this is the arrival time at the depot)
76
+ - soc: the state of charge at the given time
77
+ - event_type: the type of event, either "Departure" or "Arrival"
78
+
79
+
80
+ :return: A plotly figure object
81
+ """
82
+
83
+ fig = px.scatter(
84
+ prepared_data,
85
+ x="time",
86
+ y="soc",
87
+ symbol="event_type",
88
+ color="vehicle_type_name",
89
+ hover_data=["rotation_name"],
90
+ labels={
91
+ "soc": "State of Charge (%)",
92
+ "time": "Time",
93
+ "rotation_name": "Rotation Name",
94
+ "vehicle_type_name": "Vehicle Type",
95
+ "event_type": "Event Type",
96
+ },
97
+ )
98
+ fig.update(layout_yaxis_range=[0, 100])
99
+ return fig
100
+
101
+
102
+ def depot_event(
103
+ prepared_data: pd.DataFrame, color_scheme: str = "event_type"
104
+ ) -> go.Figure:
105
+ """
106
+ This function visualizes all events as a gantt chart using plotly
107
+ :param prepared_data: The result of the depot_event function, a dataframe with the following columns:
108
+ :param color_scheme: A string representing the color scheme to be used in the gantt chart. It can be one of the following:
109
+ - "event_type"
110
+ - "soc"
111
+ - "location"
112
+ see :func:`get_color_scheme` for more information.
113
+
114
+ - time_start: the start time of the event in datetime format
115
+ - time_end: the end time of the event in datetime format
116
+ - vehicle_id: the unique vehicle identifier which could be used for querying the vehicle in the database
117
+ - event_type: the type of event specified in the eflips model. See :class:`eflips.model.EventType` for more information
118
+ - area_id: the unique area identifier which could be used for querying the area in the database
119
+ - trip_id: the unique trip identifier which could be used for querying the trip in the database
120
+ - station_id: the unique station identifier which could be used for querying the station in the database
121
+ - location: the location of the event. This could be "depot", "trip" or "station"
122
+
123
+ :return: A plotly figure object
124
+
125
+ """
126
+ color_scheme_dict = get_color_scheme(color_scheme)
127
+ color = color_scheme_dict["color"]
128
+ color_discrete_map = color_scheme_dict["color_discrete_map"]
129
+ color_continuous_scale = color_scheme_dict["color_continuous_scale"]
130
+
131
+ fig = px.timeline(
132
+ prepared_data,
133
+ x_start="time_start",
134
+ x_end="time_end",
135
+ y="vehicle_id",
136
+ color=color,
137
+ color_discrete_map=color_discrete_map,
138
+ color_continuous_scale=color_continuous_scale,
139
+ pattern_shape="vehicle_type_name",
140
+ labels={
141
+ "time_start": "Start Time",
142
+ "time_end": "End Time",
143
+ "soc_start": "Start State of Charge (%)",
144
+ "soc_end": "End State of Charge (%)",
145
+ "area_id": "Area ID",
146
+ "vehicle_id": "Vehicle ID",
147
+ },
148
+ )
149
+ if color_scheme == "soc":
150
+ legend_title = "State of Charge"
151
+ fig.update_layout(coloraxis=dict(colorbar=dict(orientation="h", y=-0.15)))
152
+ else:
153
+ legend_title = color_scheme.replace("_", " ").title()
154
+
155
+ vehicle_types = prepared_data["vehicle_type_name"].unique()
156
+
157
+ for bar in fig.data:
158
+ for vehicle_type in vehicle_types:
159
+ if vehicle_type in bar.name:
160
+ # Substituting legendgroup with vehicle type
161
+ bar.legendgroup = str(vehicle_type)
162
+ bar.legendgrouptitle = {"text": vehicle_type}
163
+ break
164
+
165
+ fig.update_xaxes(title_text="Time")
166
+ fig.update_yaxes(title_text="Vehicles", secondary_y=False)
167
+ fig.update_layout(
168
+ legend={"title": legend_title + " , " + "Vehicle Type"},
169
+ yaxis={"showticklabels": False},
170
+ )
171
+ fig.update_layout(legend=dict(groupclick="togglegroup"))
172
+
173
+ return fig
174
+
175
+
176
+ def power_and_occupancy(prepared_data: pd.DataFrame) -> go.Figure:
177
+ """
178
+ This function visualizes the power and occupancy using plotly
179
+ :param prepared_data: The result of the power_and_occupancy function, a dataframe with the following columns:
180
+
181
+ - time: the time at which the power and occupancy was recorded
182
+ - power: the power at the given time
183
+ - occupancy: the occupancy at the given time
184
+
185
+ :return: A plotly figure object
186
+ """
187
+ fig = make_subplots(specs=[[{"secondary_y": True}]])
188
+ fig.add_trace(
189
+ go.Scatter(x=prepared_data["time"], y=prepared_data["power"], name="Power"),
190
+ secondary_y=False,
191
+ )
192
+ fig.add_trace(
193
+ go.Scatter(
194
+ x=prepared_data["time"], y=prepared_data["occupancy"], name="Occupancy"
195
+ ),
196
+ secondary_y=True,
197
+ )
198
+ # Set x-axis title
199
+ fig.update_xaxes(title_text="Time")
200
+
201
+ # Set y-axes titles
202
+ fig.update_yaxes(title_text="Power (kW)", secondary_y=False)
203
+ fig.update_yaxes(title_text="Number of Vehicles in Depot", secondary_y=True)
204
+ return fig
205
+
206
+
207
+ def specific_energy_consumption(prepared_data: pd.DataFrame) -> go.Figure:
208
+ """
209
+ Creates a histogram of the specific energy consumption of the vehicles, colored by vehicle type
210
+ :param prepared_data: A DataFrame with the following columns:
211
+ - trip_id: the unique identifier of the trip
212
+ - route_id: the unique identifier of the route
213
+ - route_name: the name of the route
214
+ - distance: the distance of the route in km
215
+ - energy_consumption: the energy consumption of the trip in kWh
216
+ - vehicle_type_id: the unique identifier of the vehicle type
217
+ - vehicle_type_name: the name of the vehicle type
218
+ :return: a plotly figure object
219
+ """
220
+ prepared_data["specific_energy_consumption"] = (
221
+ prepared_data["energy_consumption"] / prepared_data["distance"]
222
+ )
223
+ bin_count = prepared_data.shape[0] // 10
224
+ fig = go.Figure()
225
+ for vehicle_type_name, data in prepared_data.groupby("vehicle_type_name"):
226
+ fig.add_trace(
227
+ go.Histogram(x=data["specific_energy_consumption"], name=vehicle_type_name)
228
+ )
229
+
230
+ # Overlay both histograms
231
+ fig.update_layout(
232
+ barmode="overlay",
233
+ xaxis_title="Specific Energy Consumption (kWh/km)",
234
+ yaxis_title="Count",
235
+ )
236
+ # Reduce opacity to see both histograms
237
+ fig.update_traces(opacity=0.75)
238
+ fig.show()
239
+ return fig
240
+
241
+
242
+ def vehicle_soc(
243
+ prepared_data: pd.DataFrame,
244
+ descriptions: Dict[str, List[Tuple[str, datetime, datetime]]],
245
+ ) -> go.Figure:
246
+ """
247
+ This function visualizes the state of charge of a vehicle over time using plotly. Optionally, it can also visualize
248
+ event types or event locations by vertical rectangles.
249
+ :param prepared_data: A dataframe with the following columns:
250
+ - time: the time at which the SoC was recorded
251
+ - soc: the state of charge at the given time
252
+
253
+ :param descriptions: A dictionary with the following keys
254
+ - "trip": A list of route names and the time the trip started and ended
255
+ - "rotation": A list of rotation names and the time the rotation started and ended
256
+ - "charging": A list of the location of the charging and the time the charging started and ended
257
+ for each key, the value is a list of tuples with the following structure:
258
+ - (name, start_time, end_time)
259
+
260
+ :return: A plotly figure object
261
+ """
262
+
263
+ fig = px.line(
264
+ prepared_data,
265
+ x="time",
266
+ y="soc",
267
+ labels={"time": "Time", "soc": "State of Charge (%)"},
268
+ )
269
+
270
+ colors = ["red", "green", "blue"]
271
+
272
+ for event_type, event_list in descriptions.items():
273
+ color = colors.pop(0)
274
+ for event in event_list:
275
+ fig.add_vrect(
276
+ x0=event[1],
277
+ x1=event[2],
278
+ line_width=0,
279
+ fillcolor=color,
280
+ opacity=0.25,
281
+ annotation_text=event[0],
282
+ annotation_textangle=270,
283
+ )
284
+ return fig