Ordered Logit regression

1. Preparation

rm(list = ls())       # delete everything in the memory
library(foreign)      # for read.dta
library(ggplot2)      # for graphics
library(stargazer)    # Regression table
## 
## Please cite as:
##  Hlavac, Marek (2022). stargazer: Well-Formatted Regression and Summary Statistics Tables.
##  R package version 5.2.3. https://CRAN.R-project.org/package=stargazer
library(effects)      # for effect
## Loading required package: carData
## lattice theme set by effectsTheme()
## See ?effectsTheme for details.
library(MASS)         # for plor

2. Load the data

Load the Terris & Maoz data set

tm <- read.dta("terrismaoz05.dta")

Preemptively drop those observations that have missing values on DV or IVs.

tm <- na.omit(tm[c("medintrus","cumversatil","minreg302","caprat","ally1","lagprmed")])

Remember the paper setup

  • RQ: What explains the occurrence / intensity of third-party mediation in international conflict?
  • Some international conflicts experience more intrusive mediation, some experience less intrusive mediation, and others experience none

\[\begin{eqnarray*} Y= \left\{ \begin{array}{ll} 0 & \textrm{No~mediation} \\ 1 & \textrm{Less~intrusive~mediation} \\ 2 & \textrm{More~intrusive~mediation} \\ \end{array} \right. \end{eqnarray*}\]

3. Let’s examine the dependent variable

# DV
table(tm$medintrus)
## 
##                   None Information/Procedural              Directive 
##                   1244                     61                     77

medintrus is the dependent variable that measures the incidence and intrusiveness of mediation.

It has three ordered categorical values. It appears that the categories are ordered and that R recognizes them as such. We can see that R recognizes this because the category “None” is shown first in the data. If the categories were not recognized by R, the category “Directive” would come first because the letter D comes before the letter N or I.

So we don’t have to do anything about this.

If, the categories were indeed messed up, we can correct it by using the factor function with the ordered, levels, and labels options.

4. Estimate an ordered logit model

fit <- polr(medintrus ~ minreg302 + caprat + ally1 + 
              lagprmed + cumversatil, 
            data = tm, Hess=TRUE) # We also specify Hess=TRUE to have the model return the observed information matrix from optimization (called the Hessian) which is used to get standard errors.
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

It gives a Warning message. R is telling us that the predicted probabilities are very close to 0 or 1 for some observations. This might raise some concerns under some circumstances, but we can just ignore this for now.

5. Table of results

stargazer(fit, type = "text")
## 
## =========================================
##                   Dependent variable:    
##               ---------------------------
##                        medintrus         
## -----------------------------------------
## minreg302               0.007**          
##                         (0.003)          
##                                          
## caprat                  -0.008*          
##                         (0.005)          
##                                          
## ally1Alliance          1.066***          
##                         (0.211)          
##                                          
## lagprmed               0.263***          
##                         (0.060)          
##                                          
## cumversatil            0.004***          
##                        (0.0004)          
##                                          
## -----------------------------------------
## Observations             1,382           
## =========================================
## Note:         *p<0.1; **p<0.05; ***p<0.01

If you want to use proper variable names, we can supply them using the covariate.labels option.

stargazer(fit, type = "text", 
          covariate.labels = c("Minimum Regime Score", "Capability Ratio",
                               "Alliance", "Prior Mediation", 
                               "Conflict Versatility"))
## 
## ================================================
##                          Dependent variable:    
##                      ---------------------------
##                               medintrus         
## ------------------------------------------------
## Minimum Regime Score           0.007**          
##                                (0.003)          
##                                                 
## Capability Ratio               -0.008*          
##                                (0.005)          
##                                                 
## Alliance                      1.066***          
##                                (0.211)          
##                                                 
## Prior Mediation               0.263***          
##                                (0.060)          
##                                                 
## Conflict Versatility          0.004***          
##                               (0.0004)          
##                                                 
## ------------------------------------------------
## Observations                    1,382           
## ================================================
## Note:                *p<0.1; **p<0.05; ***p<0.01

If you want to save the table in a file, use the “out” option.

## text file
stargazer(fit, type = "text", out = "table.txt", 
          covariate.labels = c("Minimum Regime Score", "Capability Ratio",
                               "Alliance", "Prior Mediation", 
                               "Conflict Versatility"))
## 
## ================================================
##                          Dependent variable:    
##                      ---------------------------
##                               medintrus         
## ------------------------------------------------
## Minimum Regime Score           0.007**          
##                                (0.003)          
##                                                 
## Capability Ratio               -0.008*          
##                                (0.005)          
##                                                 
## Alliance                      1.066***          
##                                (0.211)          
##                                                 
## Prior Mediation               0.263***          
##                                (0.060)          
##                                                 
## Conflict Versatility          0.004***          
##                               (0.0004)          
##                                                 
## ------------------------------------------------
## Observations                    1,382           
## ================================================
## Note:                *p<0.1; **p<0.05; ***p<0.01
# ## html file (to be opened with an internet browser, such as Chrome)
# stargazer(fit, type = "html", out = "table.html",
#           covariate.labels = c("Minimum Regime Score", "Capability Ratio",
#                                "Alliance", "Prior Mediation", 
#                                "Conflict Versatility"))

6. Find the cut-point estimates

summary(fit)
## Call:
## polr(formula = medintrus ~ minreg302 + caprat + ally1 + lagprmed + 
##     cumversatil, data = tm, Hess = TRUE)
## 
## Coefficients:
##                   Value Std. Error t value
## minreg302      0.007349  0.0033108   2.220
## caprat        -0.008497  0.0046111  -1.843
## ally1Alliance  1.065726  0.2105638   5.061
## lagprmed       0.263148  0.0596778   4.409
## cumversatil    0.003942  0.0003839  10.268
## 
## Intercepts:
##                                  Value   Std. Error t value
## None|Information/Procedural       4.3434  0.3199    13.5767
## Information/Procedural|Directive  5.0789  0.3352    15.1513
## 
## Residual Deviance: 898.8218 
## AIC: 912.8218

We can see that the first cutpoint is 4.3434 and the second cutpoint is 5.0789.

7. Effect of conflict versatility

Calculate the marginal effects of cumversatil

eff.cv <- effect(term = "cumversatil", mod = fit)

eff.cv
## 
## cumversatil effect (probability) for None
## cumversatil
##        90       300       500       700       900 
## 0.9858355 0.9681693 0.9325552 0.8627428 0.7407561 
## 
## cumversatil effect (probability) for Information/Procedural
## cumversatil
##          90         300         500         700         900 
## 0.007325136 0.016317418 0.033942605 0.066407423 0.115600214 
## 
## cumversatil effect (probability) for Directive
## cumversatil
##          90         300         500         700         900 
## 0.006839342 0.015513264 0.033502229 0.070849751 0.143643654

Plot the effects

plot(eff.cv, rescale.axis = FALSE, 
     xlab = "Conflict Versatility", 
     ylab = "Pr(Mediation)", rug = FALSE)

It looks ugly. Let’s do it in ggplot.

library(sjPlot)

plot_model(fit, type = "pred", terms = c("cumversatil [all]"), ci.lvl = 0.95, bpe.style = "line", robust = T, legend.title = "", vcov.type = c("HC3"), colors="bw") + 
  theme_bw() +
  ggtitle(element_blank()) +
  xlab("Conflict Versatility") + ylab("Pr(Mediation)") +
  labs(title = "Effect Plot") +
  theme(plot.title = element_text(hjust = 0.5)) 

You can save a pdf file by doing this:

# Print the plot to a pdf file
g <-  plot_model(fit, type = "pred", terms = c("cumversatil [all]"), ci.lvl = 0.95, bpe.style = "line", robust = T, legend.title = "", vcov.type = c("HC3"), colors="bw") + 
  theme_bw() +
  ggtitle(element_blank()) +
  xlab("Conflict Versatility") + ylab("Pr(Mediation)") +
  labs(title = "Effect Plot") +
  theme(plot.title = element_text(hjust = 0.5)) 

# ggsave(g, filename = "eff_plot.pdf",width = 8, height = 6, dpi = 200, device = "pdf")

You could also do the area plots if you want. But a big downside is that there is no indication of uncertainty. So I won’t recommend it.

eff.cv.mr.max <- effect(term = "cumversatil", mod = fit, given.values = c("minreg302" = 60))
eff.cv.mr.min <- effect(term = "cumversatil", mod = fit, given.values = c("minreg302" = -90))

# Produce the plot for minimum
plot(eff.cv.mr.min, rescale.axis = FALSE, 
     xlab = "Conflict Versatility", ylab = "Pr(Mediation)", 
     rug = FALSE, style = "stacked")

# Produce the plot for maximum
plot(eff.cv.mr.max, rescale.axis = FALSE, 
     main = "The Effect of Conflict Versatility",
     xlab = "Conflict Versatility", ylab = "Pr(Mediation)", 
     rug = FALSE, style = "stacked")