How to read this file

This is a replication file. It contains every line of R code needed to reproduce the tables and figures of Chapter 12, together with an explanation of what each line does. The code is exactly the code we ran for the book; nothing has been simplified.

Chapter 12 reviews the literature on how electoral systems shape cameral rules and legislative procedures, focusing on the relationship between electoral system incentives and the number of standing committees in a legislature, and between those incentives and the assignment of legislators to committees of different sorts. We find support for the idea that where MPs are elected under systems with high Average Personalism (AP), chambers are likely to have relatively many committees. In a second empirical section, we find that where AP varies across districts within a system, MPs chosen in higher-AP districts are more likely to be assigned to “pork barrel” or distributive committees.

Two questions, two units of analysis, two halves of the file.

The first half asks about chambers: does a legislature elected under high-AP rules create more standing committees? One observation per country-election. This produces Figure 12.1, Table 12.1 and Figure 12.2.

The second half asks about legislators: within a single chamber, are MPs elected from higher-AP districts more likely to sit on distributive committees? One observation per legislator. This produces Table 12.2 and Figure 12.3.

The second question needs district-level committee assignment data, which exists for only five countries — Germany, Japan, New Zealand, Bolivia and Portugal. That is why five long, near-identical cleaning blocks sit in the middle of this file: each hand-harmonises one country’s records before they can be stacked together. It is bookkeeping, not analysis, and it is the bulk of the code here.

If you have never used R. Click the grey Code button above any block to show or hide the code. Every line beginning with # is a comment, ignored by R and written purely for you. Conventions used throughout:

  • <- is R’s assignment arrow; x <- 5 stores 5 under the name x.
  • list() holds several objects at once; mylist[[3]] retrieves the third.
  • for (i in 1:5) { ... } repeats the braced instructions five times.
  • df$col pulls the column col out of the data frame df.
  • %>% is the “pipe”: a %>% f() is another way of writing f(a).
  • A model formula like y ~ x reads “y is explained by x”.

Setup

Load order matters here. MASS is loaded after tidyverse, and it defines its own select(), which masks dplyr::select(). Re-loading dplyr on the next line does not undo that — R resolves a name to whichever package was attached most recently, and dplyr was already attached as part of the tidyverse.

That is why dplyr::select(...) appears with its namespace spelled out throughout this file. If you write a bare select() in a new chunk here, you will get MASS’s version and a confusing error.

# Load relevant libraries

library(tidyverse) # bundle of data-handling packages (dplyr, ggplot2, ...)
library(haven) # reads Stata .dta files, preserving value labels
library(MASS) # statistical functions; NOTE it masks dplyr::select()
library(dplyr) # re-attached here, but see the note above on masking
library(sjPlot) # model plots and tables
library(lme4) # multilevel / mixed-effects models
library(gbm) # gradient boosting machines: supply the AP and TDE predictions
library(gridExtra) # arranges several plot panels into one figure
library(stargazer) # formats regression results into publication tables

Introduction

This chapter investigates the intraparty consequences of personalistic electoral systems by focusing on legislative committee systems and the assignment of legislators to committees. Building on the notion that electoral systems with high Average Personalism (AP) encourage candidates to cultivate individual reputations, we argue that these systems also shape how legislatures organize themselves internally. Specifically, legislatures in high-AP systems tend to establish larger and more numerous committees, creating institutional opportunities for representatives to showcase their personal influence and deliver targeted benefits to their constituencies.

The chapter presents two main empirical analyses. First, we examine the relationship between a legislature’s AP score and the overall size of its committee system, hypothesizing that more personalistic electoral incentives lead to a greater number of committees. Second, we analyze individual-level data on committee assignments, testing the expectation that legislators elected in districts with higher AP scores are more likely to serve on distributive or “pork-barrel” committees that allow them to claim credit for delivering constituency-specific resources.

Our findings provide consistent evidence supporting these hypotheses, demonstrating that electoral incentives influence not only campaign behavior and voting patterns but also the very architecture of legislative institutions.

This file contains calls to datasets used in Chapter 12 (Committee Systems and Assignments), as well as the code necessary to produce all figures and tables in the chapter.

Data processing

In this section, we present the necessary steps for preparing the data used in Chapter 12 of the study, which investigates the relationship between personalistic electoral systems and legislative committee systems and assignments. The data cleaning process ensures that we focus on relevant data, remove any extraneous records, and standardize variables for further analysis.

Paths in this replication package. Every data file is addressed relative to the folder holding this document, as data/shared/... for files several chapters use, or data/ch12/... and so on for files specific to one chapter.

Nothing needs editing. knitr runs each code block with the working directory set to the document’s own folder, which is the root of this repository, so the paths resolve wherever the repository is cloned. There are no setwd() calls and no absolute paths anywhere in these files.

Which file of electoral-system scores this chapter uses

Two versions of the predicted TDE and AP scores exist. This chapter reads the earlier one; chapters 6 to 11 read the later one. That is deliberate, and worth explaining, because the later file is in most respects the better of the two.

How they differ

RealSystems_Scores_GBM (here) ..._GBM_Aug_2024 (ch. 6–11)
elections covered 1,485 1,502
countries 140 142
columns 26 36
period 1945–2015 1945–2015

The August 2024 file is larger on every count. It adds 17 elections and two countries, and carries ten extra columns — the component rules themselves (ballot type, formula, threshold and so on) alongside the scores.

Two qualifications matter, though. It is not simply the older file with more added: one election present in the earlier file is absent from the later one, so a handful of cases went out as well as in. And, more importantly, the scores themselves were revised. For the 1,484 elections common to both, the Total Duvergerian Effect differs by an average of 0.036 and by as much as 1.34. The two files are alternative estimates, not a subset and a superset.

Why this chapter keeps the earlier one

Because it uses these scores directly as the explanatory variables in Table 12.1. Feeding it revised scores moves every coefficient in that table — tested, and reported in the code comments below. The conclusions hold either way, but the printed table would no longer match the book, so the chapter stays on the file the published results were computed from.

Which parts of the chapter this affects

Only the first half. Table 12.1, Figure 12.1 and Figure 12.2 all derive from these scores. Table 12.2 and Figure 12.3 do not — the entire legislator-level analysis is built from the hand-assembled Germany, Japan, New Zealand, Bolivia and Portugal data, whose AP and TDE values are predicted directly from the GBM models further down this file. The choice of scores file has no bearing on them at all.

# Set the path to Chapter 12 materials

# the short file names below. See the note above on why this is repeated.


# The Van Dusky-Allen & Touchton dataset, in Stata format. Supplies the
# chamber-level variables, including numscl -- the number of standing
# committees, which is the outcome for the first half of the chapter.
dta <- read_dta("data/ch12/dta/VanDuskyAllenTouchtonPRQ.dta")

# National GBM
# ---------------------------------------------------------------------------
# Pre-computed country-level TDE and AP scores.
#
# THIS CHAPTER DELIBERATELY USES A DIFFERENT SCORES FILE FROM CHAPTERS 6 TO 11.
#
# Those chapters read RealSystems_Scores_GBM_Aug_2024.RData. This one reads the
# earlier RealSystems_Scores_GBM.RData, and must continue to, because it is the
# file the published Table 12.1 was computed from.
#
# The distinction matters here and not elsewhere because this chapter uses the
# stored scores DIRECTLY as its explanatory variables -- see the merge below,
# where pers.hat1 becomes AP and totalEff.hat1 becomes TDE. The two files carry
# genuinely different scores, so switching moves every coefficient in
# Table 12.1. Tested: the August 2024 file gives AP 1.550 / TDE 0.321 in
# model 1 against the published 1.556 / 0.290. Substantive conclusions are
# unaffected either way, but the printed table would no longer match.
#
# Chapter 11 could be switched safely precisely because it does NOT use the
# stored scores -- it recomputes them from the GBMs.
# ---------------------------------------------------------------------------
load("data/shared/RealSystems_Scores_GBM.RData")

# District GBM
# The fitted models from Chapter 6, used in the second half of the chapter to
# score individual districts rather than whole countries.
load(file = "data/shared/AP_district_objects_t515_d7.RData")
load(file = "data/shared/TDE_district_objects_t305_d15.RData")

This section narrows down the dataset to a subset of variables relevant for analysis. Variables related to the legislative institutions (e.g., bicameral, executive) and electoral systems (numscl, v2psprlnks_osp) are selected. Additionally, country_name is renamed to country for consistency across the dataset.

# Work with a smaller set of variables from VDAT
dta_small <- dta %>%
  dplyr::select(
    year, country_name,
    numscl, v2psprlnks_osp,
    numberlower, eleclower,
    executive, effn, bicameral,
    restrictionappt, logg
  ) %>%
  rename(country = country_name)

dta_small$country <- dplyr::recode(dta_small$country,
  "Korea South" = "South Korea",
  "Slovak Republic" = "Slovakia",
  "United States" = "United States of America"
)

# Merging the two datasets

# ---------------------------------------------------------------------------
# Keep only the columns this chapter needs from the scores file, BEFORE merging.
#
# This is a safeguard rather than a fix for a present problem. drop_na() a few
# lines below removes any row with a missing value in ANY column of the merged
# table -- including columns that play no part in the analysis. The file loaded
# above has no gaps in the columns kept here, so this line changes nothing
# today: the sample is 70 chambers with or without it.
#
# It matters because that guarantee is fragile. A later scores file with an
# extra column carrying missing values would silently delete observations. The
# August 2024 file used by chapters 6 to 11 is exactly such a file -- it adds
# ten columns, three with gaps (`democ`, derived from Polity, has 37), which
# would drop rows on the strength of variables this chapter never reads.
#
# The 14 columns kept are the merge key, the year used for the date filter, the
# five imputed TDE and AP predictions, and their averages. Nothing else in this
# chapter touches the scores file.
# ---------------------------------------------------------------------------
scoreCols <- c(
  "country", "year",
  paste0("totalEff.hat", 1:5),
  paste0("pers.hat", 1:5),
  "totalEff.hat", "pers.hat"
)

dta_merged <- merge(data2export[, scoreCols], dta_small,
  by = "country",
  all.y = T
)

# Remove pre-2005, post-2015 elections
# `year.x` is the election year from the scores file; `year.y` is VDAT's. Only
# `country` and `year` appear in both inputs, so those are the only names the
# merge disambiguates with .x / .y suffixes.
dta_merged <- dta_merged[dta_merged$year.x >= 2005 & dta_merged$year.x <= 2015, ]

# Drop rows with any missing value. See the note above on why the column
# selection has to happen before this line rather than after it.
dta_merged <- dta_merged %>% drop_na()

dta_merged <- dta_merged %>% arrange(country, year.x)

# We want to keep from our dataset the election immediately prior to 2015
# (2015 is the year for which VDAT have data)
# The following snippet drops information from all elections
# except the one immediately prior to 2015
dta_merged$proper_elections <- 0
for (i in 1:(nrow(dta_merged) - 1)) {
  if (dta_merged$country[i] == dta_merged$country[i + 1]) {
    dta_merged$proper_elections[i] <- 1
  }
}
dta_merged <- dta_merged %>%
  dplyr::filter(proper_elections != 1) %>%
  dplyr::select(-proper_elections)

The data processing steps effectively filter and clean the dataset to focus on relevant observations and ensure consistency. By removing unnecessary records (e.g., elections outside 2005-2015), recoding country names, and eliminating missing data, the dataset is prepared for subsequent analysis of legislative committee systems and their connection to personalism in electoral systems. The final dataset contains only the most relevant and clean records, ensuring that the empirical tests are based on a robust and representative sample of legislative chambers and their institutional characteristics.

In the following section, the dataset for Germany is processed by filtering out irrelevant data, selecting only elected members, and merging various sources of data to create a comprehensive dataset that includes the required variables for modeling. Additionally, predictions are made based on optimal Gradient Boosting Machine (GBM) models for both interparty and intraparty effects. The code also handles the creation of key variables, such as the pork variable, which identifies whether a member of parliament (MP) is involved in pork-barrel activities.

Five country blocks, one shared purpose. The next five chunks — Germany, Japan, New Zealand, Bolivia, Portugal — are long and look repetitive, but they are not copies. Each country records its legislators, districts and committee assignments differently, so each needs its own translation into the common format the pooled analysis expects: one row per legislator, with a district identifier, an electoral-rule description the GBM models can score, and a flag for whether the legislator sits on a distributive committee.

You do not need to read all five. Reading the Germany block carefully and skimming the rest is enough to follow the chapter; the differences between them are differences in the source records, not in intent.

This is where a replication most often goes wrong, which is why the work is written out explicitly rather than hidden inside a helper function.

#################
#### Germany ####
#################


# A large majority of MPs (7079) have legislature value 999
# Though about 116 of these have a value as having been elected
# in either a nominal or list district, and even a few appear
# to have some committee assignment, none of these appear to
# be "real" MPs. From now on, we proceed to eliminate them.

ger <- read.csv("data/ch12/csv/germany.csv")

ger.old <- ger
ger <- ger %>%
  dplyr::filter(legislature != 999)

M <- c(unique(ger$magnitude_list), 1)

# Column M guarantees that we are only selecting elected MP
# un-elected candidates have elected_list == 0 and elected_nom == 0
ger <- ger %>%
  mutate(M = if_else(elected_list == 1, magnitude_list, magnitude_nom))

