Chapter 3: Immigration Policy and Two Eras of Globalization

Required packages

library(haven)
library(dplyr)
library(tidyr)
library(psych)
library(GPArotation)
library(knitr)
# library(plm)
library(sandwich)
library(lmtest)
library(broom)
library(purrr)
library(readr)

Read data

I downloaded D131F01 D131F03, D131F04, and D131F05 from the browser and put it in data/.

Peter’s code

use "data_opentrade_closedborders_replication_cleanedup.dta", clear
set more off

Pacha’s code

peters2015 <- read_dta("data/Peters_WP_2015_data_opentrade_closedborders_replication_cleanedup.dta")

Table A14: Factor loadings

Peter’s code

factor natcode skillcode qcode reccode labcode famcode refcode asylcode citcode
  rightscode decode encode, pcf factors(2)

predict immipol imrights

Pacha’s code

# subset, otherwise the NAs in the data break the correlation matrix
vars <- c(
  "natcode", "skillcode", "qcode", "reccode", "labcode", "famcode",
  "refcode", "asylcode", "citcode", "rightscode", "decode", "encode"
)

peters2015_factor <- peters2015 %>%
  select(all_of(vars)) %>%
  drop_na()

# fa(..., fm = "pa") uses principal factor
# principal(...) uses principal component
principal_axis <- principal(
  r = peters2015_factor, nfactors = 2, rotate = "none"
)

# predict immipol and imrights
peters2015_predictions <- predict(
  principal_axis,
  data = select(peters2015, all_of(vars))
)

peters2015 <- peters2015 %>%
  mutate(
    immipol = peters2015_predictions[, 1],
    imrights = peters2015_predictions[, 2]
  )

# final table
loadings <- principal_axis$loadings

table3 <- tibble(
  variable = rownames(loadings),
  factor1 = loadings[, 1],
  factor2 = loadings[, 2],
  uniqueness = principal_axis$uniqueness
)

kable(
  table3 %>%
    mutate_if(is.numeric, round, 4) %>%
    rename(
      Variable = variable,
      `Factor Loading Immigration Policy` = factor1,
      `Factor Loading Rights of Immigrants` = factor2,
      Uniqueness = uniqueness
    )
)
Variable Factor Loading Immigration Policy Factor Loading Rights of Immigrants Uniqueness
natcode 0.3868 0.1451 0.8293
skillcode 0.7438 -0.0364 0.4455
qcode 0.4278 -0.4310 0.6313
reccode 0.5485 0.0713 0.6940
labcode 0.4264 0.5465 0.5195
famcode -0.6910 0.4365 0.3321
refcode -0.4836 0.6175 0.3849
asylcode -0.4526 0.4400 0.6016
citcode 0.2434 0.6050 0.5747
rightscode 0.4569 0.6360 0.3868
decode 0.7410 0.4097 0.2831
encode 0.7463 -0.0790 0.4368

Table 3.2: Immigration Policy Regressed on Trade Policy by Era

Peter’s code:

set more off
eststo clear
eststo: xtreg immipol imports_free ltime_trend politycl mad_gdpgrowth war ibn.year, fe vce(robust)
eststo: xtreg immipol imports_free ltime_trend politycl mad_gdpgrowth war ibn.year if year<=1879, fe vce(robust) 
eststo: xtreg immipol imports_free ltime_trend politycl mad_gdpgrowth war ibn.year if year>1879 & year<1914,  fe vce(robust)
eststo: xtreg immipol imports_free ltime_trend politycl mad_gdpgrowth war ibn.year if year>=1914 & year<=1945, fe vce(robust)
eststo: xtreg immipol imports_free ltime_trend politycl mad_gdpgrowth war ibn.year if year>1945 & year<=1972, fe vce(robust)
eststo: xtreg immipol imports_free ltime_trend politycl mad_gdpgrowth war ibn.year if year>1972, fe vce(robust)
eststo: xtreg immipol imports_free ltime_trend politycl mad_gdpgrowth war ibn.year if year>1972 & ccode!=160, fe vce(robust)
esttab using trade_only_by_eras.rtf, label b(2) se(2)  star(+ 0.10 * 0.05 ** 0.01 *** 0.001) ///
title(Immigration policy regressed on trade policy by era \label{tab:era}) ///
mtitles("All Years" "Pre-Globalization" "19th Cen. Globalization" "Interwar"  "Bretton Woods" "Post Bretton Woods" "Post Bretton Woods, No Argentina" ) ///
nogaps r2(2) replace

Pacha’s code

