Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -15,6 +15,7 @@ from datetime import datetime
|
|
15 |
import folium
|
16 |
import seaborn as sns
|
17 |
from streamlit_folium import st_folium
|
|
|
18 |
|
19 |
|
20 |
def get_current_time():
|
@@ -156,15 +157,6 @@ if selected_country != 'Overall':
|
|
156 |
st.sidebar.subheader("Section")
|
157 |
st.sidebar.caption("Select the type of information you want to explore.")
|
158 |
section = st.sidebar.radio('', ['Data Quality', 'Forecasts Quality', 'Insights'], index=1)
|
159 |
-
date_range = st.sidebar.date_input("Select Date Range for Metrics Calculation:",
|
160 |
-
value=(pd.to_datetime("2024-01-01"), pd.to_datetime(pd.Timestamp('today'))))
|
161 |
-
if len(date_range) == 2:
|
162 |
-
start_date = pd.Timestamp(date_range[0])
|
163 |
-
end_date = pd.Timestamp(date_range[1])
|
164 |
-
else:
|
165 |
-
st.error("Please select a valid date range.")
|
166 |
-
st.stop()
|
167 |
-
|
168 |
else:
|
169 |
section = None # No section is shown when "Overall" is selected
|
170 |
|
@@ -218,8 +210,12 @@ if section == 'Data Quality':
|
|
218 |
|
219 |
st.header('Data Quality')
|
220 |
|
221 |
-
st.write('The table below presents the data quality metrics
|
222 |
-
|
|
|
|
|
|
|
|
|
223 |
|
224 |
# Report % of missing values
|
225 |
missing_values = data_quality[forecast_columns].isna().mean() * 100
|
@@ -315,7 +311,7 @@ elif section == 'Forecasts Quality':
|
|
315 |
|
316 |
# Scatter plots for error distribution
|
317 |
st.subheader('Error Distribution')
|
318 |
-
st.write('The below scatter plots show the error distribution of all
|
319 |
selected_variable = st.selectbox("Select Variable for Error Distribution", list(variable_options.keys()))
|
320 |
|
321 |
# Get the corresponding columns for the selected variable
|
@@ -329,7 +325,7 @@ elif section == 'Forecasts Quality':
|
|
329 |
|
330 |
# Calculate error and plot
|
331 |
error = pred - obs
|
332 |
-
fig = px.scatter(x=obs, y=
|
333 |
fig.update_layout(title=f'Error Distribution for {selected_variable}')
|
334 |
|
335 |
st.plotly_chart(fig)
|
@@ -338,7 +334,21 @@ elif section == 'Forecasts Quality':
|
|
338 |
|
339 |
st.subheader('Accuracy Metrics (Sorted by rMAE):')
|
340 |
|
341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
st.write(output_text)
|
343 |
|
344 |
data = data.loc[start_date:end_date]
|
@@ -424,7 +434,7 @@ elif section == 'Forecasts Quality':
|
|
424 |
st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}, className="small-chart")
|
425 |
|
426 |
st.subheader('ACF plots of Errors')
|
427 |
-
st.write('The below plots show the ACF (Auto-Correlation Function) for the errors of all three data fields obtained from ENTSO-E: Solar, Wind and Load.')
|
428 |
|
429 |
# Dropdown to select the variable
|
430 |
selected_variable = st.selectbox("Select Variable for ACF of Errors", list(variable_options.keys()))
|
@@ -449,20 +459,28 @@ elif section == 'Forecasts Quality':
|
|
449 |
# Section 3: Insights
|
450 |
elif section == 'Insights':
|
451 |
st.header("Insights")
|
452 |
-
st.write("""
|
453 |
-
This section provides insights derived from the data and forecasts.
|
454 |
-
You can visualize trends, anomalies, and other important findings.
|
455 |
-
""")
|
456 |
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
|
461 |
-
|
462 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
selected_df.columns = [col.replace('_entsoe', '').replace('_', ' ') for col in selected_df.columns]
|
|
|
|
|
464 |
selected_df = selected_df.dropna()
|
465 |
-
|
|
|
466 |
sns.set_theme(style="ticks")
|
467 |
pairplot_fig = sns.pairplot(selected_df)
|
468 |
|
@@ -470,19 +488,6 @@ elif section == 'Insights':
|
|
470 |
st.pyplot(pairplot_fig)
|
471 |
|
472 |
elif selected_country == 'Overall':
|
473 |
-
st.markdown(
|
474 |
-
"""
|
475 |
-
<style>
|
476 |
-
.main-container {
|
477 |
-
padding-top: 0px; /* Remove extra padding at the top */
|
478 |
-
}
|
479 |
-
.chart-spacing {
|
480 |
-
margin-top: -40px; /* Adjust this value to control spacing between map and radar plot */
|
481 |
-
}
|
482 |
-
</style>
|
483 |
-
""",
|
484 |
-
unsafe_allow_html=True
|
485 |
-
)
|
486 |
|
487 |
st.subheader("Net Load Error Map")
|
488 |
st.write("""
|
@@ -544,7 +549,7 @@ elif selected_country == 'Overall':
|
|
544 |
fill_opacity=0.7,
|
545 |
line_opacity=0.5,
|
546 |
line_color="black", # Neutral border color
|
547 |
-
legend_name="Net Load Error"
|
548 |
).add_to(m)
|
549 |
|
550 |
# Add a GeoJson layer with custom tooltip for country, error, and date
|
@@ -553,7 +558,7 @@ elif selected_country == 'Overall':
|
|
553 |
style_function=lambda x: {'fillOpacity': 0, 'color': 'black', 'weight': 0},
|
554 |
tooltip=folium.GeoJsonTooltip(
|
555 |
fields=["name", "net_load_error", "date"],
|
556 |
-
aliases=["Country:", "Net Load Error:", "Date:"],
|
557 |
localize=True
|
558 |
)
|
559 |
).add_to(m)
|
@@ -568,15 +573,10 @@ elif selected_country == 'Overall':
|
|
568 |
'Germany': Data_DE,
|
569 |
'Netherlands': Data_NL
|
570 |
}
|
571 |
-
|
572 |
-
# Call the function to plot the map
|
573 |
plot_net_load_error_map(data_dict)
|
574 |
-
# CSS to adjust layout and remove extra spacing
|
575 |
|
576 |
st.subheader("rMAE of Forecasts published on ENTSO-E TP")
|
577 |
-
st.write("""
|
578 |
-
The radar chart below compares the forecast accuracy across Load, Onshore Wind, Offshore Wind, and Solar for each country.
|
579 |
-
""")
|
580 |
|
581 |
def calculate_mae(actual, forecast):
|
582 |
return np.mean(np.abs(actual - forecast))
|
@@ -611,10 +611,11 @@ elif selected_country == 'Overall':
|
|
611 |
angles = ['Load', 'Wind_onshore', 'Wind_offshore', 'Solar']
|
612 |
for _, row in rmae_df.iterrows():
|
613 |
fig.add_trace(go.Scatterpolar(r=[row[angle] for angle in angles], theta=angles, fill='toself', name=row['Country']))
|
614 |
-
fig.update_layout(polar=dict(radialaxis=dict(visible=True, range=[0, 2])), showlegend=True, title="rMAE Radar Chart by Country")
|
615 |
st.plotly_chart(fig)
|
616 |
|
617 |
# Main execution to create and display radar plot
|
618 |
rmae_df = create_rmae_dataframe(data_dict)
|
619 |
plot_rmae_radar_chart(rmae_df)
|
|
|
620 |
|
|
|
15 |
import folium
|
16 |
import seaborn as sns
|
17 |
from streamlit_folium import st_folium
|
18 |
+
import datetime
|
19 |
|
20 |
|
21 |
def get_current_time():
|
|
|
157 |
st.sidebar.subheader("Section")
|
158 |
st.sidebar.caption("Select the type of information you want to explore.")
|
159 |
section = st.sidebar.radio('', ['Data Quality', 'Forecasts Quality', 'Insights'], index=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
else:
|
161 |
section = None # No section is shown when "Overall" is selected
|
162 |
|
|
|
210 |
|
211 |
st.header('Data Quality')
|
212 |
|
213 |
+
st.write('The table below presents the data quality metrics focusing on the percentage of missing values and the occurrence of extreme or nonsensical values for the selected country.')
|
214 |
+
|
215 |
+
yesterday_midnight = pd.Timestamp(datetime.datetime.now().date() - pd.Timedelta(days=1)).replace(hour=23, minute=59, second=59)
|
216 |
+
|
217 |
+
# Filter data until the end of yesterday (midnight)
|
218 |
+
data_quality = data[data.index <= yesterday_midnight]
|
219 |
|
220 |
# Report % of missing values
|
221 |
missing_values = data_quality[forecast_columns].isna().mean() * 100
|
|
|
311 |
|
312 |
# Scatter plots for error distribution
|
313 |
st.subheader('Error Distribution')
|
314 |
+
st.write('The below scatter plots show the error distribution of all four fields: Solar, Wind Onshore, Wind Offshore and Load.')
|
315 |
selected_variable = st.selectbox("Select Variable for Error Distribution", list(variable_options.keys()))
|
316 |
|
317 |
# Get the corresponding columns for the selected variable
|
|
|
325 |
|
326 |
# Calculate error and plot
|
327 |
error = pred - obs
|
328 |
+
fig = px.scatter(x=obs, y=pred, labels={'x': 'Observed [MW]', 'y': 'Forecast ENTSO-E [MW]'})
|
329 |
fig.update_layout(title=f'Error Distribution for {selected_variable}')
|
330 |
|
331 |
st.plotly_chart(fig)
|
|
|
334 |
|
335 |
st.subheader('Accuracy Metrics (Sorted by rMAE):')
|
336 |
|
337 |
+
|
338 |
+
date_range = st.date_input(
|
339 |
+
"Select Date Range for Metrics Calculation:",
|
340 |
+
value=(pd.to_datetime("2024-01-01"), pd.to_datetime(pd.Timestamp('today')))
|
341 |
+
)
|
342 |
+
|
343 |
+
if len(date_range) == 2:
|
344 |
+
start_date = pd.Timestamp(date_range[0])
|
345 |
+
end_date = pd.Timestamp(date_range[1])
|
346 |
+
else:
|
347 |
+
st.error("Please select a valid date range.")
|
348 |
+
st.stop()
|
349 |
+
|
350 |
+
|
351 |
+
output_text = f"The below metrics are calculated from the selected date range from {start_date.strftime('%Y-%m-%d')} to {end_date.strftime('%Y-%m-%d')}. "
|
352 |
st.write(output_text)
|
353 |
|
354 |
data = data.loc[start_date:end_date]
|
|
|
434 |
st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}, className="small-chart")
|
435 |
|
436 |
st.subheader('ACF plots of Errors')
|
437 |
+
st.write('The below plots show the ACF (Auto-Correlation Function) for the errors of all three data fields obtained from ENTSO-E: Solar, Wind Onshore, Wind Offshore and Load.')
|
438 |
|
439 |
# Dropdown to select the variable
|
440 |
selected_variable = st.selectbox("Select Variable for ACF of Errors", list(variable_options.keys()))
|
|
|
459 |
# Section 3: Insights
|
460 |
elif section == 'Insights':
|
461 |
st.header("Insights")
|
|
|
|
|
|
|
|
|
462 |
|
463 |
+
st.write('The scatter plots below are created to explore possible correlations between the data fields: Solar, Wind Onshore, Wind Offshore, Load, and Weather Features.')
|
464 |
+
# Add a selection box for the data resolution (weekly, daily, hourly)
|
465 |
+
data_2024 = data[data.index.year == 2024]
|
466 |
|
467 |
+
resolution = st.selectbox('Select data resolution:', ['Daily', 'Hourly'])
|
468 |
+
|
469 |
+
# Resample data based on the selected resolution
|
470 |
+
if resolution == 'Hourly':
|
471 |
+
resampled_data = data_2024
|
472 |
+
elif resolution == 'Daily':
|
473 |
+
resampled_data = data_2024.resample('D').mean() # Resample to daily mean
|
474 |
+
|
475 |
+
# Select the necessary columns for the scatter plot
|
476 |
+
selected_columns = ['Load_entsoe', 'Solar_entsoe', 'Wind_offshore_entsoe', 'Wind_onshore_entsoe'] + weather_columns
|
477 |
+
selected_df = resampled_data[selected_columns]
|
478 |
selected_df.columns = [col.replace('_entsoe', '').replace('_', ' ') for col in selected_df.columns]
|
479 |
+
|
480 |
+
# Drop missing values
|
481 |
selected_df = selected_df.dropna()
|
482 |
+
|
483 |
+
# Create the scatter plots using seaborn's pairplot
|
484 |
sns.set_theme(style="ticks")
|
485 |
pairplot_fig = sns.pairplot(selected_df)
|
486 |
|
|
|
488 |
st.pyplot(pairplot_fig)
|
489 |
|
490 |
elif selected_country == 'Overall':
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
491 |
|
492 |
st.subheader("Net Load Error Map")
|
493 |
st.write("""
|
|
|
549 |
fill_opacity=0.7,
|
550 |
line_opacity=0.5,
|
551 |
line_color="black", # Neutral border color
|
552 |
+
legend_name="Net Load Error [MW]"
|
553 |
).add_to(m)
|
554 |
|
555 |
# Add a GeoJson layer with custom tooltip for country, error, and date
|
|
|
558 |
style_function=lambda x: {'fillOpacity': 0, 'color': 'black', 'weight': 0},
|
559 |
tooltip=folium.GeoJsonTooltip(
|
560 |
fields=["name", "net_load_error", "date"],
|
561 |
+
aliases=["Country:", "Net Load Error [MW]:", "Date:"],
|
562 |
localize=True
|
563 |
)
|
564 |
).add_to(m)
|
|
|
573 |
'Germany': Data_DE,
|
574 |
'Netherlands': Data_NL
|
575 |
}
|
|
|
|
|
576 |
plot_net_load_error_map(data_dict)
|
|
|
577 |
|
578 |
st.subheader("rMAE of Forecasts published on ENTSO-E TP")
|
579 |
+
st.write("""The rMAE of Forecasts chart compares the forecast accuracy of the predictions published by ENTSO-E Transparency Platform for Belgium, Germany, France, and the Netherlands. It shows the rMAE for onshore wind, offshore wind, solar, and load demand, highlighting how well forecasts perform relative to a basic persistence model across these countries and energy sectors.""")
|
|
|
|
|
580 |
|
581 |
def calculate_mae(actual, forecast):
|
582 |
return np.mean(np.abs(actual - forecast))
|
|
|
611 |
angles = ['Load', 'Wind_onshore', 'Wind_offshore', 'Solar']
|
612 |
for _, row in rmae_df.iterrows():
|
613 |
fig.add_trace(go.Scatterpolar(r=[row[angle] for angle in angles], theta=angles, fill='toself', name=row['Country']))
|
614 |
+
fig.update_layout(polar=dict(radialaxis=dict(visible=True, range=[0, 1.2])), showlegend=True, title="rMAE Radar Chart by Country")
|
615 |
st.plotly_chart(fig)
|
616 |
|
617 |
# Main execution to create and display radar plot
|
618 |
rmae_df = create_rmae_dataframe(data_dict)
|
619 |
plot_rmae_radar_chart(rmae_df)
|
620 |
+
|
621 |
|