{"spec_id":"histogram-epidemic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' histogram-epidemic: Epidemic Curve (Epi Curve)\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Created: 2026-06-02\n\nlibrary(ggplot2)\nlibrary(scales)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens (Imprint palette, theme-adaptive chrome)\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n\nIMPRINT_PALETTE <- c(\n    \"#009E73\",  # 1 - confirmed (brand green, always first)\n    \"#C475FD\",  # 2 - probable (lavender)\n    \"#4467A3\"   # 3 - suspect (blue)\n)\n\n# Data: simulated two-wave influenza outbreak (90 days, Jan-Apr 2024)\nn_days     <- 90\nstart_date <- as.Date(\"2024-01-15\")\ndates      <- seq(start_date, by = \"day\", length.out = n_days)\ndays       <- seq_len(n_days)\n\n# Two-wave epidemic curve (Gaussian mixture)\nprimary   <- 200 * exp(-((days - 28)^2) / (2 * 10^2))\nsecondary <-  80 * exp(-((days - 58)^2) / (2 *  8^2))\nlambda    <- pmax(primary + secondary, 0.5)\ntotal     <- rpois(n_days, lambda = lambda)\n\n# Split into case classifications\nconfirmed <- rbinom(n_days, size = total, prob = 0.62)\nremaining <- total - confirmed\nprobable  <- rbinom(n_days, size = remaining, prob = 0.70)\nsuspect   <- remaining - probable\n\ndf <- data.frame(\n    date      = rep(dates, 3),\n    cases     = c(confirmed, probable, suspect),\n    case_type = factor(\n        rep(c(\"Confirmed\", \"Probable\", \"Suspect\"), each = n_days),\n        levels = c(\"Confirmed\", \"Probable\", \"Suspect\")\n    )\n)\n\n# Public health intervention events\nevents <- data.frame(\n    date  = as.Date(c(\"2024-02-05\", \"2024-02-20\")),\n    label = c(\"School\\nclosures\", \"Vaccination\\ncampaign\")\n)\n\ny_max <- max(tapply(df$cases, df$date, sum), na.rm = TRUE)\n\n# Cumulative case burden for secondary y-axis\ntotal_by_date <- tapply(df$cases, df$date, sum)\ncum_df <- data.frame(\n    date  = as.Date(names(total_by_date)),\n    daily = as.numeric(total_by_date)\n)\ncum_df        <- cum_df[order(cum_df$date), ]\ncum_df$cumulative <- cumsum(cum_df$daily)\ncum_max      <- max(cum_df$cumulative)\nscale_factor <- (y_max * 1.15) / cum_max  # scale cumulative to primary axis range\n\n# Plot\np <- ggplot(df, aes(x = date, y = cases, fill = case_type)) +\n    geom_col(width = 1, position = \"stack\") +\n    geom_line(\n        data        = cum_df,\n        aes(x = date, y = cumulative * scale_factor),\n        color       = INK_MUTED,\n        linewidth   = 0.9,\n        linetype    = \"solid\",\n        inherit.aes = FALSE\n    ) +\n    geom_vline(\n        data     = events,\n        aes(xintercept = date),\n        color    = INK_SOFT,\n        linewidth = 0.7,\n        linetype = \"dashed\"\n    ) +\n    geom_text(\n        data        = events,\n        aes(x = date, y = y_max * 0.97, label = label),\n        color       = INK_MUTED,\n        size        = 2.8,\n        hjust       = -0.12,\n        lineheight  = 0.9,\n        inherit.aes = FALSE\n    ) +\n    scale_fill_manual(\n        values = IMPRINT_PALETTE,\n        name   = \"Case classification\"\n    ) +\n    scale_x_date(\n        date_breaks = \"2 weeks\",\n        date_labels = \"%b %d\",\n        expand      = expansion(mult = c(0.01, 0.02))\n    ) +\n    scale_y_continuous(\n        expand   = expansion(mult = c(0, 0.15)),\n        labels   = label_comma(),\n        sec.axis = sec_axis(\n            ~ . / scale_factor,\n            name   = \"Cumulative cases\",\n            labels = label_comma()\n        )\n    ) +\n    labs(\n        x     = \"Date of symptom onset\",\n        y     = \"New cases\",\n        title = \"histogram-epidemic · r · ggplot2 · anyplot.ai\"\n    ) +\n    theme_minimal(base_size = 8) +\n    theme(\n        plot.background    = element_rect(fill = PAGE_BG,     color = PAGE_BG),\n        panel.background   = element_rect(fill = PAGE_BG,     color = NA),\n        panel.grid.major.y = element_line(color = INK_MUTED,  linewidth = 0.25),\n        panel.grid.major.x = element_blank(),\n        panel.grid.minor   = element_blank(),\n        axis.title         = element_text(color = INK,        size = 10),\n        axis.title.y.right = element_text(color = INK_MUTED,  size = 9),\n        axis.text          = element_text(color = INK_SOFT,   size = 8),\n        axis.text.x        = element_text(angle = 30,         hjust = 1),\n        axis.text.y.right  = element_text(color = INK_MUTED,  size = 7),\n        axis.line.x        = element_line(color = INK_SOFT,   linewidth = 0.4),\n        plot.title         = element_text(color = INK,        size = 12, face = \"bold\"),\n        legend.background  = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.3),\n        legend.text        = element_text(color = INK_SOFT,   size = 8),\n        legend.title       = element_text(color = INK,        size = 10),\n        legend.position    = \"top\",\n        legend.key.size    = unit(0.45, \"cm\"),\n        plot.margin        = margin(t = 10, r = 15, b = 5, l = 5, unit = \"pt\")\n    )\n\n# Save\nggsave(\n    filename = sprintf(\"plot-%s.png\", THEME),\n    plot     = p,\n    device   = ragg::agg_png,\n    width    = 8,\n    height   = 4.5,\n    units    = \"in\",\n    dpi      = 400\n)\n"}