year_range <- list(
  c(-Inf, Inf, "all_years"),
  c(-Inf, 1879, "pre_globalization"),
  c(1879, 1914, "cen19_globalization"),
  c(1914, 1945, "interwar"),
  c(1946, 1972, "bretton_woods"),
  c(1973, Inf, "post_bretton_woods"),
  c(1973, Inf, "post_bretton_woods_no_argentina")
)

all_models <- map_df(
  year_range,
  function(y) {
    print(y)

    peters2015_subset <- peters2015 %>%
      filter(year >= y[1] & year <= y[2])

    if (y[3] == "post_bretton_woods_no_argentina") {
      peters2015_subset <- peters2015_subset %>%
        filter(ccode != 160)
    }

    # peters2015_subset <- pdata.frame(
    #   peters2015_subset,
    #   index = c("ccode", "year")
    # )

    # fml <- immipol ~ imports_free + ltime_trend + politycl + mad_gdpgrowth +
    #   war
    fml <- immipol ~ imports_free + ltime_trend + politycl + mad_gdpgrowth +
      war + as.factor(ccode) + as.factor(year)

    # similar r-sq, different slopes
    # fit <- plm(fml, data = peters2015_subset, model = "within")

    # very different r2, same slopes and I get the "grand mean"
    # very close numbers :)
    fit <- lm(fml, data = peters2015_subset)

    # rse <- coeftest(fit, vcov = vcovHC(fit, type = "HC1", cluster = "group"))
    rse <- coeftest(
      fit,
      vcov = vcovCL(fit, cluster = peters2015_subset[, "ccode"])
    )

    # remove factor variables
    rse <- rse %>%
      tidy() %>%
      filter(!grepl("factor", term))

    out <- rse %>%
      mutate(
        model = y[3],
        sign = case_when(
          p.value < 0.001 ~ "***",
          p.value < 0.01 ~ "**",
          p.value < 0.05 ~ "*",
          p.value < 0.1 ~ "+",
          TRUE ~ ""
        ),
        estimate = paste0(
          round(estimate, 2), sign, " (", round(std.error, 2),
          ")"
        )
      )

    out2 <- fit %>%
      glance() %>%
      select(adj.r.squared, nobs) %>%
      mutate(
        adj.r.squared = round(adj.r.squared, 2),
        model = y[3]
      ) %>%
      pivot_longer(adj.r.squared:nobs,
        names_to = "term",
        values_to = "estimate"
      ) %>%
      mutate(estimate = as.character(estimate))

    out %>%
      bind_rows(out2) %>%
      select(term, model, estimate)
  }
)
[1] "-Inf"      "Inf"       "all_years"
[1] "-Inf"              "1879"              "pre_globalization"
[1] "1879"                "1914"                "cen19_globalization"
[1] "1914"     "1945"     "interwar"
[1] "1946"          "1972"          "bretton_woods"
[1] "1973"               "Inf"                "post_bretton_woods"
[1] "1973"                            "Inf"                            
[3] "post_bretton_woods_no_argentina"
all_models <- all_models %>%
  pivot_wider(names_from = model, values_from = estimate)

# auxiliary table for the final presentation
all_models_aux <- all_models %>%
  select(term) %>%
  mutate(
    term2 = case_when(
      term == "(Intercept)" ~ "Constant",
      term == "imports_free" ~ "Trade openness",
      term == "ltime_trend" ~ "Years since inclusion",
      term == "politycl" ~ "Polity",
      term == "mad_gdpgrowth" ~ "GDP growth",
      term == "war" ~ "War",
      term == "adj.r.squared" ~ "Adj. R-squared",
      term == "nobs" ~ "N. obs"
    )
  )

all_models <- all_models %>%
  left_join(all_models_aux, by = "term")

# final table ----

