Del 1: Datagrundlag

Indlæsning af pakker

Alle scriptets pakker indlæses her: readxl, data.table og zoo til indlæsning og udfyldning; fixest og plm til panelanalyserne; sandwich og lmtest til bootstrap; did og patchwork til DiD-designet. tidyverse indlæses sidst, så dens funktioner ikke overskrives af de øvrige pakker.

library(readxl)
library(data.table)
library(zoo)
library(fixest)
library(plm)
library(lubridate)
library(here)
library(sandwich)
library(lmtest)
library(did)
library(patchwork)
library(tidyverse)

Lande og tidsperiode

Undersøgelsen omfatter 24 europæiske demokratier (ISO3-koder) i perioden 1990-2024 — i alt 840 land-år. Den udvidede periode fra 1975 bruges til kilder, der kun måler ved valg, så panelets første år ikke står tomme.

lande <- c("AUT","BEL","CHE","CZE","DEU","DNK","ESP","EST","FIN","FRA",
           "GBR","GRC","HUN","IRL","ISL","ITA","LTU","LVA","NLD","NOR",
           "POL","PRT","SVN","SWE")
år <- 1990:2024
år_udvidet <- 1975:2024

V-Dem

V-Dem leverer specialets primære polariseringsmål, v2cacamps: eksperters vurdering af, hvor opdelt et samfund er i fjendtlige politiske lejre (herefter samf_pol). Desuden indlæses to demokratiindeks, korruption og regimeklassifikation som kontroller og dokumentation.

V_Dem <- read_csv("Data/V-Dem/V-Dem data.csv",
                  col_select = c(country_text_id, year, v2cacamps,
                                 v2x_polyarchy, v2x_libdem,
                                 v2x_corr, v2x_regime)) |>
  filter(country_text_id %in% lande, year %in% år) |>
  rename(lande = country_text_id,
         år = year,
         samf_pol = v2cacamps,
         ele_dem = v2x_polyarchy,
         lib_dem = v2x_libdem,
         korruption = v2x_corr,
         regime = v2x_regime)

Vi kontrollerer dimensionerne: 24 lande gange 35 år skal give 840 rækker uden dubletter.

cat(nrow(V_Dem), "rækker,", n_distinct(V_Dem$lande), "unikke lande\n")
840 rækker, 24 unikke lande
dubletter_V_Dem <- V_Dem |> count(lande, år) |> filter(n > 1)
if (nrow(dubletter_V_Dem) == 0) {
  cat("Ingen dubletter (præcis én række pr. land og år)")
} else {
  print(dubletter_V_Dem)
}
Ingen dubletter (præcis én række pr. land og år)

Vi undersøger dækningen af polariseringsmålet pr. land.

V_Dem |>
  group_by(lande) |>
  summarise(mangler = sum(is.na(samf_pol)),
            første_år = min(år[!is.na(samf_pol)])) |>
  arrange(desc(mangler)) |>
  print(n = 24)
# A tibble: 24 × 3
   lande mangler første_år
   <chr>   <int>     <dbl>
 1 ISL        10      2000
 2 AUT         0      1990
 3 BEL         0      1990
 4 CHE         0      1990
 5 CZE         0      1990
 6 DEU         0      1990
 7 DNK         0      1990
 8 ESP         0      1990
 9 EST         0      1990
10 FIN         0      1990
11 FRA         0      1990
12 GBR         0      1990
13 GRC         0      1990
14 HUN         0      1990
15 IRL         0      1990
16 ITA         0      1990
17 LTU         0      1990
18 LVA         0      1990
19 NLD         0      1990
20 NOR         0      1990
21 POL         0      1990
22 PRT         0      1990
23 SVN         0      1990
24 SWE         0      1990

Island mangler 1990-99 (manglende ekspertdækning) og indgår derfor først fra 2000. Interpolation er umulig (hullet ligger i periodens start), og imputation i hovedforklaringsvariablen er fravalgt. Som robusthedscheck estimeres hovedmodellerne senere uden Island (foreløbigt valg — drøftes med vejleder).

summary(V_Dem$samf_pol)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.     NAs 
 -3.813  -2.283  -1.447  -1.239  -0.515   3.355      10 

Vi efterser sample-afgrænsningen med regimeklassifikationen (0-1 er autokratier, 2-3 demokratier).

autokrati_V_Dem <- V_Dem |>
  filter(regime < 2) |>
  group_by(lande, regime) |>
  summarise(antal = n(),
            år = paste(sort(år), collapse = ", "),
            .groups = "drop")

if (nrow(autokrati_V_Dem) == 0) {
  cat("Ingen land-år er klassificeret som autokratier.")
} else {
  print(autokrati_V_Dem)
}
# A tibble: 2 × 4
  lande regime antal år                                      
  <chr>  <dbl> <int> <chr>                                   
1 EST        1     1 1992                                    
2 HUN        1     7 2018, 2019, 2020, 2021, 2022, 2023, 2024

Estland (1992) er et isoleret overgangsår og kræver ingen handling. Ungarn (2018-2024) beholdes i hovedanalysen, fordi den demokratiske tilbagegang er tæt forbundet med netop den polarisering, vi undersøger; som robusthedscheck estimeres hovedmodellerne senere uden Ungarn (foreløbigt valg — drøftes med vejleder). Endelig tjekkes de to demokratiindeks for multikollinearitet.

cat("r =", round(cor(V_Dem$ele_dem, V_Dem$lib_dem, use = "complete.obs"), 2))
r = 0.97

Korrelationen på 0,97 betyder, at de to indeks ikke kan indgå i samme model; i stedet laves en swap-in-robusthed.


Manifesto

Manifesto leverer det sekundære polariseringsmål: den programmatiske polarisering, dvs. hvor langt partierne står fra hinanden i deres valgprogrammer. Datasættet har én række pr. parti pr. valg med højre-venstre-position (rile) og stemmeandel (pervote). Landenavnene står i klartekst, så vi bygger først en navnetabel.

manifesto_navne <- c("Austria"="AUT","Belgium"="BEL","Switzerland"="CHE",
                     "Czech Republic"="CZE","Germany"="DEU","Denmark"="DNK",
                     "Spain"="ESP","Estonia"="EST","Finland"="FIN",
                     "France"="FRA","United Kingdom"="GBR","Greece"="GRC",
                     "Hungary"="HUN","Ireland"="IRL","Iceland"="ISL",
                     "Italy"="ITA","Lithuania"="LTU","Latvia"="LVA",
                     "Netherlands"="NLD","Norway"="NOR","Poland"="POL",
                     "Portugal"="PRT","Slovenia"="SVN","Sweden"="SWE")

Vi indlæser fra 1975 (værdier fra valg før 1990 skal kunne føres frem), udleder valgår og frasorterer partier uden RILE-kodning eller stemmeandel.

Manifesto <- read_excel("Data/Manifesto/Manifesto data.xlsx") |>
  mutate(lande = manifesto_navne[countryname],
         år = year(edate)) |>
  filter(!is.na(lande), år %in% år_udvidet) |>
  select(lande, år, edate, rile, pervote) |>
  drop_na(rile, pervote) |>
  filter(pervote > 0)

cat(n_distinct(Manifesto$lande), "unikke lande,",
    nrow(distinct(Manifesto, lande, edate)), "valg")
24 unikke lande, 294 valg

Polariseringen beregnes pr. valg som en stemmeandelsvægtet standardafvigelse af partiernes RILE-positioner — store partier vægter mere end små.

Manifesto_valg <- Manifesto |>
  group_by(lande, edate, år) |>
  summarise(antal_partier = n(),
            mu = sum((pervote / sum(pervote)) * rile),
            prog_pol = sqrt(sum((pervote / sum(pervote)) * (rile - mu)^2)),
            .groups = "drop")

Fem land-år har to valg samme år; vi beholder årets sidste valg, da det er dét partisystem, der gjaldt fremadrettet. Facit: Danmark 2019 skal give 17,85.

Manifesto_valg <- Manifesto_valg |>
  group_by(lande, år) |>
  slice_max(edate) |>
  ungroup()

facit_M <- Manifesto_valg |> filter(lande == "DNK", år == 2019) |> pull(prog_pol)
if (abs(facit_M - 17.85) < 0.01) {
  cat(nrow(Manifesto_valg), "rækker. Facit ramt: DNK 2019 =", round(facit_M, 2))
} else {
  cat("FEJL: DNK 2019 =", round(facit_M, 2), "— forventet 17.85")
}
289 rækker. Facit ramt: DNK 2019 = 17.85

Som plausibilitetstjek ses de mest og mindst polariserede valg.

Manifesto_valg |> slice_max(prog_pol, n = 5) |> print()
# A tibble: 5 × 6
  lande edate                  år antal_partier    mu prog_pol
  <chr> <dttm>              <dbl>         <int> <dbl>    <dbl>
1 FIN   1983-03-21 00:00:00  1983             8 -7.58     37.4
2 HUN   2018-04-08 00:00:00  2018             7 17.4      33.2
3 CHE   2003-10-19 00:00:00  2003            10 11.5      33.1
4 ITA   2006-04-10 00:00:00  2006            13 15.2      32.9
5 SWE   1985-09-15 00:00:00  1985             6  3.11     32.7
Manifesto_valg |> slice_min(prog_pol, n = 5) |> print()
# A tibble: 5 × 6
  lande edate                  år antal_partier      mu prog_pol
  <chr> <dttm>              <dbl>         <int>   <dbl>    <dbl>
1 PRT   1991-10-06 00:00:00  1991             5 -10.2       2.37
2 AUT   2002-11-24 00:00:00  2002             5 -18.5       2.63
3 POL   2005-09-25 00:00:00  2005             7  -0.377     3.38
4 IRL   2007-05-24 00:00:00  2007             6 -11.8       3.40
5 POL   2007-10-21 00:00:00  2007             5   6.06      3.41

Toppen (Ungarn 2018, Italien 2006, Schweiz 2003) har god ansigtsvaliditet. Bemærk Polen 2005 i bunden: et samfund i dyb politisk konflikt, som RILE-målet næsten ikke registrerer, fordi konflikten var kulturel snarere end økonomisk højre-venstre. Det illustrerer, hvorfor programmatisk og samfundsmæssig polarisering er to forskellige fænomener — specialets kernepræmis.

Valgtabellen strækkes nu til et komplet panel: Værdierne udfyldes mellem valgene på tre måder (kun valgår; sidste værdi føres frem, LOCF; lineær interpolation), og der trimmes til 1990-2024 til sidst. Rækkefølgen er afgørende — udfyldning før trimning, ellers mistes værdier fra valg før 1990.

