diff --git a/validation/CASE_SELECTION.md b/validation/CASE_SELECTION.md index 9c5572e..85a141f 100644 --- a/validation/CASE_SELECTION.md +++ b/validation/CASE_SELECTION.md @@ -1,6 +1,6 @@ # Scale-example selection and interpretation -The scale examples are selected to make the two released dimensions substantively interpretable. They are illustrative reference cases rather than a representative sample or an independent validation of the posterior coordinates. Published party-history research supports the qualitative interpretation of the cases, not their exact estimated values. +The scale examples are selected to make the two released dimensions substantively interpretable without overloading the display. They are illustrative trajectories rather than a representative sample or an independent validation of the posterior coordinates. Published party-history research supports the qualitative interpretation of the cases, not their exact estimated values. ## Trajectory figure @@ -12,15 +12,4 @@ The connected paths show recognizable changes across countries and political tra All election-year posterior means in these windows form the connected paths. The labelled endpoints include 95% latent-position credible intervals. The manuscript uses published historical research to interpret the broad movements without treating the estimates as causal evidence. -## Two-dimensional landmarks - -The standalone anchors cover low, middle and high regions of both scales and include cross-pressured combinations: - -- German The Left: 2021 (PartyFacts 1545). -- German Christian Democratic Union: 2021 (PartyFacts 1375). -- Polish Law and Justice: 2019 (PartyFacts 1565). -- UK Conservative Party: 1979 (PartyFacts 1567). -- French National Front: 2022 (PartyFacts 433). -- US Republican Party: 2020 (PartyFacts 809). - -Every anchor must match the requested party-year exactly. Horizontal and vertical bars show 95% latent-position credible intervals. The machine-readable plotting table retains every path observation and anchor. +The figure deliberately omits standalone party-year anchors. Colour-blind-safe colours distinguish the three paths, while redundant line types and point shapes preserve legibility in monochrome reproduction. Horizontal and vertical bars show 95% latent-position credible intervals at each labelled endpoint. The machine-readable plotting table retains every path observation. diff --git a/validation/EVIDENCE_MANIFEST.csv b/validation/EVIDENCE_MANIFEST.csv index e383c09..d8ab387 100644 --- a/validation/EVIDENCE_MANIFEST.csv +++ b/validation/EVIDENCE_MANIFEST.csv @@ -1,6 +1,6 @@ id,topic,input,output,status 1,country coverage,release v0 election-year panel,metadata/country_coverage_v0.csv,complete -2,historical scale anchors and trajectories,release v0 election-year panel,validation/figures/party_scale_examples.pdf,complete +2,historical party trajectories,release v0 election-year panel,validation/figures/party_scale_examples.pdf,complete 4,research workflow,documented source and release workflow,validation/figures/research_workflow.pdf,complete 5,party-blocked validation,guarded blocked fit with 82 parties and completed post-estimation extraction,validation/outputs/blocked_validation_summary.csv,complete_with_convergence_limitation 6,predictive coverage,production posterior run run_2026-06-12_09-34-03,validation/outputs/ppc/posterior_predictive_by_dimension_item.csv,complete diff --git a/validation/README.md b/validation/README.md index 405afd7..9c1bbae 100644 --- a/validation/README.md +++ b/validation/README.md @@ -20,7 +20,7 @@ The validation fit retains the production warmup length and four-chain design bu ## Reproduction Entry Points -- `plot_scale_examples.R`: historically anchored party paths and scale-reference cases generated from the released election-year panel. +- `plot_scale_examples.R`: three historically interpretable party paths generated from the released election-year panel. - `run_postprocessing.R`: country coverage, scale examples, V-Party sensitivity and partially pooled source-support diagnostics. - `validate_uncertainty.jl`: production-chain predictive coverage, calibration and source/item/country/decade breakdowns. - `prepare_blocked_validation.jl`: deterministic party-blocked train/test construction. diff --git a/validation/figures/party_scale_examples.pdf b/validation/figures/party_scale_examples.pdf index 5bd702a..18c9517 100644 Binary files a/validation/figures/party_scale_examples.pdf and b/validation/figures/party_scale_examples.pdf differ diff --git a/validation/outputs/party_scale_example_plot_data.csv b/validation/outputs/party_scale_example_plot_data.csv index 40a0848..15e59d5 100644 --- a/validation/outputs/party_scale_example_plot_data.csv +++ b/validation/outputs/party_scale_example_plot_data.csv @@ -50,9 +50,3 @@ 432,"Democratic Party","US",2014,"US Democrats","trajectory",FALSE,"",0.462867505375,0.326761075,0.602355425,0.389843067375,0.309037225,0.471238225,"both_direct_or_nearby" 432,"Democratic Party","US",2016,"US Democrats","trajectory",FALSE,"",0.388854405625,0.2650008,0.511855575,0.371516706875,0.296737875,0.446610025,"both_direct_or_nearby" 432,"Democratic Party","US",2020,"US Democrats","trajectory",TRUE,"US Democrats 2020",0.299892256625,0.1963003,0.410606025,0.3345769065,0.263694925,0.405684575,"both_direct_or_nearby" -1545,"The Left","DE",2021,"Standalone anchors","anchor",TRUE,"The Left 2021",0.05463340125125,0.0205853625,0.104672875,0.2266517688,0.13204575,0.336095325,"both_direct_or_nearby" -1375,"Christian Democratic Union","DE",2021,"Standalone anchors","anchor",TRUE,"CDU 2021",0.57714137175,0.45681705,0.69505505,0.536185121875,0.433751725,0.636189175,"text_only_direct_or_nearby" -1565,"Law and Justice","PL",2019,"Standalone anchors","anchor",TRUE,"PiS 2019",0.230162367175,0.159245575,0.300962375,0.841737028125,0.78973335,0.891444425,"both_direct_or_nearby" -1567,"Conservative Party","GB",1979,"Standalone anchors","anchor",TRUE,"UK Conservatives 1979",0.863733304625,0.78370345,0.932120225,0.58421918175,0.47261405,0.691922375,"both_direct_or_nearby" -433,"National Front","FR",2022,"Standalone anchors","anchor",TRUE,"French National Front 2022",0.524320955125,0.398589175,0.645052275,0.829604491,0.73907015,0.911430725,"both_direct_or_nearby" -809,"Republican Party","US",2020,"Standalone anchors","anchor",TRUE,"US Republicans 2020",0.8507495345,0.77742365,0.917357075,0.721311676375,0.65728435,0.78442835,"both_direct_or_nearby" diff --git a/validation/plot_scale_examples.R b/validation/plot_scale_examples.R index 1e93e5b..968b1f9 100644 --- a/validation/plot_scale_examples.R +++ b/validation/plot_scale_examples.R @@ -49,31 +49,13 @@ trajectory_rows <- lapply(seq_len(nrow(trajectory_spec)), function(i) { }) trajectories <- do.call(rbind, trajectory_rows) -anchor_spec <- data.frame( - party_id = c(1545, 1375, 1565, 1567, 433, 809), - year = c(2021, 2021, 2019, 1979, 2022, 2020), - label = c( - "The Left 2021", "CDU 2021", "PiS 2019", - "UK Conservatives 1979", "French National Front 2022", - "US Republicans 2020" - ) -) - -anchors <- merge(anchor_spec, panel, by = c("party_id", "year"), all.x = TRUE, sort = FALSE) -if (anyNA(anchors$economic_lr) || anyNA(anchors$galtan)) { - stop("One or more declared scale anchors are absent from the release panel") -} -anchors$series <- "Standalone anchors" -anchors$display_role <- "anchor" -anchors$is_labelled <- TRUE - plot_columns <- c( "party_id", "party_name_english", "country", "year", "series", "display_role", "is_labelled", "label", "economic_lr", "economic_lr_q025", "economic_lr_q975", "galtan", "galtan_q025", "galtan_q975", "source_support_class" ) -plot_data <- rbind(trajectories[plot_columns], anchors[plot_columns]) +plot_data <- trajectories[plot_columns] write.csv( plot_data, file.path(output_dir, "party_scale_example_plot_data.csv"), @@ -86,21 +68,15 @@ label_positions <- data.frame( label = c( "Fidesz 1990", "Fidesz 2022", "Danish Social Democrats 2007", "Danish Social Democrats 2019", - "US Democrats 1944", "US Democrats 2020", - "The Left 2021", "CDU 2021", "PiS 2019", - "UK Conservatives 1979", "French National Front 2022", - "US Republicans 2020" + "US Democrats 1944", "US Democrats 2020" ), plot_label = c( "Fidesz\n1990", "Fidesz\n2022", "Danish Social Democrats\n2007", "Danish Social Democrats\n2019", - "US Democrats\n1944", "US Democrats\n2020", - "The Left\n2021", "CDU\n2021", "PiS\n2019", - "UK Conservatives\n1979", "French National Front\n2022", - "US Republicans\n2020" + "US Democrats\n1944", "US Democrats\n2020" ), - label_x = c(.84, .36, .12, .33, .73, .36, .11, .65, .14, .84, .58, .88), - label_y = c(.36, .93, .32, .49, .33, .27, .16, .48, .93, .51, .91, .78) + label_x = c(.84, .36, .12, .33, .73, .36), + label_y = c(.36, .93, .32, .49, .33, .27) ) labelled <- merge(labelled, label_positions, by = "label", all.x = TRUE, sort = FALSE) if (anyNA(labelled$label_x) || anyNA(labelled$label_y)) { @@ -113,39 +89,34 @@ trajectories$series <- factor(trajectories$series, levels = trajectory_levels) p <- ggplot() + geom_path( data = trajectories, - aes(x = economic_lr, y = galtan, group = series, linetype = series), - colour = "grey25", linewidth = .7, + aes(x = economic_lr, y = galtan, group = series, colour = series, linetype = series), + linewidth = .85, arrow = grid::arrow(type = "closed", length = grid::unit(.065, "inches")) ) + geom_point( data = trajectories, - aes(x = economic_lr, y = galtan), - colour = "grey35", size = 1.1 + aes(x = economic_lr, y = galtan, colour = series, shape = series), + size = 1.4 ) + geom_errorbar( data = labelled, - aes(x = economic_lr, ymin = galtan_q025, ymax = galtan_q975), - width = 0, colour = "grey45", alpha = .65 + aes(x = economic_lr, ymin = galtan_q025, ymax = galtan_q975, colour = series), + width = 0, alpha = .75 ) + geom_errorbar( data = labelled, - aes(y = galtan, xmin = economic_lr_q025, xmax = economic_lr_q975), - orientation = "y", width = 0, colour = "grey45", alpha = .65 + aes(y = galtan, xmin = economic_lr_q025, xmax = economic_lr_q975, colour = series), + orientation = "y", width = 0, alpha = .75 ) + geom_point( - data = anchors, - aes(x = economic_lr, y = galtan), - shape = 21, fill = "white", colour = "black", size = 2.5, stroke = .7 - ) + - geom_point( - data = labelled[labelled$display_role == "trajectory", ], - aes(x = economic_lr, y = galtan), - shape = 16, colour = "black", size = 2.1 + data = labelled, + aes(x = economic_lr, y = galtan, colour = series, shape = series), + size = 2.4 ) + geom_segment( data = labelled, - aes(x = economic_lr, y = galtan, xend = label_x, yend = label_y), - colour = "grey55", linewidth = .25 + aes(x = economic_lr, y = galtan, xend = label_x, yend = label_y, colour = series), + linewidth = .3, show.legend = FALSE ) + geom_text( data = labelled, @@ -155,12 +126,20 @@ p <- ggplot() + scale_linetype_manual( values = c("Fidesz" = "solid", "Danish Social Democrats" = "dashed", "US Democrats" = "dotdash") ) + + scale_colour_manual( + values = c("Fidesz" = "#D55E00", "Danish Social Democrats" = "#009E73", "US Democrats" = "#0072B2") + ) + + scale_shape_manual( + values = c("Fidesz" = 16, "Danish Social Democrats" = 17, "US Democrats" = 15) + ) + scale_x_continuous(limits = c(0, 1), breaks = c(0, .25, .5, .75, 1)) + scale_y_continuous(limits = c(0, 1), breaks = c(0, .25, .5, .75, 1)) + labs( x = "Economic: left (0) to right (1)", y = "Cultural: cosmopolitan (0) to traditionalist (1)", - linetype = "Election-year path" + colour = "Election-year path", + linetype = "Election-year path", + shape = "Election-year path" ) + theme_minimal(base_size = 9) + theme(