Python Visualization

Visualizing 2 Years of COVID-19 in Hawaii with Animated Plots

Animated plots are powerful data science tools and we will show you how to make them using Hawaii COVID data as an example

Lambert T Leong, PhD
Towards Data Science
14 min readMar 3, 2022

--

You can make these by the end of this post (Video By Author)

Learning objectives

  1. Build animated plots (like the one above) that can provide extra dimensions of information
  2. Work with real data and learn strategies for cleaning it to suit your analysis
  3. Incorporate domain knowledge and relevant events into your analysis of time series data
  4. This work build upon original work published here

Follow along by downloading the notebook on Github

Why Hawaii

Each state within the United States (US) implemented their own strategy for addressing the novel corona virus (COVID-19) pandemic. Hawaii is unique in that it is the only state that cannot be driven to from another state. Therefore, one can only get to Hawaii by airplane or boat and very few people arrive by boat. Since Hawaii is remote and people mostly arrive by plane, it presents as an interesting case study for investigating the effects of the COVID pandemic. Additionally, I am from Hawaii and have been keeping up with local current events that are relevant to the COVID situations on the islands.

You will also be able to make animations like this (Video by Author)

Animated Plots

Data visualization is an important part of data science and useful for gaining insight into the data of interest. Animated plots (see above) build upon standard visualizations and are helpful when trying to understand change especially with time series data. Here we will look at COVID data from Hawaii and hopefully, the animations will illuminate patterns such as case causing events, effects of mandates, trends in death and hospitalizations, or etc. While I do have a background in biology and am a part of the Epidemiology department at the University of Hawaii Cancer Center, I plan to offer little interpretation of the presented results since the focus of this post is on visualization. Additionally, if your data visualizations are good enough, then interpretations should be easy, obvious, and almost trivial.

The Dataset

When I started this project (6 months into the pandemic, 2020) I used public data provided by the state of Hawaii. The data is from Hawaii Data Collaborative and the data was being input into a google sheet [1], all of which are in the public domain. The data contains information such as total cases, new cases, hospitalizations, and vaccine data. As an informed resident of Hawaii, I possess knowledge that may inform me about the current state of the data. For instance, the state department of health has been overworked, under funded, and had a high turnover rate throughout the pandemic. As a result, the collection and upkeep of the data has been difficult and the state of the data, for a lack of a better word, is a little messy. These issues are not unique to this situation and happen all the time but luckily there are many ways to handle real world data that may not be perfect. We will touch on a few methods for data cleaning.

In order to gain a richer understanding of the COVID situation in Hawaii, I thought it would be handy to view trends with respect to relevant current events. As such, I compiled data on events related to the COVID situation and they can be found at the following link: EVENT_DATA. Within that data, there are references to tiers and those tiers are a part of a strategy that the city of Honolulu (largest county in Hawaii) tried to implement in an attempt to control the COVID situation. More information of the tiers can be found here.

Now that we have a brief overview of the data, let’s make some plots!

Let’s import some modules and get started!

import os, sys, time, math, random, imageio
from datetime import datetime
### data science essen ###
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.gridspec as gridspec
from pylab import *
from scipy import ndimage, misc
import seaborn as sns
from matplotlib import collections as matcoll
import textwrap
import matplotlib.dates as dates
### for streaming from google sheet ###
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import multiprocessing
from multiprocessing import Pool
### set dots per square inch (dpi) for plots ###
mpl.rcParams['figure.dpi'] = 300

The following code is used to stream the data directly from the google sheet. To access this data, you need to set up your own authentication token. Setting this up is beyond the scope of this post but the code is there for those who have already set up their api.

