Introduction

The 31st Dáil Eireann (Irish Parliament) (March 11th, 2011 — Febru- ary 6th, 2016) had 166 members who were affiliated with nine political parties or were independent. Prior to the disolution of the Dáil on Februrary 6th, the parliament had 23 votes.

Latent Class Analysis Model

Each vote casted by the ith TD can be modelled as a Bernoulli trial with \(\theta_{gm}\) probability of success, where \(g=\{1,\ldots,G\}\) is the latent group number and \(m=\{1, \ldots, M=23\}\) is the number of votes. i.e \(x_{im} \sim \mathcal{B}(\mathbf{\theta}_{gm})\). Let \(\tau = \{\tau_1, \ldots, \tau_G\}\) be the probability of the ith Dail member belonging to the gth group, and \(z_{ig}=1\) if the TD belongs to the gth group, or \(0\) otherwise. Then:

\[\begin{align*} p(x_{im} \vert \theta_{gm}) &= \theta_{gm}^{x_{im}}(1-\theta_{gm})^{1-x_{im}}\\ p(x_i, z_i \vert \tau, \Theta) &= \prod_{g=1}^G \left[ \tau_g p(x_i\vert \theta_g)\right]^{z_{ig}} \end{align*}\]

Complete-data log-likelihood of this model is defined as:

\[\begin{align*} l &= \log p(x_i, z_i\vert \tau, \Theta) = \sum_{g=1}^G z_{ig}\log \tau_g + \sum_{g=1}^G z_{ig} \log p(x_i\vert \Theta_g) \\ \mathcal{L}_{\tau, \Theta} &= \sum_{i=1}^N \sum_{g=1}^G z_{ig} \log \tau_g + \sum_{i=1}^N \sum_{m=1}^M\sum_{g=1}^G \left[ z_{ig} \left( x_{im} \log \theta_{gm} + (1-x_{im}) \log(1-\theta_{gm}) \right) \right] \end{align*}\]

Maximisation of the log-likelihood with respect to \(\theta\), \(\tau\) and \(z\) may be challenging. Luckily the Expectation Maximisation Algorithm is working well for this problem.

Expectation Maximisation Algorithm

To maximise \(\mathcal{L}\) we can use the EM algorithm, where we iterate through the Expectation and Maximisation steps.

Let’s define function \(Q(\Theta, \tau \vert \Theta^{(t)}, \tau^{(t)}) = \mathbb{E}_{X, \Theta^{(t)}, \tau^{(t)}}[\log p(X, Z \vert \Theta, \tau)]\), where the parameters with superscript (t) are the estimations from the previous step.

Expectation Step

In the expectation step, we find the expected value of the \(Q\) function, where the expected value of the group membership parameters \(z_{ig}\) is defined as:

\[ z_{ig}^{(t+1)} = \mathbb{E}[p(z_{ig}\vert X_i, \Theta^{(t)}, \tau^{(t)})]= \frac{\tau_g^{(t)}p(X_i\vert \theta_g^{(t)})}{\sum_{h=1}^G \tau_h^{(t)}p(X_i\vert \theta_h^{(t)})} \]

Maximisation Step

In Maximisation step we maximise \(Q\) wrt. model parameters \(\theta_{gm}\) and \(\tau_g\).

\[ \frac{\partial Q}{\partial \theta_{gm}}=\frac{\sum_{i=1}^N x_{im} z_{ig}}{\theta_{gm}^{(t+1)}} - \frac{\sum_{i=1}^N z_{ig}(1-x_{im})}{1-\theta_{gm}^{(t+1)}} = 0\\ \Rightarrow\ \theta_{gm}^{(t+1)} = \frac{\sum_{i=1}^N x_{im} z_{ig}}{\sum_{i=1}^N z_{ig}} \]

To maximise \(Q\) wrt. parameters \(\tau\) we have to take into consideration that \(\sum_{g=1}^G \tau_g = 1\). We can use the Lagrange multipliers method:

