开始使用免费开始使用

交互式销售仪表板表格

自从您开始构建以来,这个整体的销售仪表板已经取得了长足进展。

该仪表板已经在公司内广泛传播,并收获了很多积极反馈。不过,也有一些关于让表格组件更具交互性的需求。有人希望增加一个功能:选择某一列后,下面的散点图坐标轴能够随之改变。

本练习是课程的一部分

使用 Dash 和 Plotly 构建仪表板

查看课程

练习说明

  • 在第 25 行下方,通过设置 rowSelection 参数,在 grid 中启用单行选择。
  • 在第 63 行下方创建一个回调,使用 my_gridselectedRows 来更新 scatter_compare 图形,并将所选国家以一个大的红色点高亮显示。

交互式实操练习

通过完成这段示例代码来试试这个练习。

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(['Country'])['OrderValue'].agg(['sum', 'count', 'mean', 'median']).reset_index().rename(columns={'count':'Sales Volume', 'sum':'Total Sales ($)', 'mean':'Average Order Value ($)', 'median':'Median 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':'pink'})

money_fmt = {"function": ("params.value.toLocaleString('en-US', {style: 'currency', currency: 'USD'})")}
column_defs = [
  {"field": "Country"},
  {"field": "Total Sales ($)", "valueFormatter": money_fmt},
  {"field": "Average Order Value ($)", "valueFormatter": money_fmt},
  {"field": "Median Order Value ($)",  "valueFormatter": money_fmt},
  {"field": "Sales Volume"},
]

grid = AgGrid(
  id="my_grid",
  columnDefs=column_defs,
  rowData=large_tb.to_dict("records"),
  # Enable single-row selection
  dashGridOptions={"____": "____"},
  defaultColDef={
    "cellStyle": {                          
      "textAlign": "left",
      "backgroundColor": "black",
      "color": "white"}}
)

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'}),
  grid,
  html.Br(),
  html.Div([
    dcc.Graph(id='scatter_compare'),
    dcc.Graph(id='major_cat', figure=bar_fig_country, style={'display':'inline-block'}),
    dcc.Graph(id='minor_cat', style={'display':'inline-block'})],
    style={'width':'1000px', 'height':'650px','display':'inline-block'})
]

# Create a callback with the right input
@callback(
    Output('scatter_compare', 'figure'),
    Input('my_grid', '____')
)
def update_scatter(selected_rows):
    fig = px.scatter(
        large_tb,
        x='Sales Volume',
        y='Total Sales ($)',
        color='Country',
        title='Sales Volume vs Total Sales ($) — click a row to highlight')
    if selected_rows:
        sel_df = pd.DataFrame(selected_rows)
        highlight = px.scatter(
            sel_df,
            x='Sales Volume',
            y='Total Sales ($)',
            color_discrete_sequence=['#ff0000'],
            size=[15] * len(sel_df))        
        fig.add_trace(highlight.data[0])
    return fig

@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)
编辑并运行代码