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.
- LICENSE.md +660 -0
- eflips/eval/input/__init__.py +0 -0
- eflips/eval/input/prepare.py +86 -0
- eflips/eval/input/visualize.py +39 -0
- eflips/eval/output/__init__.py +0 -0
- eflips/eval/output/prepare.py +388 -0
- eflips/eval/output/visualize.py +284 -0
- eflips_eval-0.1.2.dist-info/LICENSE.md +660 -0
- eflips_eval-0.1.2.dist-info/METADATA +109 -0
- eflips_eval-0.1.2.dist-info/RECORD +11 -0
- eflips_eval-0.1.2.dist-info/WHEEL +4 -0
|
@@ -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
|