Manifesto_panel <- expand_grid(lande = lande, år = år_udvidet) |>
  left_join(Manifesto_valg |> select(lande, år, prog_pol),
            by = c("lande", "år")) |>
  arrange(lande, år) |>
  group_by(lande) |>
  mutate(prog_pol_valgår = prog_pol,
         prog_pol_locf = zoo::na.locf(prog_pol, na.rm = FALSE),
         prog_pol_interp = zoo::na.approx(prog_pol, x = år, na.rm = FALSE)) |>
  ungroup() |>
  filter(år %in% .env$år) |>
  select(-prog_pol)

cat(nrow(Manifesto_panel), "rækker")
840 rækker
summary(Manifesto_panel |> select(prog_pol_valgår, prog_pol_locf, prog_pol_interp))
 prog_pol_valgår  prog_pol_locf    prog_pol_interp 
 Min.   : 2.367   Min.   : 2.367   Min.   : 2.367  
 1st Qu.:10.037   1st Qu.:10.087   1st Qu.:10.269  
 Median :14.329   Median :14.431   Median :14.198  
 Mean   :15.208   Mean   :15.264   Mean   :15.149  
 3rd Qu.:18.835   3rd Qu.:19.024   3rd Qu.:18.905  
 Max.   :33.206   Max.   :33.206   Max.   :33.206  
 NAs    :629      NAs    :8        NAs    :98      

De tre versioner har praktisk talt identiske fordelinger — udfyldningsmetoden forvrider ikke målet, men afgør alene, hvilke år der indgår. LOCF-versionen (fuld dækning fra første valg) er analysens hovedversion; de to øvrige bruges som robusthed (foreløbigt valg — drøftes med vejleder).


WUI

World Uncertainty Index måler økonomisk-politisk usikkerhed pr. land og kvartal ud fra hyppigheden af ordet “uncertainty” i Economist Intelligence Units landerapporter. T2-arket indlæses, kvartalerne aggregeres til årsgennemsnit, og der trimmes til analyseperioden. Estland og Island findes ikke i WUI (22 lande).

WUI <- read_excel("Data/World Uncertainty Index/WUI data.xlsx",
                  sheet = "T2") |>
  rename(kvartal = year) |>
  mutate(år = as.integer(str_sub(kvartal, 1, 4))) |>
  select(kvartal, år, any_of(lande)) |>
  pivot_longer(cols = -c(kvartal, år),
               names_to = "lande",
               values_to = "wui_kvartal") |>
  group_by(lande, år) |>
  summarise(wui = mean(wui_kvartal, na.rm = TRUE), .groups = "drop") |>
  mutate(wui = if_else(is.nan(wui), NA_real_, wui)) |>
  filter(år %in% .env$år)

manglende_WUI <- setdiff(lande, unique(WUI$lande))
facit_WUI <- WUI |> filter(lande == "DNK", år == 2020) |> pull(wui)
if (identical(sort(manglende_WUI), c("EST", "ISL")) &&
    abs(facit_WUI - 0.336) < 0.001) {
  cat(nrow(WUI), "rækker. Som ventet mangler EST og ISL.",
      "Facit ramt: DNK 2020 =", round(facit_WUI, 3))
} else {
  cat("FEJL — manglende lande:", manglende_WUI,
      "| DNK 2020 =", round(facit_WUI, 3))
}
770 rækker. Som ventet mangler EST og ISL. Facit ramt: DNK 2020 = 0.336
WUI |> slice_max(wui, n = 5) |> print()
# A tibble: 5 × 3
  lande    år   wui
  <chr> <int> <dbl>
1 GBR    2019 1.18 
2 IRL    2019 1.09 
3 GBR    2016 0.985
4 GBR    2020 0.873
5 CHE    2020 0.821
summary(WUI$wui)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.     NAs 
0.00000 0.08346 0.15236 0.18507 0.25975 1.17975      12 

Toppen er Brexit (Storbritannien og Irland 2016-2020) og Covid — indekset fanger netop de episoder, det skal. Minimum på 0 er en substantiel værdi (“ingen målt usikkerhed”), ikke manglende data.


EPU

Economic Policy Uncertainty måler usikkerhed via avisdækning, men dækker kun 8 af vores lande (de store økonomier). EPU er derfor alene supplement; WUI forbliver det primære usikkerhedsmål. Filen har litteraturreferencer som tekstrækker i bunden, der frasorteres.

epu_navne <- c("DEU" = "Germany", "ESP" = "Spain", "FRA" = "France",
               "GBR" = "UK", "GRC" = "Greece", "IRL" = "Ireland",
               "ITA" = "Italy", "SWE" = "Sweden")

EPU <- read_excel("Data/Economic Policy Uncertainty/EPU data.xlsx") |>
  filter(str_detect(Year, "^\\d+$")) |>
  mutate(år = as.integer(Year), måned = as.integer(Month)) |>
  select(år, måned, all_of(epu_navne)) |>
  pivot_longer(cols = -c(år, måned),
               names_to = "lande",
               values_to = "epu_måned") |>
  group_by(lande, år) |>
  summarise(epu = mean(epu_måned, na.rm = TRUE), .groups = "drop") |>
  mutate(epu = if_else(is.nan(epu), NA_real_, epu)) |>
  filter(år %in% .env$år)

facit_EPU <- EPU |> filter(lande == "DEU", år == 2020) |> pull(epu)
if (abs(facit_EPU - 322.364) < 0.01) {
  cat(nrow(EPU), "rækker. Facit ramt: DEU 2020 =", round(facit_EPU, 3))
} else {
  cat("FEJL: DEU 2020 =", round(facit_EPU, 3), "— forventet 322.364")
}
280 rækker. Facit ramt: DEU 2020 = 322.364

Bemærk: EPU’s nationale indeks er normaliseret forskelligt, så niveauer kan ikke sammenlignes på tværs af lande — kun variation inden for lande er meningsfuld, hvilket netop matcher vores modeller med landefaste effekter.


ParlGov

ParlGov leverer to institutionelle variable: parlamentarisk fragmentering (ENPP, det effektive antal partier) fra valgtabellen og antal regeringsskift pr. år fra kabinettabellen. Kun nationale parlamentsvalg medtages, og alliance-rækker uden sæder frasorteres (verificeret tabsfrit: partiernes sæder summer til parlamentets total i alle 303 valg).

ParlGov_ENPP <- read_csv("Data/ParlGov/ParlGov election data.csv",
                         show_col_types = FALSE) |>
  filter(election_type == "parliament",
         country_name_short %in% lande) |>
  mutate(år = year(election_date)) |>
  filter(år %in% år_udvidet) |>
  select(lande = country_name_short, edate = election_date,
         år, seats, seats_total) |>
  drop_na(seats) |>
  filter(seats > 0) |>
  group_by(lande, edate, år) |>
  summarise(enpp = 1 / sum((seats / sum(seats))^2), .groups = "drop") |>
  group_by(lande, år) |>
  slice_max(edate) |>
  ungroup()

facit_ENPP <- ParlGov_ENPP |> filter(lande == "DNK", år == 2022) |> pull(enpp)
if (abs(facit_ENPP - 7.57) < 0.01) {
  cat(nrow(ParlGov_ENPP), "rækker. Facit ramt: DNK 2022 =", round(facit_ENPP, 2))
} else {
  cat("FEJL: DNK 2022 =", round(facit_ENPP, 2), "— forventet 7.57")
}
297 rækker. Facit ramt: DNK 2022 = 7.57

ENPP strækkes til panel med LOCF — her er det ikke en tilnærmelse, men eksakt: Parlamentets sammensætning er per definition konstant mellem valg.

ParlGov_panel <- expand_grid(lande = lande, år = år_udvidet) |>
  left_join(ParlGov_ENPP |> select(lande, år, enpp),
            by = c("lande", "år")) |>
  arrange(lande, år) |>
  group_by(lande) |>
  mutate(enpp = zoo::na.locf(enpp, na.rm = FALSE)) |>
  ungroup() |>
  filter(år %in% .env$år)

cat(nrow(ParlGov_panel), "rækker")
840 rækker

Regeringsskift tælles pr. land-år. Nul er her en substantiel værdi (ingen skift = stabilitet), så år uden kabinetter 0-fyldes — men kun til og med 2022, da kildens snapshot slutter medio 2023; 2023-24 står som NA (foreløbigt valg — drøftes med vejleder).

ParlGov_skift <- read_csv("Data/ParlGov/ParlGov cabinet data.csv",
                          show_col_types = FALSE) |>
  filter(country_name_short %in% lande) |>
  distinct(lande = country_name_short, cabinet_id, start_date) |>
  mutate(år = year(start_date)) |>
  filter(år %in% .env$år) |>
  count(lande, år, name = "reg_skift") |>
  right_join(expand_grid(lande = lande, år = .env$år),
             by = c("lande", "år")) |>
  mutate(reg_skift = case_when(
    !is.na(reg_skift) ~ reg_skift,
    år <= 2022 ~ 0L,
    TRUE ~ NA_integer_))

facit_skift <- ParlGov_skift |> filter(lande == "GRC", år == 2012) |> pull(reg_skift)
if (identical(facit_skift, 2L)) {
  cat(nrow(ParlGov_skift), "rækker. Facit ramt: GRC 2012 =", facit_skift, "skift")
} else {
  cat("FEJL: GRC 2012 =", facit_skift, "— forventet 2")
}
840 rækker. Facit ramt: GRC 2012 = 2 skift

WID

World Inequality Database leverer ulighedsmålene: top-10 %- og bund-50 %-indkomstandelen (serien sptincj992, præskat-nationalindkomst). Data kommer som 24 landefiler (ca. 656 MB), så en indlæsningsfunktion læser hver fil hurtigt og filtrerer straks til det nødvendige.

wid_navne <- c("AT"="AUT","BE"="BEL","CH"="CHE","CZ"="CZE","DE"="DEU",
               "DK"="DNK","EE"="EST","ES"="ESP","FI"="FIN","FR"="FRA",
               "GB"="GBR","GR"="GRC","HU"="HUN","IE"="IRL","IS"="ISL",
               "IT"="ITA","LT"="LTU","LV"="LVA","NL"="NLD","NO"="NOR",
               "PL"="POL","PT"="PRT","SE"="SWE","SI"="SVN")

wid_filer <- file.path("Data/World Inequality Database",
                       paste0("WID_data_", names(wid_navne), ".csv"))

læs_wid <- function(fil) {
  fread(fil, sep = ";",
        select = c("country", "variable", "percentile", "year", "value")) |>
    as_tibble() |>
    filter(variable == "sptincj992",
           percentile %in% c("p0p50", "p90p100"),
           year %in% .env$år)
}

WID <- map(wid_filer, læs_wid) |>
  list_rbind() |>
  mutate(lande = wid_navne[country]) |>
  select(lande, år = year, percentile, value) |>
  pivot_wider(names_from = percentile, values_from = value) |>
  rename(bund50 = p0p50, top10 = p90p100)

