{"spec_id":"manhattan-gwas","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' manhattan-gwas: Manhattan Plot for GWAS\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 93/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\"\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\nANYPLOT_AMBER <- \"#DDCC77\"\n\n# --- Data --------------------------------------------------------------------\n# Approximate human chromosome lengths (Mb), chromosomes 1-22\nchr_lengths <- c(\n  249, 243, 198, 191, 180, 171, 159, 145, 138, 133,\n  135, 133, 114, 107, 102, 90, 83, 80, 59, 63, 48, 51\n)\nn_chr <- length(chr_lengths)\n\nchr_info <- tibble::tibble(\n  chromosome = 1:n_chr,\n  length_mb  = chr_lengths\n) %>%\n  mutate(offset = lag(cumsum(length_mb), default = 0) * 1e6)\n\nn_snps_total <- 9000\nsnps_per_chr <- round(n_snps_total * chr_lengths / sum(chr_lengths))\n\nsnps <- lapply(1:n_chr, function(i) {\n  tibble::tibble(\n    chromosome = i,\n    position   = sort(sample(seq_len(chr_lengths[i] * 1e6), snps_per_chr[i])),\n    p_value    = runif(snps_per_chr[i])\n  )\n}) %>% bind_rows()\n\n# Simulated GWAS hits: clusters of low p-values around a few causal loci\npeak_chr      <- c(3, 8, 14, 19)\npeak_lead_log <- c(12.0, 9.2, 15.4, 7.8)\npeaks <- Map(function(chr, lead_log) {\n  cluster_size <- 24\n  spread_bp    <- rnorm(cluster_size, 0, 1.5e6)\n  lead_pos     <- sample(seq_len(chr_lengths[chr] * 1e6), 1)\n  cluster_pos  <- pmin(pmax(lead_pos + spread_bp, 1), chr_lengths[chr] * 1e6)\n  cluster_log  <- pmax(0.1, lead_log - abs(spread_bp) / 4e5 + rnorm(cluster_size, 0, 0.3))\n  tibble::tibble(\n    chromosome = chr,\n    position   = round(cluster_pos),\n    p_value    = 10^(-cluster_log)\n  )\n}, peak_chr, peak_lead_log) %>% bind_rows()\n\ngwas <- bind_rows(snps, peaks) %>%\n  left_join(chr_info, by = \"chromosome\") %>%\n  mutate(\n    bp_cum     = position + offset,\n    neg_log_p  = -log10(p_value),\n    chr_parity = ifelse(chromosome %% 2 == 0, \"even\", \"odd\")\n  )\n\nchr_axis <- chr_info %>%\n  mutate(\n    center = offset + length_mb * 1e6 / 2,\n    # Chromosomes 19-22 are short and sit close together; thin the labels\n    # past 18 to give the remaining ones breathing room.\n    label  = ifelse(chromosome > 18 & chromosome %% 2 == 0, \"\", chromosome)\n  )\n\ngenome_wide_line <- -log10(5e-8)\nsuggestive_line  <- -log10(1e-5)\nsig_hits         <- gwas %>% filter(p_value < 5e-8)\n\n# Lead SNP (highest -log10 p) per significant locus, for peak annotation\nlead_snps <- sig_hits %>%\n  group_by(chromosome) %>%\n  slice_max(neg_log_p, n = 1, with_ties = FALSE) %>%\n  ungroup()\n\n# --- Plot ----------------------------------------------------------------\np <- ggplot(gwas, aes(x = bp_cum, y = neg_log_p, color = chr_parity)) +\n  geom_hline(yintercept = suggestive_line, linetype = \"dotted\",\n             color = ANYPLOT_AMBER, linewidth = 0.5) +\n  geom_hline(yintercept = genome_wide_line, linetype = \"dashed\",\n             color = IMPRINT_PALETTE[5], linewidth = 0.6) +\n  geom_point(size = 0.6, alpha = 0.65) +\n  geom_point(data = sig_hits, aes(x = bp_cum, y = neg_log_p),\n             color = IMPRINT_PALETTE[5], size = 2.2, alpha = 0.9, inherit.aes = FALSE) +\n  geom_text(data = lead_snps, aes(x = bp_cum, y = neg_log_p, label = paste0(\"chr\", chromosome)),\n            color = INK, size = 2.6, fontface = \"bold\", vjust = -0.9, inherit.aes = FALSE) +\n  scale_color_manual(values = c(odd = IMPRINT_PALETTE[1], even = IMPRINT_PALETTE[3])) +\n  scale_x_continuous(breaks = chr_axis$center, labels = chr_axis$label,\n                      expand = expansion(mult = 0.01)) +\n  scale_y_continuous(expand = expansion(mult = c(0, 0.12))) +\n  labs(\n    title = \"manhattan-gwas · r · ggplot2 · anyplot.ai\",\n    x     = \"Chromosome\",\n    y     = expression(-log[10](italic(p) * \"-value\"))\n  ) +\n  guides(color = \"none\") +\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.x = element_blank(),\n    panel.grid.minor.x = element_blank(),\n    panel.grid.minor.y = element_blank(),\n    panel.grid.major.y = element_line(color = INK, linewidth = 0.2),\n    axis.title        = element_text(color = INK, size = 10),\n    axis.text.y       = element_text(color = INK_SOFT, size = 8),\n    axis.text.x       = element_text(color = INK_SOFT, size = 6.5),\n    plot.title        = element_text(color = INK, size = 12)\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"}