"""Example app to show all features of Vizro.""" from time import sleep from typing import List, Literal, Optional import pandas as pd import plotly.graph_objects as go import vizro.models as vm import vizro.plotly.express as px from dash import dash_table, html, get_asset_url import dash_bootstrap_components as dbc from vizro import Vizro from vizro.actions import export_data, filter_interaction from vizro.models.types import capture from vizro.tables import dash_ag_grid, dash_data_table iris = px.data.iris() tips = px.data.tips() stocks = px.data.stocks(datetimes=True) gapminder_2007 = px.data.gapminder().query("year == 2007") waterfall_df = pd.DataFrame( { "measure": ["relative", "relative", "total", "relative", "relative", "total"], "x": ["Sales", "Consulting", "Net revenue", "Purchases", "Other expenses", "Profit before tax"], "text": ["+60", "+80", "", "-40", "-20", "Total"], "y": [60, 80, 0, -40, -20, 0], } ) # HOME ------------------------------------------------------------------------ home = vm.Page( title="Homepage", layout=vm.Layout(grid=[[0, 1], [2, 3]], row_gap="16px", col_gap="24px"), components=[ vm.Card( text=""" ![](assets/images/icons/line-chart.svg#icon-top) ### Components Main components of Vizro include **charts**, **tables**, **cards**, **containers**, **buttons** and **tabs**. """, href="/graphs", ), vm.Card( text=""" ![](assets/images/icons/filters.svg#icon-top) ### Controls Vizro has two different control types **Filter** and **Parameter**. You can use any pre-existing selector inside the **Filter** or **Parameter**: * Dropdown * Checklist * RadioItems * RangeSlider * Slider * DatePicker """, href="/filters", ), vm.Card( text=""" ![](assets/images/icons/download.svg#icon-top) ### Actions Standard predefined actions are made available including **export data** and **filter interactions**. """, href="/export-data", ), vm.Card( text=""" ![](assets/images/icons/use-case.svg#icon-top) ### Extensions Vizro enables customization of **plotly express** and **graph object charts** as well as creating custom components based on Dash. """, href="/custom-charts", ), ], ) # COMPONENTS ------------------------------------------------------------------ graphs = vm.Page( title="Graphs", components=[ vm.Graph( figure=px.scatter_matrix( iris, dimensions=["sepal_length", "sepal_width", "petal_length", "petal_width"], color="species", ) ) ], controls=[vm.Filter(column="species", selector=vm.Dropdown(title="Species"))], ) ag_grid = vm.Page( title="AG Grid", components=[ vm.AgGrid( title="Dash AG Grid", figure=dash_ag_grid(data_frame=gapminder_2007, dashGridOptions={"pagination": True}) ) ], controls=[vm.Filter(column="continent")], ) table = vm.Page( title="Table", components=[ vm.Table( title="Dash DataTable", figure=dash_data_table(data_frame=gapminder_2007), ) ], controls=[vm.Filter(column="continent")], ) cards = vm.Page( title="Cards", components=[ vm.Card( text=""" # Header level 1

## Header level 2

### Header level 3

#### Header level 4

