{"spec_id":"spc-xbar-r","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' spc-xbar-r: Statistical Process Control Chart (X-bar/R)\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 88/100 | Created: 2026-06-20\n\nlibrary(ggplot2)\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 brand green  — in-control data\n  \"#C475FD\",  # 2 lavender\n  \"#4467A3\",  # 3 blue         — UCL / LCL lines\n  \"#BD8233\",  # 4 ochre        — warning limits (+/-2 sigma)\n  \"#AE3030\",  # 5 matte red    — out-of-control signal\n  \"#2ABCCD\", \"#954477\", \"#99B314\"\n)\n\n# Data: CNC shaft diameter (mm), subgroup size n = 5\nn_obs     <- 5L\nn_samples <- 30L\n\n# Standard SPC constants for n = 5\nA2 <- 0.577\nD3 <- 0.000\nD4 <- 2.114\n\nmeas <- matrix(\n  rnorm(n_samples * n_obs, mean = 25.0, sd = 0.080),\n  nrow = n_samples, ncol = n_obs\n)\n\n# Inject assignable-cause events to demonstrate detection capability\nmeas[8,  ] <- meas[8,  ] + 0.300             # mean shift\nmeas[22, ] <- rnorm(n_obs, mean = 25.0, sd = 0.260)  # elevated range\nmeas[27, ] <- meas[27, ] + 0.285             # mean shift\n\nxbar <- rowMeans(meas)\nrng  <- apply(meas, 1L, function(row) max(row) - min(row))\n\nxbar_bar <- mean(xbar)\nrbar     <- mean(rng)\n\nucl_x <- xbar_bar + A2 * rbar\nlcl_x <- xbar_bar - A2 * rbar\nuwl_x <- xbar_bar + (2.0 / 3.0) * A2 * rbar  # +2 sigma warning\nlwl_x <- xbar_bar - (2.0 / 3.0) * A2 * rbar  # -2 sigma warning\n\nucl_r <- D4 * rbar\nlcl_r <- D3 * rbar  # = 0 for n <= 6\n\nooc_x <- xbar > ucl_x | xbar < lcl_x\nooc_r <- rng  > ucl_r\n\n# Long-format data for faceting\nCHART_X      <- \"X-bar Chart (Shaft Diameter, mm)\"\nCHART_R      <- \"R Chart (Sample Range, mm)\"\nCHART_LEVELS <- c(CHART_X, CHART_R)\n\ndf_all <- data.frame(\n  sample_id = rep(seq_len(n_samples), 2L),\n  value     = c(xbar, rng),\n  status    = factor(\n    ifelse(c(ooc_x, ooc_r), \"Out of control\", \"In control\"),\n    levels = c(\"In control\", \"Out of control\")\n  ),\n  chart = factor(\n    c(rep(CHART_X, n_samples), rep(CHART_R, n_samples)),\n    levels = CHART_LEVELS\n  )\n)\n\n# Reference lines per facet (center line, bounds, warning limits)\nref_lines <- data.frame(\n  chart = factor(c(\n    rep(CHART_X, 5L), rep(CHART_R, 3L)\n  ), levels = CHART_LEVELS),\n  yval = c(xbar_bar, ucl_x, lcl_x, uwl_x, lwl_x,\n            rbar, ucl_r, lcl_r),\n  role = c(\"cl\", \"bound\", \"bound\", \"warn\", \"warn\",\n           \"cl\", \"bound\", \"bound\")\n)\n\n# Right-side text annotations (UCL / +2σ / CL / LCL; skip -2σ — too close to LCL)\nN_RIGHT   <- n_samples + 2L\nX_LIM_MAX <- n_samples + 8L  # reduced from +10 to cut wasted horizontal space\n\nann_data <- data.frame(\n  chart = factor(\n    c(rep(CHART_X, 4L), rep(CHART_R, 2L)),\n    levels = CHART_LEVELS\n  ),\n  x     = N_RIGHT,\n  y     = c(ucl_x, uwl_x, xbar_bar, lcl_x, ucl_r, rbar),\n  label = c(\n    sprintf(\"UCL = %.3f\", ucl_x),\n    \"+2σ\",\n    sprintf(\"CL = %.3f\",  xbar_bar),\n    sprintf(\"LCL = %.3f\", lcl_x),\n    sprintf(\"UCL = %.3f\", ucl_r),\n    sprintf(\"CL = %.3f\",  rbar)\n  )\n)\n\n# OOC callout annotations — label each flagged sample with its number\nooc_x_ids <- which(ooc_x)\nooc_r_ids <- which(ooc_r)\n\nooc_callouts <- data.frame(\n  chart = factor(\n    c(rep(CHART_X, length(ooc_x_ids)), rep(CHART_R, length(ooc_r_ids))),\n    levels = CHART_LEVELS\n  ),\n  sample_id = c(ooc_x_ids, ooc_r_ids),\n  y         = c(xbar[ooc_x_ids], rng[ooc_r_ids]),\n  label     = paste0(\"n=\", c(ooc_x_ids, ooc_r_ids))\n)\n\ntitle_str <- \"spc-xbar-r · r · ggplot2 · anyplot.ai\"\n\np <- ggplot(df_all, aes(x = sample_id, y = value)) +\n  # Center lines — solid neutral ink\n  geom_hline(\n    data      = ref_lines[ref_lines$role == \"cl\", ],\n    aes(yintercept = yval),\n    color     = INK, linewidth = 0.9, linetype = \"solid\"\n  ) +\n  # Control limits UCL / LCL — dashed blue\n  geom_hline(\n    data      = ref_lines[ref_lines$role == \"bound\", ],\n    aes(yintercept = yval),\n    color     = IMPRINT_PALETTE[3], linewidth = 0.7, linetype = \"dashed\"\n  ) +\n  # Warning limits +/-2 sigma — dotted ochre (X-bar chart only)\n  geom_hline(\n    data      = ref_lines[ref_lines$role == \"warn\", ],\n    aes(yintercept = yval),\n    color     = IMPRINT_PALETTE[4], linewidth = 0.5, linetype = \"dotted\"\n  ) +\n  # Data line (always brand green)\n  geom_line(color = IMPRINT_PALETTE[1], linewidth = 0.85, alpha = 0.85) +\n  # Points colored and shaped by control status\n  geom_point(aes(color = status, size = status, shape = status), alpha = 0.92) +\n  scale_color_manual(\n    values = c(\"In control\" = IMPRINT_PALETTE[1], \"Out of control\" = IMPRINT_PALETTE[5]),\n    name   = NULL\n  ) +\n  scale_size_manual(\n    values = c(\"In control\" = 2.0, \"Out of control\" = 3.5),\n    guide  = \"none\"\n  ) +\n  scale_shape_manual(\n    values = c(\"In control\" = 16L, \"Out of control\" = 18L),\n    guide  = \"none\"\n  ) +\n  # OOC callout labels — sample number above each flagged diamond\n  geom_text(\n    data     = ooc_callouts,\n    aes(x = sample_id, y = y, label = label),\n    color    = IMPRINT_PALETTE[5],\n    size     = 2.5,\n    vjust    = -1.4,\n    fontface = \"bold\"\n  ) +\n  # Right-side limit annotations\n  geom_text(\n    data  = ann_data,\n    aes(x = x, y = y, label = label),\n    hjust = 0, size = 2.8, color = INK_SOFT\n  ) +\n  facet_wrap(~ chart, ncol = 1L, scales = \"free_y\") +\n  scale_x_continuous(\n    breaks = seq(5L, n_samples, 5L),\n    limits = c(1L, X_LIM_MAX)\n  ) +\n  scale_y_continuous(expand = expansion(mult = c(0.08, 0.15))) +\n  labs(x = \"Sample Number\", y = NULL, title = title_str) +\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_MUTED, linewidth = 0.25),\n    panel.grid.minor  = element_blank(),\n    panel.border      = element_rect(color = INK_SOFT, fill = NA, linewidth = 0.4),\n    strip.background  = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.4),\n    strip.text        = element_text(color = INK, size = 9, face = \"bold\",\n                                     margin = margin(t = 4, b = 4)),\n    axis.title.x      = element_text(color = INK, size = 10),\n    axis.text         = element_text(color = INK_SOFT, size = 8),\n    legend.position   = \"bottom\",\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.key        = element_rect(fill = NA),\n    plot.title        = element_text(color = INK, size = 12, face = \"plain\"),\n    plot.margin       = margin(t = 14, r = 6, b = 10, l = 14)\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"}