kable(
  all_models %>%
    select(term2, everything(), -term) %>%
    rename(
      `Variable` = term2,
      `All Years` = all_years,
      `Pre-Globalization` = pre_globalization,
      `19th Cen. Globalization` = cen19_globalization,
      `Interwar` = interwar,
      `Bretton Woods` = bretton_woods,
      `Post Bretton Woods` = post_bretton_woods,
      `Post Bretton Woods, No Argentina` = post_bretton_woods_no_argentina
    ),
  align = c("l", rep("r", 7))
)
Variable All Years Pre-Globalization 19th Cen. Globalization Interwar Bretton Woods Post Bretton Woods Post Bretton Woods, No Argentina
Constant 3.65*** (0.81) 1.72 (1.28) 2.6*** (0.66) 5.7** (2.12) -3.33+ (1.77) 1.55 (1.28) 3.48*** (0.9)
Trade openness -3.04*** (0.9) -1.81* (0.71) -1.71** (0.6) -3.25+ (1.69) -1.27* (0.56) -1.33 (1.22) -3.6** (1.15)
Years since inclusion -0.01*** (0) 0 (0.01) -0.03*** (0) -0.03*** (0.01) 0.02 (0.01) -0.01*** (0) -0.01** (0)
Polity 0.01 (0.01) 0.06* (0.02) 0.15+ (0.09) 0.02 (0.02) 0.02+ (0.01) 0.01+ (0) 0.01 (0.01)
GDP growth 0.17 (0.16) 0.18 (0.18) 0.27** (0.09) -0.16 (0.33) 0.03 (0.36) 0.16 (0.18) 0.01 (0.16)
War 0.17 (0.12) 0.96+ (0.56) -0.02 (0.07) 0.2 (0.22) -0.01 (0.1) -0.03 (0.04) -0.03 (0.04)
Adj. R-squared 0.8 0.97 0.88 0.67 0.91 0.85 0.88
N. obs 1577 77 314 298 325 580 548

Table 3.3: Effect of Trade Policy Lagged on Immigration Policy by Era

I can recycle parts of the replication for Table 3.2.

all_models <- map_df(
  year_range,
  function(y) {
    print(y)

    peters2015_subset <- peters2015 %>%
      filter(year >= y[1] & year <= y[2])

    if (y[3] == "post_bretton_woods_no_argentina") {
      peters2015_subset <- peters2015_subset %>%
        filter(ccode != 160)
    }

    # compute lags
    peters2015_subset <- peters2015_subset %>%
      arrange(ccode, year) %>%
      group_by(ccode) %>%
      mutate(
        imports_free_l1 = lag(imports_free, 1),
        imports_free_l5 = lag(imports_free, 5)
      ) %>%
      ungroup()

    fml1 <- immipol ~ 0 + imports_free_l1 + ltime_trend +
      politycl + mad_gdpgrowth + war + as.factor(ccode) + as.factor(year)

    fml5 <- immipol ~ 0 + imports_free_l5 + ltime_trend +
      politycl + mad_gdpgrowth + war + as.factor(ccode) + as.factor(year)

    # very close numbers :)
    fit1 <- lm(fml1, data = peters2015_subset)
    fit5 <- lm(fml5, data = peters2015_subset)

    rse1 <- coeftest(
      fit1,
      vcov = vcovCL(fit1, cluster = peters2015_subset[, "ccode"])
    )

    rse5 <- coeftest(
      fit5,
      vcov = vcovCL(fit5, cluster = peters2015_subset[, "ccode"])
    )

    # remove factor variables
    rse <- rse1 %>%
      tidy() %>%
      filter(!grepl("factor", term)) %>%
      bind_rows(
        rse5 %>%
          tidy() %>%
          filter(!grepl("factor", term))
      ) %>%
      filter(term %in% c("imports_free_l1", "imports_free_l5"))

    out <- rse %>%
      mutate(
        model = y[3],
        sign = case_when(
          p.value < 0.001 ~ "***",
          p.value < 0.01 ~ "**",
          p.value < 0.05 ~ "*",
          p.value < 0.1 ~ "+",
          TRUE ~ ""
        ),
        estimate = paste0(
          round(estimate, 2), sign, " (", round(std.error, 2),
          ")"
        )
      )

    out %>%
      select(term, model, estimate)
  }
)
[1] "-Inf"      "Inf"       "all_years"
[1] "-Inf"              "1879"              "pre_globalization"
[1] "1879"                "1914"                "cen19_globalization"
[1] "1914"     "1945"     "interwar"
[1] "1946"          "1972"          "bretton_woods"
[1] "1973"               "Inf"                "post_bretton_woods"
[1] "1973"                            "Inf"                            
[3] "post_bretton_woods_no_argentina"
all_models <- all_models %>%
  pivot_wider(names_from = model, values_from = estimate)

# auxiliary table for the final presentation
all_models_aux <- all_models %>%
  select(term) %>%
  mutate(
    term2 = case_when(
      term == "imports_free_l1" ~ "One-year lag",
      term == "imports_free_l5" ~ "Five-year lag"
    )
  )

all_models <- all_models %>%
  left_join(all_models_aux, by = "term")

# final table ----