### Get the objects from list

optimalGBMIntra <- totalAP.objects$optimalGBM
optimalGBMInter <- totalEffENP.objects$optimalGBM

# Create ger_real_cases Dataset

ger_real_cases <- rbind(
  data.frame(tballot = 1, ballot_type = "closed", M = M, formula = "saintelague", new.nvotes = "One", pool_level = "party", threshold = 5),
  data.frame(tballot = 0, ballot_type = "closed", M = M, formula = "hare", new.nvotes = "One", pool_level = "party", threshold = 5),
  data.frame(tballot = 2, ballot_type = "closed", M = M, formula = "dhondt", new.nvotes = "One", pool_level = "party", threshold = 5)
)

### Get the objects from list
ger_real_cases$new.nvotes <- factor(ger_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
ger_real_cases$pool_level <- factor(ger_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
ger_real_cases$ballot_type <- factor(ger_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])

# Create ger_real_cases Dataset
ger_real_cases$formula <- factor(ger_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])
ger_real_cases$new.nvotes[ger_real_cases$M == 1] <- "One"
ger_real_cases$ballot_type[ger_real_cases$M == 1] <- "closed"
ger_real_cases$formula[ger_real_cases$M == 1] <- "plurality"

# Reorder the data
ger_real_cases <- ger_real_cases[order(ger_real_cases$ballot_type, ger_real_cases$M), ]

# This loop generates five sets of predictions for each MP using both the interparty (totalEff.hat) and intraparty (pers.hat) GBM models. The predictions are stored in the list gerData for further analysis.
gerData <- list()
for (i in 1:5) {
  # Interparty
  ger_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], ger_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  ger_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], ger_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  gerData[[i]] <- ger_real_cases
}