\[\begin{align*} \frac{\partial \left( Q + \lambda(\sum_{g=1}^G\tau_g - 1) \right)}{\partial \tau_g} &= \frac{\sum_{i=1}^N z_{ig}}{\tau_g} + \lambda = 0 \\ &\Rightarrow\ \lambda=-\frac{1}{\tau_g}\sum_{i=1}^N z_{ig} = -\sum_{i=1}^N \sum_{g=1}^G z_{ig} \\ &\Rightarrow\ \tau_g^{(t+1)} = \frac{\sum_{i=1}^N z_{ig}}{\sum_{i=1}^N \sum_{g=1}^G z_{ig}} = \frac{\sum_{i=1}^N z_{ig}}{N} \end{align*}\]

The R script which repeats these calculations required number of steps can be implemented as follows:

# LCA EM optimisation for data modelled with Bernoulli trials
sm.lca <- function(x, G=2, iters=20) {
  
  M <- ncol(x) # length of the data vector
  N <- nrow(x) # number of samples
  
  # initialise z with random numbers uniformly distributed [0,1]
  z <- matrix(runif(N * G), N, G)
  z <- z/rowSums(z)
  
  # used to store the results
  l <- c()
  tau <- theta <- NULL
  
  # iterate through the EM steps
  for(k in 1:iters) {

    z.sum <- colSums(z)
    
    theta <- (t(z) %*% x) / (z.sum+1e-6)
    
    tau <- z.sum/N
    
    z1 <- t(t(p(x, theta, G=G)) * tau) + 1e-6
    z <- z1/rowSums(z1)
    
    # BIC = 2*log likelihood - log(N)*number of parameters
    l <- c(l, sum(colSums(z)*tau) + sum((z%*%log(theta))*x) + 
             sum((1-x)*(z%*%log(1-theta))) - log(N)*((G-1) + M*G))
  }
  
  list(theta=theta, tau=tau, z=z, l=l, G=G)
}

The model has \((G-1)\) \(\tau_g\) paramters (-1 due to constrained values) and \(M\times G\) \(\theta_{gm}\) parameters. Hence the total number of parameters is equal to \((G-1)+M\times G\). We can use BIC information criterion to find the best model, where \(BIC = 2\log p(X, Z|\tau, \Theta) - \log(N)\times (G-1+MG)\).

Comparison of BIC scores for different latent group numbers

Comparison of BIC scores for different latent group numbers

The EM algorithm is guaranteed to increase the likelihood at every step. We can see that the EM algorithm converges in less than 10 iterations. The best performing model has G=3 groups.

The separation of the Dail members into 3 latent groups, with group membership probabilities \(\tau_1=0.28\), \(\tau_2=0.16\) and \(\tau_3=0.56\), is shown in Figure 2.

Expected probabilities of voting “yes” (\(\theta_{gm}\))
group
vote 1 2 3
1 0.01 0.01 0.63
2 0.01 0.01 0.61
3 0.48 0.23 0.02
4 0 0 0.87
5 0 0.02 0.87
6 0.11 0.15 0.58
7 0.42 0.1 0.01
8 0.85 0.05 0.02
9 0.85 0.05 0.02
10 0.81 0.05 0.01
11 0.88 0.03 0.02
12 0.92 0.06 0.02
13 0.95 0.07 0.03
14 0.89 0.14 0.03
15 0.27 0.16 0.57
16 0.77 0.16 0.02
17 0.18 0.08 0.68
18 0.31 0.03 0
19 0.54 0.06 0.01
20 0.54 0.04 0.01
21 0 0 0.69
22 0 0 0.68
23 0.35 0.2 0.03
Separation of Dail members into 3 distinctive groups based of their party membership

Separation of Dail members into 3 distinctive groups based of their party membership

Conclusion

