Figure Friday 2025 - week 7

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

Are you able to find interesting insights and trends in US Government spending?

In this week’s Figure Friday, we’ll track the US federal government’s expenditure going back to 2022.

Download data:

  • Go to the Hamilton Project and click the button to Download all data
  • Save the CSV sheet in the same directory as the Python code provided below (under the sample figure)
  • Update the name of the file on line 6 of the code and run

Things to consider:

  • what can you improve in the app or sample figure below (Line Chart)?
  • would you like to tell a different data story using a different graph?
  • can you create a different Dash app?

Sample figure:

Code for sample figure:
from dash import Dash, dcc
import dash_ag_grid as dag
import plotly.express as px
import pandas as pd

df = pd.read_csv("all_outlays_2025-02-13.csv")
df.sort_values(['transaction_catg_dts', 'Date'], ascending=[False, False], inplace=True)

df_usaid = df.loc[df['transaction_catg_renamed'] == 'IAP - USAID']
df_usaid['Date'] = pd.to_datetime(df_usaid['Date'])
df_usaid = df_usaid.loc[df_usaid['Date'] >= '1/2/2025']

fig = px.line(df_usaid, x='Date', y='Daily', markers=True,
              title='Federal expenditures of USAID (billions of dollars)')


app = Dash()
grid = dag.AgGrid(
    rowData=df.to_dict("records"),
    columnDefs=[{"field": i, 'filter': True, 'sortable': True} for i in df.columns],
    dashGridOptions={"pagination": True}
)

app.layout = [
    grid,
    dcc.Graph(figure=fig)
]


if __name__ == "__main__":
    app.run(debug=True)

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 Hamilton Project for the data.

3 Likes

Cool project this week. Didn’t know about this organization before today. I enjoy coming across organizations and resources that are out there illustrating and analyzing gov spend, ill continue to review more of their resources!

I created a dash app with side nav bar. Users can click into the analysis they want by the nav links in the side bar.

Provided a homepage that briefly describes what the dash app ( Welcome to the Spend Tracker Dashboard) - very general info

The First link is a bar animation of the spend (aggregate by month) an category (billions).

Then followed by some other plotly illustrations



import dash
from dash import dcc, html, Input, Output
import dash_bootstrap_components as dbc
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import uuid

# -------------------------
# Data & Figure Preparation
# -------------------------

print("Script started. Reading CSV...")
csv_path = r'path.csv'
try:
    df = pd.read_csv(csv_path, parse_dates=['Date'])
    print("CSV read successfully.")
except Exception as e:
    print("Error reading CSV:", e)
    raise

# Rename columns for consistency
df.rename(columns={'Date': 'date', '7-day moving average': 'moving_avg'}, inplace=True)

# Exclude rows with 'total' in the category name (if any)
df = df[~df['transaction_catg_renamed'].str.contains('total', case=False, na=False)]

# Create an abbreviated category column (up to 15 characters)
df['cat_abbrev'] = df['transaction_catg_renamed'].apply(
    lambda x: x if len(x) <= 15 else x[:12] + "..."
)
# Keep the full category name for hover text (will use the first encountered value per group)
df['full_cat'] = df['transaction_catg_renamed']

# Create a 'month' column representing the start of each month
df['month'] = df['date'].dt.to_period('M').apply(lambda r: r.start_time)

# -------------------------
# Animated Bar Chart (Top 20 Categories - Aggregate Spending)
# -------------------------
# Group by month and category, summing the 'Daily' spending and keeping the full category name
df_monthly = (
    df.groupby(['month', 'cat_abbrev'], as_index=False)
      .agg({'Daily': 'sum', 'full_cat': 'first'})
)

# For each month, get the top 20 categories by total aggregate spending
df_monthly_top20 = (
    df_monthly.sort_values(['month', 'Daily'], ascending=[True, False])
    .groupby('month')
    .head(20)
    .reset_index(drop=True)
)
df_monthly_top20['month_str'] = df_monthly_top20['month'].dt.strftime('%Y-%m')

