Brick by Brick: Build a multi-page dashboard (Dash Filters)
By: Simi Talkar
Tags: Data Science, Visualization, Dashboard, Choropleth Map, Folium

Concepts

Dash Core Components Input and Output

Dash supplies us with a number of interactive user interfaces. User input components such as Dropdowns, Radio Buttons, Date Pickers (Range and Single), Sliders and Text Area can easily be added to the layout. These filters are available page wide and help synchronize the various graphs in the page. Every output element such as a Graph or Card has an associated callback function that returns the “value” to be displayed. The value can be some text to be displayed in an html paragraph element, or a Dash Core Component Graph element, or a file read into an Iframe. The callback can choose to apply or ignore the value selected in the input by the user.

Details about Dash Core Components can be found here: https://dash.plotly.com/dash-core-components

NOTE: The Input and output elements used in this application can be found in layout_spatial_filter.py file

Deep dive into the Dropdown Input

To use a filter such as a Dropdown, we import dash_core_components as seen in the layout_spatial_filter.py file. This then enables us to initialize the Dropdown component.

* Every component can be uniquely identified by an id and we call our dropdown with the id “nbd_dropdown”.

* The selections in the dropdown are within the options argument with “label” being the text displayed to the user in the dropdown and “value” being the corresponding value sent in to the callback when the item is selected.

* value argument is the initial value displayed in the dropdown when the application starts up. We set this to “All”, so that we start off with the all of Seattle’s neighborhoods.

* Dropdowns can allow for single or multiple selections. We set “multi” to False, thus limiting this dropdown to single selection so that the user can explore a neighborhood at a time with complete abandon.

* With the “style” argument, CSS inline styles are specified to design the component.

Deep dive into the iFrame output

Dash’s output elements such as Graphs are provided by Dash Core Components. We will be visiting graphs that will display Plotly Graphical Objects in an ensuing article. In this article we will be updating the Folium maps that we loaded in the previous article, with dash_html_components. Specifically we use the component Iframe. Iframe is a wrapper for the <iframe> HTML5 element and allows for embedding documents within html documents. The complete list of arguments can be found here.

Crucial to our needs is the id that we set for the output element. Note that in the spirit of modularization and reuse, we can create multiple Iframe containing divs by calling get_mapcol with the id. Each call contains the id that we want to assign to the output element. We have two columns carrying Iframes in this application — ‘indv_listing_map’ and ‘nbd_map’. Remember these, as we want to use them in the callback down below.

that we set for the output element. Note that in the spirit of modularization and reuse, we can create multiple Iframe containing divs by calling get_mapcol with the id. Each call contains the id that we want to assign to the output element. We have two columns carrying Iframes in this application — ‘indv_listing_map’ and ‘nbd_map’. Remember these, as we want to use them in the callback down below. The second argument is the component property that will be updated with the output of the callback. Here the srcDoc is initialized by opening and reading the static HTML files we serve up from the assets/Maps folder. We will shortly, be creating new Folium maps and saving them as html documents. The callback associated with a component id will then read in these files and update its srcDoc.

is by opening and reading the static HTML files we serve up from the assets/Maps folder. We will shortly, be creating new Folium maps and saving them as html documents. The callback associated with a component id will then read in these files and update its srcDoc. The “width” and “height” arguments are set to specify our specifications of the room that we want these maps to take on the page.

Deep dive into the callback for the maps

Head over to the Jupyter notebook, CreateDashboardSpatialFilter.ipynb. Here you will find the callbacks for the output elements identified by the @app.callback decorators where we link the function (update_chart) that will provide the opened html document to display in the Iframe. The name of the callback function is entirely up to you. What is non-negotiable, is the sequence in which you specify the dash.dependencies.Output and dash.dependencies.Input. If you have multiple filters sending in inputs, place them in a list. The order of the inputs map to the arguments of the callback function and so pay special attention to the order.

In the next section we will take a peek into the functions saveListingMapHTML and saveNeighborhoodMapHTML that do the actual work of creating the Folium maps using the neighborhood selection passed in as “nbd”. This argument can carry either “All” or one of Seattle’s neighborhood groups like “Queen Anne” or “Ballard”…

Deep dive into code generating and saving the Folium maps as HTML

