🇮🇳 India Focus: Public Health Visualizations from India

Data stories from the world’s most populous nation

India presents a unique public health story — a nation that eradicated polio, built the world’s largest biometric health ID system, fought recurring Nipah outbreaks, and crowdsourced one of the best COVID-19 dashboards on the planet. With 1.4 billion people, 36 states/UTs, and enormous diversity in health outcomes, India’s data visualizations tell a story of both remarkable progress and persistent challenges. These visualizations are especially relevant for medical students training in the Indian health system.


I1. India’s Polio Eradication: The Surveillance Map (1995–2014)

The Story

India reported its last case of wild poliovirus on 13 January 2011 — a 2-year-old girl named Rukhsar Khatoon in Howrah, West Bengal. India was officially certified polio-free in 2014. The surveillance maps tracking AFP (Acute Flaccid Paralysis) cases district-by-district were critical to identifying persistent pockets — especially in Uttar Pradesh and Bihar — and directing pulse immunisation campaigns.

1995–2014 Surveillance Map Disease Eradication

R Recreation: India’s Polio Elimination Journey

Show R Code
# India polio case data (approximate WHO/GPEI data)
india_polio <- data.frame(
  year = 1998:2014,
  cases = c(1934, 1126, 265, 268, 1600, 225, 134, 66, 676, 874, 559, 741, 42, 1, 0, 0, 0)
)

# Key events
polio_events <- data.frame(
  year = c(1995, 2003, 2011, 2014),
  label = c("Pulse Polio\nLaunched", "Monovalent OPV\nIntroduced", "Last Case\n(13 Jan 2011)", "Polio-Free\nCertified"),
  y = c(1800, 300, 200, 100)
)