bar_fig = px.bar(
    df_monthly_top20,
    x="Daily",
    y="cat_abbrev",
    orientation='h',
    animation_frame="month_str",
    color="cat_abbrev",
    range_x=[0, df_monthly_top20["Daily"].max() * 1.2],
    labels={"cat_abbrev": "Category", "Daily": "Total Aggregate Spending (Billions)"},
    title="Animated Top 20 Monthly Aggregate Spending by Category",
    custom_data=["full_cat"]
)
bar_fig.update_traces(
    hovertemplate='<b>%{customdata[0]}</b><br>Total Aggregate Spending: %{x} Billion<br><extra></extra>'
)
bar_fig.layout.updatemenus[0].buttons[0].args[1]['frame']['duration'] = 1000
bar_fig.layout.updatemenus[0].buttons[0].args[1]['transition']['duration'] = 3
bar_fig.update_layout(margin=dict(l=150, r=20, t=50, b=50))

# -------------------------
# Line Chart (Total Monthly Spending)
# -------------------------

df['Daily'] = pd.to_numeric(df['Daily'], errors='coerce')
df_monthly_total = df.groupby(df['month'].dt.strftime('%Y-%m'))['Daily'].sum().reset_index()

line_fig = px.line(
    df_monthly_total,
    x="month",
    y="Daily",
    title="Total Monthly Spending Across All Categories",
    labels={"month": "Month", "Daily": "Total Spending (Billions)"},
    markers=True
)
line_fig.update_traces(line=dict(width=3), marker=dict(size=8))

# -------------------------
# Pie Chart (Top Categories + "Other")
# -------------------------

latest_month = df['month'].max()
df_latest_month = df[df['month'] == latest_month]
df_latest_month_grouped = df_latest_month.groupby('cat_abbrev', as_index=False)['Daily'].sum()
df_latest_month_grouped = df_latest_month_grouped.sort_values('Daily', ascending=False)

top_n = 10
df_top = df_latest_month_grouped.head(top_n)
df_other = df_latest_month_grouped.iloc[top_n:]
df_other_sum = df_other['Daily'].sum()
df_top.loc[len(df_top)] = ['Other', df_other_sum]

pie_fig = px.pie(
    df_top,
    names="cat_abbrev",
    values="Daily",
    title=f"Spending Distribution by Category ({latest_month.strftime('%Y-%m')})",
)
pie_fig.update_traces(textinfo="label+percent", pull=[0.1 if i < top_n else 0 for i in range(len(df_top))])

# -------------------------
# Sankey Diagram (Spending Flow)
# -------------------------

top_n = 10
df_sankey = df.groupby('cat_abbrev', as_index=False)['Daily'].sum().sort_values('Daily', ascending=False)
df_top = df_sankey.head(top_n)
df_other_sum = df_sankey.iloc[top_n:]['Daily'].sum()
df_top.loc[len(df_top)] = ['Other', df_other_sum]

node_labels = ["All Outlays"] + df_top['cat_abbrev'].tolist()
source = [0] * len(df_top)
target = list(range(1, len(df_top) + 1))
values = df_top["Daily"].tolist()

sankey_fig = go.Figure(data=[go.Sankey(
    node=dict(pad=15, thickness=20, label=node_labels),
    link=dict(source=source, target=target, value=values)
)])
sankey_fig.update_layout(title=f"Sankey Diagram of Total Spending Flow (Top {top_n} + Other)")

# -------------------------
# Treemap Chart (Aggregate Spend by Category)
# -------------------------
df_treemap = df.groupby('cat_abbrev', as_index=False)['Daily'].sum()
treemap_fig = px.treemap(
    df_treemap,
    path=['cat_abbrev'],
    values='Daily',
    title="Treemap of Total Aggregate Spend by Category"
)
treemap_fig.update_traces(textinfo="label+value+percent entry")