kable(
  all_models %>%
    select(term2, everything(), -term) %>%
    rename(
      `Variable` = term2,
      `All Years` = all_years,
      `Pre-Globalization` = pre_globalization,
      `19th Cen. Globalization` = cen19_globalization,
      `Interwar` = interwar,
      `Bretton Woods` = bretton_woods,
      `Post Bretton Woods` = post_bretton_woods,
      `Post Bretton Woods, No Argentina` = post_bretton_woods_no_argentina
    ),
  align = c("l", rep("r", 7))
)
Variable All Years Pre-Globalization 19th Cen. Globalization Interwar Bretton Woods Post Bretton Woods Post Bretton Woods, No Argentina
One-year lag -3.09*** (0.88) -1.39 (0.88) -1.67** (0.61) -3.3* (1.63) -1.52** (0.51) -1.42 (1.04) -3.2** (1.08)
Five-year lag -3.05*** (0.74) -1.05 (0.86) -0.8 (0.63) -2.1 (1.35) -1.45* (0.57) 0.1 (1.01) -1.25 (1.14)

Table 3.4: Effect of Trade Policy on Immigration Policy by Era and Economy Type

From page 54:

  • Large economies: Argentina, Australia, Canada, Brazil, France, Germany, Japan, the United Kingdom, and the United States.
  • Small open economies: Hong Kong, the Netherlands, New Zealand, Singapore, South Korea, Switzerland, and Taiwan.
  • Resource economies: Kuwait, Saudi Arabia, and South Africa.
# I need to use ccode, there are missing values in countrynm
sort(unique(peters2015$countrynm))
 [1] ""             "Argentina"    "Australia"    "Brazil"       "Canada"      
 [6] "France"       "Germany"      "Hong Kong"    "Japan"        "Kuwait"      
[11] "Netherlands"  "New Zealand"  "Saudi Arabia" "Singapore"    "South Africa"
[16] "South Korea"  "Switzerland"  "Taiwan"       "UK"           "US"          
# I need to know the ccode
# The PDF in data mentions COW
# Hong Kong = 711 is not in COW
cowurl <- "https://correlatesofwar.org/wp-content/uploads/COW-country-codes.csv"
cowfile <- "data/COW-country-codes.csv"

if (!file.exists(cowfile)) {
  download.file(cowurl, cowfile)
}

cow <- read_csv(cowfile) %>%
  select(ccode = `CCode`, state = `StateNme`) %>%
  distinct() %>%
  mutate(
    economy_type = case_when(
      state %in% c(
        "Argentina", "Australia", "Canada", "Brazil", "France", "Germany",
        "Japan", "United Kingdom", "United States of America"
      ) ~ "large",
      state %in% c(
        "Hong Kong SAR", "Netherlands", "New Zealand", "Singapore",
        "South Korea", "Switzerland", "Taiwan"
      ) ~ "small_open",
      state %in% c(
        "Kuwait", "Saudi Arabia", "South Africa"
      ) ~ "resource"
    )
  ) %>%
  filter(!is.na(economy_type)) %>%
  bind_rows(
    tibble(
      ccode = 711,
      state = "Hong Kong",
      economy_type = "small_open"
    )
  )

# verify
# filter(cow, economy_type == "large")
# filter(cow, economy_type == "small_open")
# filter(cow, economy_type == "resource")

peters2015 <- peters2015 %>%
  left_join(cow, by = "ccode")

# page 57
# small economy: the sum of the coefficient on trade openness and
# the interaction term of small economies with trade opennness
# resource economy: the sum of the coefficient on trade openness and
# the interaction term of resource economies with trade opennness

peters2015 <- peters2015 %>%
  mutate(
    large = ifelse(economy_type == "large", imports_free, 0),
    small_open = ifelse(economy_type == "small_open", imports_free, 0),
    resource = ifelse(economy_type == "resource", imports_free, 0)
  )