""" ), vm.Card( text=""" ### Paragraphs Commodi repudiandae consequuntur voluptatum laborum numquam blanditiis harum quisquam eius sed odit. Fugiat iusto fuga praesentium option, eaque rerum! Provident similique accusantium nemo autem. Obcaecati tenetur iure eius earum ut molestias architecto voluptate aliquam nihil, eveniet aliquid. Culpa officia aut! Impedit sit sunt quaerat, odit, tenetur error, harum nesciunt ipsum debitis quas. """ ), vm.Card( text=""" ### Block Quotes > > A block quote is a long quotation, indented to create a separate block of text. > """ ), vm.Card( text=""" ### Lists * Item A * Sub Item 1 * Sub Item 2 * Item B """ ), vm.Card( text=""" ### Emphasis This word will be *italic* This word will be **bold** This word will be _**bold and italic**_ """ ), ], ) button = vm.Page( title="Button", layout=vm.Layout(grid=[[0], [0], [0], [0], [1]]), components=[ vm.Graph( figure=px.scatter( iris, x="sepal_width", y="sepal_length", color="species", size="petal_length", ), ), vm.Button(text="Export data", actions=[vm.Action(function=export_data())]), ], controls=[vm.Filter(column="species", selector=vm.Dropdown(title="Species"))], ) containers = vm.Page( title="Containers", components=[ vm.Container( title="Container I", layout=vm.Layout(grid=[[0, 1]]), components=[ vm.Graph( figure=px.scatter( iris, x="sepal_length", y="petal_width", color="species", title="Container I - Scatter", ) ), vm.Graph( figure=px.bar( iris, x="sepal_length", y="sepal_width", color="species", title="Container I - Bar", ) ), ], ), vm.Container( title="Container II", components=[ vm.Graph( figure=px.scatter( iris, x="sepal_width", y="sepal_length", color="species", marginal_y="violin", marginal_x="box", title="Container II - Scatter", ) ) ], ), ], ) tab_1 = vm.Container( title="Tab I", components=[ vm.Graph( figure=px.bar( gapminder_2007, title="Graph 1", x="continent", y="lifeExp", color="continent", ), ), vm.Graph( figure=px.box( gapminder_2007, title="Graph 2", x="continent", y="lifeExp", color="continent", ), ), ], ) tab_2 = vm.Container( title="Tab II", components=[ vm.Graph( figure=px.scatter( gapminder_2007, title="Graph 3", x="gdpPercap", y="lifeExp", size="pop", color="continent", ), ), ], ) tabs = vm.Page(title="Tabs", components=[vm.Tabs(tabs=[tab_1, tab_2])], controls=[vm.Filter(column="continent")]) # CONTROLS -------------------------------------------------------------------- filters = vm.Page( title="Filters", components=[ vm.Graph( figure=px.scatter( iris, x="sepal_length", y="petal_width", color="species", ) ), vm.Graph( id="scatter_chart2", figure=px.scatter( iris, x="petal_length", y="sepal_width", color="species", ), ), ], controls=[ vm.Filter(column="species"), vm.Filter( column="petal_length", targets=["scatter_chart2"], selector=vm.RangeSlider(), ), ], ) parameters = vm.Page( title="Parameters", components=[ vm.Graph( id="scatter_chart_pm", figure=px.scatter( iris, x="sepal_width", y="sepal_length", color="species", size="petal_length", color_discrete_map={"setosa": "#00b4ff", "versicolor": "#ff9222"}, ), ), vm.Graph( id="bar_chart_pm", figure=px.bar( iris, x="sepal_width", y="sepal_length", color="species", color_discrete_map={"setosa": "#00b4ff", "versicolor": "#ff9222"}, ), ), ], controls=[ vm.Parameter( targets=["scatter_chart_pm.color_discrete_map.virginica", "bar_chart_pm.color_discrete_map.virginica"], selector=vm.Dropdown(options=["#ff5267", "#3949ab"], multi=False, value="#3949ab"), ) ], ) selectors = vm.Page( title="Selectors", layout=vm.Layout(grid=[[0], [1], [1], [1], [2], [2], [2], [3], [3], [3]], row_min_height="170px", row_gap="24px"), components=[ vm.Card( text=""" A selector can be used within the **Parameter** or **Filter** component to allow the user to select a value. The following selectors are available: * Dropdown (**categorical** multi and single option selector) * Checklist (**categorical** multi option selector only) * RadioItems (**categorical** single option selector only) * RangeSlider (**numerical** multi option selector only) * Slider (**numerical** single option selector only) * DatePicker(**temporal** multi and single option selector) """ ), vm.Table( id="table-gapminder", figure=dash_data_table(data_frame=gapminder_2007, page_size=10), title="Gapminder Data", ), vm.Table(id="table-tips", figure=dash_data_table(data_frame=tips, page_size=10), title="Tips Data"), vm.Graph( id="graph-stocks", figure=px.line(stocks, x="date", y="GOOG", title="Stocks Data"), ), ], controls=[ vm.Filter( targets=["table-gapminder"], column="lifeExp", selector=vm.RangeSlider(title="Range Slider (Gapminder - lifeExp)", step=1, marks=None), ), vm.Filter( targets=["table-gapminder"], column="continent", selector=vm.Checklist(title="Checklist (Gapminder - continent)"), ), vm.Filter( targets=["table-gapminder"], column="country", selector=vm.Dropdown(title="Dropdown (Gapminder - country)"), ), vm.Filter( targets=["table-tips"], column="day", selector=vm.Dropdown(title="Dropdown (Tips - day)", multi=False, value="Sat"), ), vm.Filter( targets=["table-tips"], column="sex", selector=vm.RadioItems(title="Radio Items (Tips - sex)"), ), vm.Filter( targets=["table-tips"], column="size", selector=vm.Slider(title="Slider (Tips - size)", step=1, value=2), ), vm.Filter(targets=["graph-stocks"], column="date", selector=vm.DatePicker(title="Date Picker (Stocks - date)")), ], ) # ACTIONS --------------------------------------------------------------------- export_data_action = vm.Page( title="Export data", components=[ vm.Graph(figure=px.scatter(iris, x="petal_length", y="sepal_length", color="species")), vm.Graph(figure=px.histogram(iris, x="petal_length", color="species")), vm.Button(text="Export data", actions=[vm.Action(function=export_data())]), ], controls=[vm.Filter(column="species")], ) chart_interaction = vm.Page( title="Chart interaction", components=[ vm.Graph( figure=px.box( gapminder_2007, x="continent", y="lifeExp", color="continent", custom_data=["continent"], ), actions=[vm.Action(function=filter_interaction(targets=["scatter_relation_2007"]))], ), vm.Graph( id="scatter_relation_2007", figure=px.scatter( gapminder_2007, x="gdpPercap", y="lifeExp", size="pop", color="continent", ), ), ], ) # CUSTOM CHARTS ------------------------------------------------------------------ @capture("graph") def scatter_with_line(data_frame, x, y, hline=None, title=None): """Custom scatter chart based on px.""" fig = px.scatter(data_frame=data_frame, x=x, y=y, title=title) fig.add_hline(y=hline, line_color="orange") return fig @capture("graph") def waterfall(data_frame, measure, x, y, text, title=None): # noqa: PLR0913 """Custom waterfall chart based on go.""" fig = go.Figure() fig.add_traces( go.Waterfall( measure=data_frame[measure], x=data_frame[x], y=data_frame[y], text=data_frame[text], decreasing={"marker": {"color": "#ff5267"}}, increasing={"marker": {"color": "#08bdba"}}, totals={"marker": {"color": "#00b4ff"}}, ) ) fig.update_layout(title=title) return fig custom_charts = vm.Page( title="Custom Charts", components=[ vm.Graph( id="custom_scatter", figure=scatter_with_line( x="sepal_length", y="sepal_width", hline=3.5, data_frame=iris, title="Custom px chart", ), ), vm.Graph( id="custom_waterfall", figure=waterfall( data_frame=waterfall_df, measure="measure", x="x", y="y", text="text", title="Custom go chart", ), ), ], controls=[ vm.Filter(column="petal_width", targets=["custom_scatter"]), vm.Filter( column="x", targets=["custom_waterfall"], selector=vm.Dropdown(title="Financial categories", multi=True), ), ], ) # CUSTOM TABLE ------------------------------------------------------------------ @capture("table") def my_custom_table(data_frame=None, chosen_columns: Optional[List[str]] = None): """Custom table with added logic to filter on chosen columns.""" columns = [{"name": i, "id": i} for i in chosen_columns] defaults = { "style_as_list_view": True, "style_data": {"border_bottom": "1px solid var(--border-subtle-alpha-01)", "height": "40px"}, "style_header": { "border_bottom": "1px solid var(--state-overlays-selected-hover)", "border_top": "1px solid var(--main-container-bg-color)", "height": "32px", }, } return dash_table.DataTable(data=data_frame.to_dict("records"), columns=columns, **defaults) custom_tables = vm.Page( title="Custom Tables", components=[ vm.Table( id="custom_table", title="Custom Dash DataTable", figure=my_custom_table( data_frame=gapminder_2007, chosen_columns=["country", "continent", "lifeExp", "pop", "gdpPercap"], ), ) ], controls=[ vm.Parameter( targets=["custom_table.chosen_columns"], selector=vm.Dropdown( title="Choose columns", options=gapminder_2007.columns.to_list(), multi=True, ), ) ], ) # CUSTOM COMPONENTS ------------------------------------------------------------- # 1. Extend existing components class TooltipNonCrossRangeSlider(vm.RangeSlider): """Custom numeric multi-selector `TooltipNonCrossRangeSlider`.""" type: Literal["other_range_slider"] = "other_range_slider" def build(self): """Extend existing component by calling the super build and update properties.""" range_slider_build_obj = super().build() range_slider_build_obj[self.id].allowCross = False range_slider_build_obj[self.id].tooltip = {"always_visible": True, "placement": "bottom"} return range_slider_build_obj vm.Filter.add_type("selector", TooltipNonCrossRangeSlider) # 2. Create new custom component class Jumbotron(vm.VizroBaseModel): """New custom component `Jumbotron`.""" type: Literal["jumbotron"] = "jumbotron" title: str subtitle: str text: str def build(self): """Build the new component based on Dash components.""" return html.Div([html.H2(self.title), html.H3(self.subtitle), html.P(self.text)]) vm.Page.add_type("components", Jumbotron) custom_components = vm.Page( title="Custom Components", components=[ Jumbotron( title="Custom component based on new creation", subtitle="This is a subtitle to summarize some content.", text="This is the main body of text of the Jumbotron.", ), vm.Graph( id="for_custom_chart", figure=px.scatter( iris, title="Iris Dataset", x="sepal_length", y="petal_width", color="sepal_width", ), ), ], controls=[ vm.Filter( column="sepal_length", targets=["for_custom_chart"], selector=TooltipNonCrossRangeSlider(title="Custom component based on extension"), ) ], ) # CUSTOM ACTIONS --------------------------------------------------------------- @capture("action") def my_custom_action(t: int): """Custom action.""" sleep(t) custom_actions = vm.Page( title="Custom Actions", components=[ vm.Graph( figure=px.scatter( iris, x="sepal_length", y="petal_width", color="species", ) ), vm.Button( text="Export data", actions=[ vm.Action(function=export_data()), vm.Action(function=my_custom_action(t=2)), vm.Action(function=export_data(file_format="xlsx")), ], ), ], controls=[vm.Filter(column="species", selector=vm.Dropdown(title="Species"))], ) # DASHBOARD ------------------------------------------------------------------- components = [graphs, ag_grid, table, cards, button, containers, tabs] controls = [filters, parameters, selectors] actions = [export_data_action, chart_interaction] extensions = [custom_charts, custom_tables, custom_components, custom_actions] dashboard = vm.Dashboard( title="Vizro Features", pages=[home, *components, *controls, *actions, *extensions], navigation=vm.Navigation( nav_selector=vm.NavBar( items=[ vm.NavLink(label="Homepage", pages=["Homepage"], icon="Home"), vm.NavLink( label="Features", pages={ "Components": ["Graphs", "AG Grid", "Table", "Cards", "Button", "Containers", "Tabs"], "Controls": ["Filters", "Parameters", "Selectors"], "Actions": ["Export data", "Chart interaction"], "Extensions": ["Custom Charts", "Custom Tables", "Custom Components", "Custom Actions"], }, icon="Library Add", ), ] ) ), ) app = Vizro().build(dashboard) app.dash.layout.children.append( dbc.NavLink( ["Made with ", html.Img(src=get_asset_url("images/logo.svg"), id="banner", alt="Vizro logo"), "vizro"], href="https://github.com/mckinsey/vizro", target="_blank", external_link=True, className="anchor-container", ) ) server = app.dash.server if __name__ == "__main__": app.run()