Figure Friday 2024 - week 49

join the Figure Friday session on December 13, at noon Eastern Time, to showcase your creation and receive feedback from the community.

In this week’s Figure-Friday we’ll look at the hourly demand for electricity in New England, US, provided by the US Energy Information Administration (EIA).

In case you’re interested in additional data sets for the New England regions or would like to explore the other graphs made by the EIA, visit their wholesale markets page.

Things to consider:

  • can you improve the sample figure below (filled area plot)?
  • would a different figure tell the data story better?
  • can you create a Dash app instead?

Sample figure:

Code for sample figure:
import plotly.graph_objects as go
import pandas as pd

data = pd.read_csv("https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2024/week-49/megawatt_demand_2024.csv")

# Convert the "Local Timestamp" column to a datetime format and filter for October data
data['Local Timestamp'] = pd.to_datetime(data['Local Timestamp Eastern Time (Interval Beginning)'])
october_data = data[(data['Local Timestamp'] >= '2024-10-01') & (data['Local Timestamp'] < '2024-11-01')]

# Prepare the regional data for plotting
regions = [
    "Connecticut Actual Load (MW)", "Maine Actual Load (MW)",
    "New Hampshire Actual Load (MW)", "Northeast Massachusetts Actual Load (MW)",
    "Rhode Island Actual Load (MW)", "Southeast Massachusetts Actual Load (MW)",
    "Vermont Actual Load (MW)", "Western/Central Massachusetts Actual Load (MW)"
]

# Create a filled area plot
fig = go.Figure()
for region in regions:
    fig.add_trace(go.Scatter(
        x=october_data['Local Timestamp'],
        y=october_data[region],
        mode='lines',
        stackgroup='one',  # https://plotly.com/python/reference/scatter/#scatter-stackgroup
        name=region.split(' ')[0]  # Use the first word of the region name as a label
    ))

fig.update_layout(
    title="Hourly Actual Demand for Electricity by Region in October",
    yaxis_title="Megawatts (MWh)",
    legend_title="Regions",
    template="plotly_white"
)

fig.show()

Participation Instructions:

  • Create - use the weekly data set to build your own Plotly visualization or Dash app. Or, enhance the sample figure provided in this post, using Plotly or Dash.
  • Submit - post your creation to LinkedIn or Twitter with the hashtags #FigureFriday and #plotly by midnight Thursday, your time zone. Please also submit your visualization as a new post in this thread.
  • Celebrate - join the Figure Friday sessions to showcase your creation and receive feedback from the community.

:point_right: If you prefer to collaborate with others on Discord, join the Plotly Discord channel.

Data Source:

Thank you to the US Energy Information Administration for the data.

4 Likes

Hi everyone,
Beautiful zigzag draws us data this week! :star_struck:
I wanted to add more shapes and colors. :woman_technologist:

6 Likes

This is really cool.:heart_eyes: I tried to upload it to pycafe, but it didn’t work even after many attempts. Can you recommend a good video or tutorial about it?

2 Likes

What a creative way to visualize the data, @natatsypora . Thanks for sharing :pray:

@Ester what do you mean? Did @natatsypora 's link not work for you?

2 Likes

@adamschroeder Yes, I see @natatsypora’s link, but I couldn’t upload my work.

1 Like

I think you need to create your own account, and then you will be able to push your work to your gallery.

2 Likes

Hi… Just a glimpse! but I’ll certainly go to somewhere here…

6 Likes

Now I finally did it. :tada::tada: @adamschroeder

2 Likes

Nice figure, @JuanG . It’s pretty clear from this chart that July is a heavy energy usage month… probably because of the usage of air conditioners.

It might be interesting to see how the data is looks on a Polar Bar chart. I think it could be even easier to compare between months.

2 Likes

Sorry @adamschroeder, can’t I delete app.py, which contains Hello World? Something is wrong again.

1 Like

you should be able to delete the file, or you could delete the code and put your own code in there.

1 Like

I try to change then once more the code.

1 Like

Thank you. :blush:
Another way to visualize the data by region and month :woman_technologist:

Code
import pandas as pd
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.graph_objects as go

data = pd.read_csv("https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2024/week-49/megawatt_demand_2024.csv")

# Rename columns from index 5 to 13
data.columns = [col for col in data.columns[:5]] + \
 [ (' ').join(name.split(' ')[:2]) if len(name.split(' ')) > 4 else name.split(' ')[0] for name in data.columns[5:] ]

