{"spec_id":"ks-test-comparison","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' ks-test-comparison: Kolmogorov-Smirnov Plot for Distribution Comparison\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 90/100 | Created: 2026-05-29\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(scales)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens — Imprint palette\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\"\n\nIMPRINT_PALETTE <- c(\n  \"#009E73\",  # 1 — brand green\n  \"#C475FD\",  # 2 — lavender\n  \"#4467A3\",  # 3 — blue\n  \"#BD8233\",  # 4 — ochre\n  \"#AE3030\",  # 5 — matte red\n  \"#2ABCCD\",  # 6 — cyan\n  \"#954477\",  # 7 — rose\n  \"#99B314\"   # 8 — lime\n)\n\n# Data: credit scoring — Good vs Bad customer score distributions\nn_good <- 500\nn_bad  <- 500\n\ngood_scores <- rnorm(n_good, mean = 680, sd = 60)\nbad_scores  <- rnorm(n_bad,  mean = 580, sd = 80)\n\n# Kolmogorov-Smirnov test\nks_result <- ks.test(good_scores, bad_scores)\n\n# Find K-S statistic location — maximum vertical distance between ECDFs\nall_x     <- sort(unique(c(good_scores, bad_scores)))\necdf_good <- ecdf(good_scores)\necdf_bad  <- ecdf(bad_scores)\ngood_vals <- ecdf_good(all_x)\nbad_vals  <- ecdf_bad(all_x)\ndiffs     <- abs(good_vals - bad_vals)\nmax_idx   <- which.max(diffs)\nks_x      <- all_x[max_idx]\nks_y_lo   <- min(good_vals[max_idx], bad_vals[max_idx])\nks_y_hi   <- max(good_vals[max_idx], bad_vals[max_idx])\nks_y_mid  <- (ks_y_lo + ks_y_hi) / 2\n\n# Annotation text\nks_d     <- round(as.numeric(ks_result$statistic), 3)\nks_p_fmt <- formatC(ks_result$p.value, format = \"e\", digits = 2)\nannot    <- paste0(\"D = \", ks_d, \"\\np = \", ks_p_fmt)\n\n# Long-format data frame for stat_ecdf\ndf_scores <- data.frame(\n  score = c(good_scores, bad_scores),\n  group = factor(\n    c(rep(\"Good Customers\", n_good), rep(\"Bad Customers\", n_bad)),\n    levels = c(\"Good Customers\", \"Bad Customers\")\n  )\n)\n\n# K-S segment data frame\nks_seg <- data.frame(x = ks_x, xend = ks_x, y = ks_y_lo, yend = ks_y_hi)\n\n# Plot\np <- ggplot(df_scores, aes(x = score, color = group, linetype = group)) +\n  stat_ecdf(geom = \"step\", linewidth = 1.2, pad = FALSE) +\n  geom_segment(\n    data = ks_seg,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color       = INK,\n    linewidth   = 0.9,\n    linetype    = \"dotdash\",\n    inherit.aes = FALSE\n  ) +\n  annotate(\n    \"label\",\n    x             = ks_x + 22,\n    y             = ks_y_mid,\n    label         = annot,\n    color         = INK,\n    fill          = ELEVATED_BG,\n    size          = 3.0,\n    hjust         = 0,\n    label.padding = unit(0.4, \"lines\"),\n    label.size    = 0.25,\n    label.r       = unit(0.12, \"lines\")\n  ) +\n  scale_color_manual(\n    name   = NULL,\n    values = c(\"Good Customers\" = IMPRINT_PALETTE[1],\n               \"Bad Customers\"  = IMPRINT_PALETTE[2])\n  ) +\n  scale_linetype_manual(\n    name   = NULL,\n    values = c(\"Good Customers\" = \"solid\", \"Bad Customers\" = \"longdash\")\n  ) +\n  scale_y_continuous(\n    labels = percent_format(accuracy = 1),\n    limits = c(0, 1),\n    expand = expansion(mult = c(0.01, 0.03))\n  ) +\n  scale_x_continuous(expand = expansion(mult = c(0.02, 0.06))) +\n  labs(\n    title = \"ks-test-comparison · r · ggplot2 · anyplot.ai\",\n    x     = \"Credit Score\",\n    y     = \"Cumulative Proportion\"\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.2),\n    panel.grid.minor  = element_blank(),\n    panel.border      = element_blank(),\n    axis.title        = element_text(color = INK,        size = 10),\n    axis.text         = element_text(color = INK_SOFT,   size = 8),\n    axis.line         = element_line(color = INK_SOFT,   linewidth = 0.4),\n    plot.title        = element_text(color = INK,        size = 12,\n                                     face = \"bold\",\n                                     margin = margin(b = 10)),\n    plot.margin       = margin(t = 16, r = 24, b = 12, l = 12),\n    legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT,\n                                     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   = \"bottom\",\n    legend.key.width  = unit(1.5, \"cm\")\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"}