{"spec_id":"pictogram-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' pictogram-basic: Pictogram Chart (Isotype Visualization)\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Created: 2026-06-03\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\n# Imprint palette (first series always #009E73)\nIMPRINT_PALETTE <- c(\n  \"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n  \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"\n)\n\n# Data: illustrative fruit production (thousand tons)\nICON_UNIT <- 5  # 1 icon = 5 thousand tons\n\nfruit_data <- data.frame(\n  category = c(\"Apples\", \"Grapes\", \"Oranges\", \"Mangoes\", \"Strawberries\"),\n  value    = c(38, 31, 24, 19, 12),\n  stringsAsFactors = FALSE\n)\n\n# Assign Imprint colors in descending value order (highest → brand green)\nfruit_ordered <- fruit_data[order(fruit_data$value, decreasing = TRUE), ]\ncolor_map <- setNames(\n  IMPRINT_PALETTE[seq_len(nrow(fruit_data))],\n  fruit_ordered$category\n)\n\n# Factor levels: ascending value → bottom-to-top y-axis order\ncat_levels <- fruit_data$category[order(fruit_data$value)]\n\n# Build icon grid: one row per (category x icon slot)\nn_max <- ceiling(max(fruit_data$value) / ICON_UNIT)  # 8 slots\n\nicon_list <- lapply(seq_len(nrow(fruit_data)), function(i) {\n  v    <- fruit_data$value[i]\n  cat  <- fruit_data$category[i]\n  nf   <- floor(v / ICON_UNIT)\n  frac <- (v / ICON_UNIT) - nf\n  hp   <- frac > 0.05\n  n_empty <- n_max - nf - as.integer(hp)\n\n  data.frame(\n    category   = cat,\n    icon_col   = seq_len(n_max),\n    icon_type  = c(rep(\"full\", nf),\n                   if (hp) \"partial\",\n                   rep(\"empty\", n_empty)),\n    fill_alpha = c(rep(1.0, nf),\n                   if (hp) frac,\n                   rep(0.0, n_empty)),\n    stringsAsFactors = FALSE\n  )\n})\nicons <- do.call(rbind, icon_list)\nicons$category <- factor(icons$category, levels = cat_levels)\n\n# Focal emphasis: identify the top category for storytelling\ntop_cat   <- fruit_ordered$category[1]\nratio_str <- sprintf(\"%.1f\", max(fruit_data$value) / min(fruit_data$value))\nsubtitle  <- paste0(top_cat, \" leads at \", max(fruit_data$value), \" kt — \",\n                    ratio_str, \"× the smallest category\")\n\n# Value labels: exact totals positioned right of last icon column\nlabel_df <- data.frame(\n  category = factor(fruit_data$category, levels = cat_levels),\n  x_pos    = n_max + 0.75,\n  label    = paste0(fruit_data$value, \" kt\"),\n  fontface = ifelse(fruit_data$category == top_cat, \"bold\", \"plain\"),\n  stringsAsFactors = FALSE\n)\n\n# Plot\np <- ggplot() +\n  # Background rings for all icon slots\n  geom_point(\n    data  = icons,\n    aes(x = icon_col, y = category),\n    shape = 1, size = 5, color = INK_MUTED, stroke = 0.6\n  ) +\n  # Full icons\n  geom_point(\n    data  = icons[icons$icon_type == \"full\", ],\n    aes(x = icon_col, y = category, color = category),\n    shape = 19, size = 5\n  ) +\n  # Partial icons: alpha-faded to indicate fractional unit\n  geom_point(\n    data  = icons[icons$icon_type == \"partial\", ],\n    aes(x = icon_col, y = category, color = category, alpha = fill_alpha),\n    shape = 19, size = 5\n  ) +\n  # Exact value labels at row end; top category bolded for focal emphasis\n  geom_text(\n    data  = label_df,\n    aes(x = x_pos, y = category, label = label, fontface = fontface),\n    hjust = 0, color = INK_SOFT, size = 3.5\n  ) +\n  scale_alpha_identity() +\n  scale_color_manual(values = color_map) +\n  scale_x_continuous(\n    limits = c(0.3, n_max + 2.1),\n    expand = expansion(mult = 0)\n  ) +\n  labs(\n    title    = \"pictogram-basic · r · ggplot2 · anyplot.ai\",\n    subtitle = subtitle,\n    caption  = paste0(\"Each ● represents \", ICON_UNIT,\n                      \" thousand tons  |  faded icon = partial unit\")\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       = element_blank(),\n    axis.title       = element_blank(),\n    axis.text.x      = element_blank(),\n    axis.text.y      = element_text(color = INK, size = 10, hjust = 1,\n                                    margin = margin(r = 6)),\n    axis.ticks       = element_blank(),\n    plot.title       = element_text(color = INK, size = 12, hjust = 0, face = \"bold\",\n                                    margin = margin(b = 4)),\n    plot.subtitle    = element_text(color = INK_SOFT, size = 9, hjust = 0,\n                                    margin = margin(b = 14)),\n    plot.caption     = element_text(color = INK_MUTED, size = 8, hjust = 0,\n                                    margin = margin(t = 12)),\n    legend.position  = \"none\",\n    plot.margin      = margin(22, 20, 16, 22, \"pt\")\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"}