ggplot(india_polio, aes(x = year, y = cases)) +
  geom_area(fill = "#ff9800", alpha = 0.3) +
  geom_line(colour = "#e65100", linewidth = 1.3) +
  geom_point(colour = "#e65100", size = 2.5) +
  geom_vline(xintercept = c(2003, 2011), linetype = "dashed", alpha = 0.4) +
  annotate("text", x = 2003, y = 1700, label = "Monovalent OPV\nintroduced", size = 3,
           fontface = "italic", colour = "grey40") +
  annotate("point", x = 2011, y = 1, colour = "#4caf50", size = 6, shape = 8, stroke = 2) +
  annotate("text", x = 2012.5, y = 250,
           label = "Last case: Rukhsar Khatoon\nHowrah, West Bengal\n13 January 2011",
           size = 3, fontface = "bold", colour = "#4caf50", hjust = 0) +
  annotate("rect", xmin = 2013.5, xmax = 2014.5, ymin = -50, ymax = 100,
           fill = "#4caf50", alpha = 0.2) +
  annotate("text", x = 2014, y = 60, label = "CERTIFIED\nPOLIO-FREE", size = 3,
           fontface = "bold", colour = "#2e7d32") +
  scale_y_continuous(limits = c(-50, 2000)) +
  labs(
    title = "India's Polio Eradication: From 200,000 Cases to Zero",
    subtitle = "Wild poliovirus cases in India, 1998–2014",
    x = "", y = "Confirmed Polio Cases",
    caption = "Data: WHO/GPEI | India was the most challenging country for polio eradication"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"))

Endemic States — The Final Pockets

Show R Code
# Polio cases by state (approximate, key endemic states)
state_polio <- data.frame(
  state = rep(c("Uttar Pradesh", "Bihar", "West Bengal", "Jharkhand", "Other States"), each = 6),
  year = rep(c(2006, 2007, 2008, 2009, 2010, 2011), 5),
  cases = c(
    # UP
    415, 490, 310, 602, 22, 1,
    # Bihar
    95, 140, 100, 79, 10, 0,
    # West Bengal
    24, 38, 25, 11, 5, 0,
    # Jharkhand
    12, 18, 10, 6, 1, 0,
    # Others
    130, 188, 114, 43, 4, 0
  )
)

ggplot(state_polio, aes(x = year, y = cases, fill = state)) +
  geom_area(alpha = 0.7, colour = "white", linewidth = 0.3) +
  scale_fill_manual(values = c("Uttar Pradesh" = "#e94560", "Bihar" = "#ff9800",
                                "West Bengal" = "#0f3460", "Jharkhand" = "#9c27b0",
                                "Other States" = "#b0bec5")) +
  labs(
    title = "India's Polio Hotspots: Uttar Pradesh Was the Final Frontier",
    subtitle = "Polio cases by state, 2006–2011",
    x = "", y = "Cases", fill = "State",
    caption = "Approximate data from GPEI/WHO surveillance reports"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"), legend.position = "bottom")

Key Insight for Students: India’s polio eradication — once thought impossible given its population density, sanitation challenges, and weak cold chain — became a model for the world. The district-level surveillance maps were essential: they showed that the problem wasn’t “India has polio” but rather “specific blocks in UP and Bihar have polio.” This granular targeting made eradication achievable.


I2. The “Kerala Model”: Health Outcomes Across Indian States (NFHS)

The Story

Kerala has long been studied as a global health anomaly — a low-income state with health outcomes comparable to wealthy nations. The National Family Health Survey (NFHS) data, visualized across states, makes the extraordinary interstate variation in India starkly visible: from Kerala and Tamil Nadu’s high performance to the persistent challenges in Bihar, Uttar Pradesh, and Madhya Pradesh.

NFHS-5, 2019–2021 State Comparison Dashboard Health Equity

R Recreation: Interstate Health Disparities

Show R Code
# NFHS-5 key indicators by state (approximate)
nfhs5 <- data.frame(
  state = c("Kerala", "Tamil Nadu", "Goa", "Maharashtra", "Punjab",
            "Karnataka", "Telangana", "West Bengal", "Gujarat", "Odisha",
            "Rajasthan", "Chhattisgarh", "Madhya Pradesh", "Jharkhand",
            "Uttar Pradesh", "Bihar"),
  imr = c(4, 13, 7, 19, 18, 19, 19, 22, 21, 30, 32, 35, 35, 26, 43, 34),
  institutional_delivery = c(99.9, 99.5, 99.3, 93.1, 94.6, 95.8, 94.6, 93.0, 95.2, 86.1,
                              85.1, 73.7, 81.0, 70.0, 78.9, 65.9),
  stunting_u5 = c(23.4, 25.0, 25.8, 35.2, 24.5, 35.4, 33.1, 33.8, 39.0, 31.0,
                   31.8, 34.6, 35.7, 39.6, 39.7, 42.9)
)

nfhs5$state <- factor(nfhs5$state, levels = nfhs5$state[order(nfhs5$imr)])

ggplot(nfhs5, aes(x = state, y = imr,
                   fill = ifelse(imr <= 15, "Best", ifelse(imr <= 25, "Moderate", "Worst")))) +
  geom_col(width = 0.7, show.legend = TRUE) +
  geom_text(aes(label = imr), vjust = -0.5, size = 3, fontface = "bold") +
  scale_fill_manual(values = c("Best" = "#4caf50", "Moderate" = "#ff9800", "Worst" = "#e94560"),
                    name = "Performance") +
  labs(
    title = "India's Two Worlds: Infant Mortality Rate by State (NFHS-5)",
    subtitle = "Kerala's IMR (4) is comparable to Europe; Bihar's (34) resembles sub-Saharan Africa",
    x = "", y = "Infant Mortality Rate (per 1,000 live births)",
    caption = "Data: NFHS-5 (2019–2021) | Ministry of Health & Family Welfare, Government of India"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "top"
  )

Multi-Indicator State Comparison

Show R Code
# Radar-style comparison of selected states
compare_states <- nfhs5 %>%
  filter(state %in% c("Kerala", "Tamil Nadu", "Bihar", "Uttar Pradesh", "Madhya Pradesh", "Maharashtra")) %>%
  mutate(
    # Normalize: higher is better for all (invert negative indicators)
    imr_score = 100 - (imr / max(nfhs5$imr) * 100),
    delivery_score = institutional_delivery,
    nutrition_score = 100 - (stunting_u5 / max(nfhs5$stunting_u5) * 100)
  ) %>%
  select(state, imr_score, delivery_score, nutrition_score) %>%
  pivot_longer(cols = -state, names_to = "indicator", values_to = "score") %>%
  mutate(
    indicator = case_when(
      indicator == "imr_score" ~ "Child Survival\n(inverse IMR)",
      indicator == "delivery_score" ~ "Institutional\nDelivery %",
      indicator == "nutrition_score" ~ "Child Nutrition\n(inverse stunting)"
    )
  )

ggplot(compare_states, aes(x = indicator, y = score, fill = state)) +
  geom_col(position = "dodge", width = 0.7) +
  scale_fill_manual(values = c("Kerala" = "#4caf50", "Tamil Nadu" = "#2196f3",
                                "Maharashtra" = "#9c27b0",
                                "Uttar Pradesh" = "#e94560", "Bihar" = "#ff5722",
                                "Madhya Pradesh" = "#ff9800")) +
  labs(
    title = "The India Health Scorecard: A Tale of Two Countries",
    subtitle = "Normalized scores (higher = better) across three key NFHS-5 indicators",
    x = "", y = "Score (0–100)", fill = "State"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"), legend.position = "bottom")

Explore the NFHS-5 Data

🔗 NFHS-5 State Fact Sheets →

🔗 Health Heatmap of India (interactive) →

Key Insight for Students: The “Kerala Model” shows that health outcomes are not simply a function of wealth. Kerala’s investments in female literacy, decentralised health governance, and primary care access produced rich-country health outcomes at developing-country income levels. As future doctors, remember: the health of your patients depends as much on their PIN code as on their genetic code.


I3. COVID19India.org — The Volunteer-Driven Tracker (2020–2021)

The Story

When official government dashboards were slow and fragmented during India’s COVID-19 crisis, a group of volunteer data scientists built covid19india.org — a real-time, open-source dashboard tracking cases, deaths, testing, and vaccination across all states and districts. It became India’s most trusted source for COVID-19 data, used by journalists, researchers, policymakers, and millions of citizens.

2020–2021 Crowdsourced Dashboard Citizen Science

R Recreation: India’s Two COVID Waves

Show R Code
# Simulated India COVID-19 daily cases (approximating the two major waves)
set.seed(2021)
dates <- seq(as.Date("2020-03-15"), as.Date("2021-10-31"), by = "day")
n <- length(dates)
t_seq <- 1:n

# Wave 1 (Sep 2020 peak) + Wave 2 (May 2021 Delta peak)
wave1 <- 90000 * dnorm(t_seq, 190, 40) * n
wave2 <- 400000 * dnorm(t_seq, 410, 20) * n
daily_cases <- pmax(2000, wave1 + wave2 + rnorm(n, 0, 3000))

india_covid <- data.frame(date = dates, daily_cases = daily_cases)

ggplot(india_covid, aes(x = date, y = daily_cases)) +
  geom_area(fill = "#e94560", alpha = 0.3) +
  geom_line(colour = "#e94560", linewidth = 0.5) +
  # 7-day rolling average
  geom_line(aes(y = zoo::rollmean(daily_cases, 7, fill = NA)),
            colour = "#b71c1c", linewidth = 1.2) +
  annotate("text", x = as.Date("2020-09-15"), y = 100000,
           label = "Wave 1\n(~97K peak)", size = 3, fontface = "bold") +
  annotate("text", x = as.Date("2021-05-01"), y = 420000,
           label = "Wave 2 (Delta)\n(~414K peak)", size = 3.5,
           fontface = "bold", colour = "#b71c1c") +
  annotate("text", x = as.Date("2021-04-15"), y = 350000,
           label = "Oxygen crisis\nHealthcare collapse",
           size = 2.8, colour = "grey40", fontface = "italic") +
  scale_y_continuous(labels = scales::comma) +
  labs(
    title = "India's COVID-19 Waves: The Delta Catastrophe",
    subtitle = "Simulated daily cases | Bold line = 7-day rolling average",
    x = "", y = "Daily Confirmed Cases",
    caption = "Simulated data reflecting documented wave patterns | Original source: covid19india.org"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"))

State-Level Disparities During Delta

Show R Code
# Approximate state-wise peak daily cases during Delta wave (May 2021)
delta_peak <- data.frame(
  state = c("Maharashtra", "Karnataka", "Kerala", "Tamil Nadu", "Uttar Pradesh",
            "Andhra Pradesh", "West Bengal", "Delhi", "Rajasthan", "Gujarat"),
  peak_daily = c(68000, 50000, 43000, 36000, 35000, 25000, 20000, 28000, 17000, 14000),
  pop_millions = c(124, 67, 35, 77, 231, 53, 99, 19, 79, 64)
) %>%
  mutate(
    per_million = round(peak_daily / pop_millions),
    state = factor(state, levels = state[order(per_million)])
  )

ggplot(delta_peak, aes(x = state, y = per_million, fill = per_million)) +
  geom_col(width = 0.6) +
  geom_text(aes(label = per_million), hjust = -0.2, fontface = "bold", size = 3.5) +
  coord_flip() +
  scale_fill_gradient(low = "#ffcdd2", high = "#b71c1c", guide = "none") +
  labs(
    title = "Delta Wave: Peak Daily Cases per Million by State",
    subtitle = "Delhi and Karnataka were hardest hit per capita despite different population sizes",
    x = "", y = "Peak Daily Cases per Million Population",
    caption = "Approximate data from covid19india.org (May 2021)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"))

Explore the Original

🔗 COVID19India.org (archived) →

Key Insight for Students: COVID19India.org is a landmark in citizen health data science. When institutional dashboards failed, volunteers filled the gap — scraping state bulletins, verifying data, and building APIs used by researchers worldwide. It also exposed major data quality issues: massive undercounting of deaths, inconsistent state reporting, and the gap between official numbers and ground reality. This taught India (and the world) that data infrastructure is public health infrastructure.


I4. Kerala’s Nipah Outbreak Contact Tracing (2018–2023)

The Story

Kerala’s response to recurring Nipah virus outbreaks (2018, 2019, 2021, 2023) became a global case study in contact tracing and containment visualization. With a 40–75% case fatality rate and no vaccine, Kerala’s health system relied on meticulous contact tracing maps and epi curves to contain each outbreak within weeks.

2018–2023 Contact Tracing / Epi Curve Outbreak Response

R Recreation: Nipah Outbreak Timeline & Transmission Chain

Show R Code
# Kerala Nipah outbreaks summary
nipah_outbreaks <- data.frame(
  year = c(2018, 2019, 2021, 2023),
  location = c("Kozhikode &\nMalappuram", "Ernakulam", "Kozhikode", "Kozhikode"),
  confirmed_cases = c(18, 1, 1, 6),
  deaths = c(17, 0, 1, 2),
  contacts_traced = c(2000, 314, 188, 1288)
)

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))

# Cases and deaths
barplot(rbind(nipah_outbreaks$confirmed_cases, nipah_outbreaks$deaths),
        beside = TRUE, names.arg = nipah_outbreaks$year,
        col = c("#e94560", "#333333"), border = NA,
        main = "Nipah in Kerala: Cases & Deaths",
        ylab = "Count", xlab = "Year")
legend("topright", legend = c("Confirmed Cases", "Deaths"),
       fill = c("#e94560", "#333333"), bty = "n", cex = 0.8)

# Contacts traced (shows scale of response)
barplot(nipah_outbreaks$contacts_traced, names.arg = nipah_outbreaks$year,
        col = "#0f3460", border = NA,
        main = "Scale of Contact Tracing Response",
        ylab = "Contacts Traced", xlab = "Year")

Show R Code
par(mfrow = c(1, 1))

Simulated Transmission Chain (2018 Outbreak)

Show R Code
# Simulated transmission tree for the 2018 Kozhikode outbreak
transmission <- data.frame(
  case_id = 1:18,
  day = c(1, 5, 6, 7, 8, 8, 9, 10, 10, 11, 11, 12, 12, 13, 14, 14, 15, 16),
  generation = c(1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5),
  setting = c("Community", rep("Hospital", 5), "Family", rep("Hospital", 5),
              "Family", "Community", rep("Hospital", 3), "Family"),
  status = c(rep("Died", 14), "Died", "Survived", "Survived", "Died")
)

ggplot(transmission, aes(x = day, y = generation, colour = setting, shape = status)) +
  geom_point(size = 5, alpha = 0.8) +
  geom_segment(aes(x = 1, xend = 5, y = 1, yend = 2), colour = "grey70",
               arrow = arrow(length = unit(0.15, "cm")), linewidth = 0.3) +
  geom_segment(aes(x = 5, xend = 8, y = 2, yend = 3), colour = "grey70",
               arrow = arrow(length = unit(0.15, "cm")), linewidth = 0.3) +
  geom_segment(aes(x = 8, xend = 11, y = 3, yend = 4), colour = "grey70",
               arrow = arrow(length = unit(0.15, "cm")), linewidth = 0.3) +
  geom_segment(aes(x = 11, xend = 14, y = 4, yend = 5), colour = "grey70",
               arrow = arrow(length = unit(0.15, "cm")), linewidth = 0.3) +
  scale_colour_manual(values = c("Hospital" = "#e94560", "Family" = "#ff9800", "Community" = "#0f3460")) +
  scale_shape_manual(values = c("Died" = 4, "Survived" = 16)) +
  scale_y_reverse(breaks = 1:5, labels = paste("Gen", 1:5)) +
  labs(
    title = "Nipah 2018 Kozhikode: Simulated Transmission Chain",
    subtitle = "Most transmission occurred in hospital settings — nosocomial spread was the key driver",
    x = "Day of Outbreak", y = "Transmission Generation",
    colour = "Transmission Setting", shape = "Outcome",
    caption = "Simulated based on documented outbreak pattern | CFR ~94% in 2018 outbreak"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"), legend.position = "bottom")

Key Insight for Students: Kerala’s Nipah response demonstrates what “boots on the ground” epidemiology looks like in India. With no vaccine or treatment, the entire response depended on rapid contact identification, quarantine, and infection prevention in hospitals. The 2018 outbreak killed 17 of 18 confirmed cases — but was contained in 6 weeks through aggressive surveillance. The epi curve and transmission chain charts were the war maps for this battle.


I5. NFHS Through the Decades: India’s Health Transformation

The Story

The National Family Health Survey (NFHS), conducted approximately every 5–7 years since 1992, is India’s most important health data source. Comparing NFHS rounds reveals a transformation: dramatic declines in child mortality and fertility, increases in institutional delivery, but persistent undernutrition and anaemia.

1992–2021 Longitudinal Survey Population Health

R Recreation: India’s Health Trajectory

Show R Code
# NFHS rounds comparison (approximate national averages)
nfhs_rounds <- data.frame(
  round = rep(c("NFHS-1\n(1992-93)", "NFHS-2\n(1998-99)", "NFHS-3\n(2005-06)",
                 "NFHS-4\n(2015-16)", "NFHS-5\n(2019-21)"), 4),
  indicator = rep(c("Under-5 Mortality\n(per 1,000)", "Institutional Delivery\n(%)",
                      "Children Stunted\n(%)", "Total Fertility Rate"), each = 5),
  value = c(
    # U5MR
    109, 95, 74, 50, 42,
    # Institutional delivery
    26, 34, 41, 79, 89,
    # Stunting
    52, 51, 48, 38, 36,
    # TFR
    3.4, 2.9, 2.7, 2.2, 2.0
  )
)

# Normalize for comparable display
nfhs_plot <- nfhs_rounds %>%
  mutate(round_num = rep(1:5, 4))

ggplot(nfhs_plot, aes(x = round, y = value, group = indicator, colour = indicator)) +
  geom_line(linewidth = 1.3) +
  geom_point(size = 3) +
  geom_text(aes(label = value), vjust = -1, size = 2.8) +
  facet_wrap(~ indicator, scales = "free_y", ncol = 2) +
  scale_colour_manual(values = c("#e94560", "#4caf50", "#ff9800", "#0f3460")) +
  labs(
    title = "India's Health Transformation: NFHS-1 to NFHS-5 (1992–2021)",
    subtitle = "Dramatic gains in survival and delivery care; stubborn persistence of undernutrition",
    x = "", y = "",
    caption = "Data: NFHS rounds 1–5 | International Institute for Population Sciences (IIPS), Mumbai"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold"),
    strip.text = element_text(face = "bold", size = 10),
    legend.position = "none",
    axis.text.x = element_text(size = 8, angle = 30, hjust = 1)
  )

Key Insight for Students: The NFHS data tells the story of India’s health transition: infectious disease declining, chronic disease rising, and nutrition stuck in the middle. Institutional delivery rose from 26% to 89% in three decades — an extraordinary achievement. But stunting has barely moved from 52% to 36%. This is the paradox that will define Indian public health in your careers.


I6. India’s Disease Surveillance: IDSP & IHIP

The Story

India’s Integrated Disease Surveillance Programme (IDSP), now transitioning to the Integrated Health Information Platform (IHIP), monitors epidemic-prone diseases across the country. Weekly outbreak reports and district-level dashboards form the backbone of India’s early warning system — though data quality and timeliness remain challenges.

2004–present Surveillance Dashboard Disease Surveillance

R Recreation: IDSP-Style Outbreak Summary

Show R Code
# Simulated IDSP annual outbreak data (inspired by IDSP annual summaries)
idsp_outbreaks <- data.frame(
  disease = c("Acute Diarrhoeal\nDisease", "Food Poisoning", "Dengue",
              "Chikungunya", "Malaria", "Viral Hepatitis",
              "Acute Respiratory\nInfection", "Cholera", "Measles", "Typhoid"),
  outbreaks_2022 = c(420, 280, 310, 95, 180, 120, 150, 45, 60, 90),
  outbreaks_2023 = c(480, 310, 350, 110, 160, 140, 180, 38, 55, 100)
)

idsp_long <- idsp_outbreaks %>%
  pivot_longer(cols = c(outbreaks_2022, outbreaks_2023),
               names_to = "year", values_to = "outbreaks") %>%
  mutate(year = ifelse(year == "outbreaks_2022", "2022", "2023"))

idsp_long$disease <- factor(idsp_long$disease,
  levels = idsp_outbreaks$disease[order(idsp_outbreaks$outbreaks_2023, decreasing = TRUE)])

ggplot(idsp_long, aes(x = disease, y = outbreaks, fill = year)) +
  geom_col(position = "dodge", width = 0.6) +
  geom_text(aes(label = outbreaks), position = position_dodge(0.6), vjust = -0.5, size = 2.8) +
  scale_fill_manual(values = c("2022" = "#b0bec5", "2023" = "#e94560")) +
  labs(
    title = "IDSP-Style: Reported Disease Outbreaks in India",
    subtitle = "Acute diarrhoeal disease and dengue dominate India's outbreak landscape",
    x = "", y = "Number of Outbreaks Reported", fill = "Year",
    caption = "Simulated data inspired by IDSP annual summaries | idsp.mohfw.gov.in"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold"),
    axis.text.x = element_text(angle = 40, hjust = 1),
    legend.position = "top"
  )

Explore Indian Health Dashboards

🔗 IDSP/IHIP Portal →

🔗 Health Heatmap of India →

🔗 NHM HMIS Portal →

🔗 NFHS-5 Dashboard →

Key Insight for Students: India’s surveillance system is a work in progress. IDSP reports weekly data from ~97% of districts, but quality varies enormously — private sector cases are underreported, death reporting is incomplete, and there are known “Sunday dips” in data caused by reporting gaps on weekends. When you use Indian health data, always ask: what’s not in this chart?


I7. India’s Tobacco Burden: The GBD India Story

The Story

India has the world’s second-largest population of tobacco users — and the most diverse: cigarettes, bidis, gutka, khaini, hookah, and other smokeless forms. The GBD India visualizations show that tobacco kills over 1.3 million Indians per year and is the leading preventable risk factor for cancer and cardiovascular disease.

GBD 2021 Risk Factor Analysis Tobacco Control

R Recreation

Show R Code
# India tobacco use types (approximate GATS/NFHS data)
india_tobacco <- data.frame(
  type = c("Bidi", "Cigarette", "Khaini\n(chewing)", "Gutka /\nPan Masala",
           "Hookah", "Other\nSmokeless"),
  users_millions = c(72, 30, 68, 45, 8, 22),
  category = c("Smoked", "Smoked", "Smokeless", "Smokeless", "Smoked", "Smokeless")
)

india_tobacco$type <- factor(india_tobacco$type,
  levels = india_tobacco$type[order(india_tobacco$users_millions)])

ggplot(india_tobacco, aes(x = type, y = users_millions, fill = category)) +
  geom_col(width = 0.6) +
  geom_text(aes(label = paste0(users_millions, "M")), hjust = -0.2, fontface = "bold", size = 3.5) +
  coord_flip() +
  scale_fill_manual(values = c("Smoked" = "#e94560", "Smokeless" = "#ff9800")) +
  scale_y_continuous(limits = c(0, 85)) +
  labs(
    title = "India's Unique Tobacco Landscape",
    subtitle = "India has the world's highest burden of smokeless tobacco use",
    x = "", y = "Estimated Users (millions)", fill = "Category",
    caption = "Approximate data from GATS India & NFHS-5"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"), legend.position = "bottom")

Show R Code
# Tobacco-attributable deaths in India by disease
india_tobacco_deaths <- data.frame(
  cause = c("Oral & Pharyngeal\nCancers", "Lung Cancer", "COPD",
            "Ischaemic Heart\nDisease", "Stroke", "Tuberculosis", "Other Cancers"),
  deaths_thousands = c(120, 95, 250, 300, 180, 80, 150),
  tobacco_type = c("Smokeless", "Smoked", "Smoked", "Both", "Both", "Smoked", "Both")
)

india_tobacco_deaths$cause <- factor(india_tobacco_deaths$cause,
  levels = india_tobacco_deaths$cause[order(india_tobacco_deaths$deaths_thousands)])

ggplot(india_tobacco_deaths, aes(x = cause, y = deaths_thousands, fill = tobacco_type)) +
  geom_col(width = 0.6) +
  geom_text(aes(label = paste0(deaths_thousands, "K")), hjust = -0.2, size = 3.5) +
  coord_flip() +
  scale_fill_manual(values = c("Smoked" = "#e94560", "Smokeless" = "#ff9800", "Both" = "#9c27b0")) +
  scale_y_continuous(limits = c(0, 350)) +
  labs(
    title = "Tobacco Kills 1.3 Million Indians Every Year",
    subtitle = "Tobacco-attributable deaths by cause — India has a unique dual burden of smoked + smokeless",
    x = "", y = "Deaths (thousands/year)", fill = "Primary Tobacco Type",
    caption = "Approximate data from GBD 2021 India estimates"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"), legend.position = "bottom")

Key Insight for Students: India’s tobacco burden is fundamentally different from the West. Oral cancers from smokeless tobacco (gutka, khaini) are a uniquely South Asian epidemic. In your clinical rotations, especially in oral medicine and oncology, you’ll see the consequences daily. The COTPA act and cigarette pack warnings were driven by exactly these data visualizations. Yet enforcement remains a challenge.


Discussion Questions: India Focus

  1. Polio to COVID: India eradicated polio through district-level targeting. Why couldn’t the same approach work for COVID-19?
  2. The Kerala paradox: Kerala has the best health outcomes but also had very high COVID-19 case rates. Does this undermine the “Kerala Model” or validate it?
  3. Citizen data science: COVID19India.org filled a data gap left by government. Should governments embrace or be concerned by such initiatives?
  4. Tobacco in India: India’s tobacco landscape (bidi, gutka) is very different from the West. How should tobacco control policy be adapted for this context?
  5. Data quality: A common criticism of Indian health data is underreporting. How does this affect the visualizations you’ve seen, and what can be done?