### Stream directly from google sheet ###
'''
scope = ['https://spreadsheets.google.com/feeds']
credentials = ServiceAccountCredentials.from_json_keyfile_name('./hawaii-covid-timeline-48aab0d374b0.json', scope)
gc = gspread.authorize(credentials)
spreadsheet_key = #TODO: get your key
worksheet = book.worksheet("COVID Data")
table = worksheet.get_all_values()
##Convert table data into a dataframe
df = pd.DataFrame(table[1:], columns=table[0])
##Convert number strings to floats and ints
df = df.apply(pd.to_numeric, errors='ignore')
df.head()
'''

The link takes you to a google sheet which can be downloaded as a csv file. The following code assumes you have done that already. If you are following along with the git repo, then the data should be there already.

### Reade Hawaii Covid Data ###
### access and download at https://docs.google.com/spreadsheets/d/1z5U88_NVC84CB4kPyNbp5wy6liUyA2T9iPqmHfFpjr4/edit?usp=sharing
raw_data = pd.ExcelFile('Hawaii_Data_Collaborative_COVID_Dashboard_Data.xlsx')
print(raw_data.sheet_names)
df = raw_data.parse("COVID Data")
vax = raw_data.parse("DOH Vaccination Data")
hos = raw_data.parse("Hospital Data")
### Read in timeline data by Lambert Leong ###
### download at https://www.lambertleong.com/assets/docs/data/hawaii_covid_events_JAN22.csv'###
events=pd.read_csv('https://www.lambertleong.com/assets/docs/data/hawaii_covid_events_JAN22.csv')

The data sheets contain data for the state as a whole as well as data for each county within the state of Hawaii. The first line of code parses out all data except data for the state as a whole. Half way through the pandemic, the group curating the data switched from inputting the total case count into the “Confirmed Cases” to the “Total Cases” field. The exact timing of the switch over is unknown but for the sake of this post we chose the 18th of May, 2021. The code below also merges the case table with the hospitalization and vaccine tables.

### grab state totals ###
sdf=df[df['Region']=='State']
### State started filling out 'Confirmed Cases' col then switched to 'Total Cases' ###
### We need to merge them ###
full_cc=sdf[sdf['Confirmed Cases'].notnull()]
full_tc=sdf[sdf['Total Cases'].notnull()]
old_data=sdf[sdf['Date']<='2021-05-18']
new_data=sdf[sdf['Date']>'2021-05-18']
### merge ###
old_data['total_cases']=old_data['Confirmed Cases'].astype('float64')
new_data['total_cases']=new_data['Total Cases']
old_data['new_cases']=old_data['New Confirmed Cases'].astype('float64')
new_data['new_cases']=new_data['New Cases']
### merge data and drop any duplicate rows ###
clean=old_data.append(new_data)
clean.drop_duplicates(subset=None,keep = 'first', inplace = True)
### merge case data with vaccine data ###
clean_vax=pd.merge(clean, vax.iloc[:,:2], on='Date', how='left')
clean_vax=pd.merge(clean_vax, hos.iloc[:,:-1], on='Date', how='left')

Since we will be cleaning the data in the next few coding sections we need a way of visualizing our results. Below is a quick plotting function for a plot with two y-axis and we will be calling it a few times to view our updates to the data. In the following function we also compute the hospitalization rate and the death rate by taking the total hospitalizations and deaths for that date and dividing each by the total case for that date.

### Lets make a quick plotting function for the next few concepts ###
def quick_plot(test: pd.DataFrame, scale: float=1) -> None:
### Lets create fields we are interested in ###
test['vax_rate']=test['Value']
test['death_rate']=test['Deaths_Tot']/test['total_cases']*scale
test['hospital_rate']=test['Active Hospitalized']/test['total_cases']*scale
### lets look at the data ###
fig, ax = plt.subplots()
#fig.set_size_inches(xsize, ysize)
ax2 = ax.twinx()
### plot data by date ###
dates, cases, deaths = test['Date'],test['total_cases'],test['death_rate']
vax, hos = test['vax_rate'],test['hospital_rate']
ax.plot_date(dates,cases, ls='-',marker='',color='darkorange',label='Total Cases')
ax2.plot_date(dates,vax, ls='-',marker='',color='g',label='Total Vaccination Rate')
ax2.plot_date(dates,hos, ls='-',marker='',color='b',label='Hospitalization Rate')
ax2.plot_date(dates,deaths, ls='-',marker='',color='r',label='Death Rate')
ax.legend(loc=2)
ax2.legend(loc=7)
plt.close()
return fig

