{"spec_id":"heatmap-polar","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' heatmap-polar: Polar Heatmap for Cyclic Two-Dimensional Data\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Created: 2026-09-05\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens -------------------------------------------------------\nTHEME    <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG  <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nINK      <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\n\n# --- Data -----------------------------------------------------------------\n# Hourly website visits, angular = hour of day (wraps continuously),\n# radial = day of week (Monday innermost ring, Sunday outermost).\ndays  <- c(\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\")\nhours <- 0:23\n\ntraffic <- expand.grid(day = days, hour = hours, stringsAsFactors = FALSE) %>%\n  mutate(\n    day_idx    = match(day, days),\n    is_weekend = day %in% c(\"Sat\", \"Sun\"),\n    baseline   = ifelse(\n      is_weekend,\n      20 + 70 * exp(-(hour - 14)^2 / 30),\n      40 + 90 * exp(-(hour - 8)^2 / 6) + 100 * exp(-(hour - 18)^2 / 8)\n    ),\n    visits     = pmax(0, baseline + rnorm(n(), mean = 0, sd = 8))\n  )\n\n# --- Plot -------------------------------------------------------------------\n# Tiles are offset by +0.5h so cell edges land on whole hours (0-1, 1-2, ...);\n# the y-axis leaves a hole below day_idx=1 so rings read outward from a\n# center gap instead of overlapping at a point.\np <- ggplot(traffic, aes(x = hour + 0.5, y = day_idx, fill = visits)) +\n  geom_tile(width = 1, height = 1, color = PAGE_BG, linewidth = 0.2) +\n  coord_polar(theta = \"x\") +\n  scale_x_continuous(\n    limits = c(0, 24), expand = c(0, 0),\n    breaks = c(0, 6, 12, 18), labels = c(\"12am\", \"6am\", \"12pm\", \"6pm\")\n  ) +\n  scale_y_continuous(\n    limits = c(-1.5, 7.5), expand = c(0, 0),\n    breaks = 1:7, labels = days\n  ) +\n  scale_fill_gradient(low = \"#009E73\", high = \"#4467A3\", name = \"Visits/hr\") +\n  labs(\n    title    = \"Hourly Website Traffic · heatmap-polar · r · ggplot2 · anyplot.ai\",\n    subtitle = \"Hour of day (angular) · Day of week (radial)\"\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  = element_line(color = INK_SOFT, linewidth = 0.15),\n    panel.grid.minor  = element_blank(),\n    axis.title        = element_blank(),\n    axis.ticks        = element_blank(),\n    axis.text.x       = element_text(color = INK_SOFT, size = 8),\n    axis.text.y       = element_text(color = INK, size = 8, face = \"bold\"),\n    plot.title        = element_text(color = INK, size = 12, hjust = 0.5),\n    plot.subtitle     = element_text(color = INK_SOFT, size = 8, hjust = 0.5),\n    legend.text       = element_text(color = INK_SOFT, size = 8),\n    legend.title      = element_text(color = INK, size = 10),\n    legend.background = element_rect(fill = PAGE_BG, color = NA)\n  )\n\n# --- Save -------------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 6,\n  height   = 6,\n  units    = \"in\",\n  dpi      = 400\n)\n"}