# -------------------------
# Layout & Navigation
# -------------------------

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    
    # Sidebar
    html.Div([
        html.H2("Spend Dashboard", style={"color": "white"}),
        html.Hr(style={"borderColor": "white"}),
        dbc.Nav([
            dbc.NavLink("Home", href="/home", active="exact"),
            dbc.NavLink("Animated Bar Chart", href="/bar-chart", active="exact"),
            dbc.NavLink("Line Chart", href="/line-chart", active="exact"),
            dbc.NavLink("Pie Chart", href="/pie-chart", active="exact"),
            dbc.NavLink("Sankey Diagram", href="/sankey", active="exact"),
            dbc.NavLink("Treemap", href="/treemap", active="exact"),
        ], vertical=True, pills=True)
    ], style={"width": "16rem", "position": "fixed", "height": "100vh", "background-color": "#355E3B", "padding": "1rem", "color": "white"}),

    # Content
    html.Div(id='page-content', style={"margin-left": "18rem", "padding": "2rem"})
])

# -------------------------
# Homepage Layout
# -------------------------
def homepage_layout():
    return html.Div([
        html.H1("Welcome to the Spend Tracker Dashboard", style={'textAlign': 'center'}),
        html.P(
            "This dashboard provides insights into monthly spending patterns across various categories. "
            "Explore interactive visualizations to gain a deeper understanding of spending trends over time.",
            style={'textAlign': 'center', 'fontSize': '18px'}
        ),
        html.Hr(),
        html.Div([
            html.H3("Navigation Guide:"),
            html.Ul([
                html.Li([html.B("Animated Bar Chart:"), " Visualize monthly changes in the top 20 spending categories with an animated bar chart showing aggregate spend."]),
                html.Li([html.B("Line Chart:"), " Explore the overall monthly spending trends with a clear line chart."]),
                html.Li([html.B("Pie Chart:"), " Understand the spending distribution for the latest month with a pie chart."]),
                html.Li([html.B("Sankey Diagram:"), " See the spending flow for top categories plus an 'Other' category using a Sankey diagram."]),
                html.Li([html.B("Treemap:"), " View a treemap of the total aggregate spend by category."]),
            ], style={'fontSize': '16px'}),
            html.P("Select a chart from the sidebar to get started.", style={'fontSize': '16px'}),
        ], style={'maxWidth': '600px', 'margin': '0 auto'}),
    ], style={'padding': '2rem'})