facit_t <- WID |> filter(lande == "DNK", år == 2020) |> pull(top10)
facit_b <- WID |> filter(lande == "DNK", år == 2020) |> pull(bund50)
if (abs(facit_t - 0.3324) < 0.001 && abs(facit_b - 0.2162) < 0.001) {
  cat(nrow(WID), "rækker. Facit ramt: DNK 2020 top10 =", round(facit_t, 4),
      ", bund50 =", round(facit_b, 4))
} else {
  cat("FEJL: DNK 2020 top10 =", round(facit_t, 4),
      ", bund50 =", round(facit_b, 4))
}
840 rækker. Facit ramt: DNK 2020 top10 = 0.3324 , bund50 = 0.2162

WID er panelets eneste kilde helt uden manglende værdier — dækningen er dog delvist modelgenereret (WID interpolerer mellem observationsår), hvilket accepteres, da ulighed indgår som kanalvariabel, ikke hovedforklaring (foreløbigt valg — drøftes med vejleder).


World Bank

Verdensbanken leverer de makroøkonomiske udfald og kontroller: BNP-vækst (primært udfald), investering (kanaludfald) samt BNP per capita, inflation, arbejdsløshed, handel og befolkning som kontroller. Filerne har 4 metadatarækker øverst (skip = 4). Statsgæld er inspiceret og fravalgt (637 af 840 land-år mangler; foreløbigt valg — IMF WEO som alternativ drøftes med vejleder).

wb_filer <- c(
  bnp_vækst     = "World Bank data BNP-vækst.csv",
  bnp_pc        = "World Bank data BNP per capita.csv",
  investering   = "World Bank data investering.csv",
  inflation     = "World Bank data inflation.csv",
  arbejdsløshed = "World Bank data arbejdsløshed.csv",
  handel        = "World Bank data handel.csv",
  befolkning    = "World Bank data befolkning.csv") |>
  (\(x) setNames(file.path("Data/World Bank", x), names(x)))()

læs_wb <- function(fil, varnavn) {
  read_csv(fil, skip = 4, show_col_types = FALSE,
           name_repair = "unique_quiet") |>
    filter(`Country Code` %in% lande) |>
    select(lande = `Country Code`, matches("^\\d{4}$")) |>
    pivot_longer(cols = -lande,
                 names_to = "år",
                 values_to = varnavn,
                 names_transform = as.integer) |>
    filter(år %in% .env$år)
}

WB <- imap(wb_filer, læs_wb) |>
  reduce(full_join, by = c("lande", "år"))

facit_wb <- WB |> filter(lande == "DNK", år == 2020) |> pull(bnp_vækst)
if (abs(facit_wb - (-1.780)) < 0.001) {
  cat(nrow(WB), "rækker. Facit ramt: DNK 2020 =", round(facit_wb, 3))
} else {
  cat("FEJL: DNK 2020 =", round(facit_wb, 3), "— forventet -1.780")
}
840 rækker. Facit ramt: DNK 2020 = -1.78
WB |> select(lande, år, bnp_vækst) |> slice_max(bnp_vækst, n = 3) |> print()
# A tibble: 3 × 3
  lande    år bnp_vækst
  <chr> <int>     <dbl>
1 IRL    2015      24.6
2 IRL    2021      16.3
3 EST    1997      13.1
WB |> select(lande, år, bnp_vækst) |> slice_min(bnp_vækst, n = 3) |> print()
# A tibble: 3 × 3
  lande    år bnp_vækst
  <chr> <int>     <dbl>
1 LVA    1992     -32.1
2 LTU    1992     -21.3
3 EST    1992     -21.2

Bunden er de baltiske transitionskollaps (Letland 1992: -32,1 pct.) — reelle værdier. Toppen kræver kildebevidsthed: Irland 2015 (24,6 pct.) er den kendte “leprechaun economics”-episode, hvor multinationale selskabers papirflytninger fik BNP til at springe uden reel vækst. Derfor indgår outlier-robusthed (winsorisering/uden Irland) senere i analysen (foreløbigt valg — drøftes med vejleder).


Penn World Table

PWT 11.0 leverer tre strukturkontroller: humankapital (hc), totalfaktorproduktivitet (ctfp) og arbejdskraftens indkomstandel (labsh). Versionen dækker til 2023, så 2024 står tomt.

PWT <- read_excel("Data/Penn World table/Penn World table data.xlsx",
                  sheet = "Data") |>
  select(lande = countrycode, år = year, hc, ctfp, labsh) |>
  filter(lande %in% .env$lande, år %in% .env$år)

facit_PWT <- PWT |> filter(lande == "DNK", år == 2023)
if (abs(facit_PWT$hc - 3.666) < 0.001 &&
    abs(facit_PWT$ctfp - 0.929) < 0.001 &&
    abs(facit_PWT$labsh - 0.601) < 0.001) {
  cat(nrow(PWT), "rækker. Facit ramt: DNK 2023 hc =", round(facit_PWT$hc, 3),
      ", ctfp =", round(facit_PWT$ctfp, 3),
      ", labsh =", round(facit_PWT$labsh, 3))
} else {
  cat("FEJL i PWT-facit")
}
816 rækker. Facit ramt: DNK 2023 hc = 3.666 , ctfp = 0.929 , labsh = 0.601

Samling og frysning af panelet

De ni kilder samles til ét analysepanel. Skabelonen med alle 840 land-år definerer rækkerne; kilder med mindre dækning fylder NA. Til sidst fryses panelet til fil, så analysen kan genindlæses uden hele pipelinen.

panel <- list(V_Dem, Manifesto_panel, WUI, EPU,
              ParlGov_panel, ParlGov_skift, WID, WB, PWT) |>
  reduce(left_join, by = c("lande", "år"),
         .init = expand_grid(lande = lande, år = .env$år))

dubletter_panel <- panel |> count(lande, år) |> filter(n > 1)
kerne <- panel |>
  filter(!is.na(samf_pol), !is.na(prog_pol_locf), !is.na(bnp_vækst)) |>
  nrow()

cat(nrow(panel), "rækker,", ncol(panel), "kolonner.",
    ifelse(nrow(dubletter_panel) == 0, "Ingen dubletter.", "DUBLETTER!"),
    "\nKomplette kernerækker (begge polariseringsmål + vækst):", kerne)
840 rækker, 26 kolonner. Ingen dubletter. 
Komplette kernerækker (begge polariseringsmål + vækst): 820

820 af 840 land-år har begge polariseringsmål og BNP-vækst — hovedanalysens maksimale sample. Vi måler nu sammenhængen mellem de to polariseringsmål.

pol_within <- panel |>
  group_by(lande) |>
  mutate(samf_within = samf_pol - mean(samf_pol, na.rm = TRUE),
         prog_within = prog_pol_locf - mean(prog_pol_locf, na.rm = TRUE)) |>
  ungroup()

cat("Rå korrelation: r =",
    round(cor(panel$samf_pol, panel$prog_pol_locf,
              use = "complete.obs"), 2), "\n")
Rå korrelation: r = 0.08 
cat("Within-korrelation: r =",
    round(cor(pol_within$samf_within, pol_within$prog_within,
              use = "complete.obs"), 2))
Within-korrelation: r = 0.26

De to mål fanger beslægtede, men klart forskellige fænomener: Den rå korrelation er nær nul (0,08), og selv inden for landene over tid er samvariationen kun moderat (0,26). Det bekræfter specialets kernepræmis — man kan ikke måle den ene slags polarisering og tro, man har fanget den anden.

saveRDS(panel, "Data/analysepanel.rds")
kontrol_panel <- readRDS("Data/analysepanel.rds")

if (identical(panel, kontrol_panel)) {
  cat("Panelet er frosset: Data/analysepanel.rds,",
      nrow(panel), "rækker og", ncol(panel), "kolonner")
} else {
  cat("FEJL: Det gemte panel afviger fra panelet i hukommelsen")
}
Panelet er frosset: Data/analysepanel.rds, 840 rækker og 26 kolonner

Validering af polariseringsmålet (CSES)

Specialets primære mål er ekspertbaseret. Som validering beregnes to uafhængige, surveybaserede mål for affektiv polarisering fra CSES (187.142 respondenter, 102 valg): Wagners (2021) spredning af vælgernes partifølelser og Reiljans (2020) API, der måler forskellen mellem, hvad vælgere føler for eget parti og for de øvrige. Missing-koder følger CSES-codebooken (vurderinger >10, stemmeandele ≥997).

cses_fil <- "Data/CSES/CSES data.csv"
CSES <- fread(cses_fil,
              select = c("IMD1006_UNALPHA3", "IMD1008_YEAR", "IMD1010_1",
                         paste0("IMD3008_", LETTERS[1:9]),
                         paste0("IMD5001_", LETTERS[1:9]))) |>
  as_tibble() |>
  rename(lande = IMD1006_UNALPHA3, år = IMD1008_YEAR, vægt = IMD1010_1) |>
  filter(lande %in% .env$lande) |>
  mutate(respondent = row_number())

cat(nrow(CSES), "respondenter,", n_distinct(CSES$lande), "lande")
187142 respondenter, 24 lande

Wagner-målet: pr. respondent beregnes den stemmeandelsvægtede spredning af partivurderingerne, som aggregeres til valgniveau med samplevægte.

CSES_valg <- CSES |>
  pivot_longer(cols = matches("^IMD(3008|5001)_"),
               names_pattern = "(IMD3008|IMD5001)_(.)",
               names_to = c(".value", "parti")) |>
  rename(vurdering = IMD3008, stemmeandel = IMD5001) |>
  mutate(vurdering = if_else(vurdering > 10, NA_real_, as.numeric(vurdering)),
         stemmeandel = if_else(stemmeandel >= 997, NA_real_,
                               as.numeric(stemmeandel))) |>
  drop_na(vurdering, stemmeandel) |>
  filter(stemmeandel > 0) |>
  group_by(lande, år, respondent, vægt) |>
  summarise(antal_partier = n(),
            mu = sum((stemmeandel / sum(stemmeandel)) * vurdering),
            spredning = sqrt(sum((stemmeandel / sum(stemmeandel)) *
                                   (vurdering - mu)^2)),
            .groups = "drop") |>
  filter(antal_partier >= 2) |>
  mutate(vægt = if_else(is.finite(vægt), vægt, 1)) |>
  group_by(lande, år) |>
  summarise(aff_pol = weighted.mean(spredning, vægt), .groups = "drop")

cat(nrow(CSES_valg), "valg med Wagner-mål")
102 valg med Wagner-mål

Reiljan-API’et kræver vælgernes partivalg, som kobles til partierne via CSES’ partikort.

