Train model

The COVID-19 pandemic has wreaked havoc on the world, causing millions of deaths, economic upheaval, and untold societal disruption. Throughout the crisis, data has been one of our most powerful weapons in understanding the virus‘s spread and impact. However, raw data alone is not sufficient – to extract actionable insights, we need to visualize it effectively.

Data visualization plays a crucial role in tracking and making decisions about the pandemic. It enables researchers to spot patterns, the public to grasp the magnitude of the situation, and officials to compare the effectiveness of different interventions. Creating clear, interactive data visualizations has been key to effectively communicating about COVID-19.

One of the best tools for building web-based interactive visualizations is Plotly. Plotly is a graphing library that allows building beautiful, dynamic charts in Python, R, JavaScript, and more. Its interactive nature is perfectly suited for helping users engage with and explore complex datasets.

In this post, we‘ll take a deep dive into visualizing COVID-19 data with Plotly, with a special focus on its unique text template feature. Text templates allow fine-grained control over the dynamic text elements of a visualization, such as labels, tooltips, and annotations. Used effectively, they enable creating clearer and more informative customized charts that tell a story with data.

We‘ll be analyzing the Our World in Data COVID-19 dataset, which aggregates official data on cases, deaths, hospitalizations, testing, and vaccinations for countries around the world. The code is available in this Kaggle notebook.

Plotting Cases and Deaths Over Time

First, let‘s look at how case and death numbers have evolved in several major countries. We‘ll make a multi-line chart showing new cases per day, with a subplot showing cumulative totals. Hover tooltips will allow easily seeing each country‘s counts.

import plotly.graph_objects as go
from plotly.subplots import make_subplots

countries = [‘United States‘, ‘India‘, ‘Brazil‘, ‘France‘, ‘Germany‘] df = owid_data[owid_data.location.isin(countries)] df = df.melt(id_vars=‘date‘, value_vars=countries, var_name=‘country‘, value_name=‘new_cases‘)

df2 = df.groupby([‘country‘, ‘date‘]).new_cases.sum().unstack()
df2 = df2.stack().reset_index()
df2.columns = [‘country‘, ‘date‘, ‘total_cases‘]

fig = make_subplots(rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.1,
subplot_titles=("Daily New Cases", "Cumulative Cases"))

for country in countries:
mask = df[‘country‘] == country
fig.add_trace(go.Scatter(x=df[mask][‘date‘], y=df[mask][‘new_cases‘],
mode=‘lines‘,
name=country,
hovertemplate=‘%{y:.0f} new cases on %{x}
in %{hovertext}‘,
hovertext=[country]len(df[mask])),
row=1, col=1)
fig.add_trace(go.Scatter(x=df2[df2[‘country‘] == country][‘date‘],
y=df2[df2[‘country‘] == country][‘total_cases‘],
mode=‘lines‘,
showlegend=False,
hovertemplate=‘%{y:.2s} total cases as of %{x}
in %{hovertext}‘,
hovertext=[country]
len(df2[df2[‘country‘] == country])),
row=2, col=1)

fig.update_layout(height=600, width=800,
title_text=‘COVID-19 Cases in Selected Countries‘,
yaxis1_tickformat=‘,.0f‘,
yaxis2_tickformat=‘,.0f‘,
hovermode=‘x‘)

fig.show()

There are several things to note about how text templates enhance this visualization:

  • The hovertemplate for each trace displays different information on hover – daily new cases in the first subplot, and total cumulative cases in the second. The %{variable} syntax allows dynamic insertion of the x value (date), y value (case count), and custom text (country name).
  • The tickformat parameter for the y-axes specifies to format numbers with commas to enhance readability.
  • Setting hovermode to ‘x‘ means hovering anywhere on a given x-value (date) displays the data for all countries, making it easy to compare.

From this chart, we can observe several interesting findings:

  • The US and India have seen the highest peaks of daily new cases, though at different times – the US in January 2021 and India in May 2021.
  • Brazil and France have experienced multiple waves of varying intensity, highlighting the nonlinear nature of the pandemic.
  • All five countries have seen cases rise again toward the end of 2021, likely driven by the highly transmissible Omicron variant.

