{"spec_id":"histogram-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' histogram-basic: Basic Histogram\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 88/100 | Created: 2026-05-28\n\nlibrary(ggplot2)\nlibrary(scales)\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\")\n\n# Data: Employee annual salaries — right-skewed distribution\nn <- 400\nsalaries <- c(\n  rnorm(round(n * 0.70), mean = 65000,  sd = 15000),\n  rnorm(round(n * 0.20), mean = 105000, sd = 20000),\n  rnorm(round(n * 0.10), mean = 175000, sd = 30000)\n)\nsalaries <- pmax(salaries, 28000)\ndf <- data.frame(salary = salaries)\nmed_salary <- median(df$salary)\n\n# Density line scaled to histogram count — ggplot2 idiomatic custom data layer\n# binwidth × n × density = expected count per bin\nn_bins   <- 35\nbin_w    <- diff(range(df$salary)) / n_bins\ndens_obj <- density(df$salary)\ndens_df  <- data.frame(\n  x = dens_obj$x,\n  y = dens_obj$y * nrow(df) * bin_w\n)\n\n# Plot\np <- ggplot(df, aes(x = salary)) +\n  geom_histogram(\n    bins      = n_bins,\n    fill      = IMPRINT_PALETTE[1],\n    color     = PAGE_BG,\n    linewidth = 0.3\n  ) +\n  # Smooth density envelope scaled to count — distinctive ggplot2 multi-layer idiom\n  geom_line(\n    data      = dens_df,\n    aes(x = x, y = y),\n    color     = INK_SOFT,\n    linewidth = 0.8,\n    linetype  = \"dashed\"\n  ) +\n  # Median reference line as visual focal point\n  geom_vline(\n    xintercept = med_salary,\n    color      = IMPRINT_PALETTE[4],\n    linewidth  = 0.9\n  ) +\n  annotate(\n    \"text\",\n    x     = med_salary,\n    y     = Inf,\n    label = sprintf(\"Median: $%.0fk\", med_salary / 1000),\n    color = IMPRINT_PALETTE[4],\n    hjust = -0.1,\n    vjust = 1.8,\n    size  = 3\n  ) +\n  scale_x_continuous(\n    labels = label_dollar(scale = 1e-3, suffix = \"k\"),\n    expand = expansion(mult = c(0.01, 0.02))\n  ) +\n  scale_y_continuous(expand = expansion(mult = c(0, 0.12))) +\n  labs(\n    title    = \"histogram-basic · r · ggplot2 · anyplot.ai\",\n    subtitle = \"Right-skewed: majority earn $40k–$90k, with a long executive tail\",\n    x        = \"Annual Salary\",\n    y        = \"Number of Employees\"\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.x = element_blank(),\n    panel.grid.major.y = element_line(color = INK_SOFT, linewidth = 0.2),\n    panel.grid.minor   = 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    axis.ticks         = element_blank(),\n    plot.title         = element_text(color = INK,      size = 12, face = \"bold\",\n                                      margin = margin(b = 4)),\n    plot.subtitle      = element_text(color = INK_SOFT, size = 9,\n                                      margin = margin(b = 10)),\n    plot.margin        = margin(20, 25, 15, 15)\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"}