A Dash App With Visualization Interacting with AG Grid
Let's look at a simple Dash application which has a visualization controlling Dash AG Grid. We will have a bar graph as visualization and we will filter data in the AG Grid when any bar in the bar chart is clicked. Here is a glimpse of the app:
The code for this sample application can be found at the public github repository -https://github.com/dvinayakn/interactive-visualization-ag-grid
Here is the code structure of the application:
Note that this application is not ready for deployment to Dash Enterprise. We will discuss that aspect in some other post. We have an "assets" folder where our sample data resides in the file - sales_data.csv inside the assets folder. Also, any javascript or css file placed inside the assets folder is directly accessible to the application. We have all our python code inside app.py for simplicity. In real world applications, the code is split into multiple python files and modules.
We import the necessary packages for our application :
from dash import Dash
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
from dash import dcc, html
import plotly.express as px
import dash_ag_grid as dag
import pandas as pd
Load the sample sales data in a pandas data frame:
# Load data from CSV file
sales_df = pd.read_csv('assets/sales_data.csv')
This data is at level - product, region and month.
We need to aggregate the data at product and month level to display in the visualization. Let's aggregate:
# Aggregate this data at month and product level
sales_by_product_month_df = sales_df.groupby(by=['Product Name', 'Month Name']).aggregate({'Revenue':'sum'}).reset_index()
# Above statement is equivalent to below sql statement
# select "Product Name", "Month Name", sum(Revenue) as "Revenue"
# from sales
# group by "Product Name", "Month Name"
Create an instance of Dash
app = Dash(__name__)
# Let's create a simple visualization with our dataframe
revenue_by_product_month_chart = px.bar(
data_frame=sales_by_product_month_df,
x='Month Name',# Column from data_frame which will indicate values to be plotted on x axis
y='Revenue',
# Column from data_frame which will indicate values to be plotted on y axis (measure or fact)
color='Product Name',
# This will be the legend. Colored stacks will be governed by this column in data_frame
custom_data = ['Product Name']
# This information will be included in the clickData when the bars in chart are clicked
)
Then define the layout of our application. The layout contains the visualization created above and an empty html.Div object. The empty html.Div object will hold the Dash AG Grid when an interaction is made with the visualization.
The "style" attribute of html.Div in above code snippet defines the inline style for the Div. This is a representation of standard css styles, with attributes converted to camel case. For example, justify-content in css becomes justifyContent in the inline style. This style can be referred from the css file placed in assets folder as well. The code in css file is commented as of now. If the inline style is removed and the css code is un-commented, this application will work just fine.
Now is the time to go through the callback defined to make the interactions possible. Callbacks are like event listeners and event handlers in Dash. You define which items should trigger the callback (referred to as Input), which items should be updated as a result of callback (referred to as Output) and which items should be available to the callback function as it is (referred to as State). We are not using "State" in this application yet.
The callbacks are defined with the decorator "callback" available with the Dash object created above. The first argument to the callback, which reads as below, indicates that the "children" property of an html element with id "table-container-div" should be updated with whatever is returned by the callback function.
The second argument to the callback, which reads as below, indicates that whenever "clickData" property of the chart with id "bar-chart" is updated, the following callback function should be called and results are to be assigned to "Output" of the callback.
For a graph rendered on the page, there are several properties which get updated depending on user interaction with the graph. These include hoverData, selectedData, relayoutData and clickData. In our application, we want the callback to trigger whenever the user clicks on the bar chart. Hence we use "clickData".
The third argument to the callback, which reads as below indicates that the callback should not be called when the application loads for the first time. The callback function should only be called when properties of objects as mentioned in "Input" are modified.
prevent_initial_call = True # To prevent the callback to run when the
application loads
# {
# 'points': [
# {
# 'curveNumber': 2,
# 'pointNumber': 7,
# 'pointIndex': 7,
# 'x': 'Mar 2022',
# 'y': 47463,
# 'label': 'Mar 2022',
# 'value': 47463,
# 'bbox': {
# 'x0': 711.03,
# 'x1': 781.23,
# 'y0': 192.56,
# 'y1': 192.56
# },
# 'customdata': ['Product 3']
# }
# ]
# }
#Extract the product and month which was clicked on the chart.
product_clicked = click_data['points'][0]['customdata'][0]
month_clicked = click_data['points'][0]['x']
# Filter the sales_df with the selected product and month name
filtered_df = sales_df[(sales_df['Product Name'] == product_clicked) & ( sales_df['Month Name'] == month_clicked)]
# Create and return a dash ag grid object with this data.
return dag.AgGrid(
enableEnterpriseModules=False,
columnDefs=[{"headerName": column, "field": column} for column in filtered_df.columns],
rowData= filtered_df.to_dict('records'),
columnSize="sizeToFit",
defaultColDef=dict(
resizable=True,
),
style = {'height': '250px', 'width': '60%'}
)
- Here is a great blog post for a simple “Hello World” Dash application, if you are looking to start with Dash.
- You need to get the AG Grid library installed for the code to work. Here is the command to get it installed:
Comments
Post a Comment