Enhancing the sales dashboard
The e-commerce company is thrilled with your work and keeps coming up with creative new ideas. This time, they want a powerful dashboard that combines everything you’ve built so far into one interactive interface.
Now they wish to have:
- A line chart of monthly sales filtered by major and minor categories.
- A dropdown for each category, where the minor options update based on the major selection.
- A table showing aggregated stats for those categories.
- A bar chart of total sales by country.
- Another bar chart showing minor category sales that updates when hovering over the country chart.
You've already built all the parts - now it's time to bring them together!
This exercise is part of the course
Building Dashboards with Dash and Plotly
Exercise instructions
- Turn on pagination for the
grid
by setting thepagination
parameter below line23
. - Limit each page to show 6 rows by setting the
paginationPageSize
parameter below line26
. - Insert the
grid
object into theapp.layout
list below line49
.
Hands-on interactive exercise
Have a go at this exercise by completing this sample code.
from dash import Dash, dcc, html, Input, Output, callback
import plotly.express as px
import pandas as pd
from dash_ag_grid import AgGrid
ecom_sales = pd.read_csv('/usr/local/share/datasets/ecom_sales.csv')
logo_link = 'https://assets.datacamp.com/production/repositories/5893/datasets/fdbe0accd2581a0c505dab4b29ebb66cf72a1803/e-comlogo.png'
major_categories = list(ecom_sales['Major Category'].unique())
large_tb = ecom_sales.groupby(['Major Category', 'Minor Category'])['OrderValue'].agg(['sum', 'count', 'mean']).reset_index().rename(columns={'count':'Sales Volume', 'sum':'Total Sales ($)', 'mean':'Average Order Value ($)'})
ecom_country = ecom_sales.groupby('Country')['OrderValue'].agg('sum').reset_index(name='Total Sales ($)')
bar_fig_country = px.bar(ecom_country, x='Total Sales ($)', y='Country', width=500, height=450, title='Total Sales by Country (Hover to filter the Minor Category bar chart!)', custom_data=['Country'], color='Country', color_discrete_map={'United Kingdom':'lightblue', 'Germany':'orange', 'France':'darkblue', 'Australia':'green', 'Hong Kong':'red'})
money_fmt = {"function": ("params.value.toLocaleString('en-US', {style: 'currency', currency: 'USD'})")}
column_defs = [
{"field": "Major Category"},
{"field": "Total Sales ($)", "valueFormatter": money_fmt},
{"field": "Average Order Value ($)", "valueFormatter": money_fmt},
{"field": "Sales Volume"}
]
grid = AgGrid(
columnDefs=column_defs,
rowData=large_tb.to_dict("records"),
# Turn on pagination
dashGridOptions={
"____": ____,
# Show 6 rows per page
"____": ____}
)
app = Dash()
app.layout = [
html.Img(src=logo_link, style={'margin':'30px 0px 0px 0px' }),
html.H1('Sales breakdowns'),
html.Div([
html.H2('Controls'),
html.Br(),
html.H3('Major Category Select'),
dcc.Dropdown(id='major_cat_dd',
options=[{'label':category, 'value':category} for category in major_categories],
style={'width':'200px', 'margin':'0 auto'}),
html.Br(),
html.H3('Minor Category Select'),
dcc.Dropdown(id='minor_cat_dd', style={'width':'200px', 'margin':'0 auto'})],
style={'width':'350px', 'height':'360px', 'display':'inline-block', 'vertical-align':'top', 'border':'1px solid black', 'padding':'20px'}),
html.Div([
html.H3(id='chosen_major_cat_title'),
dcc.Graph(id='sales_line')],
style={'width':'700px', 'height':'380px','display':'inline-block', 'margin-bottom':'5px'}),
# Insert the AG Grid
____,
html.Div([
html.Div(dcc.Graph(id='major_cat', figure=bar_fig_country), style={'display':'inline-block'}),
html.Div(dcc.Graph(id='minor_cat'), style={'display':'inline-block'})],
style={'width':'1000px', 'height':'650px','display':'inline-block'})
]
@callback(
Output('minor_cat_dd', 'options'),
Output('chosen_major_cat_title', 'children'),
Input('major_cat_dd', 'value'))
def update_dd(major_cat_dd):
major_minor = ecom_sales[['Major Category', 'Minor Category']].drop_duplicates()
relevant_minor = major_minor[major_minor['Major Category'] == major_cat_dd]['Minor Category'].values.tolist()
minor_options = [dict(label=x, value=x) for x in relevant_minor]
if not major_cat_dd:
major_cat_dd = 'ALL'
major_cat_title = f'This is in the Major Category of : {major_cat_dd}'
return minor_options, major_cat_title
@callback(
Output('sales_line', 'figure'),
Input('minor_cat_dd', 'value'))
def update_line(minor_cat):
minor_cat_title = 'All'
ecom_line = ecom_sales.copy()
if minor_cat:
minor_cat_title = minor_cat
ecom_line = ecom_line[ecom_line['Minor Category'] == minor_cat]
ecom_line = ecom_line.groupby('Year-Month')['OrderValue'].agg('sum').reset_index(name='Total Sales ($)')
line_graph = px.line(ecom_line, x='Year-Month', y='Total Sales ($)', title=f'Total Sales by Month for Minor Category: {minor_cat_title}', height=350)
return line_graph
@callback(
Output('minor_cat', 'figure'),
Input('major_cat', 'hoverData'))
def update_min_cat_hover(hoverData):
hover_country = 'Australia'
if hoverData:
hover_country = hoverData['points'][0]['customdata'][0]
minor_cat_df = ecom_sales[ecom_sales['Country'] == hover_country]
minor_cat_agg = minor_cat_df.groupby('Minor Category')['OrderValue'].agg('sum').reset_index(name='Total Sales ($)')
ecom_bar_minor_cat = px.bar(minor_cat_agg, x='Total Sales ($)', y='Minor Category', orientation='h', height=450, width=480,title=f'Sales by Minor Category for: {hover_country}')
ecom_bar_minor_cat.update_layout({'yaxis':{'dtick':1, 'categoryorder':'total ascending'}, 'title':{'x':0.5}})
return ecom_bar_minor_cat
if __name__ == '__main__':
app.run(debug=True)