The callback function for the neighborhood map (called when the user selects a new neighborhood in the dropdown selection) leads us to callbacks_spatial_filter.py file where I have placed the saveNeighborhoodMapHTML function. The function begins with filtering the dataframe to contain “neighborhood_group_cleansed” column to only contain the neighborhood group selected. If the user selects “All” then the entire dataframe is retained. There will be more detailing of the cleaning of the data and creation of dataframes used, in upcoming articles. The structure of the data in the rental_neighborhood_dataframe is shown here with a filtration applied.

rental_neighborhood_df

Invoke the folium.Map to create the base map (named this_map variable in the code above) to which we will add Markers, Tooltips and other elements using add_to (to add to the map) or add_child (to add to an element such as CircleMarker) .

Arguments to folium.Map

The map is centered at a location . Since we have a dataframe with the latitude and longitude for the neighborhoods and listings, we “center” the map at the mean position of the latitude and longitude.

. Since we have a dataframe with the latitude and longitude for the neighborhoods and listings, we “center” the map at the mean position of the latitude and longitude. Folium maps come with a variety of tile options that we can choose from. For the “Google” map effect select OpenStreetMap. The following tilesets are built-in to Folium. In this application, I have chosen to use CartoDB positron (for neighborhood listing aggregations) and CartoDB dark_matter (for individual listings). For the listings, a dark map was chosen since the many listings are graphed as a scatter plot with each tiny dot representing a listing. Hovering over the dot provides additional details on location and price. The lighter toned CartoDB Positron was chosen for the neighborhood aggregation listing so that the aggregate can be clearly displayed and also because I will be layering a Choropleth over the map (that is, demarcating neighborhood areas and shading them with a color intensity relative to the listings within them). Pass any of the following options to the “tiles” keyword in your application. You can also use custom tiles:

“OpenStreetMap”

“Mapbox Bright” (Limited levels of zoom for free tiles)

“Mapbox Control Room” (Limited levels of zoom for free tiles)

“Stamen” (Terrain, Toner, and Watercolor)

“Cloudmade” (Must pass API key)

“Mapbox” (Must pass API key)

“CartoDB” (positron and dark_matter)

It is critical that you choose a zoom_start value that will display the data you want the user to focus on when the map is first dished up. You will, in all likelihood need to play with this value, until the area comes into focus as you set your maps up. Here the value we start out with is 13 and the user can use Folium’s interactive Zoom in and Zoom out to explore the map further.

I have defined another function plotDot that when applied to each row in our dataframe shown above, will utilize the latitude, longitude, glow_marker_color, nbd_count_normalized to plot CircleMarkers on the map we initialized.

To understand the value being plotted, let’s understand the code below (that you will find in datamanipulation_spatial_filter.py)

We aggregate the underlying data’s listings of rentals by grouping by the neighborhood (column = neighborhood_cleansed). Note that there are multiple neighborhoods in a neighborhood_group and so when we perform this aggregation, we also select the common neighborhood_group using the “max” aggregator. The total listing in the neighborhood are “count”ed and placed in the neighborhood_cleansed column. We will be using the aggregate count of listings to “size” (set the radius) of the circle markers that will display the count of listings on the map. To keep the radius within bounds of the map, we use sklearn’s preprocessing.MinMaxScalar method to set the range of the count between 0 and 50 — all listings are uniformly “normalized” within this range.

This now brings us to the folium.CircleMarker that is called for every row of rental_neighborhood_df dataframe shown above which provides the center of the circle to be used (latitude, longitude) as well as the radius (the normalized value of count of listings aggregation). Once created, the circle marker is added to the map (this_map) using add_to. A customized tooltip is also added providing the neighborhood name and actual listing count for the neighborhood.

I also wanted to provide the count of listings on the map itself and so I used icon = DivIcon within a folium.Marker, positioned at the same latitude and longitude as the CircleMarker.

In the next implementation of this map (in file callbacks_bar_sankey.py) , a choropleth element was added to the map using the code below:

Folium Choropleth

Note how the neighborhoods within a neighborhood group are outlined above. The dataset provides us with a geojson (neighbourhoods.geojson) file that contains neighborhood names with the key_on feature.properties.neighbourhood. A one-to-one mapping between this key and the column neighborhood_cleansed in our dataframe (data = filtered_df) enables us to use this geojson file to create a Choropleth map. The color scale used is Greys that is scaled as per the count of listings in each neighborhood (neighbourhood_cleansed_count). The boundaries are drawn with the provided line_opacity and filled with fill_opacity.

We also provide a legend title (legend_name) for the color scale.