Del 1 – Opsætning

Indlæsning af pakker

Vi starter med at indlæse relevante pakker her. Pakkerne er installeret på forhånd hos os, hvorfor vi blot indlæser dem.

library(readxl)
library(data.table)
library(zoo)
library(here)
library(tidyverse)

Tidsperiode og sample

Analysen dækker udfaldsårene 1997 til 2024. Da alle uafhængige variable måles året før, bruger vi data fra 1996 til 2024. Startåret er valgt, så de central- og østeuropæiske lande er ude af de første overgangsår efter kommunismens fald, mens slutåret er sat af datadækningen. Til de længere lags af samfundsmæssig polarisering henter vi V-Dem-data tilbage til 1993.

Vi definerer desuden en udvidet periode fra 1975. Den bruges til kontrolvariablen effektivt antal partier, hvor sidste valgs værdi føres frem, indtil et nyt valg afløser den. På den måde har også årene før landets første valg i perioden en værdi.

år <- 1996:2024
år_udvidet <- 1975:2024

Vi afgrænser vores sample i tre trin. Først et institutionelt kriterium, dernæst datadækning og til sidst et regimekriterium.

Første trin omfatter europæiske lande, der i løbet af perioden er eller har været medlemmer af EU eller EFTA. Det giver et udvalg på 32 lande, der består af de nuværende 27 EU-lande, de fire EFTA-lande og Storbritannien.

kandidat_lande <- c("AUT","BEL","BGR","CHE","CYP","CZE","DEU","DNK","ESP","EST",
                    "FIN","FRA","GBR","GRC","HRV","HUN","IRL","ISL","ITA","LIE",
                    "LTU","LUX","LVA","MLT","NLD","NOR","POL","PRT","ROU","SVK",
                    "SVN","SWE")

cat("Antallet af potentielle lande er", length(kandidat_lande))
Antallet af potentielle lande er 32

Andet og tredje trin bygger på V-Dem. Landene skal være dækket af V-Dem og ved periodens begyndelse være klassificeret som mindst elektorale demokratier i V-Dems Regimes of the World, hvor 0 og 1 er autokratier, mens 2 og 3 er demokratier.

V_Dem_rå <- read_csv("Data/V-Dem/V-Dem data.csv",
                     col_select = c(country_text_id, year, v2x_regime, v2cacamps)) |>
  filter(country_text_id %in% kandidat_lande, year %in% (min(år) - 3):max(år)) |>
  rename(lande = country_text_id, år = year, regime = v2x_regime, samf_pol = v2cacamps)

regime_screening <- V_Dem_rå |>
  filter(år %in% .env$år) |>
  drop_na(regime) |>
  group_by(lande) |>
  slice_min(år) |>
  ungroup() |>
  select(lande, første_år = år, regime) |>
  arrange(regime, lande)

cat("Lande med V-Dem-dækning:", nrow(regime_screening), "\n")
Lande med V-Dem-dækning: 31 
cat("Uden dækning:", paste(setdiff(kandidat_lande, regime_screening$lande),
                           collapse = ", "), "\n\n")
Uden dækning: LIE 
regime_screening |> filter(regime < 2) |> print()
# A tibble: 1 × 3
  lande første_år regime
  <chr>     <dbl>  <dbl>
1 HRV        1996      1

Liechtenstein udgår på datadækningen, da landet ikke indgår i V-Dem. Kroatien udgår på regimekriteriet, da landet var klassificeret som elektoralt autokrati i 1996.

lande <- regime_screening |>
  filter(regime >= 2) |>
  pull(lande) |>
  sort()

cat(length(lande), "lande i samplet:", paste(lande, collapse = ", "))
30 lande i samplet: AUT, BEL, BGR, CHE, CYP, CZE, DEU, DNK, ESP, EST, FIN, FRA, GBR, GRC, HUN, IRL, ISL, ITA, LTU, LUX, LVA, MLT, NLD, NOR, POL, PRT, ROU, SVK, SVN, SWE

Vi ender således samlet set på 30 lande i samplet.


Del 2 – Datagrundlag

V-Dem

Fra V-Dem får vi det samfundsmæssige polariseringsmål. Det er eksperters vurdering af, hvor opdelt et samfund er i fjendtlige politiske lejre.

