{"spec_id":"curve-oc","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' curve-oc: Operating Characteristic (OC) Curve\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Created: 2026-06-20\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\"\n\nIMPRINT_PALETTE <- c(\n  \"#009E73\",  # 1 - brand green (first series, always)\n  \"#C475FD\",  # 2 - lavender\n  \"#4467A3\",  # 3 - blue\n  \"#BD8233\",  # 4 - ochre\n  \"#AE3030\",  # 5 - matte red (semantic anchor: bad/loss/error)\n  \"#2ABCCD\",  # 6 - cyan\n  \"#954477\",  # 7 - rose\n  \"#99B314\"   # 8 - lime\n)\n\n# Data: OC curves via binomial CDF — P(accept) = P(X <= c) where X ~ Bin(n, p)\np_seq <- seq(0, 0.15, length.out = 200)\n\nplans <- list(\n  list(n = 50,  c = 1, label = \"n=50, c=1\"),\n  list(n = 100, c = 2, label = \"n=100, c=2\"),\n  list(n = 150, c = 3, label = \"n=150, c=3\")\n)\n\ndf_list <- lapply(plans, function(plan) {\n  data.frame(\n    fraction_defective     = p_seq,\n    probability_acceptance = pbinom(plan$c, size = plan$n, prob = p_seq),\n    sampling_plan          = plan$label,\n    stringsAsFactors       = FALSE\n  )\n})\n\ndf <- do.call(rbind, df_list)\ndf$sampling_plan <- factor(df$sampling_plan, levels = sapply(plans, `[[`, \"label\"))\n\n# AQL and LTPD reference values\nAQL  <- 0.02   # acceptable quality level\nLTPD <- 0.08   # lot tolerance percent defective\n\n# Risk points on the n=100, c=2 reference plan\nref_n <- plans[[2]]$n\nref_c <- plans[[2]]$c\npa_aql  <- pbinom(ref_c, size = ref_n, prob = AQL)\npa_ltpd <- pbinom(ref_c, size = ref_n, prob = LTPD)\n\nrisk_df <- data.frame(\n  fraction_defective     = c(AQL,    LTPD),\n  probability_acceptance = c(pa_aql, pa_ltpd)\n)\n\nplan_colors <- IMPRINT_PALETTE[1:3]\n\ntitle_text <- \"curve-oc · r · ggplot2 · anyplot.ai\"\n\n# Plot\np <- ggplot(df, aes(x = fraction_defective, y = probability_acceptance,\n                    color = sampling_plan)) +\n  # Shaded risk zones\n  annotate(\"rect\",\n    xmin = 0,    xmax = AQL,\n    ymin = 0,    ymax = 1,\n    fill = IMPRINT_PALETTE[1], alpha = 0.07\n  ) +\n  annotate(\"rect\",\n    xmin = LTPD, xmax = 0.152,\n    ymin = 0,    ymax = 1,\n    fill = IMPRINT_PALETTE[5], alpha = 0.07\n  ) +\n  # Vertical reference lines\n  geom_vline(xintercept = AQL,  linetype = \"dashed\",\n             color = IMPRINT_PALETTE[1], linewidth = 0.7, alpha = 0.9) +\n  geom_vline(xintercept = LTPD, linetype = \"dashed\",\n             color = IMPRINT_PALETTE[5], linewidth = 0.7, alpha = 0.9) +\n  # OC curves\n  geom_line(linewidth = 1.1) +\n  # Risk marker points on the reference plan\n  geom_point(data = risk_df,\n             aes(x = fraction_defective, y = probability_acceptance),\n             color = INK, size = 3.5, shape = 16, inherit.aes = FALSE) +\n  # AQL label\n  annotate(\"text\",\n    x = AQL + 0.001, y = 0.05,\n    label = \"AQL\\n2%\", hjust = 0,\n    color = IMPRINT_PALETTE[1], size = 3.0, fontface = \"bold\"\n  ) +\n  # LTPD label (y=0.02 to avoid crowding with Pa annotation above)\n  annotate(\"text\",\n    x = LTPD + 0.001, y = 0.02,\n    label = \"LTPD\\n8%\", hjust = 0,\n    color = IMPRINT_PALETTE[5], size = 3.0, fontface = \"bold\"\n  ) +\n  # Producer's risk annotation (Pa at AQL)\n  annotate(\"text\",\n    x = AQL - 0.001, y = pa_aql + 0.05,\n    label = sprintf(\"Pa = %.0f%%\", pa_aql * 100),\n    hjust = 1, color = INK_SOFT, size = 3.2\n  ) +\n  # Consumer's risk annotation (Pa at LTPD)\n  annotate(\"text\",\n    x = LTPD - 0.001, y = pa_ltpd + 0.05,\n    label = sprintf(\"Pa = %.0f%%\", pa_ltpd * 100),\n    hjust = 1, color = INK_SOFT, size = 3.2\n  ) +\n  scale_color_manual(values = plan_colors, name = \"Sampling Plan\") +\n  scale_x_continuous(\n    labels = scales::percent_format(accuracy = 1),\n    limits = c(0, 0.152),\n    expand = c(0, 0)\n  ) +\n  scale_y_continuous(\n    labels = scales::percent_format(accuracy = 1),\n    limits = c(0, 1.02),\n    expand = c(0, 0)\n  ) +\n  labs(\n    title = title_text,\n    x     = \"Fraction Defective (p)\",\n    y     = \"Probability of Acceptance  Pa(p)\"\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 = scales::alpha(INK, 0.12), linewidth = 0.4),\n    panel.grid.minor  = element_blank(),\n    panel.border      = element_blank(),\n    axis.line         = element_line(color = INK_SOFT, linewidth = 0.5),\n    axis.ticks        = element_line(color = INK_SOFT, linewidth = 0.4),\n    axis.title        = element_text(color = INK,      size = 10),\n    axis.text         = element_text(color = INK_SOFT, size = 8),\n    plot.title        = element_text(color = INK,      size = 12, face = \"plain\",\n                                     margin = margin(b = 8)),\n    legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT,\n                                     linewidth = 0.4),\n    legend.text       = element_text(color = INK_SOFT, size = 8),\n    legend.title      = element_text(color = INK,      size = 10),\n    legend.position   = \"right\",\n    legend.margin     = margin(6, 8, 6, 8),\n    plot.margin       = margin(14, 16, 12, 12)\n  )\n\n# Save (landscape: 3200 x 1800 px)\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"}