data['Local Timestamp'] = pd.to_datetime(data['Local Timestamp Eastern Time (Interval Beginning)'])
data['Month'] = data['Local Timestamp'].dt.month

# Group by month and region, then sum the demand
sum_by_month_region = data.groupby(['Month'])[data.columns[5:13]].sum()

def heatmap_bar_subplots(df, colorscale='OrRd', 
                         max_color_name='rgb(127,0,0)',
                         other_color_name='rgb(254,232,200)'):
    # Calculate the sums and set colors
    max_row_sum = df.sum(axis=0).max()
    max_col_sum = df.sum(axis=1).max()
    
    color_index = [max_color_name if el == max_row_sum else other_color_name for el in df.sum(axis=0)]
    color_columns = [max_color_name if el == max_col_sum else other_color_name for el in df.sum(axis=1)]

    # Create subplots
    fig = make_subplots(rows=2, cols=2,
                        column_widths=[0.8, 0.2],
                        row_heights=[0.15, 0.85],
                        shared_yaxes=True,
                        horizontal_spacing=0,
                        vertical_spacing=0)

    # Create individual plots
    heatmap = px.imshow(df)
    bar_index = px.bar(x=df.columns, y=df.sum(axis=0)/1_000_000, text_auto='.1f',)
    bar_columns = px.bar(x=df.sum(axis=1)/1_000_000, y=df.index, orientation='h', text_auto='.1f')
    
    # Add traces to subplots
    fig.add_trace(heatmap.data[0], row=2, col=1)
    fig.add_trace(bar_index.data[0], row=1, col=1)
    fig.add_trace(bar_columns.data[0], row=2, col=2)
    
    # Update trace properties
    fig.update_traces(row=1, col=1, texttemplate='%{value:.1f}M', 
                      marker_color=color_index,
                      hovertemplate="<b>Month: %{x}</b><br><b>Total load: %{y:.2f}M</b><br>",
                      name='', textposition='outside')
    
    fig.update_traces(row=2, col=2, texttemplate='%{value:.1f}M',  
                      marker_color=color_columns,
                      hovertemplate="<b>Region: %{y}</b><br><b>Total load: %{x:.2f}M</b><br>",
                      name='', textposition='outside')
    
    fig.update_traces(row=2, col=1, 
                      hovertemplate="<b>Region: %{y}</b><br><b>Month: %{x}</b><br><b>Total load: %{z:,.0f} (MWh)</b>",
                      name='', xgap=1, ygap=1)
    
    # Update axes
    fig.update_xaxes(tickmode='linear', row=2, col=1)
    fig.update_yaxes(categoryorder='category descending', row=2, col=1)
    fig.update_yaxes(range=[1, 16], visible=False, row=1, col=1)
    fig.update_yaxes(visible=False, row=2, col=2)
    fig.update_xaxes(visible=False, range=[1, 35], row=2, col=2)
    fig.update_coloraxes(colorscale=colorscale)

    # Update layout
    fig.update_layout(height=500, width=900, 
                      margin=dict(l=10, r=20, t=50, b=10),
                      title_text='Monthly Actual Demand by Region in 2024 (Megawatt-hour)', title_x=0.5, 
                      template='plotly_dark', coloraxis_showscale=False, 
                      showlegend=False, modebar_orientation='v')

    return fig

# Create and show subplot
heatmap_bar_subplots(sum_by_month_region.T)  
My favorite is Contour Plot

5 Likes

I tried to compare the regions by months and the months by regions :woman_technologist:


Code
df_melt = sum_by_month_region.reset_index().melt(id_vars='Month', var_name='Region', value_name='Demand')

px.bar_polar(
    df_melt, 
    r='Demand', theta='Month',
    color='Region', 
    width=700, template='plotly_dark', 
    color_discrete_sequence= px.colors.qualitative.Pastel)

px.bar_polar(
    df_melt, 
    r='Demand', theta='Region',
    color='Month',   
    width=700, template='plotly_dark',
    color_discrete_sequence= px.colors.qualitative.Pastel )

But… I liked the separate presentation better :grinning:

Click to view

7 Likes

Hey @Ester,

whenever you change something, don’t forget to save it. You also need to click PUSH to persist your app changes :slight_smile: Maybe this helps? PyCafe Guide — Panel v1.5.4