ger1 <- gerData[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

ger2 <- gerData[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

ger3 <- gerData[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

ger4 <- gerData[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

ger5 <- gerData[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)

# Combine all the predictions
ger_gbms <- ger1 %>%
  full_join(ger2) %>%
  full_join(ger3) %>%
  full_join(ger4) %>%
  full_join(ger5)

# Merge the predictions with the original data
ger <- merge(ger, ger_gbms, by = "M", all = T)

# The code above generated AP and TDE for every MP under all different
# rules in Germany. The 'drop' column below is to drop all observations
# where a given rule was not applicable in an election-year

ger$drop <- ifelse(ger$formula == "plurality" & ger$tballot == 1, 1,
  ifelse(ger$formula == "dhondt" & ger$election_year == 1983, 1,
    ifelse(ger$formula == "hare" & ger$election_year == 1987, 1,
      ifelse(ger$formula == "hare" & ger$election_year == 1998, 1,
        ifelse(ger$formula == "hare" & ger$election_year == 2005, 1,
          ifelse(ger$formula == "saintelague" & ger$election_year == 2009, 1, 0)
        )
      )
    )
  )
)

ger <- ger %>%
  dplyr::filter(drop == 1, ) %>%
  dplyr::select(-drop)

# Generate the pork variable in accordance to Shugart et al.'s book

ger$pork <- ifelse(ger$sedepe10 > 0 | # sedepe11 is missing
  ger$sedepe18 > 0 |
  ger$sedepe19 > 0 |
  ger$sedepe28 > 0 |
  ger$sedepe36 > 0, 1, 0)
unique(with(ger, rowSums(cbind(pork, pork_committee)))) # This should turn 2 and NA
## [1] NA  2
ger$pork <- replace_na(ger$pork, 0)

# Defining CDU and CSU as a single party

ger$party_name <- ifelse(ger$party_name == "CDU" | ger$party_name == "CSU",
  "CDU/CSU", ger$party_name
)

The final dataset, which includes predictions for both AP and TDE, is now ready for further analysis on the relationship between electoral systems, personalism, and legislative behavior.

In this section, we process the data for Japan, both under the mixed system and under the Single Non-Transferable Vote (SNTV) system.

###############################
#### Japan -- mixed system ####
###############################


jpn <- read.csv("data/ch12/csv/japan.csv")

M <- c(unique(jpn$magnitude_list), 1)

# It appears that JPN has a large number of individuals with elected_list=0 and elected_nom=0
jpn <- jpn %>%
  mutate(M = case_when(
    elected_list == 1 ~ magnitude_list,
    elected_nom == 1 ~ magnitude_nom,
    TRUE ~ NA_integer_
  ))

jpn <- jpn[jpn$M >= 1 & !is.na(jpn$M), ]

M <- M[-1]

# Create jpn_real_cases Dataset

jpn_real_cases <- rbind(data.frame(tballot = 2, ballot_type = "closed", M = M, formula = "dhondt", new.nvotes = "One", pool_level = "party", threshold = 0))
jpn_real_cases$new.nvotes <- factor(jpn_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
jpn_real_cases$pool_level <- factor(jpn_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
jpn_real_cases$ballot_type <- factor(jpn_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])
jpn_real_cases$formula <- factor(jpn_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])
jpn_real_cases$new.nvotes[jpn_real_cases$M == 1] <- "One"
jpn_real_cases$ballot_type[jpn_real_cases$M == 1] <- "closed"
jpn_real_cases$formula[jpn_real_cases$M == 1] <- "plurality"

# Reorder the data
jpn_real_cases <- jpn_real_cases[order(jpn_real_cases$ballot_type, jpn_real_cases$M), ]

# In this loop, we generate predictions for interparty and intraparty effects using the pre-trained optimalGBMInter and optimalGBMIntra models. These predictions are stored in jpnData for further analysis.
# Predict Interparty and Intraparty
jpnData <- list()
for (i in 1:5) {
  # Interparty
  jpn_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], jpn_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  jpn_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], jpn_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  jpnData[[i]] <- jpn_real_cases
}


# Combine all the predictions
jpn1 <- jpnData[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

jpn2 <- jpnData[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

jpn3 <- jpnData[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

jpn4 <- jpnData[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

jpn5 <- jpnData[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)

# The five datasets (jpn1 through jpn5) are combined into one large dataset
jpn_gbms <- jpn1 %>%
  full_join(jpn2) %>%
  full_join(jpn3) %>%
  full_join(jpn4) %>%
  full_join(jpn5)

# The jpn dataset is merged with the jpn_gbms dataset
jpn <- merge(jpn, jpn_gbms, by = "M", all = T)

# A pork variable is created based on the presence of certain activities (indicated by columns like sedepe10, sedepe11, etc.). If any of these columns have non-zero values, the MP is considered involved in pork-barrel politics.
jpn$pork <- ifelse(jpn$sedepe10 > 0 | # sedepe28 is missing
  jpn$sedepe11 > 0 |
  jpn$sedepe18 > 0 |
  jpn$sedepe19 > 0 |
  jpn$sedepe36 > 0, 1, 0)

jpn$pork <- replace_na(jpn$pork, 0)

# unique (with (jpn, rowSums (cbind (pork, porkcommittee)))) # not possible because porkcommittee is all NA

#######################
#### Japan -- SNTV ####
#######################


jpn1 <- read.csv("data/ch12/csv/japan.csv")

jpn1 <- jpn1[jpn1$election_year < 1996, ] # Only elections before 1996, under the old system

# To make japan sntv a "different" country from japan MM

jpn1$country <- "Japan1"

M <- 1:6

jpn1$M <- jpn1$elected_nom * jpn1$magnitude_nom

jpn1 <- jpn1[jpn1$M >= 1 & !is.na(jpn1$M), ]

# Create jpn1_real_cases Dataset

jpn1_real_cases <- rbind(data.frame(tballot = 2, ballot_type = "open", M = M, formula = "plurality", new.nvotes = "One", pool_level = "candidate", threshold = 0))
jpn1_real_cases$new.nvotes <- factor(jpn1_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
jpn1_real_cases$pool_level <- factor(jpn1_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
jpn1_real_cases$ballot_type <- factor(jpn1_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])
jpn1_real_cases$formula <- factor(jpn1_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])

# Reorder the data
jpn1_real_cases <- jpn1_real_cases[order(jpn1_real_cases$ballot_type, jpn1_real_cases$M), ]

### We need to run predictions five times
# Predict Interparty and Intraparty
jpn1Data <- list()
for (i in 1:5) {
  # Interparty
  jpn1_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], jpn1_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  jpn1_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], jpn1_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  jpn1Data[[i]] <- jpn1_real_cases
}

jpn11 <- jpn1Data[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

jpn12 <- jpn1Data[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

jpn13 <- jpn1Data[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

jpn14 <- jpn1Data[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

jpn15 <- jpn1Data[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)

# Combine all the predictions
jpn1_gbms <- jpn11 %>%
  full_join(jpn12) %>%
  full_join(jpn13) %>%
  full_join(jpn14) %>%
  full_join(jpn15)

jpn1 <- merge(jpn1, jpn1_gbms, by = "M", all = T)

# The pork variable is created in the same way as for the mixed system, identifying MPs involved in pork-barrel activities.
jpn1$pork <- ifelse(jpn1$sedepe10 > 0 | # sedepe28 missing
  jpn1$sedepe11 > 0 |
  jpn1$sedepe18 > 0 |
  jpn1$sedepe19 > 0 |
  jpn1$sedepe36 > 0, 1, 0)

jpn1$pork <- replace_na(jpn1$pork, 0)

# unique (with (jpn1, rowSums (cbind (pork, porkcommittee))))

Both datasets for Japan, under the mixed system and the SNTV system, are carefully cleaned and prepared for further analysis. The filtering steps ensure that only valid elected MPs are included, while the creation of variables like M and pork allows us to model personalistic behavior. Predictions for interparty and intraparty effects are generated using pre-trained Gradient Boosting Machine models, providing the necessary data for investigating the relationship between electoral systems and legislative behavior. T

The following section deals with data cleaning for New Zealand, processing two distinct electoral systems: the Mixed Member Proportional (MMP) system and the Single Non-Transferable Vote (SNTV) system.

#####################
#### New Zealand ####
#####################


nzl <- read.csv("data/ch12/csv/new_zealand.csv")

# M is created to represent the electoral magnitude based on whether a candidate was elected through a list or nominal system. Non-elected candidates are excluded from the dataset.
M <- c(1, 50, 51, 53, 55)

nzl <- nzl %>%
  mutate(M = case_when(
    elected_list == 1 ~ magnitude_list,
    elected_nom == 1 ~ magnitude_nom,
    TRUE ~ NA_integer_
  ))

nzl <- nzl[nzl$M >= 1 & !is.na(nzl$M), ]

# nzl <- nzl[nzl$election_year==1996 | nzl$election_year==1999 | nzl$election_year==2002 | nzl$election_year==2005 | nzl$election_year==2008,]


# This line ensures that only election years from 1996 onward are included, reflecting the adoption of the MMP system in New Zealand starting in 1996.

nzl <- nzl[nzl$election_year >= 1996, ]

# Create nzl_real_cases Dataset

nzl_real_cases <- rbind(data.frame(data.frame(tballot = 0, ballot_type = "closed", M = M, formula = "saintelague", new.nvotes = "One", pool_level = "party", threshold = 5)))
nzl_real_cases$new.nvotes <- factor(nzl_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
nzl_real_cases$pool_level <- factor(nzl_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
nzl_real_cases$ballot_type <- factor(nzl_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])
nzl_real_cases$formula <- factor(nzl_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])
nzl_real_cases$new.nvotes[nzl_real_cases$M == 1] <- "One"
nzl_real_cases$ballot_type[nzl_real_cases$M == 1] <- "closed"
nzl_real_cases$formula[nzl_real_cases$M == 1] <- "plurality"

# The data is ordered by ballot_type and M to facilitate the subsequent modeling process.
nzl_real_cases <- nzl_real_cases[order(nzl_real_cases$ballot_type, nzl_real_cases$M), ]


# In this loop, we generate predictions for both interparty (totalEff.hat) and intraparty (pers.hat) effects using the pre-trained Gradient Boosting Machine (GBM) models.

nzlData <- list()
for (i in 1:5) {
  # Interparty
  nzl_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], nzl_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  nzl_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], nzl_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  nzlData[[i]] <- nzl_real_cases
}

# The results from the five iterations in nzlData are processed to select relevant columns and rename the prediction columns for easier identification.

nzl1 <- nzlData[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

nzl2 <- nzlData[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

nzl3 <- nzlData[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

nzl4 <- nzlData[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

nzl5 <- nzlData[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)


# The five datasets (nzl1 through nzl5) are combined into a single dataset, nzl_gbms, which contains the predictions for interparty and intraparty effects for each combination of variables.


nzl_gbms <- nzl1 %>%
  full_join(nzl2) %>%
  full_join(nzl3) %>%
  full_join(nzl4) %>%
  full_join(nzl5)

nzl <- merge(nzl, nzl_gbms, by = "M", all = T)

# The pork variable is created to identify MPs involved in pork-barrel activities.

nzl$pork <- ifelse(nzl$sedepe10 > 0 | # sedepe19, 28, 36
  nzl$sedepe11 > 0 |
  nzl$sedepe18 > 0, 1, 0)

nzl$pork <- replace_na(nzl$pork, 0)


# unique (with (nzl, rowSums (cbind (pork, porkcommittee)))) # here, we have values of 1, which means that we are missing something


#####################
#### New Zealand ####
#####################

# The nzl1 dataset is read, and data for elections prior to 1996 is selected, as this reflects the SNTV system before the switch to MMP.

nzl1 <- read.csv("data/ch12/csv/new_zealand.csv")

nzl1 <- nzl1[nzl1$election_year < 1996, ]

nzl1$country <- "New Zealand1"


# A new variable M is created, reflecting the electoral magnitude based on whether candidates were elected through a list or nominal system.

M <- 1

nzl1 <- nzl1 %>%
  mutate(M = case_when(
    elected_list == 1 ~ magnitude_list,
    elected_nom == 1 ~ magnitude_nom,
    TRUE ~ NA_integer_
  ))

nzl1 <- nzl1[nzl1$M >= 1 & !is.na(nzl1$M), ]


# Create nzl1_real_cases Dataset

nzl1_real_cases <- rbind(data.frame(data.frame(tballot = 0, ballot_type = "closed", M = 1, formula = "plurality", new.nvotes = "One", pool_level = "party", threshold = 0)))
nzl1_real_cases$new.nvotes <- factor(nzl1_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
nzl1_real_cases$pool_level <- factor(nzl1_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
nzl1_real_cases$ballot_type <- factor(nzl1_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])
nzl1_real_cases$formula <- factor(nzl1_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])
nzl1_real_cases$new.nvotes[nzl1_real_cases$M == 1] <- "One"
nzl1_real_cases$ballot_type[nzl1_real_cases$M == 1] <- "closed"
nzl1_real_cases$formula[nzl1_real_cases$M == 1] <- "plurality"

# Reorder the data
nzl1_real_cases <- nzl1_real_cases[order(nzl1_real_cases$ballot_type, nzl1_real_cases$M), ]

# Predictions for interparty and intraparty effects are made using the pre-trained models for the SNTV system

nzl1Data <- list()
for (i in 1:5) {
  # Interparty
  nzl1_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], nzl1_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  nzl1_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], nzl1_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  nzl1Data[[i]] <- nzl1_real_cases
}

nzl11 <- nzl1Data[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

nzl12 <- nzl1Data[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

nzl13 <- nzl1Data[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

nzl14 <- nzl1Data[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

nzl15 <- nzl1Data[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)


# Combine all the predictions
nzl1_gbms <- nzl11 %>%
  full_join(nzl12) %>%
  full_join(nzl13) %>%
  full_join(nzl14) %>%
  full_join(nzl15)

nzl1 <- merge(nzl1, nzl1_gbms, by = "M", all = T)

# The results from all five iterations are combined and merged with the original dataset
# The pork variable is created for the SNTV system to track MPs involved in pork-barrel activities.
nzl1$pork <- ifelse(nzl1$sedepe10 > 0 |
  nzl1$sedepe11 > 0 |
  nzl1$sedepe18 > 0, 1, 0)

nzl1$pork <- replace_na(nzl1$pork, 0)

# unique (with (nzl1, rowSums (cbind (pork, porkcommittee)))) # here, we have values of 1, which means that we are missing something

The data cleaning process for New Zealand’s Mixed Member Proportional (MMP) and Single Non-Transferable Vote (SNTV) systems involves filtering out non-elected candidates, generating relevant variables such as M for electoral magnitude, and creating pork variables to track involvement in pork-barrel politics.

In the following section, the dataset for Bolivia is processed to include only elected candidates, removing any non-elected candidates based on the M variable. The data is filtered to include election years from 1997 onwards, reflecting the electoral changes in Bolivia.

#################
#### Bolivia ####
#################

bol <- read.csv("data/ch12/csv/bolivia.csv")

# Create the M variable based on magnitude list
M <- c(unique(bol$magnitude_list), 1)

bol <- bol %>%
  mutate(M = case_when(
    elected_list == 1 ~ magnitude_list,
    elected_nom == 1 ~ magnitude_nom,
    TRUE ~ NA_integer_
  ))

bol <- bol[bol$M >= 1 & !is.na(bol$M), ] # Remove non-elected candidates

# Filter for election years from 1997 onwards
bol <- bol[bol$electionyear >= 1997, ]

# Create the bol_real_cases dataset for various ballot types and formulas

# Set factor levels based on pre-defined model levels
bol_real_cases <- rbind(data.frame(data.frame(tballot = 0, ballot_type = "closed", M = M, formula = "dhondt", new.nvotes = "One", pool_level = "party", threshold = 3)))
bol_real_cases$new.nvotes <- factor(bol_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
bol_real_cases$pool_level <- factor(bol_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
bol_real_cases$ballot_type <- factor(bol_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])

# Adjust values for M==1
bol_real_cases$formula <- factor(bol_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])
bol_real_cases$new.nvotes[bol_real_cases$M == 1] <- "One"
bol_real_cases$ballot_type[bol_real_cases$M == 1] <- "closed"
bol_real_cases$formula[bol_real_cases$M == 1] <- "plurality"

# Reorder the data
bol_real_cases <- bol_real_cases[order(bol_real_cases$ballot_type, bol_real_cases$M), ]

# Loop to predict interparty and intraparty effects five times
bolData <- list()
for (i in 1:5) {
  # Interparty
  bol_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], bol_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  bol_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], bol_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  bolData[[i]] <- bol_real_cases
}


# Select relevant columns for each prediction iteration and rename
bol1 <- bolData[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

bol2 <- bolData[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

bol3 <- bolData[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

bol4 <- bolData[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

bol5 <- bolData[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)

# Combine all predictions into a single dataset
bol_gbms <- bol1 %>%
  full_join(bol2) %>%
  full_join(bol3) %>%
  full_join(bol4) %>%
  full_join(bol5)

# Merge the predictions with the original dataset
bol <- merge(bol, bol_gbms, by = "M", all = T)

# Create the pork variable based on specific columns indicating pork-barrel politics
bol$pork <- ifelse(bol$sedepe10 > 0 | # sedepe11, 19, 36 missing
  bol$sedepe28 > 0 | # sedepe28 exists, but all 0
  bol$sedepe18 > 0, 1, 0)

bol$pork <- replace_na(bol$pork, 0)

# with (bol, rowSums (cbind (pork, porkcommittee)))

###################
#### Bolivia 1 ####
###################

bol1 <- read.csv("data/ch12/csv/bolivia.csv")

# Filter for specific election years
bol1 <- bol1[bol1$electionyear == 1980 | bol1$electionyear == 1985 | bol1$electionyear == 1989, ]

M <- c(unique(bol1$magnitude_list), 1) # Create M variable for Bolivia1 dataset

# Mark Bolivia1 dataset as a separate country for identification
bol1$country <- "Bolivia1"

# Create M variable for Bolivia1 dataset, based on election method
bol1 <- bol1 %>%
  mutate(M = case_when(
    elected_list == 1 ~ magnitude_list,
    elected_nom == 1 ~ magnitude_nom,
    TRUE ~ NA_integer_
  ))

bol1 <- bol1[bol1$M >= 1 & !is.na(bol1$M), ]


# Create bol_real_cases Dataset

bol1_real_cases <- rbind(data.frame(data.frame(tballot = 0, ballot_type = "closed", M = M, formula = "hare", new.nvotes = "One", pool_level = "party", threshold = 0)))
# Set factor levels based on pre-defined model levels
bol1_real_cases$new.nvotes <- factor(bol1_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
bol1_real_cases$pool_level <- factor(bol1_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
bol1_real_cases$ballot_type <- factor(bol1_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])
# Adjust values for M==1
bol1_real_cases$formula <- factor(bol1_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])
bol1_real_cases$new.nvotes[bol1_real_cases$M == 1] <- "One"
bol1_real_cases$ballot_type[bol1_real_cases$M == 1] <- "closed"
bol1_real_cases$formula[bol1_real_cases$M == 1] <- "plurality"

# Reorder the dataset
bol1_real_cases <- bol1_real_cases[order(bol1_real_cases$ballot_type, bol1_real_cases$M), ]

# Loop to predict interparty and intraparty effects five times
bol1Data <- list()
for (i in 1:5) {
  # Interparty
  bol1_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], bol1_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  bol1_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], bol1_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  bol1Data[[i]] <- bol1_real_cases
}

# Process the predictions for each iteration and merge the results
bolA <- bol1Data[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

bolB <- bol1Data[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

bolC <- bol1Data[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

bolD <- bol1Data[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

bolE <- bol1Data[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)


# Combine all predictions
bol1_gbms <- bolA %>%
  full_join(bolB) %>%
  full_join(bolC) %>%
  full_join(bolD) %>%
  full_join(bolE)

# Merge the predictions with the original dataset
bol1 <- merge(bol1, bol1_gbms, by = "M", all = T)

bol1$pork <- ifelse(bol1$sedepe10 > 0 | # sedepe11, 19, 36 missing
  bol1$sedepe28 > 0 |
  bol1$sedepe18 > 0, 1, 0)

# Create the pork variable for Bolivia1
bol1$pork <- replace_na(bol1$pork, 0)

# with (bol1, rowSums (cbind (pork, pork_committee)))

###################
#### Bolivia 2 ####
###################

bol2 <- read.csv("data/ch12/csv/bolivia.csv")

# Filter for the 1993 elections
bol2 <- bol2[bol2$electionyear == 1993, ]

# Create M variable for Bolivia2
M <- c(unique(bol2$magnitude_list), 1)

# Mark Bolivia2 dataset as a separate country for identification
bol2$country <- "Bolivia2"

# Create M variable for Bolivia2 dataset, based on election method
bol2 <- bol2 %>%
  mutate(M = case_when(
    elected_list == 1 ~ magnitude_list,
    elected_nom == 1 ~ magnitude_nom,
    TRUE ~ NA_integer_
  ))

# Filter out non-elected candidates
bol2 <- bol2[bol2$M >= 1 & !is.na(bol2$M), ]


# Create bol_real_cases Dataset

bol2_real_cases <- rbind(data.frame(data.frame(tballot = 0, ballot_type = "closed", M = M, formula = "saintelague", new.nvotes = "One", pool_level = "party", threshold = 0)))
# Set factor levels based on pre-defined model levels
bol2_real_cases$new.nvotes <- factor(bol2_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
bol2_real_cases$pool_level <- factor(bol2_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
bol2_real_cases$ballot_type <- factor(bol2_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])

# Adjust values for M==1
bol2_real_cases$formula <- factor(bol2_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])
bol2_real_cases$new.nvotes[bol2_real_cases$M == 1] <- "One"
bol2_real_cases$ballot_type[bol2_real_cases$M == 1] <- "closed"
bol2_real_cases$formula[bol2_real_cases$M == 1] <- "plurality"

# Reorder the data
bol2_real_cases <- bol2_real_cases[order(bol2_real_cases$ballot_type, bol2_real_cases$M), ]


# Loop to predict interparty and intraparty effects five times
bol2Data <- list()
for (i in 1:5) {
  # Interparty
  bol2_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], bol2_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  bol2_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], bol2_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  bol2Data[[i]] <- bol2_real_cases
}


# Process the predictions for each iteration and merge the results
bola <- bol2Data[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

bolb <- bol2Data[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

bolc <- bol2Data[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

bold <- bol2Data[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

bole <- bol2Data[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)

# Combine all predictions
bol2_gbms <- bola %>%
  full_join(bolb) %>%
  full_join(bolc) %>%
  full_join(bold) %>%
  full_join(bole)

# Merge the predictions with the original dataset
bol2 <- merge(bol2, bol2_gbms, by = "M", all = T)

# Create the pork variable for Bolivia2
bol2$pork <- ifelse(bol2$sedepe10 > 0 | # sedepe11, 19, 36 missing
  bol2$sedepe18 > 0 |
  bol2$sedepe28, 1, 0)

bol2$pork <- replace_na(bol2$pork, 0)

# with (bol2, rowSums (cbind (pork, pork_committee)))

We now turn to the case of Portugal, a country with a closed-list proportional representation system and relatively low intraparty competition.

##################
#### Portugal ####
##################

por <- read.csv("data/ch12/csv/Portugal.csv")

# Extract all unique district magnitudes used in list elections
M <- c(unique(por$magnitude_list))

# Create the M variable (district magnitude), set to zero for non-elected candidates
por$M <- por$elected_list * por$magnitude_list

# Keep only observations with valid (non-missing, positive) magnitude
por <- por[por$M >= 1 & !is.na(por$M), ]

# Create por_real_cases Dataset

por_real_cases <- rbind(data.frame(data.frame(tballot = 0, ballot_type = "closed", M = M, formula = "dhondt", new.nvotes = "One", pool_level = "party", threshold = 0)))

# Format variables as factors with levels matching the trained GBM models
por_real_cases$new.nvotes <- factor(por_real_cases$new.nvotes, levels = optimalGBMIntra[[1]]$var.levels[[3]])
por_real_cases$pool_level <- factor(por_real_cases$pool_level, levels = optimalGBMIntra[[1]]$var.levels[[2]])
por_real_cases$ballot_type <- factor(por_real_cases$ballot_type, levels = optimalGBMIntra[[1]]$var.levels[[1]])
por_real_cases$formula <- factor(por_real_cases$formula, levels = optimalGBMIntra[[1]]$var.levels[[6]])

# Sort cases by ballot type and district magnitude
por_real_cases <- por_real_cases[order(por_real_cases$ballot_type, por_real_cases$M), ]


# Generate predictions from GBM models (both inter- and intraparty) for 5 imputed datasets
porData <- list()
for (i in 1:5) {
  # Interparty
  por_real_cases$totalEff.hat <- predict(optimalGBMInter[[i]], por_real_cases, n.trees = optimalGBMInter[[i]]$n.trees)
  # Intraparty
  por_real_cases$pers.hat <- predict(optimalGBMIntra[[i]], por_real_cases, n.trees = optimalGBMIntra[[i]]$n.trees)
  # Put in a list
  porData[[i]] <- por_real_cases
}

# Extract and rename predictions for each imputation
por1 <- porData[[1]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat1 = totalEff.hat, pers.hat1 = pers.hat)

por2 <- porData[[2]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat2 = totalEff.hat, pers.hat2 = pers.hat)

por3 <- porData[[3]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat3 = totalEff.hat, pers.hat3 = pers.hat)

por4 <- porData[[4]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat4 = totalEff.hat, pers.hat4 = pers.hat)

por5 <- porData[[5]] %>%
  dplyr::select(formula, M, tballot, totalEff.hat, pers.hat, ballot_type) %>%
  rename(totalEff.hat5 = totalEff.hat, pers.hat5 = pers.hat)

# Combine all five sets of predictions into a single dataset
por_gbms <- por1 %>%
  full_join(por2) %>%
  full_join(por3) %>%
  full_join(por4) %>%
  full_join(por5)

# Merge the predictions with the original Portugal dataset by district magnitude (M)
por <- merge(por, por_gbms, by = "M", all = T)

# Create the 'pork' variable: 1 if the MP is assigned to any distributive (pork-barrel) committee
por$pork <- ifelse(por$sedepe10 > 0 | # sedepe19, 36 missing
  por$sedepe11 > 0 |
  por$sedepe18 > 0 |
  por$sedepe28 > 0, 1, 0)


por$pork <- replace_na(por$pork, 0)

# with (por, rowSums (cbind (pork, porkcommittee)))

Before running statistical models, we merge the legislator-level datasets from each country and harmonize their structure. To facilitate accurate inference, we define competitive environments—groupings of legislators operating under similar electoral incentives in the same year and country. These clusters will be used to calculate robust standard errors. Next, we retain only relevant variables from each dataset and standardize variable names across cases. The combined dataset (dta) includes individual-, party-, and country-level information on legislators, their electoral environments, and their assignment to distributive (“pork”) committees. Finally, we compute additional derived variables such as party size and proportion of MPs in pork committees.

# #########################################
# #### Competitive environment clusters ####
# ##########################################
#
# # We cluster standard errors at the "competitive environment" level
# # For our purposes, the "competitive environment" of MPs is the AP-country-year group in which they find themselves
#
# ger$comp.env <-  factor (paste (ger$pers.hat1, ger$country, ger$election_year, sep="-"))
# jpn$comp.env <-  factor (paste (jpn$pers.hat1, jpn$country, jpn$election_year, sep="-"))
# jpn1$comp.env <-  factor (paste (jpn1$pers.hat1, jpn1$country, jpn1$election_year, sep="-"))
# nzl$comp.env <-  factor (paste (nzl$pers.hat1, nzl$country, nzl$election_year, sep="-"))
# bol$comp.env <-  factor (paste (bol$pers.hat1, bol$country, bol$electionyear, sep="-"))
# por$comp.env <-  factor (paste (por$pers.hat1, por$country, por$electionyear, sep="-"))

##########################
#### Merging datasets ####
##########################

# For each country, keep only relevant variables, rename where necessary, and drop rows with missing country info

ger <- ger %>%
  dplyr::select(
    country, election_year, pork, female, incumbent, party_name,
    minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(year = election_year) %>%
  dplyr::filter(is.na(country) == F)

jpn <- jpn %>%
  dplyr::select(
    country, election_year, pork, female, incumbent, party_name,
    minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(year = election_year) %>%
  dplyr::filter(is.na(country) == F)

jpn1 <- jpn1 %>%
  dplyr::select(
    country, election_year, pork, female, incumbent, party_name,
    minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(year = election_year) %>%
  dplyr::filter(is.na(country) == F)

nzl <- nzl %>%
  dplyr::select(
    country, election_year, pork, female, incumbent, party_name,
    minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(year = election_year) %>%
  dplyr::filter(is.na(country) == F)

nzl1 <- nzl1 %>%
  dplyr::select(
    country, election_year, pork, female, incumbent, party_name,
    minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(year = election_year) %>%
  dplyr::filter(is.na(country) == F)

bol <- bol %>%
  dplyr::select(
    country, electionyear, pork, female, incumbent, party, minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(party_name = party, year = electionyear) %>%
  dplyr::filter(is.na(country) == F)

bol1 <- bol1 %>%
  dplyr::select(
    country, electionyear, pork, female, incumbent, party, minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(party_name = party, year = electionyear) %>%
  dplyr::filter(is.na(country) == F)

bol2 <- bol2 %>%
  dplyr::select(
    country, electionyear, pork, female, incumbent, party, minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(party_name = party, year = electionyear) %>%
  dplyr::filter(is.na(country) == F)

por <- por %>%
  dplyr::select(
    country, electionyear, pork, female, incumbent_election, party_name, minister, localassembly,
    totalEff.hat1, totalEff.hat2, totalEff.hat3,
    totalEff.hat4, totalEff.hat5, pers.hat1,
    pers.hat2, pers.hat3, pers.hat4, pers.hat5, ballot_type, M
  ) %>%
  rename(incumbent = incumbent_election, year = electionyear) %>%
  dplyr::filter(is.na(country) == F)

# Merge all datasets into a single object
dta <- rbind(ger, jpn, jpn1, bol, bol1, bol2, por, nzl) # , nzl1

# Handle missing data in key binary variables by setting NA to 0
dta$minister <- replace_na(dta$minister, 0)
dta$localassembly <- replace_na(dta$localassembly, 0)

# Ensure incumbent is binary and not NA
dta$incumbent <- as.numeric(dta$incumbent)
dta$incumbent <- replace_na(dta$incumbent, 0)
dta$incumbent <- as.numeric(ifelse(dta$incumbent > 0, 1, 0))

# Create a count column (1 per observation) to calculate group sizes
dta$count <- 1
dta$year <- as.factor(dta$year)
dta <- dta %>%
  group_by(country, year) %>%
  mutate(total = sum(count))

# Compute total number of MPs per country-year
dta <- dta %>%
  group_by(country, year, party_name) %>%
  mutate(total_party = sum(count))

# Calculate the proportion of seats held by each party
# 'prop' is an individual-level rendition of a legislator's
dta$prop <- dta$total_party / dta$total

# Compute number of MPs per party assigned to a pork-barrel committee
dta <- dta %>%
  group_by(country, year, party_name) %>%
  mutate(total_pork = sum(pork))
# Clean the environment: keep only the merged dataset and previously loaded country-level data
rm(list = setdiff(ls(), c("dta", "dta_merged")))

Figure 12.1: Distribution of Number of Committees

Figure 12.1 provides descriptive evidence about the relationship between the level of personalism in electoral systems and the size of legislative committee systems. The left panel displays the distribution of the number of standing committees across legislatures in the sample. The right panel presents a boxplot of the number of committees across quartiles of predicted Average Personalism (AP), capturing whether more personalistic systems tend to have more extensive committee structures.

# Create a new variable that bins the predicted personalism score (pers.hat) into quartiles
dta_merged$pers.hat.quart <- cut(
  dta_merged$pers.hat,
  c(
    0,
    quantile(dta_merged$pers.hat,
      prob = c(0.25, 0.5, 0.75)
    ),
    max(dta_merged$pers.hat)
  )
)

# Set plotting area for the histogram to occupy the left 40% of the horizontal space
par(fig = c(0, 0.4, 0, 1))

# Create histogram of the number of standing committees (numscl)
hist(dta_merged$numscl,
  breaks = 120, axes = F,
  xlab = "Number of committees",
  xlim = c(0, 100),
  main = ""
)
# Add y-axis ticks
axis(2)
# Add custom x-axis ticks at regular intervals
axis(1, at = c(0, 20, 40, 80, 100), labels = c(0, 20, 40, 80, 100))

# Annotate plot with the mean number of committees
text(x = 65, y = 8, labels = paste0("mean=", round(mean(dta_merged$numscl), 2)), pos = 4, cex = 1.1)

# Annotate plot with the variance of number of committees
text(x = 65, y = 7.7, labels = paste0("var=", round(var(dta_merged$numscl), 2)), pos = 4, cex = 1.1)

# Define new plotting area for the boxplot, occupying the remaining 60% of the space
par(fig = c(.41, 1, 0, 1), new = T)

# Create boxplot of number of committees by personalism quartiles
with(
  dta_merged,
  boxplot(numscl ~ pers.hat.quart,
    xlab = "Average personalism (quartiles)",
    ylab = "Number of committees"
  )
)

Figure 12.1 supports the core claim of Chapter 12 that electoral systems with higher personal vote-seeking incentives—measured by higher Average Personalism (AP)—are associated with larger committee systems. The histogram on the left shows that while most legislatures have between 10 and 30 committees, a few systems have many more, resulting in high variance (201.78), suggesting overdispersion in the outcome variable. The boxplot on the right indicates that the median number of committees increases across AP quartiles, peaking in the third quartile, consistent with the expectation that systems encouraging personal reputations provide institutional tools (like more committees) for individual legislators to signal responsiveness. This pattern offers preliminary, descriptive support for the hypothesis that intraparty electoral incentives shape institutional design, a relationship further tested through regression models later in the chapter.

How to read Figure 12.1. The distribution of numscl, the number of standing committees, across chambers. Descriptive groundwork before any modelling: it shows the outcome is genuinely variable — some legislatures run a handful of committees, others dozens — which is what makes the question worth asking. Note the shape, since a long right tail is what motivates the count models used in Table 12.1 rather than ordinary linear regression.

Table 12.1: Predictors of Number of Legislative Committees

Table 12.1 presents the results of three statistical models estimating the relationship between electoral system incentives—captured by Average Personalism (AP) and Total Duvergerian Effect (TDE)—and the size of legislative committee systems. The table builds on the descriptive evidence from Figure 12.1 by testing whether legislatures with stronger personal vote incentives tend to establish more standing committees. Model 1 includes only AP and TDE. Model 2 adds institutional and structural controls, while Model 3 estimates a negative binomial regression to account for overdispersion in the count outcome.

# Drop extra AP and TDE columns and rename the first set as "AP" and "TDE" for model 1
smallData1 <- dta_merged %>%
  dplyr::select(-c(pers.hat2, pers.hat3, pers.hat4, pers.hat5, totalEff.hat2, totalEff.hat3, totalEff.hat4, totalEff.hat5)) %>%
  rename(AP = pers.hat1, TDE = totalEff.hat1)

# Repeat for the four other imputed datasets, rotating which AP/TDE is kept

# retains pers.hat2 and totalEff.hat2
smallData2 <- dta_merged %>%
  dplyr::select(-c(pers.hat1, pers.hat3, pers.hat4, pers.hat5, totalEff.hat1, totalEff.hat3, totalEff.hat4, totalEff.hat5)) %>%
  rename(AP = pers.hat2, TDE = totalEff.hat2)

# retains pers.hat3 and totalEff.hat3
smallData3 <- dta_merged %>%
  dplyr::select(-c(pers.hat1, pers.hat2, pers.hat4, pers.hat5, totalEff.hat1, totalEff.hat2, totalEff.hat4, totalEff.hat5)) %>%
  rename(AP = pers.hat3, TDE = totalEff.hat3)

# retains pers.hat4 and totalEff.hat4
smallData4 <- dta_merged %>%
  dplyr::select(-c(pers.hat1, pers.hat2, pers.hat3, pers.hat5, totalEff.hat1, totalEff.hat2, totalEff.hat3, totalEff.hat5)) %>%
  rename(AP = pers.hat4, TDE = totalEff.hat4)

# retains pers.hat5 and totalEff.hat5
smallData5 <- dta_merged %>%
  dplyr::select(-c(pers.hat1, pers.hat2, pers.hat3, pers.hat4, totalEff.hat1, totalEff.hat2, totalEff.hat3, totalEff.hat4)) %>%
  rename(AP = pers.hat5, TDE = totalEff.hat5)

# Combine all datasets into a list
allData <- list(smallData1, smallData2, smallData3, smallData4, smallData5)

# Remove the individual objects to clean memory
rm(smallData1, smallData2, smallData3, smallData4, smallData5)


## First model
#  Run Poisson regressions predicting the number of committees from AP and TDE (only)


lm1 <- glm(numscl ~ AP + TDE,
  family = "poisson", data = allData[[1]]
)
lm2 <- glm(numscl ~ AP + TDE,
  family = "poisson", data = allData[[2]]
)
lm3 <- glm(numscl ~ AP + TDE,
  family = "poisson", data = allData[[3]]
)
lm4 <- glm(numscl ~ AP + TDE,
  family = "poisson", data = allData[[4]]
)
lm5 <- glm(numscl ~ AP + TDE,
  family = "poisson", data = allData[[5]]
)


# Combine results using Rubin’s rules (multiple imputation)
list0 <- list(lm1, lm2, lm3, lm4, lm5)
out2 <- summary(mice::pool(list0))

# Replace them in a lm file, we will use a copy of lm3
lm_c1 <- lm3

# Get Betas and SE
se1 <- out2$std.error
for (i in 1:nrow(out2)) {
  lm_c1[["coefficients"]][[i]] <- out2$estimate[i]
}


## Second model
#  Estimate Poisson models adding institutional controls

lm1 <- glm(
  numscl ~ AP + TDE +
    v2psprlnks_osp + numberlower + executive +
    bicameral + restrictionappt + logg,
  family = "poisson", data = allData[[1]]
)
lm2 <- glm(
  numscl ~ AP + TDE +
    v2psprlnks_osp + numberlower + executive +
    bicameral + restrictionappt + logg,
  family = "poisson", data = allData[[2]]
)
lm3 <- glm(
  numscl ~ AP + TDE +
    v2psprlnks_osp + numberlower + executive +
    bicameral + restrictionappt + logg,
  family = "poisson", data = allData[[3]]
)
lm4 <- glm(
  numscl ~ AP + TDE +
    v2psprlnks_osp + numberlower + executive +
    bicameral + restrictionappt + logg,
  family = "poisson", data = allData[[4]]
)
lm5 <- glm(
  numscl ~ AP + TDE +
    v2psprlnks_osp + numberlower + executive +
    bicameral + restrictionappt + logg,
  family = "poisson", data = allData[[5]]
)


# Pool and extract results as above
list0 <- list(lm1, lm2, lm3, lm4, lm5)
out2 <- summary(mice::pool(list0))

# Replace them in a lm file, we will use a copy of lm3
lm_c2 <- lm3

# Get Betas and SE
se2 <- out2$std.error
for (i in 1:nrow(out2)) {
  lm_c2[["coefficients"]][[i]] <- out2$estimate[i]
}

### Negative binomial
# Run Negative Binomial regression to address overdispersion
# Use log(number of legislators) as an offset
#
# IMPORTANT: the code below is reproduced EXACTLY as published, including a
# mistake. `offset(log(numberlower))` is passed after the formula and a comma,
# as a separate argument. That does not create an offset.
#
# R matches named arguments first and then fills the remaining formals in
# order. Since `data` is supplied by name, the expression lands in glm.nb's
# third formal, `weights`. And offset() outside a formula is simply the
# identity function -- it returns its argument unchanged. So these models are
# WEIGHTED by log(number of legislators) rather than offset by it.
#
# It is kept as-is here so that Table 12.1 reproduces the printed table. The
# section immediately after the table sets out what the intended model gives.

lm1 <- glm.nb(
  numscl ~ AP + TDE +
    v2psprlnks_osp + executive +
    bicameral + restrictionappt + logg,
  offset(log(numberlower)),
  data = allData[[1]]
)
lm2 <- glm.nb(
  numscl ~ AP + TDE +
    v2psprlnks_osp + executive +
    bicameral + restrictionappt + logg,
  offset(log(numberlower)),
  data = allData[[2]]
)
lm3 <- glm.nb(
  numscl ~ AP + TDE +
    v2psprlnks_osp + executive +
    bicameral + restrictionappt + logg,
  offset(log(numberlower)),
  data = allData[[3]]
)
lm4 <- glm.nb(
  numscl ~ AP + TDE +
    v2psprlnks_osp + executive +
    bicameral + restrictionappt + logg,
  offset(log(numberlower)),
  data = allData[[4]]
)
lm5 <- glm.nb(
  numscl ~ AP + TDE +
    v2psprlnks_osp + executive +
    bicameral + restrictionappt + logg,
  offset(log(numberlower)),
  data = allData[[5]]
)


# Pool and replace coefficients as before
list0 <- list(lm1, lm2, lm3, lm4, lm5)
out2 <- summary(mice::pool(list0))

# Replace them in a lm file, we will use a copy of lm3
lm_c3 <- lm3

# Get Betas and SE
se3 <- out2$std.error
for (i in 1:nrow(out2)) {
  lm_c3[["coefficients"]][[i]] <- out2$estimate[i]
}


# Generate text output of all three models with stargazer
stargazer(lm_c1, lm_c2, lm_c3,
  model.names = F,
  se = list(se1, se2, se3),
  dep.var.labels = "Number of committees",
  covariate.labels = c(
    "AP", "TDE",
    "Programmatic parties",
    "Number of legislators",
    "Parliamentary regime",
    "Bicameral legislature",
    "Constitutional constraints",
    "GDP per capita (log)"
  ), type = "text"
)
## 
## ==============================================================
##                                    Dependent variable:        
##                            -----------------------------------
##                                   Number of committees        
##                              (1)       (2)          (3)       
## --------------------------------------------------------------
## AP                         1.560***   0.769       1.090**     
##                            (0.512)   (0.509)      (0.450)     
##                                                               
## TDE                        0.290*** 0.161***      0.189***    
##                            (0.048)   (0.057)      (0.052)     
##                                                               
## Programmatic parties                -0.190***    -0.183***    
##                                      (0.035)      (0.032)     
##                                                               
## Number of legislators               0.001***                  
##                                     (0.0002)                  
##                                                               
## Parliamentary regime                -0.271***    -0.251***    
##                                      (0.066)      (0.059)     
##                                                               
## Bicameral legislature               0.304***      0.401***    
##                                      (0.072)      (0.053)     
##                                                               
## Constitutional constraints          -0.178**      -0.122**    
##                                      (0.071)      (0.059)     
##                                                               
## GDP per capita (log)                  0.047       0.093**     
##                                      (0.042)      (0.038)     
##                                                               
## Constant                   2.130*** 2.480***      1.980***    
##                            (0.235)   (0.417)      (0.383)     
##                                                               
## --------------------------------------------------------------
## Observations                  70       70            70       
## Log Likelihood             -393.000 -307.000     -1,318.000   
## theta                                         7.270*** (0.699)
## Akaike Inf. Crit.          792.000   633.000     2,651.000    
## ==============================================================
## Note:                              *p<0.1; **p<0.05; ***p<0.01
# Latex table
# stargazer(lm_c1, lm_c2, lm_c3,
#                      caption = "Predictors of the number of committees in 70 legislatures observed around 2015. Models 1 and 2 are Poisson regression models; Model 3 is a negative binomial model that uses number of legislators (log scale) as an offset.",
#                      label = "T:committee_size",
#                      model.names = F,
#                      se = list(se1, se2, se3),
#                      dep.var.labels = "Number of committees",
#                      covariate.labels = c("AP", "TDE",
#                                        "Programmatic parties",
#                                        "Number of legislators",
#                                        "Parliamentary regime",
#                                        "Bicameral legislature",
#                                        "Constitutional constraints",
#                                        "GDP per capita (log)"),
#                      type="latex")

Table 12.1 shows that higher Average Personalism (AP) scores are associated with larger legislative committee systems, even after accounting for potential confounders and distributional characteristics of the outcome. In Model 1, both AP and TDE are significant and positively associated with the number of committees, suggesting that both personalistic and multiparty environments drive committee proliferation. In Model 2, once controls are added, the AP effect shrinks and becomes insignificant, though other predictors behave as expected: programmatic parties, parliamentary regimes, and constitutional constraints are all negatively associated with the number of committees, while bicameralism and legislative size increase it. Finally, Model 3 addresses overdispersion with a negative binomial specification and shows that AP’s effect rebounds in size and statistical significance. These findings support the chapter’s central claim: legislatures in personalistic systems adopt institutional features—like large committee systems—that help individual legislators enhance their reputations and constituent responsiveness.

How to read Table 12.1. Chamber-level models with the number of standing committees as the outcome. Read the AP row across the columns: a positive coefficient supports the claim that personalistic electoral incentives go with more numerous committees, since more committees mean more chairmanships and more venues in which an individual legislator can be seen to act.

Check TDE as a placebo — the argument here is about intraparty incentives, so the interparty measure should not carry the result. Then watch what happens to AP as the institutional controls enter in later columns: chamber size and bicameralism are obvious rival explanations for committee counts, and the coefficient surviving them is what makes the finding interesting.

Corrigendum: the offset in Model 3

An error in our code, found after the book was published

Model 3 of Table 12.1 does not estimate the model we described. We discovered this while preparing these replication files, after the book had gone to press. The table above reproduces what was published; this section sets out what the intended model gives. Both are shown so that readers can judge for themselves.

What we meant to do. Legislatures differ enormously in size, and a chamber of 600 members will tend to have more committees than one of 60 simply because there are more members to staff them. We therefore wanted to model the number of committees relative to chamber size — a rate, committees per legislator. In a count model that is done with an offset: a term whose coefficient is fixed at 1 rather than estimated, which turns the outcome from a count into a rate.

What the code actually did. The offset term was written outside the model formula, as a separate argument:

glm.nb(numscl ~ AP + TDE + ... , offset(log(numberlower)), data = allData[[i]])
                                 ^ outside the formula

R assigns unnamed arguments to whichever slots remain once the named ones are taken. Because data was named, the offset expression fell into the next available slot, which is weights. And outside a formula, offset() does nothing at all — it simply hands back the value it was given. The result is a model in which each legislature is weighted by the log of its size, so that larger chambers pull harder on the estimates, while the outcome remains a raw count.

Weighting and offsetting are different operations answering different questions. The published Model 3 therefore describes committee counts, with large chambers given more influence, rather than committee rates as the chapter’s text implies.

What changes. Models 1 and 2 are Poisson models with no offset and are entirely unaffected; they stand exactly as published. Model 3 changes, and the table below reports it three ways.

The table below sets the model as published beside two alternatives: the specification we intended, and one that leaves chamber size out altogether.

# ---------------------------------------------------------------------------
# Three versions of the Model 3 negative binomial, fitted to the same five
# datasets and pooled the same way. They differ only in how chamber size is
# handled:
#
#   (1) AS PUBLISHED   offset() outside the formula -> read as WEIGHTS.
#                      Chambers weighted by log(size); outcome stays a count.
#   (2) CORRECTED      offset() inside the formula -> a genuine offset.
#                      Outcome becomes a rate: committees per legislator.
#   (3) NO ADJUSTMENT  chamber size left out entirely; outcome is a raw count.
#
# Column (1) reproduces column 3 of Table 12.1 exactly.
# ---------------------------------------------------------------------------

# Fit one specification across the five datasets and pool with Rubin's rules,
# writing the pooled coefficients into a fitted object so stargazer can display
# them -- the same device used for Table 12.1.
pool_nb <- function(fitfun) {
  fits <- lapply(allData, fitfun)
  out <- summary(mice::pool(fits))
  obj <- fits[[3]]
  for (i in 1:nrow(out)) obj[["coefficients"]][[i]] <- out$estimate[i]
  list(model = obj, se = out$std.error)
}

# (1) As published: offset() sits outside the formula, so it becomes `weights`
nb_published <- pool_nb(function(d) {
  glm.nb(
    numscl ~ AP + TDE + v2psprlnks_osp + executive +
      bicameral + restrictionappt + logg,
    offset(log(numberlower)),
    data = d
  )
})

# (2) Corrected: offset() inside the formula, so it is a genuine offset
nb_offset <- pool_nb(function(d) {
  glm.nb(numscl ~ AP + TDE + v2psprlnks_osp + executive +
    bicameral + restrictionappt + logg +
    offset(log(numberlower)), data = d)
})

# (3) No adjustment for chamber size at all
nb_plain <- pool_nb(function(d) {
  glm.nb(numscl ~ AP + TDE + v2psprlnks_osp + executive +
    bicameral + restrictionappt + logg, data = d)
})

stargazer(nb_published$model, nb_offset$model, nb_plain$model,
  model.names = FALSE,
  se = list(nb_published$se, nb_offset$se, nb_plain$se),
  dep.var.labels = "Number of committees",
  column.labels = c("As published", "Corrected offset", "No adjustment"),
  covariate.labels = c(
    "AP", "TDE",
    "Programmatic parties",
    "Parliamentary regime",
    "Bicameral legislature",
    "Constitutional constraints",
    "GDP per capita (log)"
  ),
  type = "text"
)
## 
## =============================================================================
##                                           Dependent variable:                
##                            --------------------------------------------------
##                                           Number of committees               
##                              As published   Corrected offset  No adjustment  
##                                  (1)              (2)              (3)       
## -----------------------------------------------------------------------------
## AP                             1.090**           0.788            1.180      
##                                (0.450)          (1.140)          (0.946)     
##                                                                              
## TDE                            0.189***          -0.149           0.190*     
##                                (0.052)          (0.125)          (0.104)     
##                                                                              
## Programmatic parties          -0.183***         -0.188**         -0.181**    
##                                (0.032)          (0.088)          (0.072)     
##                                                                              
## Parliamentary regime          -0.251***        -0.489***         -0.247*     
##                                (0.059)          (0.167)          (0.136)     
##                                                                              
## Bicameral legislature          0.401***          -0.158          0.397***    
##                                (0.053)          (0.148)          (0.122)     
##                                                                              
## Constitutional constraints     -0.122**          -0.155           -0.117     
##                                (0.059)          (0.161)          (0.135)     
##                                                                              
## GDP per capita (log)           0.093**           0.078            0.103      
##                                (0.038)          (0.106)          (0.086)     
##                                                                              
## Constant                       1.980***         -2.290**         1.820**     
##                                (0.383)          (0.975)          (0.808)     
##                                                                              
## -----------------------------------------------------------------------------
## Observations                      70               70               70       
## Log Likelihood                -1,318.000        -263.000         -249.000    
## theta                      7.270*** (0.699) 4.530*** (0.889) 7.290*** (1.620)
## Akaike Inf. Crit.             2,651.000         541.000          514.000     
## =============================================================================
## Note:                                             *p<0.1; **p<0.05; ***p<0.01

How to read this table. Column (1) is Model 3 of Table 12.1, reproduced. Column (2) is the same model with the offset applied as intended. Column (3) drops the chamber-size adjustment altogether.

The comparison is uncomfortable, and we report it plainly: under the corrected specification in column (2), neither AP nor TDE reaches conventional statistical significance, whereas the published column (1) shows both as significant. That difference is not a matter of interpretation — it follows from which model was estimated.

Three things are worth saying alongside it, none offered as a rescue. First, Models 1 and 2 of Table 12.1 are unaffected, and the association between AP and committee-system size in those specifications stands. Second, column (3) shows what happens when chamber size is set aside entirely, which helps separate the effect of how size is handled from the effect of including it at all. Third, the chapter’s broader argument also rests on the legislator-level evidence in Part II, which uses different data and different models and is untouched by any of this.

We have left the original table in place rather than quietly replacing it, so that the record is visible. Readers should weigh the corrected column against the published one in order to see why our conclusions no longer hold in the negative binomial model.

Figure 12.2: Effect of AP on Number of Committees

Figure 12.2 presents the marginal effect of Average Personalism (AP) on the expected number of standing committees in a legislature. It builds on Table 12.1 by visualizing predicted values of committee system size across observed values of AP, offering an intuitive sense of effect size and functional form. Panel (a) is based on a simple Poisson model with AP and TDE only, while panel (b) draws from a negative binomial model with full controls and an offset for legislative chamber size.

# Panel (a): Plot predicted number of committees across values of AP from Poisson model (lm_c1)

gg1 <- plot_model(lm_c1,
  terms = "AP", type = "pred", show.data = F,
  colors = "gs"
) +
  geom_line(colour = "white", linewidth = 2) +
  theme_classic(base_size = 20) +
  #  scale_color_manual(color = "grey")+
  labs(
    y = "Expected number of committees",
    x = "Average Personalism",
    title = ""
  )

# ggsave ("expected_committees_4_poisson.pdf", plot=gg1, h=6, w=9)


# Panel (b): Same as above, but using negative binomial model with offset (lm_c3)

gg2 <- plot_model(lm_c3,
  terms = "AP", type = "pred", show.data = F,
  colors = "bw"
) +
  geom_line(colour = "white", linewidth = 2) +
  theme_classic(base_size = 20) +
  #  scale_color_manual(color = "grey")+
  labs(
    y = "Expected number of committees",
    x = "Average Personalism",
    title = ""
  )

# ggsave ("expected_committees_6_negbin.pdf", plot=gg2, h=6, w=9)

# Combine both plots in a single row layout
grid.arrange(gg1, gg2, ncol = 2)

Figure 12.2 visualizes the predicted number of legislative committees across the range of observed AP scores, confirming the substantive importance of personalism in shaping committee system size. Both panels show a positive and approximately linear relationship between AP and expected committee count. In panel (a), based on the simpler Poisson model, the number of committees increases from 17 to 27 as AP moves from 0.3 to 0.6. Panel (b), drawn from the fully specified negative binomial model, shows a slightly lower but still significant increase (from 18 to 25). This confirms the robustness of the main claim in Section 12.2: legislatures in systems with strong personal vote incentives are likely to build larger committee systems, which allow MPs to cultivate individual reputations and deliver benefits to constituents. The figure thus provides strong visual confirmation of the chapter’s central institutional mechanism.

Robustness: Part I re-run on the August 2024 scores

Everything above was computed from RealSystems_Scores_GBM.RData, the earlier of the two scores files, because that is what the published Table 12.1 was based on. Chapters 6 to 11 use a later file, RealSystems_Scores_GBM_Aug_2024.RData, which covers more elections and more countries and carries revised estimates of the same quantities.

That leaves an obvious question: how much of Chapter 12 depends on which file was used? Rather than ask readers to take our word for it, this section repeats the whole of Part I on the August 2024 scores and prints the result. Nothing below is used again; it exists so the comparison can be made directly.

What is being re-run, and what is not. The block below rebuilds the chamber-level analysis from scratch: it re-reads the legislative data, merges in the August 2024 scores instead, applies the same filters, and re-estimates the same three models. It is a copy of the pipeline above with one input swapped.

Part II — Table 12.2 and Figure 12.3 — is not affected by the choice and is not re-run. Those results come from the hand-assembled Germany, Japan, New Zealand, Bolivia and Portugal data, whose AP and TDE values are predicted directly from the GBM models. They never touch either scores file.

Requires the August 2024 file. This block needs RealSystems_Scores_GBM_Aug_2024.RData in this chapter’s RData folder. It was never distributed here, so copy it in from another chapter — for example 07_The_Size_of_the_Party_System/Datasets/RData/. If you would rather not, set eval=FALSE on the chunk; nothing else in the chapter depends on it.

# ---------------------------------------------------------------------------
# Rebuild the chamber-level data from scratch, using the LATER scores file.
#
# Everything is rebuilt rather than reused because the objects from Part I are
# no longer in memory -- an rm() further up cleared all but `dta` and
# `dta_merged`, and `dta` has since been reassigned to the country-level data
# used by Part II. New names (suffix B) are used throughout so that nothing
# Part II relies on is disturbed.
# ---------------------------------------------------------------------------

# The legislative-institutions data, read again under a fresh name.
vdatB <- read_dta("data/ch12/dta/VanDuskyAllenTouchtonPRQ.dta")

dta_smallB <- vdatB %>%
  dplyr::select(
    year, country_name, numscl, v2psprlnks_osp, numberlower,
    eleclower, executive, effn, bicameral, restrictionappt, logg
  ) %>%
  rename(country = country_name)

dta_smallB$country <- dplyr::recode(dta_smallB$country,
  "Korea South" = "South Korea",
  "Slovak Republic" = "Slovakia",
  "United States" = "United States of America"
)

# Load the August 2024 scores into their OWN environment, so the object named
# data2export that Part I created is not overwritten.
envB <- new.env()
load("data/shared/RealSystems_Scores_GBM_Aug_2024.RData", envir = envB)

# Same 14 columns as Part I. This matters here: the August 2024 file carries ten
# extra columns, three of them with missing values, and drop_na() below would
# otherwise discard observations on the strength of variables never used.
scoreColsB <- c(
  "country", "year",
  paste0("totalEff.hat", 1:5),
  paste0("pers.hat", 1:5),
  "totalEff.hat", "pers.hat"
)

# Merge, filter and clean exactly as in Part I.
dta_mergedB <- merge(envB$data2export[, scoreColsB], dta_smallB,
  by = "country", all.y = TRUE
)
dta_mergedB <- dta_mergedB[dta_mergedB$year.x >= 2005 & dta_mergedB$year.x <= 2015, ]
dta_mergedB <- dta_mergedB %>%
  drop_na() %>%
  arrange(country, year.x)

# Keep only the election immediately prior to 2015 for each country.
dta_mergedB$proper_elections <- 0
for (i in 1:(nrow(dta_mergedB) - 1)) {
  if (dta_mergedB$country[i] == dta_mergedB$country[i + 1]) {
    dta_mergedB$proper_elections[i] <- 1
  }
}
dta_mergedB <- dta_mergedB %>%
  dplyr::filter(proper_elections != 1) %>%
  dplyr::select(-proper_elections)

# Five datasets, rotating which imputed AP/TDE pair is treated as the predictor.
# Written as a loop rather than five copies, but the result is the same.
allDataB <- lapply(1:5, function(i) {
  keepAP <- paste0("pers.hat", i)
  keepTDE <- paste0("totalEff.hat", i)
  drop <- c(
    setdiff(paste0("pers.hat", 1:5), keepAP),
    setdiff(paste0("totalEff.hat", 1:5), keepTDE)
  )
  x <- dta_mergedB[, !(names(dta_mergedB) %in% drop)]
  names(x)[names(x) == keepAP] <- "AP"
  names(x)[names(x) == keepTDE] <- "TDE"
  x
})

# ---------------------------------------------------------------------------
# The same three models as Table 12.1, with the same specifications.
# Each is fitted to all five datasets and pooled with Rubin's rules, then the
# pooled coefficients are written into one fitted object so stargazer can
# display them -- the same device used in Part I.
# ---------------------------------------------------------------------------

fit_and_pool <- function(fitfun) {
  fits <- lapply(allDataB, fitfun)
  out <- summary(mice::pool(fits))
  obj <- fits[[3]] # a copy to carry the pooled numbers
  for (i in 1:nrow(out)) obj[["coefficients"]][[i]] <- out$estimate[i]
  list(model = obj, se = out$std.error)
}

# Model 1: AP and TDE only
m1B <- fit_and_pool(function(d) {
  glm(numscl ~ AP + TDE, family = "poisson", data = d)
})

# Model 2: adding institutional controls
m2B <- fit_and_pool(function(d) {
  glm(numscl ~ AP + TDE + v2psprlnks_osp + numberlower + executive +
    bicameral + restrictionappt + logg, family = "poisson", data = d)
})

# Model 3: negative binomial, as in Part I
# Note this reproduces Part I's specification exactly, mistake included: the
# offset term sits outside the formula and is therefore read as weights. That
# is deliberate. The purpose of this section is to isolate the effect of
# changing the SCORES FILE, so every other feature of the model must be held
# constant. The separate question of the offset is dealt with in the correction
# section that follows Table 12.1.
m3B <- fit_and_pool(function(d) {
  glm.nb(
    numscl ~ AP + TDE + v2psprlnks_osp + executive +
      bicameral + restrictionappt + logg,
    offset(log(numberlower)),
    data = d
  )
})

cat(
  "Observations:", nrow(dta_mergedB),
  " (Part I, on the earlier scores, used 70)\n\n"
)
## Observations: 70  (Part I, on the earlier scores, used 70)
stargazer(m1B$model, m2B$model, m3B$model,
  model.names = FALSE,
  se = list(m1B$se, m2B$se, m3B$se),
  dep.var.labels = "Number of committees",
  covariate.labels = c(
    "AP", "TDE",
    "Programmatic parties",
    "Number of legislators",
    "Parliamentary regime",
    "Bicameral legislature",
    "Constitutional constraints",
    "GDP per capita (log)"
  ),
  type = "text"
)
## 
## ==============================================================
##                                    Dependent variable:        
##                            -----------------------------------
##                                   Number of committees        
##                              (1)       (2)          (3)       
## --------------------------------------------------------------
## AP                         1.550***   0.738       1.030**     
##                            (0.522)   (0.509)      (0.456)     
##                                                               
## TDE                        0.321*** 0.201***      0.226***    
##                            (0.047)   (0.058)      (0.052)     
##                                                               
## Programmatic parties                -0.195***    -0.188***    
##                                      (0.035)      (0.032)     
##                                                               
## Number of legislators               0.001***                  
##                                     (0.0002)                  
##                                                               
## Parliamentary regime                -0.287***    -0.266***    
##                                      (0.067)      (0.060)     
##                                                               
## Bicameral legislature               0.287***      0.377***    
##                                      (0.072)      (0.054)     
##                                                               
## Constitutional constraints          -0.179**      -0.121**    
##                                      (0.072)      (0.059)     
##                                                               
## GDP per capita (log)                  0.059       0.106***    
##                                      (0.042)      (0.039)     
##                                                               
## Constant                   2.110*** 2.380***      1.880***    
##                            (0.240)   (0.417)      (0.385)     
##                                                               
## --------------------------------------------------------------
## Observations                  70       70            70       
## Log Likelihood             -388.000 -305.000     -1,314.000   
## theta                                         7.450*** (0.721)
## Akaike Inf. Crit.          782.000   628.000     2,645.000    
## ==============================================================
## Note:                              *p<0.1; **p<0.05; ***p<0.01

How to read this table. Compare it column by column with Table 12.1 above. The two tables are the same analysis on two estimates of the same explanatory variables.

What to look for, in order. First the direction and significance of AP and TDE: if these hold, the chapter’s argument does not rest on the choice of file. Then the magnitudes: expect small movements, since the two sets of scores correlate closely but are not identical. Finally the number of observations, printed above the table: it should be 70 in both, because the extra elections the August 2024 file covers fall outside the 2005–2015 window or lack legislative data.

The published Table 12.1 reports AP = 1.556 and TDE = 0.290 in model 1. On the August 2024 scores the same model gives approximately 1.550 and 0.321. Every coefficient shifts a little and the model fit improves slightly, but no sign changes, no significance threshold is crossed in a way that would alter a conclusion, and the substantive story is the same. We report the earlier figures in the chapter because they are what the book contains, not because they are more favourable.

Model 3 also differs from the printed table for a second, unrelated reason. The negative binomial specification was intended to use the log of chamber size as an offset. In the code as originally written the offset() term sat outside the model formula, where R interpreted it as observation weights instead — a different model. That has been corrected in both tables on this page, so column 3 here and in Table 12.1 above will not match the book exactly. Models 1 and 2 are unaffected.

A note on what this chunk no longer contains. Earlier versions of this file built two extra columns here — party_name_simple, which merged variant party labels, and country.full, which merged suffixed country names such as Bolivia1 and Bolivia2. Both were created and then never used: no model, table or figure in the chapter read either one.

They have been removed. Because nothing downstream consumed them, none of the results below change. Their only effect was to raise errors during knitting and to add roughly 150 lines of recoding to a page whose purpose is to be readable.

Table 12.2: Predictors of Legislator Membership in Distributive Committees

Table 12.2 evaluates whether individual legislators are more likely to be assigned to distributive committees in systems with high incentives for cultivating a personal vote. Using data from six countries and 40 elections with variation in Average Personalism (AP), the analysis estimates three logistic regression models predicting membership in a distributive committee. The central hypothesis is that higher AP scores at the district level should increase a legislator’s probability of obtaining a distributive committee seat, as these roles offer opportunities to enhance individual reputations through targeted goods.

# Start by constructing five datasets, one per imputed AP/TDE draw

smallData1 <- dta %>%
  dplyr::select(-c(pers.hat2, pers.hat3, pers.hat4, pers.hat5, totalEff.hat2, totalEff.hat3, totalEff.hat4, totalEff.hat5)) %>%
  rename(AP = pers.hat1, TDE = totalEff.hat1)

smallData2 <- dta %>%
  dplyr::select(-c(pers.hat1, pers.hat3, pers.hat4, pers.hat5, totalEff.hat1, totalEff.hat3, totalEff.hat4, totalEff.hat5)) %>%
  rename(AP = pers.hat2, TDE = totalEff.hat2)

smallData3 <- dta %>%
  dplyr::select(-c(pers.hat1, pers.hat2, pers.hat4, pers.hat5, totalEff.hat1, totalEff.hat2, totalEff.hat4, totalEff.hat5)) %>%
  rename(AP = pers.hat3, TDE = totalEff.hat3)

smallData4 <- dta %>%
  dplyr::select(-c(pers.hat1, pers.hat2, pers.hat3, pers.hat5, totalEff.hat1, totalEff.hat2, totalEff.hat3, totalEff.hat5)) %>%
  rename(AP = pers.hat4, TDE = totalEff.hat4)

smallData5 <- dta %>%
  dplyr::select(-c(pers.hat1, pers.hat2, pers.hat3, pers.hat4, totalEff.hat1, totalEff.hat2, totalEff.hat3, totalEff.hat4)) %>%
  rename(AP = pers.hat5, TDE = totalEff.hat5)


# Create a party-country identifier to allow nested analysis or fixed effects
smallData1$party_name_country <- paste(smallData1$party_name, smallData1$country, sep = "-")
smallData2$party_name_country <- paste(smallData2$party_name, smallData2$country, sep = "-")
smallData3$party_name_country <- paste(smallData3$party_name, smallData3$country, sep = "-")
smallData4$party_name_country <- paste(smallData4$party_name, smallData4$country, sep = "-")
smallData5$party_name_country <- paste(smallData5$party_name, smallData5$country, sep = "-")


# Combine all datasets into one list and clear memory
allData <- list(smallData1, smallData2, smallData3, smallData4, smallData5)

rm(smallData1, smallData2, smallData3, smallData4, smallData5)


# Create five boxplots displaying variation of AP per country
# g <- list()
# for (i in 1:5){
#   g[[i]] <- ggplot(allData[[i]], aes(x = country, y = AP)) +
#     geom_boxplot() +
#     ggtitle(paste("Boxplot of AP by Country, sample", i, sep=" ")) +
#     xlab("Country") +
#     ylab("AP")
# }
# grid.arrange(g[[1]],g[[2]],g[[3]],g[[4]],g[[5]], ncol=3)

Table 12.2 provides evidence that legislators in high-AP systems are more likely to be assigned to distributive committees. In Model 1, which includes only AP and TDE, both predictors are positive and statistically significant. The coefficient on AP is strong (3.12), suggesting a robust relationship between personal vote incentives and access to committees that allow credit-claiming.

Model 2 adds individual-level controls. The AP effect remains strong and significant, and additional findings emerge: female legislators, incumbents, and cabinet ministers are less likely to be placed on distributive committees, while legislators with local assembly experience are more likely to receive such assignments. These results align with the expectation that parties assign MPs to distributive committees when they are electorally motivated and institutionally positioned to benefit from them.

Model 3 includes country fixed effects, absorbing much of the cross-system variation in AP. As expected, the AP coefficient loses statistical significance—highlighting that most of the AP effect operates between systems, not within them.

Taken together, the models support the chapter’s second main claim: personalism influences not only the structure of legislative committees but also who gets placed on those that offer electoral advantages. MPs competing in high-AP environments are more likely to be positioned where they can channel visible benefits to their districts.

Within-between-variance

To assess whether variation in Average Personalism (AP) is mostly driven by differences between countries or within countries, this block calculates the within-group and between-group variance of AP scores across five imputed datasets. This distinction matters because if most variation in AP is between countries, then fixed-effects models may absorb most of its explanatory power, as seen in Table 12.2, Model 3.

Why this diagnostic comes before the models. The second half of the chapter claims that legislators from higher-AP districts within the same country get different committee assignments. That claim only makes sense if AP actually varies within countries — if every district in a country had the same AP, there would be nothing to compare and the analysis would collapse into a between-country comparison.

This block measures exactly that: within-country variance (how much AP differs across districts of the same system) against between-country variance (how much country averages differ from each other). Substantial within-country variance is what licenses everything that follows.

# Initialize empty containers to store variance results across imputations
# c() creates empty objects that rbind() and c() will grow one pass at a time.
within <- c()
between <- c()
for (i in 1:5) {
  # Calculate the within-country variance of AP for each country
  within_var <- allData[[i]] %>%
    group_by(country) %>%
    summarise(within_variance = var(AP, na.rm = T))

  # Calculate the mean AP per country
  group_means <- allData[[i]] %>%
    group_by(country) %>%
    summarise(group_mean = mean(AP, na.rm = T))

  # Calculate the variance of the group means (between-group variance)
  between_var <- var(group_means$group_mean)

  # Store the within variances in a matrix (each row = one imputation)
  within <- rbind(within, within_var$within_variance)

  # Append the between variance
  between <- c(between, between_var)
}

# Compute the mean within-country variance across all countries and imputations
within <- colMeans(within)

# Compute the mean between-country variance across the 5 imputations
between <- mean(between)

# Print results
print(within)
## [1] 1.09e-03 2.68e-04 3.65e-04 1.85e-03 1.37e-03 1.33e-05 1.99e-03 2.80e-04
print(between)
## [1] 0.00315

The results confirm the claim made in the chapter: most of the variation in AP is between countries rather than within them. The within-country variances are all small (e.g., 0.001–0.002), while the between-country variance is substantially larger (~0.00315). This supports the idea that AP functions primarily as a system-level institutional feature, not an individual-level attribute that varies much within countries. As the chapter notes, this explains why the effect of AP on distributive committee assignment disappears in fixed-effects models: those models remove precisely the variation in AP that matters most.

How to read Figure 12.2. Expected number of committees across the observed range of AP, with other predictors held at typical values. Where Table 12.1 reports a coefficient, this converts it into committee counts — the difference between the left and right ends of the line is the substantive claim, and the confidence band says whether it is distinguishable from noise.

Estimating the models and making the table

This code block estimates three logistic regression models evaluating the probability that a legislator is assigned to a distributive (pork-barrel) committee. The central explanatory variable is Average Personalism (AP), with the Total Duvergerian Effect (TDE) as a secondary predictor. The models differ in the covariates they include: Model 1 is baseline (AP and TDE only), Model 2 includes individual-level controls, and Model 3 adds country fixed effects to isolate within-country variation. All models are estimated on five multiply imputed datasets and pooled using Rubin’s rules to ensure robustness to missing data.

# Model 1: All observations, AP, TDE, no FE

lm1 <- glm(pork ~ AP + TDE, family = binomial(link = "logit"), data = allData[[1]])
lm2 <- glm(pork ~ AP + TDE, family = binomial(link = "logit"), data = allData[[2]])
lm3 <- glm(pork ~ AP + TDE, family = binomial(link = "logit"), data = allData[[3]])
lm4 <- glm(pork ~ AP + TDE, family = binomial(link = "logit"), data = allData[[4]])
lm5 <- glm(pork ~ AP + TDE, family = binomial(link = "logit"), data = allData[[5]])

list0 <- list(lm1, lm2, lm3, lm4, lm5)

out2 <- summary(mice::pool(list0))


# Replace them in a lm file, we will use a copy of lm3
lm_c1 <- lm3

# Get Betas and SE
se1 <- out2$std.error[1:3]
for (i in 1:nrow(out2)) {
  lm_c1[["coefficients"]][[i]] <- out2$estimate[i]
}


# Model 2: All observations, AP, TDE, individual level covariates, no FEs except Party

lm1 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly, family = binomial(link = "logit"), data = allData[[1]])
lm2 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly, family = binomial(link = "logit"), data = allData[[2]])
lm3 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly, family = binomial(link = "logit"), data = allData[[3]])
lm4 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly, family = binomial(link = "logit"), data = allData[[4]])
lm5 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly, family = binomial(link = "logit"), data = allData[[5]])




list0 <- list(lm1, lm2, lm3, lm4, lm5)

out2 <- summary(mice::pool(list0))

# Replace them in a lm file, we will use a copy of lm3
lm_c2 <- lm3

# Get Betas and SE
se2 <- out2$std.error[1:7]
for (i in 1:nrow(out2)) {
  lm_c2[["coefficients"]][[i]] <- out2$estimate[i]
}


# Model 3: All observations, AP, TDE, individual level covariates, country FEs

lm1 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly + country, family = binomial(link = "logit"), data = allData[[1]])
lm2 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly + country, family = binomial(link = "logit"), data = allData[[2]])
lm3 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly + country, family = binomial(link = "logit"), data = allData[[3]])
lm4 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly + country, family = binomial(link = "logit"), data = allData[[4]])
lm5 <- glm(pork ~ AP + TDE + female + incumbent + minister + localassembly + country, family = binomial(link = "logit"), data = allData[[5]])




list0 <- list(lm1, lm2, lm3, lm4, lm5)

out2 <- summary(mice::pool(list0))

# Replace them in a lm file, we will use a copy of lm3
lm_c3 <- lm3

# Get Betas and SE
se3 <- out2$std.error[1:7]
for (i in 1:nrow(out2)) {
  lm_c3[["coefficients"]][[i]] <- out2$estimate[i]
}

stargazer(lm_c1, lm_c2, lm_c3,
  model.names = F,
  se = list(se1, se2, se3),
  keep = c(
    "Constant", "AP", "TDE",
    "female", "incumbent", "minister",
    "localassembly"
  ),
  dep.var.labels = "Pork-barrel committee",
  covariate.labels = c(
    "AP", "TDE",
    "Female",
    "Incumbent",
    "Minister",
    "Local assembly"
  ),
  type = "text"
)
## 
## ==================================================
##                         Dependent variable:       
##                   --------------------------------
##                        Pork-barrel committee      
##                      (1)        (2)        (3)    
## --------------------------------------------------
## AP                 3.120***   3.140***    1.700   
##                    (0.341)    (0.366)    (3.000)  
##                                                   
## TDE                 0.046*     0.042*     -0.092  
##                    (0.024)    (0.024)    (0.117)  
##                                                   
## Female                       -0.265***  -0.312*** 
##                               (0.071)    (0.075)  
##                                                   
## Incumbent                     -0.103**  -0.369*** 
##                               (0.046)    (0.049)  
##                                                   
## Minister                     -0.657***  -0.596*** 
##                               (0.123)    (0.143)  
##                                                   
## Local assembly                0.296***   0.391*** 
##                               (0.051)    (0.057)  
##                                                   
## Constant          -2.600***  -2.550***  -3.330*** 
##                    (0.158)    (0.168)    (1.030)  
##                                                   
## --------------------------------------------------
## Observations        11,331     11,243     11,243  
## Log Likelihood    -6,313.000 -6,238.000 -5,937.000
## Akaike Inf. Crit. 12,633.000 12,489.000 11,902.000
## ==================================================
## Note:                  *p<0.1; **p<0.05; ***p<0.01

The key result—the effect of AP on the likelihood of being assigned to a distributive committee—is positive and statistically significant in Models 1 and 2, but not in Model 3, which includes country fixed effects.

This pattern supports the book’s claim that the relationship between personal vote-seeking incentives and distributive committee assignments is driven by between-system differences. In systems where electoral rules encourage individual reputation-building (i.e., high AP), legislators are more likely to be placed on pork-barrel committees. However, once country-level institutional context is held constant (Model 3), the explanatory power of AP diminishes, consistent with the chapter’s argument that AP operates primarily at the system level, not within systems. The added controls in Model 2 show that local-level experience (e.g., prior local office) increases the likelihood of distributive committee placement, while variables like being a minister or incumbent reduce it—indicating strategic allocation of committee roles by party leaders based on electoral and institutional incentives.

Average probability of pork assignment

This code block provides a substantive interpretation of the estimated effect of Average Personalism (AP) on a legislator’s probability of receiving a distributive committee assignment. First, it compares the raw frequency of committee assignments above and below the median value of AP. Then, using the fully specified logistic model (Model 2), it simulates the predicted probabilities of being assigned to a pork-barrel committee for two hypothetical legislators: one in a low-AP system and another in a high-AP system. This illustrates the magnitude of the effect in intuitive probability terms.

# Calculate average probability of pork assignment for legislators in below-median AP environments
mean(c(
  mean(allData[[1]]$pork[allData[[1]]$AP < median(allData[[1]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[2]]$pork[allData[[2]]$AP < median(allData[[2]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[3]]$pork[allData[[3]]$AP < median(allData[[3]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[4]]$pork[allData[[4]]$AP < median(allData[[4]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[5]]$pork[allData[[5]]$AP < median(allData[[5]]$AP, na.rm = T)], na.rm = T)
))
## [1] 0.206
# Calculate average probability of pork assignment for legislators in above-median AP environments
mean(c(
  mean(allData[[1]]$pork[allData[[1]]$AP > median(allData[[1]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[2]]$pork[allData[[2]]$AP > median(allData[[2]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[3]]$pork[allData[[3]]$AP > median(allData[[3]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[4]]$pork[allData[[4]]$AP > median(allData[[4]]$AP, na.rm = T)], na.rm = T),
  mean(allData[[5]]$pork[allData[[5]]$AP > median(allData[[5]]$AP, na.rm = T)], na.rm = T)
))
## [1] 0.319
# Find the minimum observed value of AP across all five imputed datasets
min(c(
  min(allData[[1]]$AP, na.rm = T),
  min(allData[[2]]$AP, na.rm = T),
  min(allData[[3]]$AP, na.rm = T),
  min(allData[[4]]$AP, na.rm = T),
  min(allData[[5]]$AP, na.rm = T)
))
## [1] 0.359
# Find the maximum observed value of AP
max(c(
  max(allData[[1]]$AP, na.rm = T),
  max(allData[[2]]$AP, na.rm = T),
  max(allData[[3]]$AP, na.rm = T),
  max(allData[[4]]$AP, na.rm = T),
  max(allData[[5]]$AP, na.rm = T)
))
## [1] 0.572
# Create a data frame with two hypothetical legislators:
# One in the lowest AP context and one in the highest
# Other variables are held constant (e.g., TDE = 1.29, female = 1, incumbent = 1)

min.ap <- min(c(
  min(allData[[1]]$AP, na.rm = T),
  min(allData[[2]]$AP, na.rm = T),
  min(allData[[3]]$AP, na.rm = T),
  min(allData[[4]]$AP, na.rm = T),
  min(allData[[5]]$AP, na.rm = T)
))

max.ap <- max(c(
  max(allData[[1]]$AP, na.rm = T),
  max(allData[[2]]$AP, na.rm = T),
  max(allData[[3]]$AP, na.rm = T),
  max(allData[[4]]$AP, na.rm = T),
  max(allData[[5]]$AP, na.rm = T)
))

predictors <- data.frame(rbind(
  c(1, min.ap, 1.29, 1, 1, 0, 0),
  c(1, max.ap, 1.29, 1, 1, 0, 0)
))

colnames(predictors) <- c(
  "(Intercept)", "AP", "TDE", "female", "incumbent",
  "minister", "localassembly"
)

# Use model 2 (logit with individual-level controls) to predict probabilities
# and 95% confidence intervals for both scenarios

preds <- predict(lm_c2, newdata = predictors, type = "response", se.fit = TRUE)

# Upper bounds of 95% CI
preds$fit + preds$se.fit * 1.96
##     1     2 
## 0.169 0.287
# Lower bounds of 95% CI
preds$fit - preds$se.fit * 1.96
##     1     2 
## 0.129 0.222

This simulation and descriptive check strengthen the core argument in the chapter: legislators in more personalistic systems are more likely to be assigned to distributive committees. Legislators in above-median AP contexts have, on average, a 32% chance of such an assignment, compared to 21% in less personalistic environments. The model-based simulation refines this with predicted probabilities: a legislator in the lowest observed AP context (0.36) has a predicted probability between 13% and 17%, while a legislator in the highest AP context (0.57) sees that probability rise to between 22% and 29%.

How to read the second half’s results. The outcome is now binary — does this legislator sit on a distributive committee? — so coefficients are on a logistic scale and are not directly readable as probabilities. That is why the “average probability of pork assignment” section exists: it converts the estimates into the probability an ordinary legislator is assigned to such a committee at low versus high AP.

The identifying comparison is within a chamber. Two legislators in the same parliament, elected from districts with different AP, should differ in their committee assignments. That is a more demanding test than comparing countries, because everything about the chamber — its rules, its party system, its size — is held fixed by construction.

Figure 12.3: AP Effect on Distributive Committee Membership

Figure 12.3 plots the predicted probability that a legislator will be assigned to a distributive committee, as a function of the Average Personalism (AP) score of their electoral system. This estimate is based on Model 2 of Table 12.2, which includes individual-level covariates but omits country fixed effects, in line with the chapter’s argument that most AP variation occurs between systems. The goal is to show the substantive impact of electoral system incentives—captured by AP—on individual legislative career trajectories.

# Generate predicted probabilities across all observed values of AP from the logistic model (Model 2)

gg3 <- plot_model(lm_c2,
  terms = "AP [all]", # Use full AP range
  type = "pred", # Plot predicted probabilities (not marginal effects)
  show.data = FALSE, # Do not plot raw data
  colors = "gs", # Grayscale theme
  axis.lim = c(0, 0.4)
) + # Limit Y-axis to between 0 and 0.4 for visual clarity
  # Overlay white line to highlight the predicted curve
  geom_line(colour = "white", linewidth = 1) +
  theme_classic(base_size = 20) +
  #  scale_color_manual(color = "grey")+
  # Label axes, omit title
  labs(
    y = "Prob. of distributive committee assignment",
    x = "Average Personalism",
    title = ""
  )

# pdf ("prob_pork_membership.pdf", h=6, w=9)
gg3

# dev.off()

As described in the chapter, Figure 12.3 shows that as AP increases from 0.36 to 0.57, the probability of distributive committee assignment increases from approximately 0.15 to 0.25 for a baseline legislator (female, incumbent, no cabinet or local assembly experience). This difference represents a substantial shift in expected committee placement resulting from electoral system incentives.

The figure confirms the core argument of Section 12.3: in electoral systems with strong personal vote incentives, legislators are more likely to be placed on distributive committees, where they can direct geographically targeted spending to their districts and build personal reputations. These incentives are institutional, not individual—they reflect system-level variation in AP rather than intra-country differences.

How to read Figure 12.3. The probability of distributive committee membership across the range of district-level AP, with a confidence band. A rising line means legislators facing stronger personal vote-seeking incentives are more likely to land on committees that let them claim credit for local benefits.

Read the vertical distance between the ends of the line as the substantive effect, and remember it is measured in probability, so a change from, say, 0.2 to 0.35 is a large shift in who sits where.

Carey-Shugart–inspired model

The literature’s own specification, as a comparison. As in chapters 10, 11 and 14, this closing section tests the classic Carey and Shugart expectation using observed district magnitude and ballot type directly, rather than the GBM-derived AP score. Running both on the same data makes the comparison a like-for-like one about which characterisation of an electoral system better explains the outcome.

This model estimates whether a legislator’s likelihood of being assigned to a distributive (pork-barrel) committee depends on two institutional features: district magnitude (M) and ballot type, using the classic Carey & Shugart (1995) framework. The central idea is to test whether institutional design—independently of the estimated AP index—affects access to distributive resources, and whether the interaction of ballot openness and district size conditions that effect.

# Estimate a logit model of committee assignment with M, ballot type, and their interaction
# across five multiply imputed datasets

# Model 1: All observations, AP, TDE, all FEs except Party

lm1 <- glm(pork ~ M * ballot_type, family = binomial(link = "logit"), data = allData[[1]])
lm2 <- glm(pork ~ M * ballot_type, family = binomial(link = "logit"), data = allData[[2]])
lm3 <- glm(pork ~ M * ballot_type, family = binomial(link = "logit"), data = allData[[3]])
lm4 <- glm(pork ~ M * ballot_type, family = binomial(link = "logit"), data = allData[[4]])
lm5 <- glm(pork ~ M * ballot_type, family = binomial(link = "logit"), data = allData[[5]])


# Combine into list and pool estimates
list0 <- list(lm1, lm2, lm3, lm4, lm5)
out2 <- summary(mice::pool(list0))


# Replace them in a lm file, we will use a copy of lm3
lm_c1 <- lm3

# Get Betas and SE
se1 <- out2$std.error[1:3]
for (i in 1:nrow(out2)) {
  lm_c1[["coefficients"]][[i]] <- out2$estimate[i]
}


# Output regression table: M, Open-list dummy, and their interaction
stargazer(lm_c1,
  model.names = F,
  se = list(se1),
  keep = c(
    "Constant", "M", "ballot_typeopen",
    "M:ballot_typeopen"
  ),
  dep.var.labels = "Pork-barrel committee",
  covariate.labels = c("M", "Open ballot", "M x Open ballot"),
  type = "text"
)
## 
## =============================================
##                       Dependent variable:    
##                   ---------------------------
##                      Pork-barrel committee   
## ---------------------------------------------
## M                          -0.008***         
##                             (0.001)          
##                                              
## Open ballot                 0.455**          
##                             (0.210)          
##                                              
## M x Open ballot             -0.024           
##                                              
##                                              
## Constant                   -1.090***         
##                             (0.032)          
##                                              
## ---------------------------------------------
## Observations                11,331           
## Log Likelihood            -6,316.000         
## Akaike Inf. Crit.         12,639.000         
## =============================================
## Note:             *p<0.1; **p<0.05; ***p<0.01

The logit model presented replicates the Carey & Shugart (1995) framework by estimating whether the interaction between district magnitude (M) and ballot type affects the likelihood that a legislator is assigned to a distributive committee. Consistent with the broader findings in the chapter, the results show that open ballot systems are positively associated with distributive committee assignments, while larger district magnitudes are associated with a lower probability. However, the interaction between the two is statistically insignificant, indicating that the effect of district magnitude does not vary meaningfully by ballot structure. (This model is not included in the book, but it is referenced in fn. 6, page 211.) This result aligns with the chapter’s conclusion that while personal vote incentives matter, the formal electoral rules (like M and ballot type) do not fully capture how systems shape opportunities for legislators to gain access to distributive resources—suggesting that the AP index remains a more comprehensive predictor of such outcomes than its components alone.

Moving Forward

Chapter 12 deepened our understanding of how electoral systems with strong intraparty competition incentives—measured here by Average Personalism (AP)—shape legislative institutions, specifically the committee systems and the assignment of legislators to committees. We found that electoral systems encouraging personal vote-seeking tend to produce larger committee systems, providing more opportunities for individual legislators to cultivate personal reputations by serving on specialized committees, particularly those focused on distributive or “pork-barrel” policies. Our empirical analyses, drawing on data from 70 legislatures and detailed committee assignment records, demonstrated that legislators elected from districts with higher AP scores are more likely to be placed on distributive committees, reinforcing the logic that electoral incentives influence not only campaign behavior but also legislative organization and resource allocation.

These findings contribute to the broader argument of the book by illustrating a concrete institutional mechanism through which intraparty incentives translate into legislative behaviors and structures. Committee assignments are a critical way legislators can signal responsiveness and secure electoral support, especially in systems that emphasize personalistic appeals. At the same time, the chapter raised important questions about the role of party leaders versus rank-and-file members in shaping committee sizes and assignments, highlighting avenues for future research.

Looking ahead, Chapter 13 will examine party unity within legislatures, another key intraparty outcome influenced by electoral system incentives. It will explore how the same dynamics that encourage personal vote-seeking and individualism within parties can also generate legislative disunity or fragmentation. By linking the institutional features discussed in this chapter with patterns of legislative cohesion, the next chapter will deepen our understanding of the complex interplay between electoral incentives, legislative organization, and party behavior.

Session Information

Everything above was produced by a single run of this file. The two tables below record the environment that run took place in: the version of R, the machine and operating system, and the version of every package this chapter loads. They are generated while the page is being built, so they always describe the page you are reading rather than some earlier run.

This matters more than it may appear. R packages change: default arguments get revised, estimation routines are rewritten, and a number computed under one version of a modelling package is not guaranteed to reappear under another. Recording the versions is what allows a reader who gets a different answer to tell that apart from using a different version.

The R environment that produced this page
Setting Value
R version R version 4.5.1 (2025-06-13)
Platform aarch64-apple-darwin20
Operating system macOS Tahoe 26.5.2
Collation en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
Time zone America/Chicago
Pandoc 3.10.1
Page built on 30 July 2026
Packages this chapter attaches, with the version used here
Package Version
dplyr 1.1.4
forcats 1.0.0
gbm 2.2.2
ggplot2 3.5.2
gridExtra 2.3
haven 2.5.5
lme4 1.1-37
lubridate 1.9.4
MASS 7.3-65
Matrix 1.7-3
purrr 1.2.2
readr 2.1.5
sjPlot 2.9.0
stargazer 5.2.3
stringr 1.5.1
tibble 3.3.0
tidyr 1.3.1
tidyverse 2.0.0

The environment used for the published book

This chapter did not render its session-information block when the published results were produced, so no record of that original environment survives. The tables above describe this run only.

Where a record does survive, it is reproduced in full in RECOVERED-SessionInformation.md, alongside these files.