all_models <- map_df(
  year_range,
  function(y) {
    print(y)

    peters2015_subset <- peters2015 %>%
      filter(year >= y[1] & year <= y[2])

    if (y[3] == "post_bretton_woods_no_argentina") {
      peters2015_subset <- peters2015_subset %>%
        filter(ccode != 160)
    }

    peters20215_subset_large <- peters2015_subset %>%
      filter(economy_type == "large")

    fml1 <- immipol ~ 0 + imports_free + ltime_trend + politycl +
      mad_gdpgrowth + war + as.factor(ccode) + as.factor(year)

    fml2 <- immipol ~ 0 + small_open + imports_free + ltime_trend +
      politycl + mad_gdpgrowth + war + as.factor(ccode) + as.factor(year)

    fml3 <- immipol ~ 0 + resource + imports_free + ltime_trend +
      politycl + mad_gdpgrowth + war + as.factor(ccode) + as.factor(year)

    # very close numbers :)
    fit1 <- lm(fml1, data = peters20215_subset_large)
    fit2 <- lm(fml2, data = peters2015_subset)
    fit3 <- lm(fml3, data = peters2015_subset)

    vcov1 <- vcovCL(fit1, cluster = peters20215_subset_large[, "ccode"])
    vcov2 <- vcovCL(fit2, cluster = peters2015_subset[, "ccode"])
    vcov3 <- vcovCL(fit3, cluster = peters2015_subset[, "ccode"])

    rse1 <- coeftest(fit1, vcov = vcov1)
    rse2 <- coeftest(fit2, vcov = vcov2)
    rse3 <- coeftest(fit3, vcov = vcov3)

    rse2 <- rse2 %>%
      tidy() %>%
      filter(term == "small_open")

    rse3 <- rse3 %>%
      tidy() %>%
      filter(term == "resource")

    if (nrow(rse2) > 0) {
      se2 <- sqrt(vcov2["small_open", "small_open"] +
        vcov2["imports_free", "imports_free"] +
        2 * vcov2["small_open", "imports_free"])

      pv2 <- 2 * (1 - pnorm(abs(sum(coef(fit2)[
        c("small_open", "imports_free")
      ]) / se2)))

      rse2 <- rse2 %>%
        bind_rows(
          rse1 %>%
            tidy() %>%
            filter(term == "imports_free")
        ) %>%
        mutate(term = "Small economies") %>%
        group_by(term) %>%
        summarise(
          estimate = sum(estimate),
          std.error = se2,
          p.value = pv2
        )
    }

    if (nrow(rse3) > 0) {
      se3 <- sqrt(vcov3["resource", "resource"] +
        vcov3["imports_free", "imports_free"] +
        2 * vcov3["resource", "imports_free"])

      pv3 <- 2 * (1 - pnorm(abs(sum(coef(fit3)[
        c("resource", "imports_free")
      ]) / se3)))

      rse3 <- rse3 %>%
        bind_rows(
          rse1 %>%
            tidy() %>%
            filter(term == "imports_free")
        ) %>%
        mutate(term = "Resource economies") %>%
        group_by(term) %>%
        summarise(
          estimate = sum(estimate),
          std.error = se3,
          p.value = pv3
        )
    }

    # remove factor variables
    rse <- rse1 %>%
      tidy() %>%
      filter(term == "imports_free") %>%
      mutate(term = "Large economies") %>%
      bind_rows(rse2) %>%
      bind_rows(rse3)

    out <- rse %>%
      mutate(
        model = y[3],
        sign = case_when(
          p.value < 0.001 ~ "***",
          p.value < 0.01 ~ "**",
          p.value < 0.05 ~ "*",
          p.value < 0.1 ~ "+",
          TRUE ~ ""
        ),
        estimate = paste0(
          round(estimate, 2), sign, " (", round(std.error, 2),
          ")"
        )
      )

    out %>%
      select(term, model, estimate)
  }
)
[1] "-Inf"      "Inf"       "all_years"
[1] "-Inf"              "1879"              "pre_globalization"
[1] "1879"                "1914"                "cen19_globalization"
[1] "1914"     "1945"     "interwar"
[1] "1946"          "1972"          "bretton_woods"
[1] "1973"               "Inf"                "post_bretton_woods"
[1] "1973"                            "Inf"                            
[3] "post_bretton_woods_no_argentina"
all_models <- all_models %>%
  pivot_wider(names_from = model, values_from = estimate)

kable(
  all_models %>%
    rename(
      `Variable` = term,
      `All Years` = all_years,
      `Pre-Globalization` = pre_globalization,
      `19th Cen. Globalization` = cen19_globalization,
      `Interwar` = interwar,
      `Bretton Woods` = bretton_woods,
      `Post Bretton Woods` = post_bretton_woods,
      `Post Bretton Woods, No Argentina` = post_bretton_woods_no_argentina
    ),
  align = c("l", rep("r", 7))
)
Variable All Years Pre-Globalization 19th Cen. Globalization Interwar Bretton Woods Post Bretton Woods Post Bretton Woods, No Argentina
Large economies -3.45*** (0.95) -1.55+ (0.8) -1.9** (0.69) -3.47+ (1.84) -1.67*** (0.49) -1.27 (1.37) -5.69+ (2.95)
Small economies -0.15 (0.83) -2.11 (1.47) -0.73 (0.82) -7.12*** (1.28) 0.95** (0.34) -1.88 (1.23) -3.22 (1.35)
Resource economies 1.16 (1.64) NA NA NA -4.28*** (0.93) -5.68*** (0.69) -8.04*** (0.77)