You don’t have to delete the app.py, just add your changes in there. It will automatically syncronize your changes.

1 Like

Thank you very much. I saved and pushed. Thank you for the link. :star_struck:

2 Likes

Hi, guys… work in progress…
energy_usage_w49_v1

Code
"""Just importing"""
from dash import Dash, html, Output, Input, callback, dcc, no_update
import dash_mantine_components as dmc
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template
import pandas as pd
import plotly.express as px

# loads the "bootstrap" template and sets it as the default
# load_figure_template("bootstrap")

# importing and preparing the data ++++++++++++++++++++++++++++++++++++++
data = pd.read_csv("https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2024/week-49/megawatt_demand_2024.csv")

# Convert the "Local Timestamp" column to a datetime format and filter for October data
data['Local Timestamp'] = pd.to_datetime(data['Local Timestamp Eastern Time (Interval Beginning)'])
october_data = data[(data['Local Timestamp'] >= '2024-10-01') & (data['Local Timestamp'] < '2024-11-01')]

# Prepare the regional data for plotting
regions = [
    "Connecticut Actual Load (MW)", "Maine Actual Load (MW)",
    "New Hampshire Actual Load (MW)", "Northeast Massachusetts Actual Load (MW)",
    "Rhode Island Actual Load (MW)", "Southeast Massachusetts Actual Load (MW)",
    "Vermont Actual Load (MW)", "Western/Central Massachusetts Actual Load (MW)"
]
df = data.select_dtypes(include=['floating', 'datetime']).copy()
df.set_index('Local Timestamp', inplace=True) # for resampling

# renaming columns for shorter displays cols
old_values = df.columns.values.tolist()
new_values = [elem[:-17] for elem in old_values]
mapper = {old:new for old, new in zip(old_values, new_values)}
df.rename(mapper=mapper, axis=1, inplace=True)

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

radio_group = dmc.RadioGroup(
    id= 'radiogroup_period',
    label='Select period to aggregate',
    value=[],
    size='sm',
    # mb=5,
    children= dmc.Group([
        dmc.Radio(label='Year', value='YE'),
        dmc.Radio(label='Quarter', value='QE'),
        dmc.Radio(label='Month', value='ME'),
    ], className='m-2'), className='border ps-2 my-1',
)

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col(html.H3("New England energy usage",
                        className="text-start text-primary my-3"),
                width=12)
    ]),
    dbc.Row([
        dbc.Col(radio_group, width=7),
        dbc.Col(html.H4('Radar Chart',
                        className='text-center text-primary my-2'),
                width=5)
    ], align='center'),
    dbc.Row([
        dbc.Col(dcc.Graph(id="bar_chart_1", figure={}, className='shadow rounded'), width=7),
        dbc.Col(dcc.Graph(id="radar_chart", figure={}, className='shadow rounded'), width=5),
    ]),
], fluid=True)


@callback(
    Output("bar_chart_1", "figure"),
    Output('radar_chart', 'figure'),
    Input("radiogroup_period", "value"),
    prevent_initial_call = True
)
def update_bar_chart(value):
    # print(value)
    resample = (df
                .resample(value)
                .sum()
    )
    res_melted = (resample
                  .melt(var_name='state', value_name='Load_MW', ignore_index=False)
                  .reset_index()
                  .sort_values(by='Load_MW')
    )
    fig_bar = px.bar(res_melted, x='Load_MW', y='state', color='Local Timestamp',
              labels={'state':''}, height=450,
              title =f'Energy usage by Y2024 - {value}',
              template='plotly_white', color_discrete_sequence= px.colors.qualitative.D3,
             )
    fig_bar_polar = px.bar_polar(
                    res_melted,
                    r='Load_MW', theta='state',
                    color='Local Timestamp',
                    width=700, template='plotly_white',
                    color_discrete_sequence= px.colors.qualitative.D3
    )
    return fig_bar, fig_bar_polar

if __name__ == "__main__":
    app.run(debug=True, port=9099, jupyter_mode='inline')

4 Likes

Wow this is neat! Good job Natatsypora!

2 Likes

Hi Juan, I like the radar chart. What are you using to record the demo?

1 Like

I agree, @natatsypora . The separate bar polar chart presentation is much easier to read than everything on one chart. Thanks for testing it out.

2 Likes