Skip to content

Dashboard Design — COVID-19 Data Analysis

Dashboard Purpose

A public-facing tracker that informs without misleading. The design challenge here is unique: every choice (raw vs per-capita, log vs linear scale, date range) can distort the story. This dashboard prioritises honesty over drama.


Python Visualisation (matplotlib/seaborn focus)

Unlike the other projects, this one emphasises Python charting because the time-series work and per-capita normalisation are best demonstrated in code.

Chart 1 — Global Wave Timeline

import pandas as pd
import matplotlib.pyplot as plt

world = pd.read_csv("covid_world_clean.csv", parse_dates=["date"])

fig, ax = plt.subplots(figsize=(14, 5))
ax.fill_between(world["date"], world["new_cases_7d"], alpha=0.3, color="#0D9488")
ax.plot(world["date"], world["new_cases_7d"], color="#0D9488", linewidth=1.5)
ax.set_title("Global Daily COVID-19 Cases (7-Day Average)", fontsize=14)
ax.set_ylabel("New Cases (7-day avg)")
ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f"{x/1e6:.1f}M"))
ax.grid(True, alpha=0.3)

# Annotate the waves
ax.annotate("Delta wave", xy=(pd.Timestamp("2021-08-15"), 650000),
            xytext=(pd.Timestamp("2021-05-01"), 800000),
            arrowprops=dict(arrowstyle="->", color="#64748B"), fontsize=9)
ax.annotate("Omicron wave", xy=(pd.Timestamp("2022-01-20"), 3000000),
            xytext=(pd.Timestamp("2021-10-01"), 3200000),
            arrowprops=dict(arrowstyle="->", color="#64748B"), fontsize=9)

plt.tight_layout()
plt.savefig("global_waves.png", dpi=150, bbox_inches="tight")
plt.show()

Chart 2 — Fair Country Comparison (Small Multiples)

focus = pd.read_csv("covid_focus_clean.csv", parse_dates=["date"])
countries = ["United States", "United Kingdom", "India", "Brazil",
             "Germany", "South Korea"]

fig, axes = plt.subplots(2, 3, figsize=(16, 8), sharex=True)
for ax, country in zip(axes.flat, countries):
    sub = focus[focus["location"] == country]
    ax.plot(sub["date"], sub["new_cases_per_million"].rolling(7).mean(),
            color="#0D9488", linewidth=1.5)
    ax.set_title(country)
    ax.grid(True, alpha=0.3)

fig.suptitle("Daily New Cases per Million (7-Day Avg) — Fair Comparison", fontsize=14)
plt.tight_layout()
plt.savefig("country_comparison.png", dpi=150, bbox_inches="tight")
plt.show()

Small multiples beat one cluttered chart

Plotting 6 countries on one axis creates spaghetti. Small multiples (one panel per country, shared axes) let readers compare shapes fairly. Per-million on the y-axis ensures the comparison is honest.

Chart 3 — Log vs Linear Scale (Teaching Moment)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))

for country in ["United States", "South Korea"]:
    sub = focus[focus["location"] == country]
    ax1.plot(sub["date"], sub["total_cases_per_million"], label=country)
    ax2.plot(sub["date"], sub["total_cases_per_million"], label=country)

ax1.set_title("Linear Scale")
ax2.set_yscale("log")
ax2.set_title("Log Scale")
for ax in (ax1, ax2):
    ax.legend()
    ax.grid(True, alpha=0.3)
plt.suptitle("Same Data, Two Scales — Choose Honestly")
plt.tight_layout()
plt.show()

If Building in Tableau or Power BI

Page 1 — Global Tracker

  • KPI cards: total cases, total deaths, global CFR, % vaccinated
  • World map (choropleth) coloured by cases per million, NOT total cases
  • Global wave timeline (area chart, 7-day average)

Page 2 — Country Comparison

  • Multi-select country slicer
  • Per-capita line chart (small multiples or a single chart with selectable countries)
  • A toggle for linear/log scale
  • A "data caveats" text box (always visible)

Ethical Design Rules

Every choice can mislead — make honest defaults

  • Default to per-capita, not raw counts — raw counts always favour large countries
  • Use 7-day averages — raw daily data has weekend sawtooth noise
  • Show full date ranges — don't let users cherry-pick a misleading window
  • Display caveats prominently — testing differences, reporting lags, CFR limitations
  • Avoid red-heavy "scary" palettes — inform, don't frighten
  • Never compare CFR across countries without noting testing differences

← SQL Analysis · Next: Insights and Recommendations →