Brick by Brick: Build a multi-page dashboard (Dash Filters)

Simi Talkar
Analytics Vidhya
Published in
10 min readDec 16, 2020

Part 3: This is the third in a multi-part series on incrementally building a dashboard using Plotly Dash. We add selection items to a dropdown filter that will filter the Folium maps in the dashboard we set up (we dynamically create the maps here).

Himalayan blue poppy

The data from Inside AirBnB provides publicly available data for AirBnB listings in Seattle, USA. We will use the dashboard framework we created in the first part of this series and include spatial data to explore the locations of the listings.

GOAL

Implementation of Dash Dropdown

In the above picture, you see the addition of a single selection dropdown list (under the label “Pick a Neighborhood”) in the left filter bar, containing all the neighborhood groups in Seattle. There are seventeen of these and selecting one of them updates the Folium maps displayed, to show the listings within that neighborhood group alone. The dropdown list also contains “All”, selecting which displays listings for the entire Seattle area.

INSTALLATIONS

Follow along the instruction in this Medium article to set up and run the Dash app server from Jupyter notebook. The notebook in Github also lists the packages used (such as Folium) as well as their versions to help you get going.

CODE AND DOWNLOADS

The code for this series can be found at this Github repo. The application is run from Jupyter notebook. There are .py (Python) files that will contain code for the layout and data manipulation. I used, and highly recommend, Visual Studio Code for working with these as there are a number of handy extensions available for formatting and auto-completion that are particularly helpful to build the HTML DOM structure and coding in Python.

The files used to create the dashboard with the maps are:
Notebook:
CreateDashboardSpatialFilter.ipynb
.py files :
layout_spatial_filter.py
datamanipulation_spatial_filter.py
callback_spatial_filter.py
.html files:
IndvListingsMap.html
NeighborhoodCountMap.html

FOLDER STRUCTURE

As a reminder, with the initialization of the app with app = JupyterDash(__name__), Dash assumes a folder structure with assets like CSS files and icon and image files residing in the assets folder. Data files are loaded from the data folder. As compared to the earlier parts of this series, since we will be generating more neighborhood maps, we will store into and read the Folium maps from assets/Maps folder.

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.
  • 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.
  • 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.
  • 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.

And Finally
We wrap up this detailed, exhaustive article on the usage of Dash Core Components input element (Dropdown) here. But note that in the files you will see additional elements that use the dropdown selection. The cards that display the count of listing and average rent for each neighborhood use the same pattern as described above. Try these out for yourself and if you have any questions, feel free to comment, question and reach out.

Teaser:

Coming up in the next article, I will show you how the user can dig into property types available for rent in a Sankey diagram. We will also explore the data cleaning. The code of this is in the files in the Github Repo with suffix (_bar_sankey).

Sankey and bar charts

--

--

Simi Talkar
Analytics Vidhya

Certified DS Associate (DP-100, DA-100), pursuing Masters in University Of Michigan’s Applied Data Science Program https://www.linkedin.com/in/simi-talkar/