# Read the vote data using the readKH function
Pdata <- readKH("https://voteview.com/static/data/out/votes/S112_votes.ord",
desc="112th U.S. Senate",
debug=TRUE)
## Attempting to read file in Keith Poole/Howard Rosenthal (KH) format.
## Attempting to create roll call object
## 112th U.S. Senate
## 103 legislators and 486 roll calls
## Frequency counts for vote types:
## rollCallMatrix
## 0 1 6 7 9
## 978 30210 17006 21 1843
# Load the rollcall data from the URL
url <- "https://voteview.com/static/data/out/rollcalls/S112_rollcalls.csv"
Pdata$vote.data <- read.csv(file=url, header=TRUE)
# Convert the date column to Date format
Pdata$vote.data$date <- as.Date(Pdata$vote.data$date, format="%Y-%m-%d")
# Set the dimension names for the votes matrix
dimnames(Pdata$votes)[[2]] <- paste(Pdata$vote.data$session, Pdata$vote.data$number,
sep="-")
The class is rollcall. I confirm:
class(Pdata)
## [1] "rollcall"
dim(Pdata$votes)
## [1] 103 486
So, there are 103 senators * 486 bills = 50,058 votes in the data.
Per this website (not the official metadata, but seems to use the same practices), 1 means “Yea”, 6 means “Nay”, and 9 means that the senator was not present/didn’t vote.
Pdata$votes[(nrow(Pdata$votes) - 19):nrow(Pdata$votes),
(ncol(Pdata$votes) - 4):ncol(Pdata$votes)]
## 2- 2- 2- 2- 2-
## JOHNSON (D SD) 6 1 1 1 1
## THUNE (R SD) 1 6 6 1 1
## ALEXANDER (R TN) 1 6 9 9 1
## CORKER (R TN) 1 6 1 1 1
## CORNYN (R TX) 1 6 6 6 1
## HUTCHISON (R TX) 1 1 6 1 1
## HATCH (R UT) 1 6 6 6 1
## LEE (R UT) 1 6 1 6 6
## LEAHY (D VT) 6 1 9 1 1
## SANDERS (Indep VT) 6 1 1 1 1
## WEBB (D VA) 6 1 1 1 1
## WARNER (D VA) 9 9 1 1 1
## CANTWELL (D WA) 6 1 1 1 1
## MURRAY (D WA) 6 1 1 1 1
## ROCKEFELLER (D WV) 6 1 1 1 1
## MANCHIN (D WV) 6 1 1 1 1
## KOHL (D WI) 6 1 1 1 1
## JOHNSON (R WI) 1 6 1 1 1
## BARRASSO (R WY) 1 6 6 6 1
## ENZI (R WY) 1 6 6 6 1
# Found by manually counting the rows in Pdata$legis.data
polarity <- c(75, 3)
result <- wnominate(Pdata, polarity = polarity)
##
## Preparing to run W-NOMINATE...
##
## Checking data...
##
## All members meet minimum vote requirements.
##
## Votes dropped:
## ... 76 of 486 total votes dropped.
##
## Running W-NOMINATE...
##
## Getting bill parameters...
## Getting legislator coordinates...
## Starting estimation of Beta...
## Getting bill parameters...
## Getting legislator coordinates...
## Starting estimation of Beta...
## Getting bill parameters...
## Getting legislator coordinates...
## Getting bill parameters...
## Getting legislator coordinates...
## Estimating weights...
## Getting bill parameters...
## Getting legislator coordinates...
## Estimating weights...
## Getting bill parameters...
## Getting legislator coordinates...
##
##
## W-NOMINATE estimation completed successfully.
## W-NOMINATE took 31.3 seconds to execute.
summary(result)
##
##
## SUMMARY OF W-NOMINATE OBJECT
## ----------------------------
##
## Number of Legislators: 102 (1 legislators deleted)
## Number of Votes: 410 (76 votes deleted)
## Number of Dimensions: 2
## Predicted Yeas: 21569 of 23170 (93.1%) predictions correct
## Predicted Nays: 14998 of 16766 (89.5%) predictions correct
## Correct Classifiction: 90.48% 91.56%
## APRE: 0.701 0.735
## GMP: 0.79 0.818
##
##
## The first 10 legislator estimates are:
## coord1D coord2D
## OBAMA (D NA) -0.872 0.223
## SESSIONS (R AL) 0.494 0.025
## SHELBY (R AL) 0.405 0.358
## MURKOWSKI (R AK) -0.020 -0.424
## BEGICH (D AK) -0.831 -0.532
## MCCAIN (R AZ) 0.432 -0.327
## KYL (R AZ) 0.429 0.042
## BOOZMAN (R AR) 0.338 0.617
## PRYOR (D AR) -0.688 0.422
## BOXER (D CA) -0.970 -0.117
result$fits
## correctclass1D correctclass2D apre1D apre2D gmp1D
## 90.4822693 91.5640030 0.7007322 0.7347453 0.7904449
## gmp2D
## 0.8184410
Yes, I do think that the model performs well, because the correctclass1D and correctclass2D percentages, which represent the percentages of votes that the model correctly predicted as “Yea” and “Nay” in each dimension, are relatively high: 90.48% for the first dimension, and 91.56% in the second dimension. It is fair to say that the model is highly accurate.
# All plots at once
plot(result)
## NULL
Interpretations:
W-NOMINATE Coordinates: There seems to be a clear clustering along party lines, with Republican senators tending to score positively (>0) for Dimension 1 and Democratic senators tending to score negatively (<0) for Dimension 1. Dimension 2 does not seem to be as relevant to the clustering as Dimension 2, with both parties being relatively scattered from -1 to +1. It does appear that the Democratic senators’ coordinates are more tightly clustered together, though, with a smaller range than the Republican senators with respect to Dimension 2. This suggests that Dimension 1, which I interpret broadly as conservative-liberal lean, does explain the voting behavior of the 112th senate fairly accurately. This might be because senators just tend to vote along party lines, or that the 486 bills on the floor were highly ideologically charged. Having looked at some of these bills on voteview.com, I can say that not all of the bills were ideologically charged, so it is more likely that the senators just tended to vote along party lines.
Scree Plot: The Scree Plot confirms that Dimension 2 may not be as relevant, with only the first dimension having an eigenvalue >1. The second dimension’s eigenvalue is larger than zero (I would estimate it at 0.5), while dimension 3 onward have eigenvalues of roughly 0. Again, this suggests that Dimension 1 (conservative-liberal lean) does explain the voting behavior of the 112th senate fairly accurately, for the reasons listed above.
Cutting Lines: The lines, which represent randomly selected bills, separate the Yeas and Nays given the ideological positions of the senators within 2D space. The cutting lines for the 112th senate mainly run straight up and down, corresponding to that first latent dimension (conservative-liberal lean), and while there are a few lines running left-right (corresponding to Dimension 2), they aren’t as prominent. This corroborates the conclusion from the W-NOMINATE and Scree Plots that Dimension 1 (conservative-liberal lean) does explain the voting behavior of the 112th senate fairly accurately, for the reasons listed in bullet 1.
Cutting Line Angles: The distribution of the cutting line angles for all 486 bills is narrow-normal, verging on peaked, emphasizing the high predictive power of Dimension 1 (conservative-liberal lean). Most of the cutting line angles are between 80-110 degrees, which is around 90 degrees, which corresponds to a line running straight up and down. Again, this plot affirms the conclusion that Dimension 1 does explain the voting behavior of the 112th senate fairly accurately, for the reasons listed in bullet 1.
To find the names and states, I went to the result$legislators data frame and sorted the coord1D/coord2D columns in descending order.
1st Dimension:
Highest ideological score: Senator Demint from SC.
Lowest ideological score: Senator Akaka from HI.
2nd Dimension:
Highest ideological score: Senator Cochran from MS.
Lowest ideological score: Senator Heller from NV.
Taking your advice and calculating the distance from the party mean score, using the Euclidean distance metric:
# Get the party means
party_means <- aggregate(
cbind(coord1D, coord2D) ~ party,
data = result$legislators,
FUN = mean)
# Merge party means back into result$legislators
loyal_leg <- merge(
result$legislators,
party_means,
by = "party",
suffixes = c("", "_partymean"))
# Get each senator's Euclidean distance from their party mean
loyal_leg$partydist <- sqrt(
(loyal_leg$coord1D - loyal_leg$coord1D_partymean)^2 +
(loyal_leg$coord2D - loyal_leg$coord2D_partymean)^2)
I then went to my new loyal_leg data frame and sorted by partydist, then combed through the party column to find the senators with the highest distances. Here they are:
The most disloyal Democratic senator is Lieberman from CT, with a Euclidean distance from the party mean of 0.88.
The most disloyal Republican senator is Paul from KY, with a Euclidean distance from the party mean of 0.65.
I found the standard deviation of the coordinates for each party, as well as the mean Euclidean distance from the mean party coordinates (the latter of which was calculated in Part 11).
# Standard deviations
aggregate(
cbind(coord1D, coord2D) ~ party,
data = result$legislators,
FUN = sd)
## party coord1D coord2D
## 1 D 0.1124647 0.2799342
## 2 Indep NA NA
## 3 R 0.2416528 0.4630681
# Mean Euclidean distance
aggregate(
partydist ~ party,
data = loyal_leg,
FUN = mean)
## party partydist
## 1 D 0.2604192
## 2 Indep 0.0000000
## 3 R 0.4482534
It is clear that the Democrats have more party cohesion than the Republicans, because the standard deviation of the Republican senators’ coordinates is around twice that of the Democrats’, and also because the mean Euclidean distance from the Republican party mean is around twice that of the Democrats’. This may be because the Democrats were feeling more united (Obama, a Democrat, was President during the 112th senate), because they had a majority of the seats, and/or because they felt they needed to stick together given their reduced numbers after the 2010 midterms. Source: Wikipedia (I know, I know). It’s unclear whether the Democrats were unusually cohesive, or if the Republicans were unusually not cohesive, or both. I would have to look at other senate meetings to be able to say anything more.
# Democrats
dems <- ggplot(subset(result$legislators, party == "D"), aes(x = coord1D, y = coord2D)) +
geom_point(color = "blue") +
labs(title = "Democrat Coordinates", x = "Dimension 1", y = "Dimension 2") +
theme_minimal()
# Republicans
reps <- ggplot(subset(result$legislators, party == "R"), aes(x = coord1D, y = coord2D)) +
geom_point(color = "red") +
labs(title = "Republican Coordinates", x = "Dimension 1", y = "Dimension 2") +
theme_minimal()
# All senators
sens <- ggplot(result$legislators, aes(x = coord1D, y = coord2D)) +
geom_point(aes(color = party)) +
scale_color_manual(values = c("blue", "grey", "red")) +
labs(title = "All Senator Coordinates", x = "Dimension 1", y = "Dimension 2") +
theme_minimal()
# Used patchwork library for this: https://cran.r-project.org/web/packages/patchwork/patchwork.pdf
sens|(dems/reps)