The subplot of cumulative cases provides additional context, showing how these daily fluctuations compound over time to starkly differing total burdens across countries. The US and India stand out as by far the most impacted in terms of sheer case numbers.

Comparing States in the US

Next let‘s zoom in on the United States and look at COVID spread across states. A choropleth map is a great way to visualize geographic patterns. We‘ll create one in Plotly that colors each state according to its cumulative cases per capita:

import plotly.graph_objects as go

states_df = pd.read_csv(‘https://raw.githubusercontent.com/plotly/datasets/master/2014_usa_states.csv‘)
df = owid_data[(owid_data.location == ‘United States‘) & (owid_data.date == ‘2022-02-12‘)] df = df[[‘total_cases‘, ‘population‘]].merge(states_df, left_on=‘iso_code‘, right_on=‘Postal‘, how=‘left‘)
df[‘cases_per_100k‘] = df.total_cases / df.population * 1e5

fig = go.Figure(data=go.Choropleth(
locations=df[‘Postal‘],
z=df[‘cases_per_100k‘],
locationmode=‘USA-states‘,
colorscale=‘Reds‘,
autocolorscale=False,
text=df[‘State‘],
marker_line_color=‘white‘,
colorbar_title=‘Cases per 100K‘
))

fig.update_layout(
title_text=‘COVID-19 Cases per 100,000 People by US State‘,
geo = dict(
scope=‘usa‘,
projection=go.layout.geo.Projection(type = ‘albers usa‘),
showlakes=True,
lakecolor=‘rgb(255, 255, 255)‘
)
)

fig.update_traces(
hovertemplate=‘%{text}
%{z:.0f} cases per 100K‘
)

fig.show()

The map shows some clear geographic disparities in COVID‘s impact on the US:

  • Northeastern states like Rhode Island, New York, and New Jersey have the highest case rates, likely due to high population density and early outbreaks before preventative measures.
  • Some Southern states like Tennessee, Florida, and South Carolina also have elevated case rates.
  • In contrast, sparsely populated Western states like Oregon, Utah, and Washington have been less hard hit on a per-capita basis.

The text template functionality enhances the map by showing clean state labels and case rates when hovering over each state. Specifying text=df[‘State‘] sets a custom hover label for each location, while the z=df[‘cases_per_100k‘] sets the colored value and the %{z} field displays it on hover.

Heatmap of Case Counts Over Time

To visualize COVID‘s spread over time in more granular detail, we can use a heatmap showing case counts by country and date. Plotly‘s density_heatmap() figure factory creates an effective heatmap, enhanced with a custom colorscale and templated hover text:

import plotly.figure_factory as ff

top20_countries = owid_data.groupby(‘location‘).total_cases.max().nlargest(20).index
df = owid_data[owid_data.location.isin(top20_countries)] df = df[[‘location‘, ‘date‘, ‘total_cases‘]]

fig = ff.create_annotated_heatmap(
z=df.set_index([‘location‘, ‘date‘]).unstack().values,
x=df.date.dt.strftime(‘%b %d, %Y‘).unique().tolist(),
y=top20_countries,
colorscale=‘YlOrRd‘,
hovertemplate=‘%{x}
%{y}
%{z}‘,
showscale=True
)

fig.update_layout(
title=‘Heatmap of COVID-19 Cumulative Cases by Country Over Time‘,
xaxis_title="Month",
yaxis_title="Country"
)

fig.show()

This heatmap compactly displays a large amount of information about COVID‘s differential spread:

  • China and several European countries like Italy, Spain, France and Germany were the initial hotspots in early 2020.
  • By mid-2020, the pandemic‘s epicenter had shifted to the Americas, with the US, Brazil and Mexico seeing rapid growth in cases.
  • India and several South American countries like Argentina, Colombia, and Peru saw their primary surges later, in mid-2021.
  • Towards the end of 2021, the heatmap shows another global swell of cases likely caused by Omicron, with the US, UK, and France particularly hard-hit.

Text templates make the hover interaction informative and elegant, displaying the date, country, and case total in a clean vertical format.

Modeling and Projecting COVID-19 Spread

Beyond visualizing historical COVID data, creating projections of future spread is critical for planning public health responses. Fortunately, data visualization is also a powerful tool for evaluating and communicating the results of predictive models.

One common approach is using time series forecasting models like ARIMA or Prophet to project future case numbers based on historical trends. The projections can then be plotted against actual data to assess the model‘s performance.

Here‘s an example of using Prophet to forecast future cases in the US and plotting the results with Plotly:

from fbprophet import Prophet

df = owid_data[owid_data.location == ‘United States‘].rename(columns={‘date‘: ‘ds‘, ‘total_cases‘: ‘y‘})[[‘ds‘,‘y‘]]

m = Prophet()
m.fit(df)

future = m.make_future_dataframe(periods=30)
forecast = m.predict(future)

fig = go.Figure()

fig.add_trace(go.Scatter(
x=df[‘ds‘], y=df[‘y‘],
mode=‘lines‘,
name=‘Actual‘,
line=dict(color=‘royalblue‘, width=4)
))

fig.add_trace(go.Scatter(
x=forecast[‘ds‘], y=forecast[‘yhat‘],
mode=‘lines‘,
name=‘Predicted‘,
line=dict(color=‘firebrick‘, width=4, dash=‘dot‘),
))

fig.add_trace(go.Scatter(
x=forecast[‘ds‘], y=forecast[‘yhat_lower‘],
mode=‘lines‘,
name=‘Lower Bound‘,
line=dict(color=‘gray‘, width=1),
))

fig.add_trace(go.Scatter(
x=forecast[‘ds‘], y=forecast[‘yhat_upper‘],
mode=‘lines‘,
name=‘Upper Bound‘,
line=dict(color=‘gray‘, width=1),
fill=‘tonexty‘
))

fig.update_layout(
title=‘Forecasted vs. Actual COVID-19 Cases in the United States‘,
hovermode=‘x‘,
xaxis_title=‘Date‘,
yaxis_title=‘Cumulative Cases‘,
yaxis_tickformat=‘,.0f‘,
template=‘plotly_white‘
)

fig.show()

This plots actual case counts to date along with Prophet‘s projected counts and uncertainty intervals. We can see that cases are projected to keep rising approximately linearly, with widening uncertainty further into the future.

Plotting model results in this way helps evaluate how well the model fits historical data and convey the uncertainty in future projections. Dynamic Plotly visualizations are especially helpful for explaining models to non-technical stakeholders.

Conclusion: Telling Stories and Driving Decisions with COVID Data

We‘ve seen how Plotly‘s declarative API, rich library of chart types, and text template functionality enable creating a variety of powerful, interactive visualizations of COVID-19 data. From high-level international comparisons to granular county-level heatmaps, these visualizations reveal important insights about the virus‘s global spread and impact.

However, this is just scratching the surface of what‘s possible. Blending multiple datasets, integrating case fatality rates and other epidemiological metrics, digging into demographic disparities, and overlaying policy interventions are just a few additional angles to explore.

The unifying theme is that data visualization is a vital tool in making sense of the vast, complex, and often messy data surrounding the COVID-19 pandemic. By presenting the data in a clear, engaging, interactive way, visualizations help us derive insights, tell compelling stories, and ultimately drive better decisions.

If you‘re inspired to start creating your own COVID-19 visualizations, I encourage you to check out the Plotly Python documentation and play around with the datasets available on Kaggle. Experiment with different chart types, text template customizations, and layouts to find the most effective way to communicate your insights.

Visualization, like any form of communication, is a skill that improves with practice. There‘s no better way to hone your skills than diving into a dataset as pressing and impactful as COVID-19 data.

Beyond just visualizing existing data, I‘d also encourage you to think about how predictive modeling can help forecast the pandemic‘s future trajectory. Building a model, evaluating it with visualizations, and communicating your results is a powerful way to contribute to data-driven decision making.

The world needs compelling data visualization and robust epidemic modeling now more than ever. I hope this post has equipped you with some of the tools and inspiration to contribute to the global effort to understand and fight COVID-19. Stay safe, and happy visualizing!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Similar Posts