The data contained blank cells and when we import the data into our dataframe, pandas reads those valuse as Nan or nan. We use the “fillna()” function to fill those blanks with zeros so that all data in columns are number data types (i.e. int, float, etc). We call the plot function we made to view the data for the first time.

test=clean_vax.copy(deep=True)
### lets fill nan values with zeros ###
test=test.fillna(0)
first_pass=quick_plot(test)
first_pass#.savefig('covid_caseVrate_init.png', dpi=300, bbox_inches='tight')
(Image by Author)

The plot above shows us that we have two problems. The first problem is related to the Nan value and how we blindly filled them in with zeros. Blindly filling Nan’s with zeros created the extreme changes in rate seen with the jagged pattern in the green plot. The second problem is related to the scale for the red and blue plots or the death and hospitalization rate plots. We fix the first problem by using the interpolate function which can fill the Nan values in with more realistic values nearer to the missing value. The following code handles the interpolation.

test=clean_vax.copy(deep=True)
### interpolate ###
test=test.interpolate()
test=test.fillna(0)
interopl=quick_plot(test)
interopl
(Image by Author)

As you can see (above) the jagged patterns have been fixed with the interpolate function. However, there is still a steep vertical green line which would indicate that the vaccine rate went from zero to 50% within the span of a day. That is not realistic and it is caused by the fact that all the values preceding the initial values were empty or Nan. While we do not know the exact vaccine rate prior to the vertical green line, the event data tells us when the vaccine was received in Hawaii. We can use that date to infer a morehttps://docs.google.com/spreadsheets/d/1z5U88_NVC84CB4kPyNbp5wy6liUyA2T9iPqmHfFpjr4/edit?usp=sharing
raw_data = pd.ExcelFile(‘Hawaii_Data_Collaborative_COVID_Dashboard_Data.xlsx’)
print(raw_data.sheet_names)
df = raw_data.parse(“COVID Data”)
vax = raw_data.parse(“DOH Vaccination Data”)
hos = raw_data.parse(“Hospital Data”)
### Read in timeline data by Lambert Leong ###
### download at https://www.lambertleong.com/assets/docs/data/hawaii_covid_events_JAN22.csv'###
events=pd.read_csv(‘https://www.lambertleong.com/assets/docs/data/hawaii_covid_events_JAN22.csv') realistic curve and setting that point to zero would help with a better interpolation. The code below does just that.

### find when the vaccines got to the islands and set that point to zero ###
start_vax=events[events['Event'].str.contains('vaccine')]['Date'].values[0]
test=clean_vax.copy(deep=True)
test['Value'][test[test.Date==start_vax].index[0]]=0
### interpolate ###
test=test.interpolate()
test=test.fillna(0)
fix_vax=quick_plot(test)
fix_vax#.savefig('covid_caseVrate_iterpol.png', dpi=300, bbox_inches='tight')
(Image by Author)

As you can see the vertical green line has turned into a more gradual increase starting from the date the vaccine arrived in Hawaii. This is an example of how using relevant exterior data can be baked in and used in your data analysis or interpretation. We did not specify what function we would like to use for interpolation and the default is linear thus, the straight line. The sigmoid curve is commonly observed in biology from energy expenditure to enzyme kinetics. It is likely that the rate of vaccination would follow the sigmoid curve as well. However, to my knowledge there is no sigmoid interpolation built in to this function. The next best thing would be to use a quadratic function. After all operation warp speed was a thing and there was a push to vaccinate as fast as possible which could mirror a quadratic rate. We use the next code snippet to get a more realistic interpolated curve.

We also address the second issue with the following code. The second issue is related to the resolution of the blue and red curves. The rates are low and it is hard to see. We multiply the rates by 10 so that it aligns better with the vaccine rate. By multiplying the rate by 10 we are effectively calculating the rate per 10,000 cases because the total case, used as the denominator in our rate calculations above, is in the 10's of thousands. We pass the value 10 to the plot function to indicate the changing in scale.

### find when the vaccines got to the islands and set that point to zero ###
start_vax=events[events['Event'].str.contains('vaccine')]['Date'].values[0]
test=clean_vax.copy(deep=True)
test['Value'][test[test.Date==start_vax].index[0]]=0
### interpolate ###
'''
sigmoid curve is more realistic for vaccine rate but since there is not a good way to
model quickely, we settle for a quadratic interpolation.
'''
test=test.interpolate(method='quadratic',limit_direction='forward')
test=test.fillna(0)

fix_scale=quick_plot(test,10)
fix_scale#.savefig('covid_caseVrate_fixed.png', dpi=300, bbox_inches='tight')
(Image by Author)

Now everything looks like it should. Only the legend needs to be updated to reflect the change in scale for the death and hospitalization rates.

Next we merge the case data with the events data to create our a master data table to be used in the following animations. We adjust the datatime strings by replacing the dashes with slashes to be more condensed and readable on the plots.

### merge case data with vaccine data ###
clean_vax=pd.merge(clean, vax.iloc[:,:2], on='Date', how='left')
clean_vax=pd.merge(clean_vax, hos.iloc[:,:-1], on='Date', how='left')

### find when the vaccines got to the islands and set that point to zero ###
start_vax=events[events['Event'].str.contains('vaccine')]['Date'].values[0]
clean_vax['Value'][clean_vax[clean_vax.Date==start_vax].index[0]]=0
### handleing trailing NaN ###
max_vax=clean_vax.Value.idxmax()
clean_vax.Value[max_vax:]=clean_vax.Value.max()
### interpolate ###
clean_vax=clean_vax.interpolate(method='quadratic',limit_direction='forward')
clean_vax=clean_vax.fillna(0)
### clean date strings for presentation ###
clean_vax.Date=clean_vax['Date'].dt.strftime('%-m/%-d/%y')
### merge with events ###
full=pd.merge(clean_vax,events, how='left', on='Date')
### create fields we want###
full['vax_rate']=full['Value']
full['death_rate']=full['Deaths_Tot']/full['total_cases']*10
full['hospital_rate']=full['Active Hospitalized']/full['total_cases']*10
full.death_rate.max(), full.total_cases.max()/10, full.hospital_rate.max()

We have our fully merged data and we can use the new 2 axis plot function to build our final plot.

def plot_2axis(dates: pd.Series,cases: pd.Series, deaths: pd.Series, vax: pd.Series, hos: pd.Series, ymax1:float=None,ymax2:float=None, xsize: float=7,ysize: float=5):
fig, ax = plt.subplots()
fig.set_size_inches(xsize, ysize)
ax2 = ax.twinx()
### plot data by date ###
ax.plot_date(dates,cases, ls='-',marker='',color='darkorange',label='Total Cases')
ax2.plot_date(dates,vax, ls='-',marker='',color='g',label='Total Vaccination Rate')
ax2.plot_date(dates,hos, ls='-',marker='',color='b',label='Hospitalization Rate per 10K cases')
ax2.plot_date(dates,deaths, ls='-',marker='',color='r',label='Death Rate per 10K cases')
### set axis limits ##
if not ymax1:
ymax1=cases.max()*1.1
if not ymax2:
ymax2=max([deaths.max(),vax.max(),hos.max()])*1.1 #1
ax.set_ylim([0,ymax1])
ax2.set_ylim([0.0,ymax2])

ax.margins(x=0)
ax.xaxis.set_major_locator(MultipleLocator(30))
ax.xaxis.set_minor_locator(MultipleLocator(1))
### date orientation and freq ###
fig.autofmt_xdate(ha='center',rotation=90) #BTW, this is the slowest operation
### grid lines ###
ax.grid(axis='y',color='darkorange', alpha=0.5)
ax2.grid(axis='y')
ax.grid(axis='x')
ax.locator_params(axis="y", nbins=6)
ax2.locator_params(axis="y", nbins=6)
### legend ###
ax.legend(loc=2, title="Total Cases\n(left y-axis)", fontsize='small', fancybox=True)
ax2.legend(loc=9, title="Rates\n(right y-axis)", fontsize='small', fancybox=True, framealpha=0.5)
### scoreboard ###
ax.set_ylabel('Total Cases', fontsize='medium')
ax2.set_ylabel('Rates', fontsize='medium')
ax.set_xlabel('Date', fontsize='medium')
### title ###
ax.set_title('Hawaii COVID-19 Cases and Rates')
scoreboard= 'Total Cases (orange) = '+str(round(cases.values[-1],0))
scoreboard+='\nDeath Rate (red) = '+str(round(deaths.values[-1],2))
scoreboard+='\nHospitalization Rate (blue) = '+str(round(hos.values[-1],2))
scoreboard+='\nVaccination Rate (green) = '+str(round(vax.values[-1],2))
ax.text(0.3, 0.55, scoreboard, horizontalalignment='left', verticalalignment='center',
transform=ax.transAxes,fontsize='small')
ax.text(0.1, 0.051, 'lambertleong.com',
fontsize='large', color='gray',
ha='left', va='bottom', alpha=0.5, transform=ax.transAxes)
ax.text(0.65, 0.38, 'lambertleong.eth',
fontsize='large', color='gray',
ha='left', va='bottom', alpha=0.5, transform=ax.transAxes)
plt.close()
return fig

We use the code below to call the plot function defined above.

dates,t,d,v,h=full['Date'],full['total_cases'],full['death_rate'],full['vax_rate'],full['hospital_rate']
ymax1=full.total_cases.max()
ymax2=1
chart=plot_2axis(dates,t,d,v,h,ymax1=ymax1,ymax2=ymax2)
chart#.savefig('hawaii_covid_caseVrate.png', dpi=100, bbox_inches='tight')
(Image by Author)

Look at that beautiful and informative plot!!! But we are not done. We now need to animate with the code below. The animation code basically calls the plot function in a loop and it passes a more populated version of the dataframe to it each time. The plots are passed back, converted into an image, and imageio is used to write the image sequence to any video format, in this case mp4.

### Build Animation for case vs rate ###
'''
writer = imageio.get_writer('static_2axis.mp4', fps=20)
max_y=full.total_cases.max()
ysize, xsize = 5.25,7
ymax1=full.total_cases.max()
ymax2=1
xmax=full.Date.values[-1]
start = time.time()
for i in range(1,full.shape[0]):
#if i>365:
# break
sub_df=full.iloc[:i]
dates,t,d,v,h=sub_df['Date'],sub_df['total_cases'],sub_df['death_rate'],sub_df['vax_rate'],sub_df['hospital_rate']
chart=plot_2axis(dates,t,d,v,h,xsize=xsize,ysize=ysize, ymax1=ymax1,ymax2=ymax2)
dpi=int(chart.dpi)
chart.canvas.draw()
image = np.frombuffer(chart.canvas.tostring_rgb(), dtype='uint8').reshape(int(ysize*dpi),int(xsize*dpi), 3)
#print('\t\tto image')
writer.append_data(image)
print('processed =',i,' of ',full.shape[0],end='\r')
writer.close()
end = time.time()
print('2 axis build time =',end - start)
'''
(Video by Author)

The video above was made with all the code up to this point. Animations add to the story and you can gather more information from watching the video version of the plot.

The plot functions are commented out because they are admittedly slow. While there are many ways to speed up the video build time, such as parallelization, those HPC topics span beyond the scope and I may follow up with a post on how to handle speed issues. Since we don’t plan to constantly be constructing videos at a high throughput, the current speed is fine. You can run it and enjoy a few sips of coffee or tea while it builds.

Event Plots

Now we can fully utilize the event data and create annotated animate plots with respect to any data column within our dataframe. We use the following function to handle all the plotting.

def build_covid_timeline_plot(data_df: pd.DataFrame, data_field: str, title:str, xsize:float=15 ,ysize:float=10,video:bool=False,
plt_color:str='blue'):#
''' creates a timeline plot which contains a line plot with date on the x axis and some eval metric on the y
args:
data_df (pd dataframe): dataframe containing atleast a date field and eval metric field
(stored cummulatively) and "Events" field
data_field (str): field to be plotted on the y axis
xsize (int): plot width in inches, default = 15
ysize (int): plot height in inches, default = 9
plt_color (str): color of the line on the plot
video (str): path to dir that stores video frames
'''
#define plot dimensions
fig, ax = plt.subplots()
fig.set_size_inches(xsize, ysize)
ymax=int(data_df.loc[data_df[data_field].idxmax()][data_field])+20
ymin=-(ymax// 1.5)
ytot=ymax+(-1*ymin)
#plotting
ax.plot_date(data_df['Date'],data_df[data_field], ls='-',marker='',color=plt_color)
ax.set_ylim([ymin,ymax])
ax.axhline(y=0,color='black')#, xmin)
#event plotting
last_event=''
rot=0
color=None
event_count=0
large_list=0
for i, row in data_df.iterrows():
e_type=row['Type']
if pd.isnull(e_type):
continue
e_type=str(e_type)
if 'open' in e_type:
color='green'
elif 'close' in e_type:
color='red'
elif 'yield' in e_type:
color='darkorange'
elif 'event' in e_type:
color='black'
offset=1-(event_count%5*.18)-0.05 #.98 to pull off bottom axis
event_count+=1
bottom_y=(-1*ymin*offset)/ytot
top_y=(int(row[data_field])+(-1*ymin))/ytot
my_wrap = textwrap.TextWrapper(width = 15)
#print(row)
event_list = my_wrap.wrap(text=row['Event'])
event_list=('\n').join(event_list)
ax.axvline(row['Date'],ymin=ymin,ymax=top_y,c=color,label=e_type)
ax.text(row['Date'],offset*ymin,event_list,rotation=rot, fontsize='x-small', ha='center',va='bottom', wrap=False,
bbox=dict(boxstyle='square', fc='w', ec=color)#,alpha=.7)
)
last_event=event_list
for tick in ax.yaxis.get_major_ticks():
tick.label.set_fontsize(8)
ax.margins(x=0)
ax.xaxis.set_major_locator(MultipleLocator(30))
ax.xaxis.set_minor_locator(MultipleLocator(1))
fig.autofmt_xdate(ha='center',rotation=90)
yticks = ax.yaxis.get_major_ticks()
for i in range(1,len(yticks)//2-1):
yticks[i].set_visible(False)
tick_params(axis='x', which='major', labelsize=8)
ax.grid(axis='x')
ax.grid(axis='y')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.set_title('Calendar Events and Hawaii COVID-19 \n'+title+' = '+
str(int(data_df.iloc[-1][data_field]))+' as of '+data_df.iloc[-1]['Date'], fontsize='xx-large')
ax.set_ylabel('Number of '+title+' Cases', fontsize='medium')
ax.set_xlabel('Date', fontsize='medium')
handles, labels = ax.get_legend_handles_labels()
handout=[]
lablout=[]
#rename legend
for h,l in zip(handles,labels):
if l == 'yield':
l='warning'
if l not in lablout:
lablout.append(l)
handout.append(h)
ax.legend(handout, lablout,loc=7, title="Type of Event", fontsize='small', fancybox=True)
ax.text(10, ytot*.0, 'lambertleong.com',
fontsize='xx-large', color='gray',
ha='left', va='bottom', alpha=0.5)
#fig.tight_layout()
ax.text(data_df['Date'].values[-1],data_df[data_field].values[-1], str(int(data_df[data_field].values[-1])),
fontsize='large', color='black',
ha='right', va='center', alpha=1.0)
if video:
ax.text(0.08,0.88,last_event,rotation=rot, fontsize='xx-large', ha='left', va='top', wrap=False,
bbox=dict(boxstyle='square', fc='w', ec=color), transform=ax.transAxes)
plt.close()
return fig

Since there is lots of events within the event data, I built in a way to choose the size of the plots so that the annotations don’t get too squashed together. Lets call the function and view the plot.

xsize=16
ysize=12
chart=build_covid_timeline_plot(full, 'total_cases','Total Cases', xsize=xsize ,ysize=ysize,
plt_color='orchid',video=True)#, save='test', video=None)
chart#.savefig('totalcasesVevents.png', dpi=100, bbox_inches='tight')
(Image by Author)

There we go! We can see all the relevant events with respect to total cases. With a plot like this you can see how different events may have caused changes in slopes. Take a look at the events related to delta and omicron. Also when saving the plots, play around with the dpi so that you can get the resolution you desire.

chart=build_covid_timeline_plot(full, 'new_cases','New Cases', xsize=xsize ,ysize=ysize,
plt_color='chocolate')
chart#.savefig('newcasesVevents.png', dpi=300, bbox_inches='tight')
(Image by Author)

Above is the same event plot but instead we can look at new cases with respect to events. The code below will help us animate these event plots. We handle both plots at the same time but the logic is the same as the previous animation code. Again, it is not the fastest so start it and go stretch your legs for a quick sec while it builds.

### Build Animation ###
'''
start = time.time()
writer = imageio.get_writer('case_events2.mp4', fps=15)
writer2 = imageio.get_writer('new_case_events2.mp4', fps=15)
for i in range(1,full.shape[0]):
#if i>365:
# break
sub_df=full.iloc[:i]
chart=build_covid_timeline_plot(sub_df, 'total_cases','Total Cases', xsize=xsize ,ysize=ysize,
plt_color='orchid',video=True)
dpi=int(chart.dpi)
chart.canvas.draw()
image = np.frombuffer(chart.canvas.tostring_rgb(), dtype='uint8').reshape(ysize*dpi, xsize*dpi, 3)
#print('\t\tto image')
writer.append_data(image)
### writer2 below ###
chart2=build_covid_timeline_plot(sub_df, 'new_cases','New Cases', xsize=xsize ,ysize=ysize,
plt_color='chocolate',video=True)
dpi=int(chart2.dpi)
chart2.canvas.draw()
image2 = np.frombuffer(chart2.canvas.tostring_rgb(), dtype='uint8').reshape(ysize*dpi, xsize*dpi, 3)
writer2.append_data(image2)
print('processed =',i,end='\r')
writer.close()
writer2.close()
end = time.time()
print('event plot build time =',end - start)
'''
(Video by Author)

There you have it! You have made multiple animated plots that are very informative. I now encourage you to take the code and logic and apply it to your own data analysis or projects.

Continuing the Convo

We’d love to continue the convo, welcome your feedback, and love to hear your interpretations!

References

  1. Hawaii Data Collaborative. (2021) Hawaii Data Collaborative COVID Dashboard Data. Retrieved Jan 31, 2022, from https://health.hawaii.gov/coronavirusdisease2019/current-situation-in-hawaii/
  2. Lambert Leong. (2021)hawaii_covid_events_JAN22.csv. Retrieved Feb 25, 2022, from https://www.lambertleong.com/assets/docs/data/hawaii_covid_events_JAN22.csv

--

--