V_Dem <- V_Dem_rå |>
  filter(lande %in% .env$lande, år %in% .env$år)

Vi kontrollerer dimensionerne. 30 lande ganget med 29 år skal give 870 rækker uden dubletter.

dubletter_V_Dem <- V_Dem |> count(lande, år) |> filter(n > 1)

if (nrow(dubletter_V_Dem) == 0) {
  cat(nrow(V_Dem), "rækker,", n_distinct(V_Dem$lande),
      "unikke lande og ingen dubletter")
} else {
  cat(nrow(V_Dem), "rækker,", n_distinct(V_Dem$lande), "unikke lande\n")
  print(dubletter_V_Dem)
}
870 rækker, 30 unikke lande og ingen dubletter

Vi undersøger dækningen af det samfundsmæssige polariseringsmål 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 = 30)
# A tibble: 30 × 3
   lande mangler første_år
   <chr>   <int>     <dbl>
 1 ISL         4      2000
 2 AUT         0      1996
 3 BEL         0      1996
 4 BGR         0      1996
 5 CHE         0      1996
 6 CYP         0      1996
 7 CZE         0      1996
 8 DEU         0      1996
 9 DNK         0      1996
10 ESP         0      1996
11 EST         0      1996
12 FIN         0      1996
13 FRA         0      1996
14 GBR         0      1996
15 GRC         0      1996
16 HUN         0      1996
17 IRL         0      1996
18 ITA         0      1996
19 LTU         0      1996
20 LUX         0      1996
21 LVA         0      1996
22 MLT         0      1996
23 NLD         0      1996
24 NOR         0      1996
25 POL         0      1996
26 PRT         0      1996
27 ROU         0      1996
28 SVK         0      1996
29 SVN         0      1996
30 SWE         0      1996

Island mangler ekspertdækning i 1996-1999 og indgår derfor først fra 2000. Vi bruger også samfundsmæssig polarisering målt et, to og tre år før. Lagsene beregnes ud fra data tilbage til 1993, så også udfaldsåret 1997 har lags tre år tilbage.

samf_pol_lags <- V_Dem_rå |>
  filter(lande %in% .env$lande) |>
  arrange(lande, år) |>
  group_by(lande) |>
  mutate(samf_pol_l1 = lag(samf_pol),
         samf_pol_l2 = lag(samf_pol, 2),
         samf_pol_l3 = lag(samf_pol, 3)) |>
  ungroup() |>
  filter(år %in% .env$år) |>
  select(lande, år, samf_pol_l1, samf_pol_l2, samf_pol_l3)

Manifesto

Fra Manifesto får vi målet for partipolarisering. Partipolarisering måler, hvor langt partierne står fra hinanden i deres valgprogrammer. Datasættet har en 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","Bulgaria"="BGR",
                     "Switzerland"="CHE","Cyprus"="CYP",
                     "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","Luxembourg"="LUX",
                     "Latvia"="LVA","Malta"="MLT","Netherlands"="NLD",
                     "Norway"="NOR","Poland"="POL","Portugal"="PRT",
                     "Romania"="ROU","Slovakia"="SVK","Slovenia"="SVN",
                     "Sweden"="SWE")

cat("Navnetabellen indeholder", length(manifesto_navne), "lande")
Navnetabellen indeholder 30 lande

Vi indlæser fra 1975, så værdier fra valg før 1996 kan føres frem. Vi udleder valgåret af valgdatoen og frasorterer partier uden rile-kodning, uden stemmeandel eller med en stemmeandel på nul.

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 og",
    nrow(distinct(Manifesto, lande, edate)), "valg")
30 unikke lande og 344 valg

Polariseringen beregnes pr. valg som en stemmeandelsvægtet standardafvigelse af partiernes rile-positioner, hvor store partier vægter mere end små. For et valg med n partier beregnes først hvert partis vægt som dets renormaliserede stemmeandel. Renormaliseringen er nødvendig, fordi de kodede partiers stemmeandele ikke nødvendigvis summer til 100 procent. Resten af stemmerne går til småpartier uden rile-kodning, og vægtene skal summe til 1.

w_i = \frac{v_i}{\sum_{j=1}^{n} v_j}

hvor w_i er parti i’s vægt og v_i er dets stemmeandel (pervote). Dernæst beregnes det vægtede midtpunkt (\mu), altså tyngdepunktet i partisystemet.

