1) Load Data

# 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="-")

2) R Class of Legislative Data Sets

The class is rollcall. I confirm:

class(Pdata)
## [1] "rollcall"

3) Number of Votes in the Data

dim(Pdata$votes)
## [1] 103 486

So, there are 103 senators * 486 bills = 50,058 votes in the data.

4) Interpreting Matrix Cells

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.

5) Voting Records

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

6) Assign Senators to Polarity Vectors

# Found by manually counting the rows in Pdata$legis.data 
polarity <- c(75, 3)

7) Run WNOMINATE

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

8) Discuss Model GoF

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.

9) Plots + Interpretations

# All plots at once 
plot(result)

## NULL

Interpretations:

10) Senators’ Ideological Scores

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:

2nd Dimension:

11) Senators’ Disloyalty

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.

12) Party Cohesion

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.

13) Party Plots

# 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)