Making interactive webbased graphs with Python and Streamlit

Rene Smit
6 min readDec 20, 2020

--

When the lockdown was announced last week in the Netherlands, they said it will be for 5 weeks. I was wondering why they chose 5 weeks, so I decided to calculate myself. In this story I will tell you how I did it with help of Python, matplotlib and Streamlit.

I will go very fast and won’t explain everything in detail. I just mentioned the things that took me long time to find out. For questions, don’t hesitate to contact me.

Of course this model and graph is a very simplified one.

The result

I understand now also why they choose 19th of January as preliminary ending of the lockdown. If we manage to lower the R to 0.75, we will enter the yellow zone (attentive) on that date. Fingers crossed!

Here is the result: https://share.streamlit.io/rcsmit/covidcases/main/number_of_cases_interactive.py

You can find the code on my github https://github.com/rcsmit/COVIDcases/blob/main/number_of_cases_interactive.py

The formulas

First thing to know is how to calculate the number of cases (N)on day t given a start of N0 cases.

Formulas for the rate of corona

First we calculate the half time. R is the famous reproduction number, saying how much persons 1 person contaminates. We assume that it takes 4 days for the virus to spread from one person to another person.

The second formula shows the number of cases on a certain day.

Setting up

Besides the usual suspects as numpy, matplotlib etc. we have to install streamlit by pip install streamlit . You can run your code with streamlit run code.py

You can also request an account at http://share.streamlit.io to make your web-app accessible for everyone. Read more about it here. Apparently there is a waiting list, but I got my account in no time!

You need a github account for this. Important to know is that you add an requirements.txt file in the directory on github, with the word matplotlib in it otherwise it won’t work.

Let’s code

I will talk you globally through the code. The code here is slightly different from the code in the github. Copy-paste-use that code to be sure that everything works. (Edit: the code is now different, please look in the history of github)

# Import our modules that we are using
import streamlit as st
import matplotlib.pyplot as plt
import numpy as np
import math
import matplotlib.dates as mdates
import datetime as dt
from matplotlib.font_manager import FontProperties
from datetime import datetime
from matplotlib import figure
from matplotlib.backends.backend_agg import RendererAgg_lock = RendererAgg.lock

Import the libraries. If needed install them with pip, but I think most are already installed as default. (as said above, when using online on github, add to the directory the file requirements.txt with the word matplotlib in it)

The last lines have been added later because I read here that matplotlib is not very stable when multiple users/threads are using it. And indeed I had also some crashes during the development. The same article also mentions altair because it is faster, but for the performance for me is good enough, so I don’t bother to change it at the moment. (also I had problems integrating plot of the different surfaces in various colours.)

#VARIABLES
# startdate in m/d/yyyy
# https://www.bddataplan.nl/corona/
numberofcasesdayzero = 331
STARTDATE = “12/15/2020”
Rold = 1.24

# Some manipulation of the x-values
startx = dt.datetime.strptime(STARTDATE,’%m/%d/%Y’).date()
then = startx + dt.timedelta(days=NUMBEROFDAYS)

# x = dagnummer gerekend vanaf 1 januari 1970 (?)
# z = dagnummer van 1 tot NUMBEROFDAYS
x = mdates.drange(startx,then,dt.timedelta(days=1))
z = np.array(range(NUMBEROFDAYS))

date_format = “%m/%d/%Y”
a = datetime.strptime(STARTDATE, date_format)
positivetests = []
positivetests.append (numberofcasesdayzero)

Here we setup the variables and the lists. The results will be put in the list positivetests. The start value is put in the list as first value.

st.sidebar.title('Parameters')
NUMBEROFDAYS = st.sidebar.slider(‘Number of days in graph’, 15, 150, 60)
TURNINGPOINTDAY = st.sidebar.slider(‘Number of days needed to go to new R’, 1, 30,10)
Rnew = st.sidebar.slider(‘R-number’, 0.1, 2.0, 0.75)

This is the real charm of streamlit. Just define the sliders as a variable and you are good to go! Streamlit puts it in a sidebar if you add this keyword.

The parameters for the slider are (label, min, max, default)

# START CALCULATING — — — — — — — — — — — — — — —for t in range(1, NUMBEROFDAYS):
if t<TURNINGPOINTDAY :
Ry = Rold — (t/TURNINGPOINTDAY * (Rold — Rnew))
else:
Ry = Rnew