# -------------------------
# Page Switching Callback
# -------------------------
@app.callback(Output('page-content', 'children'), [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/bar-chart':
        graph_id = f'bar-chart-graph-{uuid.uuid4()}'
        return dcc.Graph(id=graph_id, figure=bar_fig)
    elif pathname == '/line-chart':
        graph_id = f'line-chart-graph-{uuid.uuid4()}'
        return dcc.Graph(id=graph_id, figure=line_fig)
    elif pathname == '/pie-chart':
        graph_id = f'pie-chart-graph-{uuid.uuid4()}'
        return dcc.Graph(id=graph_id, figure=pie_fig)
    elif pathname == '/sankey':
        graph_id = f'sankey-graph-{uuid.uuid4()}'
        return dcc.Graph(id=graph_id, figure=sankey_fig)
    elif pathname == '/treemap':
        graph_id = f'treemap-graph-{uuid.uuid4()}'
        return dcc.Graph(id=graph_id, figure=treemap_fig)
    elif pathname in ['/home', '/']:
        return homepage_layout()
    else:
        return html.Div([
            html.H3("404: Page not found"),
            html.P("The page you are looking for does not exist.")
        ])

# -------------------------
# Run App
# -------------------------
if __name__ == "__main__":
    print("Starting Dash server on http://127.0.0.1:8050 ...")
    app.run_server(debug=False)

8 Likes

I like your layout and charts. :star_struck:

2 Likes

This Dash app is the Hamilton Project Spending Dashboard that visualizes financial transactions using interactive elements. It loads and processes transaction data from a CSV file, allowing users to filter data by year, month, and metric (Daily, Weekly, or 7-Day Moving Average).

Key Features:

  • Data Processing: Cleans, converts, and filters transaction data to focus on the top 10 spending categories.
  • Interactive Dashboard:
    • KPI Cards: Show total weekly, daily spending, and 7-day moving average.
    • Dropdown Filters: Let users refine data by year, month, and metric.
    • Bar Chart: Displays spending trends by category using Plotly Express.
    • AG Grid Table: Shows detailed transaction data with filtering, sorting, and conditional formatting
  • Callbacks: Update visualizations and KPIs dynamically based on user selections.

The app uses Dash Bootstrap Components for styling and dash_ag_grid for an interactive table. It runs locally and updates in real-time with user input.

If I have time this week, I’ll add annotations.

8 Likes

Hi everyone,

I’m excited to share my latest project for Figure Friday 2025 Week 7 – The Hamilton Project. This dashboard offers an interactive exploration of US federal outlays with multiple visualizations (line, bar, pie, heatmap, and more) built using Plotly Dash. The project dives deep into federal spending trends and highlights key insights through comprehensive data analysis.

You can check out more details and explore the project on the following platforms:

Uploading: Снимок экрана 2025-02-18 123242.png…

I look forward to your feedback and suggestions!







6 Likes

Update!
I also added a line chart with a radio button.

5 Likes

Hello Everyone,

Here goes my webapp of this week 7. This web app visualizes the US federal government’s expenditure trends and percentage changes over time. Here’s a concise summary of its key features:

  • Data Visualization: It shows comprehensive charts that depict total US federal spending trends and percentage changes by various transaction categories.
  • User Interaction: Users can select different time frequencies (daily, weekly, monthly, quarterly) and specific transaction categories to customize the visualizations.
  • Sidebar Controls: The sidebar provides radio buttons and a modal for users to choose time frequency and transaction categories.
  • Charts: The main content area displays two types of charts:
    • An area chart for total US federal spending trends.
    • A line chart for the percentage change in US federal expenditure.

The code

import numpy as np
import pandas as pd
import plotly.express as px
import dash
import dash_bootstrap_components as dbc
from dash import Dash, dcc,html, Input, Output, State

df = pd.read_csv("all_outlays_2025-02-14.csv", parse_dates=['Date'])

df['pct_change'] = df['Daily'].pct_change()
df['pct_change'] = df['pct_change'].replace([np.inf, -np.inf], np.nan)
df['pct_change'] = df['pct_change'].fillna(0)

pivot_df = (
    df.pivot_table(
        index=["Date"],
        columns="transaction_catg_renamed",
        values="Daily",
        aggfunc="sum"
    )
    .fillna(0)
)

pivot_pct_df = (
    df.pivot_table(
        index=["Date"],
        columns="transaction_catg_renamed",
        values="pct_change",
    )
    .fillna(0)
)

categories = df['transaction_catg_renamed'].unique()

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

app.title = "US federal government’s expenditure"

sidebar = dbc.Col([
    html.Hr(),
    html.H5("Selecting Time Frequency"),
    dbc.RadioItems(
        id="period-filter",
        options=[
            {'label': 'Date', 'value': 'D'},
            {'label': 'Weekly', 'value': 'W'},
            {'label': 'Monthly', 'value': 'ME'},
            {'label': 'Quarterly', 'value': 'QE'}
        ],
        value='ME',
        inline=False, 
        class_name="btn btn-info",
    ),
    html.Hr(),
    html.H5("Transaction Categories"),
    html.Hr(),
    dbc.Button("Select Categories", id="open-modal", n_clicks=0, className="btn btn-info"),
    dbc.Modal([
        dbc.ModalHeader("Select Categories"),
        dbc.ModalBody(dbc.Checklist(
            id='category-checklist',
            options=[{'label': cat, 'value': cat} for cat in categories],
            value=  ['IAP - USAID',
                     'Dept of Education (ED)', 
                     'Dept of Commerce (DOC)', 
                     'Dept of Energy (DOE)', 
                     'Dept of Justice (DOJ)'],
            inline=False,  
            switch=True,
            style={"backgroundColor": "#f0f0f0", "padding": "8px", "fontSize": "16px"}
        )),
        dbc.ModalFooter(
            dbc.Button("Close", id="close-modal", className="btn btn-info"
                      )
        )
    ], id="modal", is_open=False),
    html.Div(id="selected-categories", style={"marginTop": "10px"})
  
], width=3, style={'background-color': '#f8f9fa', 'padding': '20px', 'text-align': 'center', 'position': 'fixed',
    'height': '100vh',
    'overflow': 'auto'}) 



content = dbc.Col([
    html.H3("The Hamilton Project: Tracking US federal expenditure since 2022",
            style={"text-align": "center"}),
    html.Hr(),
    dbc.Row([
        html.H5("Total US Federal Spending Trends by Category",
                style={"backgroundColor": "#f0f0f0", "padding": "8px", "text-align": "center"}),
        dbc.Col(dcc.Graph(id="area-chart"), width=12),
    ]),
    dbc.Row([
        html.H5("Percentage Change in US Federal Expenditure by Category",
                style={"backgroundColor": "#f0f0f0", "padding": "8px", "text-align": "center"}),
        dbc.Col(dcc.Graph(id="line-chart"), width=12),
    ]),
], width={"size": 9, "offset": 3})

app.layout = dbc.Container([
    dbc.Row([
        sidebar,
        content
    ])
], fluid=True)

@app.callback(
    Output("modal", "is_open"),
    [Input("open-modal", "n_clicks"), Input("close-modal", "n_clicks")],
    [State("modal", "is_open")],
)
def toggle_modal(n1, n2, is_open):
    if n1 or n2:
        return not is_open
    return is_open

@app.callback(
    Output("selected-categories", "children"),
    Input('category-checklist', 'value')
)
def display_selected_categories(selected_categories):
    return f"Selected Categories: {', '.join(selected_categories)}" if selected_categories else "No categories selected"

@app.callback(
    Output("area-chart", "figure"),
    Output("line-chart", "figure"),
    Input("period-filter", "value"),
    Input('category-checklist', 'value')
)
def update_charts(selected_period, selected_categories):

    resample_str = selected_period

 
    filtered_pivot_df = pivot_df[selected_categories]
    filtered_pivot_pct_df = pivot_pct_df[selected_categories]

  
    resampled_pivot_df = filtered_pivot_df.resample(resample_str).sum()
    resampled_pivot_pct_df = filtered_pivot_pct_df.resample(resample_str).mean()

    fig_area = px.area(resampled_pivot_df, template='presentation', color_discrete_sequence=px.colors.qualitative.Prism,
                       labels={'transaction_catg_renamed': 'Transaction Categories', 'Date': '', 'value': 'US$ in Billions'},
                       markers=True)
    fig_area.update_layout(legend=dict(title=None, orientation="h", y=1, yanchor="bottom", x=0.5, xanchor="center",
                                        font=dict(size=16)))

    fig_line = px.line(resampled_pivot_pct_df, template='presentation', color_discrete_sequence=px.colors.qualitative.Prism,
                        labels={'transaction_catg_renamed': 'Transaction Categories', 'Date': '', 'value': 'Percentage Change %'},
                        markers=True)
    fig_line.update_layout(showlegend=False)

    return fig_area, fig_line

if __name__ == '__main__':
    app.run_server(debug=True, jupyter_mode='external')



The app

8 Likes

hi @ThomasD21M
Nice sidebar and graphs. Is there a reason you chose different time frames? The pie chart shows data for February 2025 while the treemap appears to show all-time data.

1 Like

HI @Ester
I love the Dash AG Grid that you added at the bottom.

I’m not sure if this is a bug or it’s in the code, but the draw open freeform button of the graph is pre-selected by default.

Regarding the Monthly dropdown, I usually see months ordered chronologically instead of alphabetically. Jan, Feb, March, etc.

1 Like

Great usage of the are chart, @Avacsiglo21 .
In the time frequency selection, I would just say Day instead of Date. It’s more inline with quarters, months, weeks, “days”.

I loved your modal for the category selection and the checklist you added inside of it.

2 Likes

Hi Adams, Totally Agree with you. Thank you

2 Likes

Thank you! I will check it for FF session…

2 Likes

An honest mistake with the consistently, good catch.

2 Likes

This is my first time to submit a dashboard to Figure Friday. My dashboarding skills are barely past the Hello World level; I hope to improve them in the coming weeks.

I enhanced the solution offered for this week’s exercise which included a line plot and dash AG table. I added a histogram, changed changed the currency units from billions of US dollars to millions of US dollars, changed the graph template to ‘simple_white’, and updated axis labels and graph titles. A pulldown selects the spending category, then updates the data in the line plot, histogram and the table.

This screen shot has selected IAP-USAID. The line plot matches the plot given in this week’s exercise.

This screen shot shows federal salary payments. I assume from the peaks that federal employees are paid every other Friday, with paydays last month in January happening on January 3 and January 17.

Here is the code:

from dash import Dash, dcc, html, Input, Output, jupyter_dash
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
import plotly.express as px
import polars as pl
from datetime import datetime
#-------------------------------------------------------------------------------
#  This week 7 submission is an enhancement to the code offered for this exercise.
#  My dashboard skill are barely past the hello world level, hope to get better.
#-------------------------------------------------------------------------------

# Read the given csv data to polars dataframe df
csv_data_source = 'all_outlays_2025-02-14.csv'

df = (
    pl.scan_csv(
        csv_data_source, 
        infer_schema_length=0  # this reads all columns as string
    )
    .rename({'transaction_catg_renamed': 'TRANS_CAT'})
    .with_columns(
        pl.col('Date').str.strptime(pl.Date, '%Y-%m-%d'),
        pl.col('Daily').cast(pl.Float32),
    )
    .with_columns(
        DAILY_MIL_USD = (pl.col('Daily')*1000),
    )
    .filter(pl.col('Date') > pl.lit(datetime(2025, 1, 2)))
    .select('TRANS_CAT', 'Date', 'DAILY_MIL_USD')
    .collect()
)
# make df_usaid using filtered, sorted version of big df.
default_cat = 'IAP - USAID'

grid = dag.AgGrid(
    rowData=[],
    columnDefs=[{"field": i, 'filter': True, 'sortable': True} for i in df.columns],
    dashGridOptions={"pagination": True},
    id='my_grid'
)

drop_down_list = sorted(list(set(df['TRANS_CAT'])))
print(f'{len(drop_down_list) = }')
print(f'{drop_down_list = }')

print(df.filter(pl.col('TRANS_CAT')== default_cat))

app = Dash(external_stylesheets=[dbc.themes.SANDSTONE])
app.layout = dbc.Container([
    dbc.Row([dcc.Dropdown(drop_down_list, default_cat, id='my_dropdown'),]),
    html.Div(id='dd-output-container'),
    dbc.Row([
        dbc.Col(dcc.Graph(id='line_plot'), width=4),
        dbc.Col(dcc.Graph(id='hist_plot'), width=4),
    ]),
    dbc.Row([
        dbc.Col([grid], width=4),
    ])
])

df_selected = df
@app.callback(
        Output('line_plot', 'figure'),
        Output('hist_plot', 'figure'),
        Output('my_grid', 'rowData'),
        Input('my_dropdown', 'value')
)

def update_dashboard(selected_group):
    df_selected = df.filter(pl.col('TRANS_CAT') == selected_group)
    line_plot = px.line(
        df_selected, 
        x='Date', 
        y='DAILY_MIL_USD', 
        markers=True,
        title=f'Expenditures of {selected_group}'.upper(),
        template='simple_white',
        height=400, width=600,
        line_shape='spline'
    )
    line_plot.update_layout(
        xaxis_title='',
        yaxis_title='Daily Expenditures (MILLION $US)'.upper(),
    )
    hist_plot = px.histogram(
        df_selected, 
        x='DAILY_MIL_USD', 
        title=f'Expenditures of {selected_group}'.upper(),
        template='simple_white',
        height=400, width=600,
    )
    hist_plot.update_layout(
        xaxis_title='Daily Expenditures (MILLION $US)'.upper(),
        yaxis_title='count'.upper(),
    )
    print(df_selected)
    return line_plot, hist_plot, df_selected.to_pandas().to_dict('records')

if __name__ == "__main__":
    app.run(jupyter_height=500, jupyter_width='70%')

7 Likes

@Mike_Purtell Congratulations on your first dashboard :partying_face: I’m happy for you. Nice job aligning the graphs to the top row and adding a table underneath it all. You’re going to become an expert in Dash in no time :slight_smile:

1 Like

You are so kind @adamschroeder. First dashboard took a long time for me to figure things out, even with help, but I had a lot of fun doing so. I look forward to more of these. Thank you.

3 Likes