We can see that there is a clear separation between the government and the opposition. Fine Gael formed the government together with the Labour party and hence they tend to cast votes similarly. Their group has 90 parliament members. The opposition is separated into two groups: active (46 members) and passive (30 members). The examination of voting pattern for each of the groups (Table 1) shows that active opposition was more disciplined than the other groups. Up to 95% of them turned up to vote “yes” on some of the issues. They were very busy from the 8th vote onwards. The passive opposition was either against most of the tries of changing the legislations or they were absent during the voting. It includes some of the high profile politicians from both the government and opposition, who missed most of their opportunities to vote. The high profile government politicians presence was not necessary if they had the majority, and they could focus on their other tasks. The government balloted “yes” in the beginning and at the end of the term. It looks like they wanted to fulfil some of their promises at the start, and secure the next term at the end, by making changes favourable for the electorate.

This analysis is favourable for the government because the expected opposition against the laws they want to put through the voting is 90:76. In the light of the latest events, it is worth noting that Fianna Fail was Fine Gaels fiercest enemy. They had to overcome a lot of disagreements to form the current government. The only TDs who belong to all of the groups are Independents. It is not surprising, considering randomness between their agendas. Nonetheless, closer inspection shows, that the only Independent TD who is in the same group as the ruling parties (TD Brian Walsh), has never voted.

Appendix (Code)

load("dailvotes.Rdata")
X <- 1*(votes[,-c(1:3)]=='y')

p <- function(x, theta, G=2) {
  M <- ncol(x)
  N <- nrow(x)
  dum <- array(apply(theta, 1, dbinom, size = 1, 
                     x = t(x)), dim = c(M, N, G))
  apply(dum, c(2, 3), prod)
}

sm.lca <- function(x, G=2, iters=20) {
  
  M <- ncol(x)
  N <- nrow(x)
  
  z <- matrix(runif(N * G), N, G)
  z <- z/rowSums(z)
  
  l <- c()
  
  tau <- theta <- NULL
  
  for(k in 1:iters) {

    z.sum <- colSums(z)
    
    theta <- (t(z) %*% x) / (z.sum+1e-6)
    
    tau <- z.sum/N
    
    z1 <- t(t(p(x, theta, G=G)) * tau) + 1e-6
    z <- z1/rowSums(z1)
    
    l <- c(l, sum(colSums(z)*tau) + sum((z%*%log(theta))*x) + 
             sum((1-x)*(z%*%log(1-theta))) - log(N)*((G-1) + M*G))
  }
  
  list(theta=theta, tau=tau, z=z, l=l, G=G)
}


llk.hist <- list()
lca.list <- list()

for (G in 2:8) {

  lca.list[[G]] <- sm.lca(X, G, iters=20)
  llk.hist[[G]] = lca.list[[G]]$l

}

best.bics <- matrix(unlist(llk.hist), nrow=7, byrow=T)[,20]
best.bic <- which.max(best.bics) + 1
best.bic

plot(NULL, ylim=range(llk.hist), xlim=c(1,20), xlab="iteration", ylab="BIC")
mtext("BIC improvement as a function of the number of latent groups")

library(viridis)

col.pal <- viridis(7)
c.idx <- 1
lwd.vals <- rep(1, 7)
lwd.vals[best.bic-1] <- 3

for(g in 2:8) {
  h <- lca.list[[g]]
  lines(h$l, type="b", col=col.pal[c.idx], lwd=1+2*(h$G == best.bic))
  c.idx <- c.idx + 1
}

legend("bottomright", legend = c(2:8), lty=1, col=col.pal, lwd=lwd.vals)



Z <- apply(lca.list[[best.bic]]$z, 1, which.max)

library(ggplot2)
library(magrittr)

votes$class <- Z

votes %>% ggplot(aes(x=Party, fill=Party)) + geom_bar() + 
  facet_wrap(~class) +
  coord_flip() + scale_fill_brewer(palette = "PuOr") + theme_light() +
  theme(axis.text.x=element_text(angle=90), legend.position = "none")