if Ry == 1:
# prevent an [divide by zero]-error
Ry = 1.000001
thalf = 4 * math.log(0.5) / math.log(Ry)
positivetests.append(positivetests[t-1] * (0.5**(1/thalf)))

Here we do the calculation. We calculate the R for every day in the transition phase and try to avoid a divide by zero-error. For every day the new value is added to the list positivetests

st.title(‘Positive COVID-tests in NL’)fig1, ax = plt.subplots()
plt.plot(x, positivetests)
positivetests = []

Here we are plotting the graph. After we empty the list (I got some errors when not doing it)

with _lock:
# Add X and y Label and limits
plt.xlabel(‘date’)
plt.xlim(x[0], x[-1])
plt.ylabel(‘positive tests per 100k inhabitants in 7 days’)
plt.ylim(bottom = 0)
# add horizontal lines and surfaces
plt.fill_between(x, 0, 49, color=’yellow’, alpha=0.3, label=’waakzaam’)
plt.fill_between(x, 50, 149, color=’orange’, alpha=0.3, label=’zorgelijk’)
plt.fill_between(x, 150, 249, color=’red’, alpha=0.3, label=’ernstig’)
plt.fill_between(x, 250, 499, color=’purple’, alpha=0.3, label=’zeer ernstig’)
plt.fill_between(x, 500, 1000, color=’grey’, alpha=0.3, label=’zeer zeer ernstig’)
plt.axhline(y=0, color=’green’, alpha=.6,linestyle=’ — ‘ )
plt.axhline(y=49, color=’yellow’, alpha=.6,linestyle=’ — ‘)
plt.axhline(y=149, color=’orange’, alpha=.6,linestyle=’ — ‘)
plt.axhline(y=249, color=’red’, alpha=.6,linestyle=’ — ‘)
plt.axhline(y=499, color=’purple’, alpha=.6,linestyle=’ — ‘)
plt.axvline(x=x[0]+35, color=’purple’, alpha=.6,linestyle=’ — ‘,label = “19/01/2021”)
# Add a grid
plt.grid(alpha=.4,linestyle=’ — ‘)
#Add a Legend
fontP = FontProperties()
fontP.set_size(‘xx-small’)
plt.legend( loc=’upper right’, prop=fontP)
# Add a title
titlex = (
‘Pos. tests per 100k inhabitants in 7 days.\n’
‘Number of cases on ‘+ str(STARTDATE) + ‘ = ‘ + str(numberofcasesdayzero) + ‘\n’
‘Rold = ‘ + str(Rold) +
‘ // Rnew reached in ‘ + str(TURNINGPOINTDAY) + ‘ days (linear change)’ )
plt.title(titlex , fontsize=10)# lay-out of the x axis
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter(‘%Y-%m-%d’))
plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=5))
plt.gcf().autofmt_xdate()
plt.gca().set_title(titlex , fontsize=10

Plotting the grid and some surfaces indicating the zone in which we are. The vertical line is the day when the lockdown should end (let’s cross the fingers). We do some manipulation to show dates on the x-axis instead of daynumbers. The with _lock: has to do with improving the stability (see above)

    st.pyplot(fig1)

And this is the magic word to show your graph on the screen :)

tekst = (
‘<hr>Made by Rene Smit. (<a href=\’http://www.twitter.com/rcsmit\'>@rcsmit</a>) <br>’
‘Overdrachtstijd is 4 dagen. Disclaimer is following. Provided As-is etc.<br>’
‘Sourcecode : <a href=\”https://github.com/rcsmit/COVIDcases/edit/main/number_of_cases_interactive.py\">github.com/rcsmit</a>' )
st.sidebar.markdown(tekst, unsafe_allow_html=True)

With st.markdown you can show some html-code, here we put it in the sidebar

Good to know

In theory you should be able to use subplots of matplotlib to show mulitple graphs. I didn’t succeed to get what I want because for every graph you will need another scale and horizontal lines/fills, so I just made different plots and replacing fig1 for fig2 etc. Unfortunately you get some repeating code when you use it.

Conclusion

I really liked to work with Streamlit to make these interactive graph! Do not hesitate to contact me for questions (@rcsmit)

Links

This is another very cool COVID dashboard made with streamlit: https://penn-chime.phl.io/

If you like this you might also like this simulator by Lukas Calmbach (github) inspirated by this article

--

--

Rene Smit
Rene Smit

Written by Rene Smit

Tourism, coaching & yoga teacher. Python. Minimalist. Vegan. Connect, reflect & serve

No responses yet