#!/usr/bin/env Rscript script_args <- commandArgs(trailingOnly = FALSE) file_arg <- script_args[grepl("^--file=", script_args)][1] script_path <- normalizePath(sub("^--file=", "", file_arg)) repo_root <- normalizePath(file.path(dirname(script_path), "..")) setwd(repo_root) dir.create(file.path("revision", "figures"), recursive = TRUE, showWarnings = FALSE) library(grid) # Draw an overarching section container with a title header draw_section <- function(x, y, width, height, title, bg = "#FAFAFA", border = "#333333") { grid.roundrect(x = unit(x, "npc"), y = unit(y, "npc"), width = unit(width, "npc"), height = unit(height, "npc"), r = unit(.018, "snpc"), gp = gpar(fill = bg, col = border, lwd = .9)) # Header bar header_h <- .040 grid.roundrect(x = unit(x, "npc"), y = unit(y + height/2 - header_h/2, "npc"), width = unit(width, "npc"), height = unit(header_h, "npc"), r = unit(.018, "snpc"), gp = gpar(fill = "#EBEBEB", col = border, lwd = .9)) grid.text(title, x = unit(x - width*0.46, "npc"), y = unit(y + height/2 - header_h/2, "npc"), just = "left", gp = gpar(fontsize = 9, fontface = "bold", col = "#111111")) } # Sub-box draw_box <- function(x, y, width, height, title, text_items = c(), bg = "#FFFFFF", border = "#555555") { grid.roundrect(x = unit(x, "npc"), y = unit(y, "npc"), width = unit(width, "npc"), height = unit(height, "npc"), r = unit(.015, "snpc"), gp = gpar(fill = bg, col = border, lwd = .75)) grid.text(title, x = unit(x, "npc"), y = unit(y + height*0.28, "npc"), gp = gpar(fontsize = 8.2, fontface = "bold", col = "#111111")) if (length(text_items) > 0) { txt <- paste(text_items, collapse = "\n") grid.text(txt, x = unit(x, "npc"), y = unit(y - height*0.18, "npc"), gp = gpar(fontsize = 7.4, fontface = "plain", col = "#333333")) } } arrow <- function(x0, y0, x1, y1) { grid.lines(x = unit(c(x0, x1), "npc"), y = unit(c(y0, y1), "npc"), arrow = grid::arrow(length = unit(.055, "inches"), type = "closed"), gp = gpar(col = "#222222", fill = "#222222", lwd = .9)) } cairo_pdf(file.path("revision", "figures", "research_workflow.pdf"), width = 8.2, height = 7.6, onefile = TRUE) grid.newpage() # =========================================================================== # Area 1: Data Acquisition # =========================================================================== draw_section(.5, .865, .94, .21, "1. Data Acquisition") draw_box(.27, .84, .42, .12, "Text Data Projects", c("• Manifesto platforms", "• Campaign media text")) draw_box(.73, .84, .42, .12, "Expert Survey Projects", c("• CHES, V-Party, POPPA & GPS", "• Morgan historical survey")) arrow(.5, .76, .5, .72) # =========================================================================== # Area 2: Data Harmonization # =========================================================================== draw_section(.5, .62, .94, .18, "2. Data Harmonization") draw_box(.20, .595, .30, .10, "Party Identification", c("• PartyFacts harmonization", "• Alliance mapping")) draw_box(.50, .595, .26, .10, "Item Orientation", c("• Directional polarity", "• Inverted item reversal")) draw_box(.80, .595, .28, .10, "Scale Harmonization", c("• Text item counts", "• Expert scale mapping")) arrow(.5, .53, .5, .49) # =========================================================================== # Area 3: Dynamic Two-Dimensional Modeling # =========================================================================== draw_section(.5, .385, .94, .19, "3. Dynamic Two-Dimensional Modeling", bg = "#F4F4F4", border = "#111111") draw_box(.20, .36, .30, .11, "Latent State Process", c("• Economic & Cultural scales", "• Segment random walks")) draw_box(.50, .36, .26, .11, "Likelihood Functions", c("• Hurdle-binomial text counts", "• Beta-binomial expert means")) draw_box(.80, .36, .28, .11, "Estimation & Anchoring", c("• General left-right mixtures", "• Hamiltonian Monte Carlo")) arrow(.5, .29, .5, .25) # =========================================================================== # Area 4: Data Extraction & Validation # =========================================================================== draw_section(.5, .135, .94, .20, "4. Data Extraction & Validation") draw_box(.20, .11, .30, .12, "Primary Panel Release", c("• 1944–2025 election panel", "• Posterior means & CIs")) draw_box(.50, .11, .26, .12, "Secondary Output", c("• Annual trajectories", "• Composition metadata")) draw_box(.80, .11, .28, .12, "Validation & Diagnostics", c("• Blocked holdout & PPC", "• Ablation & sensitivity")) dev.off() message("Wrote validation/figures/research_workflow.pdf")