\mu = \sum_{i=1}^{n} w_i \, r_i

hvor r_i er parti i’s rile-position (rile). Endelig beregnes polariseringen som den vægtede standardafvigelse omkring midtpunktet.

P = \sqrt{\textstyle\sum_{i=1}^{n} w_i \, (r_i - \mu)^2}

hvor P er polariseringsmålet. I samme trin beregner vi det effektive antal partier som 1 divideret med summen af de kvadrerede stemmeandele. Det bruges som kontrolvariabel.

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

cat(nrow(Manifesto_valg), "valg med beregnet polarisering")
344 valg med beregnet polarisering

Tallet svarer til antallet af valg i indlæsningen. Nu kigger vi på, hvilke lande som har haft dobbeltvalg.

dobbeltvalg <- Manifesto_valg |> count(lande, år) |> filter(n > 1)
if (nrow(dobbeltvalg) == 0) {
  cat("Ingen land-år har mere end ét valg.")
} else {
  print(dobbeltvalg)
}
# A tibble: 5 × 3
  lande    år     n
  <chr> <dbl> <int>
1 ESP    2019     2
2 GRC    1989     2
3 GRC    2012     2
4 GRC    2015     2
5 IRL    1982     2

Nogle land-år har to valg samme år. Vi beholder det givne års sidste valg, da det er dét, som gjaldt fremadrettet.

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

cat(nrow(Manifesto_valg), "land-år efter reduktion")
339 land-år efter reduktion

Nu laver vi en manuel test af, hvordan vi beregner polariseringen pr. valg som en stemmeandelsvægtet standardafvigelse af partiernes rile-positioner, hvor store partier vægter mere end små. Vi laver udregningen i hånden, hvorefter vi ser, at vi får det samme med kode. Vi tager GBR (2005) som eksempel, fordi valget kun har tre kodede partier.

GBR (2005) havde følgende tre partier: Labour (rile −3,090; pervote 35,186 pct.), Liberal Democrats (rile 3,212; pervote 22,047 pct.) og Conservatives (rile 14,535; pervote 32,359 pct.).

De tre partiers stemmeandele summer ikke til 100 pct., men til 35,186 + 22,047 + 32,359 = 89,592 pct., fordi resten af stemmerne gik til småpartier uden rile-kodning. Derfor renormaliseres andelene først, så vægtene summer til 1: Labour 35,186/89,592 = 0,3927, Liberal Democrats 22,047/89,592 = 0,2461 og Conservatives 32,359/89,592 = 0,3612. Kontrol: 0,3927 + 0,2461 + 0,3612 = 1,0000.

Dernæst det vægtede midtpunkt (μ): 0,3927·(−3,090) + 0,2461·3,212 + 0,3612·14,535 = 4,83.

Hvert partis afstand til midtpunktet: Labour −3,090 − 4,83 = −7,92; Liberal Democrats 3,212 − 4,83 = −1,62; Conservatives 14,535 − 4,83 = 9,71.

Afstandene kvadreres og vægtes med partiets størrelse: Labour (−7,92)² · 0,3927 = 24,61; Liberal Democrats (−1,62)² · 0,2461 = 0,64; Conservatives (9,71)² · 0,3612 = 34,04.

Summen er 24,61 + 0,64 + 34,04 = 59,30, og kvadratroden fører tallet tilbage til rile-skalaens enheder: √59,30 = 7,70.

kontrol_GBR <- Manifesto_valg |>
  filter(lande == "GBR", år == 2005) |>
  pull(prog_pol)

cat("prog_pol =", format(kontrol_GBR, digits = 3, nsmall = 2, decimal.mark = ","))
prog_pol = 7,70

Manifesto måler kun ved valg. Danmark har f.eks. et valg i 2015 og igen i 2019, men hvad står der så i årene 2016, 2017 og 2018? Svaret afhænger af, hvad der måles.

Partipolarisering bruger kun valgår. Årene mellem valgene står tomme, så analysen bygger kun på det, der faktisk er målt.

Effektivt antal partier er en kontrolvariabel i alle modeller og skal derfor have en værdi hvert år. Her føres sidste valgs værdi frem, indtil et nyt valg afløser den.

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