CSES_R <- fread(cses_fil,
                select = c("IMD1006_UNALPHA3", "IMD1008_YEAR", "IMD1010_1",
                           "IMD3002_LH_PL", "IMD3002_LH_DC",
                           paste0("IMD3008_", LETTERS[1:9]),
                           paste0("IMD5000_", LETTERS[1:9]),
                           paste0("IMD5001_", LETTERS[1:9]))) |>
  as_tibble() |>
  rename(lande = IMD1006_UNALPHA3, år = IMD1008_YEAR, vægt = IMD1010_1) |>
  filter(lande %in% .env$lande) |>
  mutate(respondent = row_number(),
         vægt = if_else(is.finite(vægt), vægt, 1),
         stemme = if_else(IMD3002_LH_PL < 9999990,
                          IMD3002_LH_PL, IMD3002_LH_DC))

partikort <- CSES_R |>
  select(lande, år, starts_with("IMD5000_")) |>
  distinct() |>
  pivot_longer(cols = starts_with("IMD5000_"),
               names_pattern = "IMD5000_(.)",
               names_to = "vælgerparti",
               values_to = "partikode") |>
  filter(partikode < 9999990) |>
  distinct(lande, år, vælgerparti, partikode)

CSES_api <- CSES_R |>
  inner_join(partikort, by = c("lande", "år", "stemme" = "partikode")) |>
  pivot_longer(cols = matches("^IMD(3008|5001)_"),
               names_pattern = "(IMD3008|IMD5001)_(.)",
               names_to = c(".value", "vurderet_parti")) |>
  rename(vurdering = IMD3008, stemmeandel = IMD5001) |>
  mutate(vurdering = if_else(vurdering > 10, NA_real_, as.numeric(vurdering)),
         stemmeandel = if_else(stemmeandel >= 997, NA_real_,
                               as.numeric(stemmeandel))) |>
  drop_na(vurdering, stemmeandel) |>
  filter(stemmeandel > 0) |>
  group_by(lande, år, vælgerparti, vurderet_parti) |>
  summarise(gns_vurdering = weighted.mean(vurdering, vægt),
            stemmeandel = first(stemmeandel),
            .groups = "drop") |>
  group_by(lande, år, vælgerparti) |>
  mutate(egen = gns_vurdering[vurderet_parti == vælgerparti][1]) |>
  filter(!is.na(egen), vurderet_parti != vælgerparti) |>
  summarise(egen = first(egen),
            vs_egen = first(stemmeandel[1]),
            fjendtlighed = sum((egen - gns_vurdering) *
                                 (stemmeandel / sum(stemmeandel))),
            .groups = "drop_last") |>
  mutate(vs_norm = vs_egen / sum(vs_egen)) |>
  summarise(api_pol = sum(vs_norm * fjendtlighed), .groups = "drop")

cat(nrow(CSES_api), "valg med Reiljan-API")
102 valg med Reiljan-API

Begge mål korreleres nu med v2cacamps — inden for lande, som analysen identificerer på.

validering <- CSES_valg |>
  inner_join(CSES_api, by = c("lande", "år")) |>
  inner_join(V_Dem |> select(lande, år, samf_pol), by = c("lande", "år")) |>
  drop_na(samf_pol) |>
  group_by(lande) |>
  filter(n() >= 2) |>
  mutate(across(c(aff_pol, api_pol, samf_pol), \(x) x - mean(x))) |>
  ungroup()

cat("Within-korrelationer med samf_pol:\n")
Within-korrelationer med samf_pol:
cat("Wagner (følelsesspredning): r =",
    round(cor(validering$aff_pol, validering$samf_pol,
              use = "complete.obs"), 2), "\n")
Wagner (følelsesspredning): r = 0.01 
cat("Reiljan-API (lejrdannelse): r =",
    round(cor(validering$api_pol, validering$samf_pol,
              use = "complete.obs"), 2))
Reiljan-API (lejrdannelse): r = 0.34

Valideringen viser både konvergent og diskriminant validitet: v2cacamps korrelerer med Reiljans lejrdannelses-mål (within-r = 0,34), men ikke med Wagners rene følelsesspredning (r ≈ 0). Det bekræfter, at v2cacamps måler netop dét, vi bruger det til — opdeling i fjendtlige lejre, ikke blot spredte følelser.


Del 2: Analyse

Blok 0: Deskriptiv analyse

Analysen indledes visuelt. De to polariseringsmål z-standardiseres inden for hvert land (de lever på forskellige skalaer) og plottes pr. land — det viser præcis den variation over tid, modellerne senere identificerer på.

panel |>
  group_by(lande) |>
  mutate(samf_z = as.numeric(scale(samf_pol)),
         prog_z = as.numeric(scale(prog_pol_locf))) |>
  ungroup() |>
  select(lande, år, samf_z, prog_z) |>
  pivot_longer(cols = c(samf_z, prog_z),
               names_to = "mål", values_to = "værdi") |>
  mutate(mål = recode(mål,
                      samf_z = "Samfundsmæssig (V-Dem)",
                      prog_z = "Programmatisk (Manifesto, LOCF)")) |>
  ggplot(aes(x = år, y = værdi, colour = mål)) +
  geom_line(linewidth = 0.4, na.rm = TRUE) +
  facet_wrap(~ lande, ncol = 4) +
  scale_colour_manual(values = c("Samfundsmæssig (V-Dem)" = "#c0392b",
                                 "Programmatisk (Manifesto, LOCF)" = "#2c3e50")) +
  labs(x = NULL, y = "Standardiseret værdi (within land)", colour = NULL) +
  theme_minimal(base_size = 9) +
  theme(legend.position = "bottom")

Tre mønstre træder frem: I Ungarn stiger begge mål sammen (Orbán-æraen); i Polen divergerer de (samfundskonflikt uden programmatisk afstand — 2005-eksemplet i grafisk form); og i næsten alle lande stiger den samfundsmæssige polarisering markant efter 2020 — en fælles europæisk bevægelse, som partiprogrammerne ikke følger.

panel |>
  select(samf_pol, prog_pol_locf, bnp_vækst, wui, epu, enpp, reg_skift,
         top10, investering, inflation, arbejdsløshed, handel) |>
  pivot_longer(everything(), names_to = "variabel", values_to = "værdi") |>
  group_by(variabel) |>
  summarise(n = sum(!is.na(værdi)),
            gns = mean(værdi, na.rm = TRUE),
            sd = sd(værdi, na.rm = TRUE),
            min = min(værdi, na.rm = TRUE),
            max = max(værdi, na.rm = TRUE)) |>
  mutate(across(c(gns, sd, min, max), \(x) round(x, 2))) |>
  arrange(desc(n)) |>
  print(n = 12)
# A tibble: 12 × 6
   variabel          n    gns    sd    min     max
   <chr>         <int>  <dbl> <dbl>  <dbl>   <dbl>
 1 top10           840   0.33  0.04   0.21    0.45
 2 enpp            838   4.38  1.61   1.98   10.8 
 3 bnp_vækst       834   2.04  4.01 -32.1    24.6 
 4 prog_pol_locf   832  15.3   6.63   2.37   33.2 
 5 inflation       831   8.77 58.1   -4.45 1021.  
 6 samf_pol        830  -1.24  1.31  -3.81    3.36
 7 handel          821  95.3  40.3   33.8   259.  
 8 investering     821  23.3   4.36  11.9    53.7 
 9 arbejdsløshed   816   7.97  4.33   1.1    27.7 
10 reg_skift       795   0.52  0.64   0       3   
11 wui             758   0.19  0.14   0       1.18
12 epu             247 137.   94.7   37.6   740.  

Yderpunkterne er de kendte episoder: Letlands transitionskollaps (-32,1), Irlands leprechaun-år (24,6) og de baltiske hyperinflationsår (1.021 pct.).

Som bro til analysen vises kanalkædens tre led for fire lande med hver sin rolle: Storbritannien (kæden polarisering → usikkerhed → vækst), Grækenland (den omvendte retning), samt Ungarn og Polen (høj polarisering, upåvirket økonomi).

panel |>
  filter(lande %in% c("GBR", "GRC", "HUN", "POL")) |>
  group_by(lande) |>
  mutate(across(c(samf_pol, wui, bnp_vækst), \(x) as.numeric(scale(x)))) |>
  ungroup() |>
  select(lande, år, samf_pol, wui, bnp_vækst) |>
  pivot_longer(cols = c(samf_pol, wui, bnp_vækst),
               names_to = "led", values_to = "værdi") |>
  mutate(led = factor(led,
                      levels = c("samf_pol", "wui", "bnp_vækst"),
                      labels = c("Samfundsmæssig polarisering",
                                 "Usikkerhed (WUI)", "BNP-vækst"))) |>
  ggplot(aes(x = år, y = værdi, colour = led)) +
  geom_hline(yintercept = 0, linewidth = 0.3, colour = "grey70") +
  geom_line(linewidth = 0.5, na.rm = TRUE) +
  facet_wrap(~ lande, ncol = 2) +
  scale_colour_manual(values = c("Samfundsmæssig polarisering" = "#c0392b",
                                 "Usikkerhed (WUI)" = "#27ae60",
                                 "BNP-vækst" = "#2c3e50")) +
  labs(x = NULL, y = "Standardiseret værdi (within land)", colour = NULL) +
  theme_minimal(base_size = 10) +
  theme(legend.position = "bottom")

Grækenland viser den omvendte rækkefølge tydeligst: Økonomien kollapser 2009-12, før polariseringen stiger — og afpolariserer igen under normaliseringen efter 2019.


Blok 1: Paneldiagnostik

Før modellerne testes nøglevariablene for enhedsrødder (“vandrer serien uden anker?”). Ikke-stationære variable kan give falske sammenhænge. Nulhypotesen er en unit root; lave p-værdier betyder stationaritet. IPS-testen tillader landespecifikke parametre, Maddala-Wu håndterer panelets huller.

ur_variable <- c("samf_pol", "prog_pol_locf", "bnp_vækst", "wui")

kør_purtest <- function(data, variabel, exo) {
  klar <- data |>
    group_by(lande) |>
    filter(sum(!is.na(.data[[variabel]])) >= 20) |>
    ungroup() |>
    select(lande, år, all_of(variabel)) |>
    pdata.frame(index = c("lande", "år"))

  ips <- purtest(klar[[variabel]], test = "ips", exo = exo,
                 lags = "AIC", pmax = 2)
  mw  <- purtest(klar[[variabel]], test = "madwu", exo = exo,
                 lags = "AIC", pmax = 2)
  cat(variabel, "( exo =", exo, "):\n",
      "  IPS:        p =", format.pval(ips$statistic$p.value, digits = 3), "\n",
      "  Maddala-Wu: p =", format.pval(mw$statistic$p.value, digits = 3), "\n")
}

