Step-by-step guide to your first interactive dash app — Part II

Paolo Molignini, PhD
8 min readOct 25, 2022

--

This is the second part of my guide to writing and deploying a dash app for data visualization. If you haven’t read part I, you can find it here, while the repository for the monkeypox-tracking app that we are using as an example can be found here. Hopefully you also have all the necessary packages installed on your system by now.

So far, we have talked about the data and folder structure for our app: we want to build a simple visualization tool to track the spread of the monkeypox pandemic through a choropleth map and a chart that shows the cumulative cases as a function of time for each country along with some exponential fit. Today, we will look at the core of our dash app: app.py. This is where the app definition and activation takes place. To make it as clean and streamlined as possible, I have kept only the necessary commands in app.py, and delegated all the rest to different python modules stored in the folder modules. We will talk about them later. Let’s first examine the structure of app.py.

The module app.py consists of the following parts:

  • Import statements.
  • Data generation.
  • App execution.
  • Layout.
  • Callbacks.
  • Server call.

Let’s go through these sections one by one to understand what they do and how they contribute to make the app functional. We will then go into the specifics of each module later on.

Import statements

For the app to function properly, we need to import various python packages. The first one is of course Dash itself. The classes Input, Output, State, and callback are needed to define callbacks — i.e. dynamic changes to the app when performing an action. The inputs are typically buttons being clicked, sliders being moved etc., while the outputs can be any properties of the components displayed in the app, such as changing the content of a figure, the color or font of a text, the fields of a dropdown menu, and more. The limit is really just your creativity! State is a function that delivers — you guessed it — the state of a component without it being necessarily an input. In that way, we can access information about our components that have not being directly selected by an action. In even simpler word: Input triggers the callback, State does not.

Besides the components needed for the callback definition, we also import dash bootstrap components in line 7. While these are an add-on, and not strictly necessary, they offer a big library of visualization tools (think fancy buttons, sliders, dropdown menus, radio buttons etc.) with more complex functionalities, customization, and in general slicker looks, so I definitely recommend installing them and using them for your app! In our case, we use two main useful dbc tools: a dbc external stylesheet that changes the look of our app, and the Col/Row components to arrange some elements of our layout in a columns and rows.

As we are dealing with a database in table format from which we extract and manipulate entries, we will also need to import pandas.

Datetime is a format for the storage of dates and times and it has useful functions to do computations in that format, so we need it to efficiently convert dates and times into strings or timestamps and viceversa.

Finally, we have to import all our custom modules that contain app-specific inputs, functions to extract and manipulate the data, perform fits, and generate the layout and the figures. We will discuss these in part III and IV.

Data generation

The data generation part is responsible for importing the .csv file for the current date as a pandas dataframe (line 30) and, by reading from it the countries which have nonzero cases, making a list of the countries whose cases we want to display (line 31). Note that all the input variables such as current_date_tag have already been indirectly defined by importing the module input.

It also extracts the evolution of the cases by country via the data_extractor function defined in module extractor (line 34) and saves the data fitted with the function data_fit in the dataframe cases_fit_df (line 35).

Finally, it uses the functions get_worldmap_fig and get_tot_cases_fig defined in the plots module to generate the figures we want to display based on the data we have (lines 38 & 39). The first is the choropleth worldmap, where each country is colored based on how many cases it displays on the selected day. The second is the figure that shows the time evolution of the cases for each selected countries (optionally with the exponential fit).

This part is really where the main computation and data manipulation takes place. Once we have created the figures we want, we just need to display them by configuring the layout of the app, start the server, and execute it.

Server initialization

Before defining the layout, we need to initialize the app and its server. This is done by the command

app = dash.Dash(__name__, ***** )

where __name__ indicates the type of execution (we will only have a main execution at the bottom of the page), and ***** stands for some additional options we can give to customize the execution. In our case, we use two options. The flag prevent_initial_callbacks blocks some annoying self-reloading of the app once it starts. We also provide external_stylesheets via dash bootstrap components to give our app a cooler look.

After the app definition, we can define the server with

server = app.server

If there is anything else you would like to add to the head or body of the app html page, such as a java script, metatags, traffic monitoring programs etc., you can do it with the index_string command on line 51. In my case, I have added a button at the bottom of the page that redirects to my buymeacoffe page, where you can donate some money to support me, for instance if you find this guide useful ;).

Layout

Once the server has been initialized, you can start building the layout of your app! This is the sequence of components you want to display in your app. This is literally the order in which your buttons, figures, text, links etc. will appear on your app’s webpage. In my case, I have built the function get_layout in module layout where all the components are defined. I think this modularization is useful, because if you want to quickly change something in the way your app looks, you can simply shuffle components around in a single function. We will talk about the details of the layout in part IV.

Callbacks

Callbacks are the core of a dash app’s interactivity. They define the response of the app to actions such as clicking certain buttons, selecting items from dropdown menus, moving sliders etc. As you can see, they are simply python functions with the additional decorator

@app.callback(***)

which tells the app that the function you have written should be triggered whenever the inputs contained in *** are changed.

In my app, I use a single callback function. However, you are free to create as many callbacks as you want. The only thing you have to be mindful of is that you cannot have different callbacks that refer to the same outputs. In other words, each component can be the target of at most one callback. On the other hand, a single callback can affect multiple outputs. We will talk in detail about the callback above in part VI.

Callback functionalities are very rich in plotly/Dash and this guide will only scratch the surface of what one can do with them. If you want to explore more complex functionalities with callbacks, you can check out advanced callbacks or pattern-matching callbacks (this latter is very useful when you want a callback that responds to a dynamic number of components).

Running the server

Now that we have structured the aspect and behavior of our app, the only thing left to do is to run it! The easiest way to run a Dash app is to define an if-statement which will run the server whenever app.py is executed. The command is

app.run_server(*****)

where ***** are once again additional options that we can give to monitor and control the app execution. debug=True will trigger information displayed on the app if something goes wrong and is useful when testing your app (you might want to set this to False in the final deployment). The use_reloader flag will reload the app dynamically and is has to be set to False on a local system to prevent Flask to be loaded twice at initialization (note that enabling the debug mode directly enables the reloader, so it needs to be deactivated by specifying the flag). The option dev_tools_hot_reload will automatically reload the browser page when you modify and save your code and can be useful in production if you don’t want to reload the app manually every time. These are just a couple of the developer options you can set. Another useful one allows you for instance to define the port on which the server runs, so you can avoid conflicts when you running several apps at once. You can find more developer tools here.

Now, whenever you run

python3 app.py

as a main process in the terminal, the if-statement will get called and the Dash server will run. There should be some text appearing that tells you where the server is running (this can be set up with the developer options mentioned above). For instance, I get:

If I open a browser and go to this http address (or equivalently go to local:8050), I see the app displayed in its full glory.

There you have it! You now know how the app is built and how to execute it. I encourage you to try to run the app on your system and play around a bit with its functionalities before reading the next chapters. There, I will explain in detail how the data is extracted, manipulated, and visualized.

Thanks for reading, and see you next time! :)

--

--

Paolo Molignini, PhD

Researcher in theoretical quantum physics at Stockholm University with a passion for programming, data science, and probability & statistics.