Vi undersøger dækningen af partipolarisering pr. land.

Manifesto_panel |>
  group_by(lande) |>
  summarise(valg = sum(!is.na(prog_pol_valgår)),
            første = min(år[!is.na(prog_pol_valgår)]),
            sidste = max(år[!is.na(prog_pol_valgår)])) |>
  arrange(valg, lande) |>
  print(n = 30)
# A tibble: 30 × 4
   lande  valg første sidste
   <chr> <int>  <dbl>  <dbl>
 1 CYP       5   1996   2016
 2 LUX       5   1999   2018
 3 BEL       6   1999   2019
 4 CHE       6   1999   2019
 5 EST       6   1999   2019
 6 FIN       6   1999   2019
 7 FRA       6   1997   2022
 8 IRL       6   1997   2020
 9 ITA       6   1996   2018
10 NOR       6   1997   2017
11 ROU       6   1996   2016
12 SVK       6   1998   2016
13 AUT       7   1999   2019
14 BGR       7   1997   2017
15 DEU       7   1998   2021
16 DNK       7   1998   2019
17 HUN       7   1998   2022
18 LTU       7   1996   2020
19 MLT       7   1996   2022
20 POL       7   1997   2019
21 SVN       7   1996   2018
22 SWE       7   1998   2022
23 CZE       8   1996   2021
24 GBR       8   1997   2024
25 LVA       8   1998   2022
26 NLD       8   1998   2021
27 PRT       8   1999   2022
28 ESP       9   1996   2023
29 GRC       9   1996   2023
30 ISL       9   1999   2024

Eurobarometer

Fra Eurobarometer får vi målet for vælgerpolarisering. Respondenterne placerer sig selv på en venstre-højre-skala fra 1 (venstre) til 10 (højre), og vælgerpolarisering er den vægtede standardafvigelse i deres svar pr. land og år.

Målet ligger færdigberegnet i filen Eurobarometer data.csv, som vi har lavet ud fra de enkelte Eurobarometer-runder. Har et land flere runder i samme år, bruger vi gennemsnittet. Huller på op til to år er udfyldt med lineær interpolation. Der er ingen målinger i 2012.

EB <- read_csv("Data/Eurobarometer/Eurobarometer data.csv", show_col_types = FALSE) |>
  filter(lande %in% .env$lande, år %in% .env$år)

Vi undersøger dækningen af vælgerpolarisering pr. land.

EB |>
  group_by(lande) |>
  summarise(målt = sum(kilde == "målt"),
            interpoleret = sum(kilde == "interpoleret"),
            første = min(år[kilde == "målt"]),
            sidste = max(år[kilde == "målt"])) |>
  arrange(målt, lande) |>
  print(n = 30)
# A tibble: 30 × 5
   lande  målt interpoleret første sidste
   <chr> <int>        <int>  <dbl>  <dbl>
 1 CHE       4            0   2020   2023
 2 NOR       5            0   1996   2023
 3 ISL       6            2   2010   2022
 4 BGR      19            1   2004   2023
 5 CYP      19            1   2004   2023
 6 CZE      19            1   2004   2023
 7 EST      19            1   2004   2023
 8 HUN      19            1   2004   2023
 9 LTU      19            1   2004   2023
10 LVA      19            1   2004   2023
11 MLT      19            1   2004   2023
12 POL      19            1   2004   2023
13 ROU      19            1   2004   2023
14 SVK      19            1   2004   2023
15 SVN      19            1   2004   2023
16 AUT      27            1   1996   2023
17 BEL      27            1   1996   2023
18 DEU      27            1   1996   2023
19 DNK      27            1   1996   2023
20 ESP      27            1   1996   2023
21 FIN      27            1   1996   2023
22 FRA      27            1   1996   2023
23 GBR      27            1   1996   2023
24 GRC      27            1   1996   2023
25 IRL      27            1   1996   2023
26 ITA      27            1   1996   2023
27 LUX      27            1   1996   2023
28 NLD      27            1   1996   2023
29 PRT      27            1   1996   2023
30 SWE      27            1   1996   2023

De 15 lande, der var medlemmer af EU i 1996, er målt i alle år undtagen 2012. De central- og østeuropæiske lande samt Cypern og Malta er med fra 2004. Norge, Island og Schweiz er kun med i enkelte år.


