Spaces:
Runtime error
Runtime error
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 |
|