for (v in ur_variable) kør_purtest(panel, v, exo = "intercept")
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
Warning in purtest(klar[[variabel]], test = "madwu", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
samf_pol ( exo = intercept ):
   IPS:        p = 1 
   Maddala-Wu: p = 0.78 
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
prog_pol_locf ( exo = intercept ):
   IPS:        p = 1.66e-06 
   Maddala-Wu: p = 5.22e-08 
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
bnp_vækst ( exo = intercept ):
   IPS:        p = <2e-16 
   Maddala-Wu: p = <2e-16 
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
wui ( exo = intercept ):
   IPS:        p = <2e-16 
   Maddala-Wu: p = <2e-16 
cat("\nSupplerende: samf_pol med deterministisk trend\n")

Supplerende: samf_pol med deterministisk trend
kør_purtest(panel, "samf_pol", exo = "trend")
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
Warning in purtest(klar[[variabel]], test = "ips", exo = exo, lags = "AIC", :
NA value(s) encountered and dropped, results may not be reliable
samf_pol ( exo = trend ):
   IPS:        p = 0.979 
   Maddala-Wu: p = 0.233 

BNP-vækst, WUI og det programmatiske mål er klart stationære. Den samfundsmæssige polarisering er det ikke — heller ikke med trend. Konsekvensen trækkes gennem hele analysen: samf_pol indgår som førstedifferens (årets ændring), hvor den optræder som udfald eller i VAR-modellerne, mens H1-hovedmodellen i niveau suppleres med en obligatorisk differens-robusthed.


Blok 2: H1 — polarisering og økonomisk vækst

Først bygges alle analysens afledte variable ét sted: z-standardiserede polariseringsmål (koefficienter kan da sammenlignes direkte), lags af alle forklaringer (sidste års værdi forklarer årets vækst — det reducerer simultanitet), differenser, winsoriseret vækst, kriseår-dummy og regionsinddeling.

analysedata <- panel |>
  mutate(samf_z = as.numeric(scale(samf_pol)),
         prog_z = as.numeric(scale(prog_pol_locf)),
         prog_valgår_z = as.numeric(scale(prog_pol_valgår)),
         prog_interp_z = as.numeric(scale(prog_pol_interp)),
         log_bnp_pc = log(bnp_pc),
         vækst_wins = pmin(pmax(bnp_vækst,
                                quantile(bnp_vækst, 0.01, na.rm = TRUE)),
                           quantile(bnp_vækst, 0.99, na.rm = TRUE)),
         region = case_when(
           lande %in% c("CZE","EST","HUN","LTU","LVA","POL","SVN") ~ "CEE",
           lande %in% c("ESP","GRC","ITA","PRT") ~ "Syd",
           TRUE ~ "Vest")) |>
  arrange(lande, år) |>
  group_by(lande) |>
  mutate(d_samf_z = samf_z - lag(samf_z),
         samf_z_l1 = lag(samf_z),
         prog_z_l1 = lag(prog_z),
         prog_valgår_z_l1 = lag(prog_valgår_z),
         prog_interp_z_l1 = lag(prog_interp_z),
         d_samf_z_l1 = lag(d_samf_z),
         d_samf_z_l2 = lag(d_samf_z, 2),
         bnp_vækst_l1 = lag(bnp_vækst),
         bnp_vækst_l2 = lag(bnp_vækst, 2),
         log_bnp_pc_l1 = lag(log_bnp_pc),
         handel_l1 = lag(handel),
         arbejdsløshed_l1 = lag(arbejdsløshed),
         inflation_l1 = lag(inflation),
         top10_l1 = lag(top10),
         wui_l1 = lag(wui),
         investering_l1 = lag(investering),
         ele_dem_l1 = lag(ele_dem),
         lib_dem_l1 = lag(lib_dem),
         krise_l1 = lag(as.integer(bnp_vækst < 0))) |>
  ungroup()

H1 tester, om polarisering koster vækst. Fire modeller med landefaste og årsfaste effekter (TWFE) og standardfejl klynget på lande: bivariat, med kontroller, det programmatiske mål, og begge mål samtidig (“horse-race”).

h1_m1 <- feols(bnp_vækst ~ samf_z_l1 | lande + år,
               data = analysedata, cluster = ~ lande)
NOTE: 34 observations removed because of NA values (LHS: 6, RHS: 34).
h1_m2 <- feols(bnp_vækst ~ samf_z_l1 + log_bnp_pc_l1 + handel_l1 +
                 arbejdsløshed_l1 + inflation_l1 | lande + år,
               data = analysedata, cluster = ~ lande)
NOTE: 72 observations removed because of NA values (LHS: 6, RHS: 72).
h1_m3 <- feols(bnp_vækst ~ prog_z_l1 + log_bnp_pc_l1 + handel_l1 +
                 arbejdsløshed_l1 + inflation_l1 | lande + år,
               data = analysedata, cluster = ~ lande)
NOTE: 63 observations removed because of NA values (LHS: 6, RHS: 63).
h1_m4 <- feols(bnp_vækst ~ samf_z_l1 + prog_z_l1 + log_bnp_pc_l1 + handel_l1 +
                 arbejdsløshed_l1 + inflation_l1 | lande + år,
               data = analysedata, cluster = ~ lande)
NOTE: 72 observations removed because of NA values (LHS: 6, RHS: 72).
etable(h1_m1, h1_m2, h1_m3, h1_m4,
       headers = c("Bivariat", "Samf + kontroller",
                   "Prog + kontroller", "Horse-race"),
       digits = 3, fitstat = ~ n + r2 + wr2)
                          h1_m1             h1_m2             h1_m3
                       Bivariat Samf + kontroller Prog + kontroller
Dependent Var.:      bnp_vækst        bnp_vækst        bnp_vækst
                                                                   
samf_z_l1        -1.38. (0.765)   -0.458* (0.212)                  
log_bnp_pc_l1                      -6.07** (1.61)   -5.75*** (1.47)
handel_l1                         0.061** (0.017)   0.059** (0.017)
arbejdsløshed_l1                   -0.059 (0.070)    -0.041 (0.065)
inflation_l1                       -0.040 (0.025)    -0.034 (0.023)
prog_z_l1                                            -0.288 (0.202)
Fixed-Effects:   --------------   ---------------   ---------------
lande                       Yes               Yes               Yes
år                         Yes               Yes               Yes
________________ ______________   _______________   _______________
S.E.: Clustered       by: lande         by: lande         by: lande
Observations                806               768               777
R2                      0.49286           0.64403           0.64168
Within R2               0.03313           0.11909           0.11707

                           h1_m4
                      Horse-race
Dependent Var.:       bnp_vækst
                                
samf_z_l1         -0.365 (0.214)
log_bnp_pc_l1     -5.97** (1.60)
handel_l1        0.062** (0.017)
arbejdsløshed_l1  -0.051 (0.067)
inflation_l1      -0.037 (0.024)
prog_z_l1         -0.206 (0.203)
Fixed-Effects:   ---------------
lande                        Yes
år                          Yes
________________ _______________
S.E.: Clustered        by: lande
Observations                 768
R2                       0.64580
Within R2                0.12346
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Den samfundsmæssige polarisering får en negativ og netop signifikant koefficient med kontroller (-0,458*): Én standardafvigelses højere polarisering følges af knap et halvt procentpoint lavere vækst året efter. I horse-racet mister den dog signifikansen. Den programmatiske polarisering er nul i alle kolonner — partiernes programafstand koster ikke målbar vækst. Kontrollerne opfører sig som forventet (konvergens, handelsåbenhed), hvilket styrker tilliden til modellen.

To robusthedsskridt lukker blokken. Først differens-versionen (jf. Blok 1): Koster en stigning i polariseringen vækst?

h1_m5 <- feols(bnp_vækst ~ d_samf_z_l1 + log_bnp_pc_l1 + handel_l1 +
                 arbejdsløshed_l1 + inflation_l1 | lande + år,
               data = analysedata, cluster = ~ lande)
NOTE: 73 observations removed because of NA values (LHS: 6, RHS: 73).
etable(h1_m2, h1_m5,
       headers = c("Niveau (hovedmodel)", "Differens (robusthed)"),
       digits = 3, fitstat = ~ n + r2 + wr2)
                               h1_m2                 h1_m5
                 Niveau (hovedmodel) Differens (robusthed)
Dependent Var.:           bnp_vækst            bnp_vækst
                                                          
samf_z_l1            -0.458* (0.212)                      
log_bnp_pc_l1         -6.07** (1.61)       -5.82*** (1.48)
handel_l1            0.061** (0.017)       0.058** (0.017)
arbejdsløshed_l1      -0.059 (0.070)        -0.055 (0.067)
inflation_l1          -0.040 (0.025)        -0.039 (0.025)
d_samf_z_l1                                 -0.746 (0.599)
Fixed-Effects:       ---------------       ---------------
lande                            Yes                   Yes
år                              Yes                   Yes
________________     _______________       _______________
S.E.: Clustered            by: lande             by: lande
Observations                     768                   767
R2                           0.64403               0.64312
Within R2                    0.11909               0.11706
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Differens-koefficienten er negativ (-0,746), men insignifikant — det strengere krav overlever effekten ikke.

Dernæst wild cluster bootstrap: Med kun 24 lande kan klyngede standardfejl være for optimistiske. Modellen genestimeres som almindelig OLS med eksplicitte lande- og årsdummier (matematisk identisk med TWFE), hvorpå bootstrappen kan køre.

boot_data <- analysedata |>
  select(bnp_vækst, samf_z_l1, prog_z_l1, log_bnp_pc_l1, handel_l1,
         arbejdsløshed_l1, inflation_l1, lande, år) |>
  drop_na() |>
  mutate(år = factor(år))

h1_m2_lm <- lm(bnp_vækst ~ samf_z_l1 + log_bnp_pc_l1 + handel_l1 +
                 arbejdsløshed_l1 + inflation_l1 + lande + år,
               data = boot_data)
h1_m3_lm <- lm(bnp_vækst ~ prog_z_l1 + log_bnp_pc_l1 + handel_l1 +
                 arbejdsløshed_l1 + inflation_l1 + lande + år,
               data = boot_data)

cat("Facit-kontrol mod fixest (skal være identiske):\n")
Facit-kontrol mod fixest (skal være identiske):
cat("samf_z_l1: lm =", round(coef(h1_m2_lm)["samf_z_l1"], 3),
    "| fixest =", round(coef(h1_m2)["samf_z_l1"], 3), "\n\n")
samf_z_l1: lm = -0.458 | fixest = -0.458 
set.seed(119)
boot_samf <- coeftest(h1_m2_lm,
                      vcov = vcovBS(h1_m2_lm, cluster = boot_data$lande,
                                    type = "wild", R = 9999))
set.seed(119)
boot_prog <- coeftest(h1_m3_lm,
                      vcov = vcovBS(h1_m3_lm, cluster = boot_data$lande,
                                    type = "wild", R = 9999))

cat("samf_z: klynge-p =", round(pvalue(h1_m2)["samf_z_l1"], 3),
    "| bootstrap-p =", round(boot_samf["samf_z_l1", "Pr(>|t|)"], 3), "\n")
samf_z: klynge-p = 0.041 | bootstrap-p = 0.024 
cat("prog_z: klynge-p =", round(pvalue(h1_m3)["prog_z_l1"], 3),
    "| bootstrap-p =", round(boot_prog["prog_z_l1", "Pr(>|t|)"], 3))
prog_z: klynge-p = 0.166 | bootstrap-p = 0.199

Bootstrappen bekræfter hovedmodellens stjerne (p = 0,024). Blokkens samlede konklusion: en konsistent negativ tendens for samfundsmæssig polarisering, hvis statistiske sikkerhed dog afhænger af specifikationen (jf. robusthedsblokken) — og et robust nul for programmatisk polarisering.


Blok 3: H2 — økonomi og polarisering

Kausalpilen vendes: Fører økonomisk modgang til stigende polarisering (grievance-hypotesen)? Udfaldet er polariseringens førstedifferens (jf. Blok 1), forklaringerne sidste års vækst, arbejdsløshed og ulighed.

h2_m1 <- feols(d_samf_z ~ bnp_vækst_l1 | lande + år,
               data = analysedata, cluster = ~ lande)
NOTE: 40 observations removed because of NA values (LHS: 34, RHS: 30).
h2_m2 <- feols(d_samf_z ~ bnp_vækst_l1 + arbejdsløshed_l1 + top10_l1 |
                 lande + år,
               data = analysedata, cluster = ~ lande)
NOTE: 57 observations removed because of NA values (LHS: 34, RHS: 48).
etable(h2_m1, h2_m2,
       headers = c("Bivariat", "Med arbejdsløshed og ulighed"),
       digits = 3, fitstat = ~ n + r2 + wr2)
                         h2_m1                        h2_m2
                      Bivariat Med arbejdsløshed og ulighed
Dependent Var.:       d_samf_z                     d_samf_z
                                                           
bnp_vækst_l1    0.003 (0.003)                0.003 (0.003)
arbejdsløshed_l1                             -0.003 (0.002)
top10_l1                                      0.226 (0.379)
Fixed-Effects:   -------------               --------------
lande                      Yes                          Yes
år                        Yes                          Yes
________________ _____________               ______________
S.E.: Clustered      by: lande                    by: lande
Observations               800                          783
R2                     0.12164                      0.12441
Within R2              0.00359                      0.00648
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Intet signal: Koefficienterne er nær nul, og vækst og arbejdsløshed har endda “forkert” fortegn. Bemærk dog designets pris: Årseffekterne fjerner de fælles europæiske chok, så modellen kun ser, om lande ramt hårdere end gennemsnittet polariserer mere end gennemsnittet. Virker grievance-mekanismen via fælles kriser, kan den ikke ses her — det testes i næste blok.


Blok 4: Granger-kausalitet og VAR

Retningsspørgsmålet — hvem bevæger sig først, økonomien eller polariseringen? — testes med Dumitrescu-Hurlins panel-Granger-test. Modsat TWFE indgår ingen årseffekter, så de fælles kriser tæller med i identifikationen. Samplet kræver sammenhængende serier (Island falder fra).

var_data <- analysedata |>
  select(lande, år, d_samf_z, bnp_vækst) |>
  drop_na() |>
  group_by(lande) |>
  filter(n() == max(år) - min(år) + 1, n() >= 25) |>
  ungroup()

cat("Lande i Granger-samplet:", n_distinct(var_data$lande), "\n")
Lande i Granger-samplet: 23 
cat("Frafaldne:", paste(setdiff(lande, unique(var_data$lande)),
                        collapse = ", "), "\n\n")
Frafaldne: ISL 
var_pdata <- pdata.frame(var_data, index = c("lande", "år"))

granger_v_p <- pgrangertest(d_samf_z ~ bnp_vækst, data = var_pdata, order = 2)
granger_p_v <- pgrangertest(bnp_vækst ~ d_samf_z, data = var_pdata, order = 2)
granger_v_p_1 <- pgrangertest(d_samf_z ~ bnp_vækst, data = var_pdata, order = 1)
granger_p_v_1 <- pgrangertest(bnp_vækst ~ d_samf_z, data = var_pdata, order = 1)

cat("Med 2 lags (hovedversion):\n")
Med 2 lags (hovedversion):
cat("Vækst → Δpolarisering: p =", format.pval(granger_v_p$p.value, digits = 3), "\n")
Vækst → Δpolarisering: p = <2e-16 
cat("Δpolarisering → vækst: p =", format.pval(granger_p_v$p.value, digits = 3), "\n\n")
Δpolarisering → vækst: p = 0.381 
cat("Med 1 lag (robusthed):\n")
Med 1 lag (robusthed):
cat("Vækst → Δpolarisering: p =", format.pval(granger_v_p_1$p.value, digits = 3), "\n")
Vækst → Δpolarisering: p = 4.85e-14 
cat("Δpolarisering → vækst: p =", format.pval(granger_p_v_1$p.value, digits = 3))
Δpolarisering → vækst: p = 0.334

Retningsdommen er entydig og robust over lag-valg: Væksten forudsiger polariseringsændringerne (p < 2,2e-16), mens den omvendte retning er nul (p = 0,38). Kombineret med Blok 3 tegner det ét billede: Grievance-mekanismen virker via de fælles europæiske kriser, ikke via landespecifik modgang.

To VAR-ligninger sætter størrelse på retningen (kun landefaste effekter — konsistent med Granger-testen).

var_lign_pol <- feols(d_samf_z ~ d_samf_z_l1 + d_samf_z_l2 +
                        bnp_vækst_l1 + bnp_vækst_l2 | lande,
                      data = analysedata, cluster = ~ lande)
NOTE: 82 observations removed because of NA values (LHS: 34, RHS: 82).
var_lign_vækst <- feols(bnp_vækst ~ bnp_vækst_l1 + bnp_vækst_l2 +
                          d_samf_z_l1 + d_samf_z_l2 | lande,
                        data = analysedata, cluster = ~ lande)
NOTE: 82 observations removed because of NA values (LHS: 6, RHS: 82).
etable(var_lign_pol, var_lign_vækst,
       headers = c("Udfald: Δpolarisering", "Udfald: vækst"),
       digits = 3, fitstat = ~ n + r2 + wr2)
                         var_lign_pol  var_lign_vækst
                Udfald: Δpolarisering   Udfald: vækst
Dependent Var.:              d_samf_z       bnp_vækst
                                                      
d_samf_z_l1            0.060. (0.030)   -0.579 (0.910)
d_samf_z_l2            -0.039 (0.040)  -2.21** (0.663)
bnp_vækst_l1         -0.005* (0.002) 0.315*** (0.053)
bnp_vækst_l2          0.006* (0.003)  -0.072* (0.026)
Fixed-Effects:        --------------- ----------------
lande                             Yes              Yes
_______________       _______________ ________________
S.E.: Clustered             by: lande        by: lande
Observations                      758              758
R2                            0.04345          0.20001
Within R2                     0.02186          0.12592
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Effekten er statistisk stærk, men lille og forbigående: Ét procentpoint lavere vækst giver 0,005 standardafvigelses større polariseringsstigning året efter — og fortegnet vender ved lag 2, så rykket klinger af igen. I vækstligningen ses et enkeltstående modsignal (d_samf_z_l2 = -2,21**), som efterprøves i robusthedsblokken, før det tillægges betydning.


Blok 5: Kanalkæden

Mekanismen bag H1 testes led for led: polarisering → usikkerhed → investering → vækst. Analysen er deskriptiv — kausale mediationseffekter kan ikke identificeres i observationsdata (Imai, Keele & Tingley 2010) — og dokumenterer alene, om hvert led viser det forudsagte mønster.

kanal_1 <- feols(wui ~ d_samf_z_l1 | lande + år,
                 data = analysedata, cluster = ~ lande)
NOTE: 118 observations removed because of NA values (LHS: 82, RHS: 58).
kanal_2 <- feols(investering ~ wui_l1 | lande + år,
                 data = analysedata, cluster = ~ lande)
NOTE: 110 observations removed because of NA values (LHS: 19, RHS: 104).
kanal_3 <- feols(bnp_vækst ~ investering_l1 | lande + år,
                 data = analysedata, cluster = ~ lande)
NOTE: 43 observations removed because of NA values (LHS: 6, RHS: 43).
etable(kanal_1, kanal_2, kanal_3,
       headers = c("Led 1: Δpol → usikkerhed",
                   "Led 2: usikkerhed → investering",
                   "Led 3: investering → vækst"),
       digits = 3, fitstat = ~ n + r2 + wr2)
                                 kanal_1                         kanal_2
                Led 1: Δpol → usikkerhed Led 2: usikkerhed → investering
Dependent Var.:                      wui                     investering
                                                                        
d_samf_z_l1                0.036 (0.032)                                
wui_l1                                                       1.57 (1.89)
investering_l1                                                          
Fixed-Effects:             -------------                     -----------
lande                                Yes                             Yes
år                                  Yes                             Yes
_______________            _____________                     ___________
S.E.: Clustered                by: lande                       by: lande
Observations                         722                             730
R2                               0.42525                         0.49066
Within R2                        0.00267                         0.00326

                                   kanal_3
                Led 3: investering → vækst
Dependent Var.:                 bnp_vækst
                                          
d_samf_z_l1                               
wui_l1                                    
investering_l1              0.094* (0.038)
Fixed-Effects:              --------------
lande                                  Yes
år                                    Yes
_______________             ______________
S.E.: Clustered                  by: lande
Observations                           797
R2                                 0.58030
Within R2                          0.01599
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Kæden knækker i første led: Stigende polarisering skaber ikke målbar usikkerhed inden for landene, og usikkerheden flytter heller ikke investeringerne (led 2, endda forkert fortegn). Kun det mekaniske slutled holder (investering → vækst, 0,094*). Det er den empiriske forklaring på H1-svagheden: Uden et fungerende første led findes ingen transmissionsvej fra polarisering til vækst — de økonomiske aktører ser tilsyneladende igennem den politiske støj i disse institutionelt stærke lande.


Blok 6: Robusthed

H1-hovedmodellen efterprøves systematisk: uden Island, Ungarn og Irland (sidstnævnte med winsoriseret vækst), med Manifesto-målets to alternative versioner og med demokratikontrol (begge indeks). Alle tjek er planlagt før estimation.

rob_uden_isl <- feols(bnp_vækst ~ samf_z_l1 + log_bnp_pc_l1 + handel_l1 +
                        arbejdsløshed_l1 + inflation_l1 | lande + år,
                      data = analysedata |> filter(lande != "ISL"),
                      cluster = ~ lande)
NOTE: 61 observations removed because of NA values (LHS: 6, RHS: 61).
rob_uden_hun <- feols(bnp_vækst ~ samf_z_l1 + log_bnp_pc_l1 + handel_l1 +
                        arbejdsløshed_l1 + inflation_l1 | lande + år,
                      data = analysedata |> filter(lande != "HUN"),
                      cluster = ~ lande)
NOTE: 70 observations removed because of NA values (LHS: 6, RHS: 70).
rob_uden_irl <- feols(vækst_wins ~ samf_z_l1 + log_bnp_pc_l1 + handel_l1 +
                        arbejdsløshed_l1 + inflation_l1 | lande + år,
                      data = analysedata |> filter(lande != "IRL"),
                      cluster = ~ lande)
NOTE: 70 observations removed because of NA values (LHS: 6, RHS: 70).
rob_valgår <- feols(bnp_vækst ~ prog_valgår_z_l1 + log_bnp_pc_l1 + handel_l1 +
                      arbejdsløshed_l1 + inflation_l1 | lande + år,
                    data = analysedata, cluster = ~ lande)
NOTE: 642 observations removed because of NA values (LHS: 6, RHS: 642).
rob_interp <- feols(bnp_vækst ~ prog_interp_z_l1 + log_bnp_pc_l1 + handel_l1 +
                      arbejdsløshed_l1 + inflation_l1 | lande + år,
                    data = analysedata, cluster = ~ lande)
NOTE: 130 observations removed because of NA values (LHS: 6, RHS: 130).
rob_ele <- feols(bnp_vækst ~ samf_z_l1 + ele_dem_l1 + log_bnp_pc_l1 +
                   handel_l1 + arbejdsløshed_l1 + inflation_l1 | lande + år,
                 data = analysedata, cluster = ~ lande)
NOTE: 72 observations removed because of NA values (LHS: 6, RHS: 72).
rob_lib <- feols(bnp_vækst ~ samf_z_l1 + lib_dem_l1 + log_bnp_pc_l1 +
                   handel_l1 + arbejdsløshed_l1 + inflation_l1 | lande + år,
                 data = analysedata, cluster = ~ lande)
NOTE: 72 observations removed because of NA values (LHS: 6, RHS: 72).
etable(h1_m2, rob_uden_isl, rob_uden_hun, rob_uden_irl,
       rob_valgår, rob_interp, rob_ele, rob_lib,
       headers = c("Hoved", "u. ISL", "u. HUN", "u. IRL/wins",
                   "Valgår", "Interp", "+ele_dem", "+lib_dem"),
       digits = 3, fitstat = ~ n + wr2, se.below = TRUE)
                     h1_m2 rob_ude.. rob_ude...1  rob_uden.. rob_val..
                     Hoved    u. ISL      u. HUN u. IRL/wins   Valgår
Dependent Var.:  bnp_vækst bnp_vækst  bnp_vækst vækst_wins bnp_vækst
                                                                      
samf_z_l1         -0.458*   -0.358      -0.502      -0.401            
                  (0.212)   (0.194)     (0.286)     (0.230)           
log_bnp_pc_l1     -6.07**   -6.02**     -6.21**     -5.08*** -4.90*   
                  (1.61)    (1.62)      (1.82)      (1.34)   (1.77)   
handel_l1          0.061**   0.062**     0.065**     0.044**  0.063***
                  (0.017)   (0.017)     (0.020)     (0.012)  (0.015)  
arbejdsløshed_l1  -0.059    -0.057      -0.055      -0.046    0.009   
                  (0.070)   (0.070)     (0.074)     (0.068)  (0.071)  
inflation_l1      -0.040    -0.038      -0.041      -0.038    0.004   
                  (0.025)   (0.024)     (0.026)     (0.024)  (0.008)  
prog_valgår_z_l1                                             -0.291   
                                                             (0.239)  
prog_interp_z_l1                                                      
                                                                      
ele_dem_l1                                                            
                                                                      
lib_dem_l1                                                            
                                                                      
Fixed-Effects:   --------- ---------   ---------  ---------- ---------
lande                  Yes       Yes         Yes         Yes       Yes
år                    Yes       Yes         Yes         Yes       Yes
________________ _________ _________   _________  __________ _________
S.E.: Clustered  by: lande by: lande   by: lande   by: lande by: lande
Observations           768       744         735         735       198
Within R2          0.11909   0.12322     0.11851     0.09952   0.12875

                 rob_int..   rob_ele   rob_lib
                    Interp  +ele_dem  +lib_dem
Dependent Var.:  bnp_vækst bnp_vækst bnp_vækst
                                              
samf_z_l1                   -0.681**  -0.718**
                            (0.194)   (0.195) 
log_bnp_pc_l1     -4.80*    -6.16***  -6.08** 
                  (1.74)    (1.62)    (1.63)  
handel_l1          0.061**   0.060**   0.060**
                  (0.017)   (0.018)   (0.018) 
arbejdsløshed_l1  -0.028    -0.051    -0.049  
                  (0.067)   (0.072)   (0.072) 
inflation_l1      -0.030    -0.039    -0.039  
                  (0.022)   (0.024)   (0.024) 
prog_valgår_z_l1                              
                                              
prog_interp_z_l1  -0.328                      
                  (0.225)                     
ele_dem_l1                  -4.75**           
                            (1.42)            
lib_dem_l1                            -4.28** 
                                      (1.44)  
Fixed-Effects:   --------- --------- ---------
lande                  Yes       Yes       Yes
år                    Yes       Yes       Yes
________________ _________ _________ _________
S.E.: Clustered  by: lande by: lande by: lande
Observations           710       768       768
Within R2          0.10722   0.12418   0.12478
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Tabellens budskab er tydeligt: Retningen er konsistent (alle otte koefficienter negative), men signifikansen overlever ingen af de tre landeeksklusioner — effekten er specifikationsafhængig. Manifesto-nullet er derimod versionsrobust. Bemærkelsesværdigt styrkes polariseringseffekten med demokratikontrol (-0,68/-0,72): Uden kontrollen maskerer demokratikvaliteten en del af effekten (jf. Ungarn, hvor polarisering og demokratisk tilbagegang følges ad). Fundet rapporteres som robusthedsresultat, ikke ny hovedmodel (drøftes med vejleder).

H2 efterprøves med kriseår-dummy (Funke et al. 2016): Måske driver egentlige recessioner polariseringen, ikke marginal vækstvariation.

rob_krise <- feols(d_samf_z ~ krise_l1 | lande + år,
                   data = analysedata, cluster = ~ lande)
NOTE: 40 observations removed because of NA values (LHS: 34, RHS: 30).
etable(h2_m1, rob_krise,
       headers = c("H2: kontinuert vækst", "H2: kriseår-dummy"),
       digits = 3, fitstat = ~ n + wr2)
                               h2_m1         rob_krise
                H2: kontinuert vækst H2: kriseår-dummy
Dependent Var.:             d_samf_z          d_samf_z
                                                      
bnp_vækst_l1          0.003 (0.003)                  
krise_l1                               -0.048. (0.027)
Fixed-Effects:         -------------   ---------------
lande                            Yes               Yes
år                              Yes               Yes
_______________        _____________   _______________
S.E.: Clustered            by: lande         by: lande
Observations                     800               800
Within R2                    0.00359           0.00586
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Heller ikke kriseår-dummyen finder et signal — H2-nullet i TWFE-form står.

VAR-vækstligningen efterprøves med årseffekter og med 1 lag — særligt modsignalet fra Blok 4.

rob_var_årfe <- feols(bnp_vækst ~ bnp_vækst_l1 + bnp_vækst_l2 +
                        d_samf_z_l1 + d_samf_z_l2 | lande + år,
                      data = analysedata, cluster = ~ lande)
NOTE: 82 observations removed because of NA values (LHS: 6, RHS: 82).
rob_var_1lag <- feols(bnp_vækst ~ bnp_vækst_l1 + d_samf_z_l1 | lande,
                      data = analysedata, cluster = ~ lande)
NOTE: 58 observations removed because of NA values (LHS: 6, RHS: 58).
etable(var_lign_vækst, rob_var_årfe, rob_var_1lag,
       headers = c("VAR-hoved (2 lags)", "+årseffekter", "1 lag"),
       digits = 3, fitstat = ~ n + wr2)
                   var_lign_vækst    rob_var_årfe     rob_var_1lag
                VAR-hoved (2 lags)    +årseffekter            1 lag
Dependent Var.:         bnp_vækst       bnp_vækst       bnp_vækst
                                                                    
bnp_vækst_l1     0.315*** (0.053) 0.411*** (0.060) 0.361*** (0.059)
bnp_vækst_l2      -0.072* (0.026)  -0.075* (0.032)                 
d_samf_z_l1         -0.579 (0.910)   -1.03. (0.503)   -0.645 (0.963)
d_samf_z_l2        -2.21** (0.663)   -1.02. (0.524)                 
Fixed-Effects:    ---------------- ---------------- ----------------
lande                          Yes              Yes              Yes
år                             No              Yes               No
_______________   ________________ ________________ ________________
S.E.: Clustered          by: lande        by: lande        by: lande
Observations                   758              758              782
Within R2                  0.12592          0.22439          0.14219
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Modsignalet overlever hverken årseffekter eller 1 lag og afskrives som specifikationsafhængigt. Retningskonklusionen fra Blok 4 står urørt.

Endelig regional heterogenitet. Deklareret forbehold: Syd har kun 4 klynger, så kolonnen er indikativ, ikke konklusiv.

rob_regioner <- map(c("Vest", "Syd", "CEE"), \(r)
  feols(bnp_vækst ~ samf_z_l1 + log_bnp_pc_l1 + handel_l1 +
          arbejdsløshed_l1 + inflation_l1 | lande + år,
        data = analysedata |> filter(region == r), cluster = ~ lande))
NOTE: 35 observations removed because of NA values (RHS: 35).
NOTE: 8 observations removed because of NA values (RHS: 8).
NOTE: 29 observations removed because of NA values (LHS: 6, RHS: 29).
etable(rob_regioner,
       headers = c("Vest (13)", "Syd (4)", "CEE (7)"),
       digits = 3, fitstat = ~ n + wr2)
                          model 1        model 2        model 3
                        Vest (13)        Syd (4)        CEE (7)
Dependent Var.:        bnp_vækst     bnp_vækst     bnp_vækst
                                                               
samf_z_l1         -0.683. (0.342) -0.274 (0.315) -0.005 (0.249)
log_bnp_pc_l1       -9.08* (4.06)   -5.18 (2.56)   -5.65 (3.98)
handel_l1          0.081* (0.034)  0.049 (0.036) 0.034. (0.015)
arbejdsløshed_l1   -0.143 (0.151) -0.120 (0.067) -0.085 (0.235)
inflation_l1     -0.383** (0.093) -0.135 (0.107) -0.023 (0.019)
Fixed-Effects:   ---------------- -------------- --------------
lande                         Yes            Yes            Yes
år                           Yes            Yes            Yes
________________ ________________ ______________ ______________
S.E.: Clustered         by: lande      by: lande      by: lande
Observations                  420            132            216
Within R2                 0.21297        0.08741        0.08895
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Mønstret er uventet: En eventuel polariseringseffekt er stærkest i Vesteuropa (-0,68·) og fraværende i Central- og Østeuropa (-0,005). En mulig læsning: I CEE er polarisering kronisk og sammenvævet med transitionen, mens polariseringsudsving i konsoliderede vestlige demokratier er sjældnere og dermed mere informative. Åbent spørgsmål til vejleder.


Blok 7: DiD — eurokrisen som kausalt design

Som kausalt designelement estimeres et staggered difference-in-differences med eurokrisen som chok: Steg polariseringen mere i de kriseramte lande (GIIPS) end i sammenlignelige ikke-ramte? Estimator: Callaway & Sant’Anna (2021). Fire foreløbige valg (drøftes med vejleder): treated er GIIPS; timing er bailout-/kriseår; udfaldet er samf_pol i niveau (event-study-formen håndterer ikke-stationariteten via parallel trends); kontrolgruppen er aldrig-ramte lande uden Baltikum, hvis egne 2009-kriser ville forurene sammenligningen. Pakken testes først på eksempeldata.

data(mpdta)
test_did <- att_gt(yname = "lemp", tname = "year", idname = "countyreal",
                   gname = "first.treat", data = mpdta)
test_agg <- aggte(test_did, type = "simple")

if (is.finite(test_agg$overall.att)) {
  cat("Pakketest bestået: did-pakken estimerer (ATT =",
      round(test_agg$overall.att, 4), "på eksempeldata)")
} else {
  cat("FEJL: did-pakken returnerer ikke et gyldigt estimat")
}
Pakketest bestået: did-pakken estimerer (ATT = -0.04 på eksempeldata)
did_data <- panel |>
  filter(!lande %in% c("EST", "LVA", "LTU")) |>
  mutate(gruppe = case_when(lande %in% c("GRC", "IRL") ~ 2010,
                            lande == "PRT" ~ 2011,
                            lande %in% c("ESP", "ITA") ~ 2012,
                            TRUE ~ 0),
         lande_id = as.integer(factor(lande))) |>
  select(lande, lande_id, år, gruppe, samf_pol) |>
  drop_na(samf_pol)

cat(n_distinct(did_data$lande), "lande, heraf",
    n_distinct(did_data$lande[did_data$gruppe > 0]), "treated")
21 lande, heraf 5 treated
set.seed(119)
did_att <- att_gt(yname = "samf_pol", tname = "år", idname = "lande_id",
                  gname = "gruppe", data = did_data,
                  control_group = "nevertreated",
                  clustervars = "lande_id")
Warning in did_standardization(data, args): 1 units are missing in some
periods. Converting to balanced panel by dropping them.
Warning in did_standardization(data, args): Some groups in your dataset have very few observations, which may cause estimation problems.
  Check groups: 2012,2010,2011.
Warning in att_gt(yname = "samf_pol", tname = "år", idname = "lande_id", : Not
returning pre-test Wald statistic due to singular covariance matrix
did_es <- aggte(did_att, type = "dynamic", min_e = -10, max_e = 10)
summary(did_es)

Call:
aggte(MP = did_att, type = "dynamic", min_e = -10, max_e = 10)

Reference: Callaway, Brantly and Pedro H.C. Sant'Anna.  "Difference-in-Differences with Multiple Time Periods." Journal of Econometrics, Vol. 225, No. 2, pp. 200-230, 2021. <https://doi.org/10.1016/j.jeconom.2020.12.001>, <https://arxiv.org/abs/1803.09015> 


Overall summary of ATT's based on event-study/dynamic aggregation:  
     ATT    Std. Error     [ 95%  Conf. Int.] 
 -0.1372        0.1723     -0.475      0.2005 


Dynamic Effects:
 Event time Estimate Std. Error [95% Simult.  Conf. Band] 
        -10  -0.1775     0.1377       -0.5228      0.1679 
         -9  -0.0243     0.0276       -0.0935      0.0450 
         -8  -0.0209     0.0381       -0.1165      0.0746 
         -7  -0.0176     0.0337       -0.1021      0.0668 
         -6  -0.0481     0.0310       -0.1259      0.0296 
         -5  -0.0387     0.0305       -0.1153      0.0378 
         -4  -0.0010     0.0479       -0.1211      0.1191 
         -3   0.0751     0.0540       -0.0602      0.2104 
         -2   0.0957     0.1195       -0.2041      0.3955 
         -1   0.0720     0.0936       -0.1627      0.3067 
          0  -0.0658     0.1930       -0.5498      0.4183 
          1  -0.0025     0.1564       -0.3947      0.3898 
          2  -0.0326     0.1617       -0.4381      0.3730 
          3  -0.0561     0.1668       -0.4743      0.3622 
          4   0.0157     0.1567       -0.3773      0.4088 
          5  -0.0396     0.1592       -0.4388      0.3596 
          6  -0.1172     0.1974       -0.6122      0.3779 
          7  -0.2394     0.2028       -0.7481      0.2693 
          8  -0.2303     0.1988       -0.7289      0.2684 
          9  -0.4587     0.2689       -1.1332      0.2157 
         10  -0.2833     0.3382       -1.1317      0.5651 
---
Signif. codes: `*' confidence band does not cover 0

Control Group:  Never Treated,  Anticipation Periods:  0
Estimation Method:  Doubly Robust
ggdid(did_es) +
  labs(title = NULL,
       x = "År relativt til krisestart",
       y = "Effekt på samfundsmæssig polarisering")

Før-perioden ligger fladt omkring nul (parallel trends holder — designet er gyldigt), og efter-perioden viser ingen effekt: samlet ATT = -0,14, insignifikant. De hårdest kriseramte lande polariserede altså ikke mere end de mildere ramte. Kombineret med Granger-resultatet skærpes fortolkningen: Polariseringen følger de fælles kriser, ikke den differentielle eksponering — begge grupper polariserede, så forskellen er nul.

To planlagte robusthedstjek: uden Irland (både treated og artefakt-tungt) og med “notyettreated” som kontrolgruppe.

set.seed(119)
did_uden_irl <- att_gt(yname = "samf_pol", tname = "år", idname = "lande_id",
                       gname = "gruppe",
                       data = did_data |> filter(lande != "IRL"),
                       control_group = "nevertreated",
                       clustervars = "lande_id")
Warning in did_standardization(data, args): 1 units are missing in some
periods. Converting to balanced panel by dropping them.
Warning in did_standardization(data, args): Some groups in your dataset have very few observations, which may cause estimation problems.
  Check groups: 2012,2010,2011.
Warning in att_gt(yname = "samf_pol", tname = "år", idname = "lande_id", : Not
returning pre-test Wald statistic due to singular covariance matrix
es_uden_irl <- aggte(did_uden_irl, type = "dynamic", min_e = -10, max_e = 10)

set.seed(119)
did_nyt <- att_gt(yname = "samf_pol", tname = "år", idname = "lande_id",
                  gname = "gruppe", data = did_data,
                  control_group = "notyettreated",
                  clustervars = "lande_id")
Warning in did_standardization(data, args): 1 units are missing in some
periods. Converting to balanced panel by dropping them.
Warning in did_standardization(data, args): Some groups in your dataset have very few observations, which may cause estimation problems.
  Check groups: 2012,2010,2011.
Warning in att_gt(yname = "samf_pol", tname = "år", idname = "lande_id", : Not
returning pre-test Wald statistic due to singular covariance matrix
es_nyt <- aggte(did_nyt, type = "dynamic", min_e = -10, max_e = 10)

cat("Samlet ATT, tre versioner:\n")
Samlet ATT, tre versioner:
cat("Hovedversion:  ", round(did_es$overall.att, 3),
    "( SE", round(did_es$overall.se, 3), ")\n")
Hovedversion:   -0.137 ( SE 0.172 )
cat("Uden Irland:   ", round(es_uden_irl$overall.att, 3),
    "( SE", round(es_uden_irl$overall.se, 3), ")\n")
Uden Irland:    -0.132 ( SE 0.201 )
cat("Notyettreated: ", round(es_nyt$overall.att, 3),
    "( SE", round(es_nyt$overall.se, 3), ")\n")
Notyettreated:  -0.136 ( SE 0.171 )
(ggdid(es_uden_irl) + labs(title = "Uden Irland",
                           x = NULL, y = "Effekt på samf_pol")) /
  (ggdid(es_nyt) + labs(title = "Kontrol: notyettreated",
                        x = "År relativt til krisestart",
                        y = "Effekt på samf_pol"))

Begge tjek bekræfter hovedresultatet — nul-forskellen er robust over for enkeltlande og kontrolgruppevalg.


Samlet billede

Analysen efterlader fire hovedfund. For det første er samfundsmæssig og programmatisk polarisering to distinkte fænomener (within-korrelation 0,26; Polen 2005 som nøgleeksempel), og v2cacamps er valideret som mål for lejrdannelse (Reiljan-korrelationen). For det andet koster programmatisk polarisering ikke målbar vækst — et robust nul på tværs af alle specifikationer. For det tredje viser samfundsmæssig polarisering en konsistent negativ vækst-tendens, hvis statistiske sikkerhed dog afhænger af specifikationen — og kanalanalysen forklarer hvorfor: Transmissionsvejen via usikkerhed og investeringer er brudt i første led. For det fjerde — analysens stærkeste fund — peger kausalpilen den anden vej: Væksten forudsiger polariseringsændringerne (Granger p < 2,2e-16, robust), mens det omvendte er nul; effekten er dog beskeden og forbigående, og DiD-designet viser, at den virker via fælles europæiske kriser snarere end landespecifik modgang. Samlet: I konsoliderede europæiske demokratier er politisk polarisering primært et produkt af økonomien — ikke en årsag til dens problemer — og de institutionelle rammer ser ud til at afskærme økonomien fra polariseringen. Effekterne i litteraturen, der peger den anden vej, er fundet i svagere institutionelle kontekster; vores fund afgrænser dermed, hvor polarisering bliver økonomisk farlig.