World Inequality Database

World Inequality Database giver os vores uligheds- og omfordelingsmål.

Vi henter Gini-koefficienten både før og efter skat. Gini efter skat måler uligheden i den disponible indkomst, altså den indkomst, folk faktisk råder over. Gini før skat er målt efter pensioner og arbejdsløshedsunderstøttelse, men før øvrige skatter og overførsler. Forskellen mellem de to er omfordelingen. De to sidste mål bruges til at undersøge, om en sammenhæng med ulighed efter skat kommer fra uligheden før skat eller fra omfordelingen.

Data ligger som separate landefiler. Vi bruger derfor en funktion, der læser én fil ad gangen og med det samme filtrerer ned til de rækker, vi skal bruge, i stedet for at indlæse alt og rydde op bagefter.

wid_navne <- c("AT"="AUT","BE"="BEL","BG"="BGR","CH"="CHE","CY"="CYP",
               "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","LU"="LUX",
               "LV"="LVA","MT"="MLT","NL"="NLD","NO"="NOR","PL"="POL",
               "PT"="PRT","RO"="ROU","SE"="SWE","SI"="SVN","SK"="SVK")

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 %in% c("gdiincj992", "gptincj992"),
           percentile == "p0p100",
           year %in% .env$år)
}

WID <- map(wid_filer, læs_wid) |>
  list_rbind() |>
  mutate(lande = wid_navne[country],
         variable = recode(variable,
                           gdiincj992 = "gini",
                           gptincj992 = "gini_før")) |>
  select(lande, år = year, variable, value) |>
  pivot_wider(names_from = variable, values_from = value) |>
  mutate(gini = gini * 100,
         gini_før = gini_før * 100,
         omfordeling = gini_før - gini)

cat(nrow(WID), "rækker og", n_distinct(WID$lande), "lande")
870 rækker og 30 lande

Vi ser på, om vi har manglende observationer.

cat("Manglende observationer: Gini efter skat", sum(is.na(WID$gini)),
    "| Gini før skat", sum(is.na(WID$gini_før)))
Manglende observationer: Gini efter skat 0 | Gini før skat 0

Vi har således ingen manglende observationer i WID.


World Bank

World Bank giver os de resterende økonomiske variable, som vi skal bruge.

wb_filer <- c(
  bnp_vækst     = "World Bank data BNP-vækst.csv",
  bnp_pc        = "World Bank data BNP per capita.csv",
  inflation     = "World Bank data inflation.csv",
  arbejdsløshed = "World Bank data arbejdsløshed.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"))

cat(nrow(WB), "rækker og", n_distinct(WB$lande), "lande")
870 rækker og 30 lande

Vi ser på, om vi har manglende observationer.

manglende_detaljer <- WB |>
  pivot_longer(cols = -c(lande, år),
               names_to = "variabel", values_to = "værdi") |>
  filter(is.na(værdi)) |>
  group_by(variabel, lande) |>
  summarise(antal = n(),
            år = paste(sort(år), collapse = ", "),
            .groups = "drop") |>
  arrange(variabel, lande)

if (nrow(manglende_detaljer) == 0) {
  cat("Ingen manglende observationer")
} else {
  print(manglende_detaljer, n = Inf, width = Inf)
}
Ingen manglende observationer

Vi har således ingen manglende observationer i World Bank.


Samling og frysning af panelet

Vores kilder samles nu til ét analysepanel. Panelet indeholder kun de variable, vi bruger i analysen.

panel <- list(V_Dem |> select(-regime),
              samf_pol_lags,
              Manifesto_panel,
              EB |> select(lande, år, eb_pol, eb_pol_interp),
              WID, WB) |>
  reduce(left_join, by = c("lande", "år"),
         .init = expand_grid(lande = lande, år = .env$år))

dubletter_panel <- panel |> count(lande, år) |> filter(n > 1)

cat(nrow(panel), "rækker,", ncol(panel), "kolonner",
    ifelse(nrow(dubletter_panel) == 0, "og ingen dubletter", "MEN DUBLETTER!"))
870 rækker, 17 kolonner og ingen dubletter

Nu fryser vi panelet, så vi kan arbejde ud fra det.

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

if (identical(panel, kontrol_panel)) {
  cat(paste0("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 (870 rækker og 17 kolonner)