Испанские партии в Конгрессе

Граф связей политических партий Испании на основе схожести голосования в Конгрессе депутатов в 2024-2025 гг.

Автор

Карина Чадаева

Дата публикации

06.03.2025

Немного о данных

Конгресс депутатов Испании состоит из 350 членов, избираемых по пропорциональной системе в многомандатных округах, соответствующих испанским провинциям. Каждая провинция представлена минимум двумя депутатами, а города Сеута и Мелилья — одним депутатом каждый.

Состав депутатов в Конгрессе меняется через регулярные выборы, которые проводятся каждые четыре года. Однако возможны и досрочные выборы в случае роспуска парламента, как это произошло 23 июля 2023 года.

Congreso de los Diputados

Датасет был собран мною на основе данных о голосовании, выложенных на официальный сайт испанского Конгресса.

Я взяла данные за весь 2024 год и за первые 2 месяца 2025 года. За это время состав депутатов нижней палаты не менялся. В датасете представлены данные о дате, номере сессии голосования, о конкретных вопросах, по которым проводилось голосование, а также данные о депутате: имя и фамилия, номер места, принадлежность к одной из парламентских групп и отданный голос (варианты: “да”, “нет”, “воздержался от голосования”).

В испанском парламенте политические партии объединяются в парламентские группы (Grupos Parlamentarios), которые могут включать несколько партий:

Сокращение Полное название Основные партии
GP Grupo Parlamentario Popular PP (Partido Popular)
GS Grupo Parlamentario Socialista PSOE (Partido Socialista Obrero Español)
GVOX Grupo Parlamentario VOX VOX
GSUMAR Grupo Parlamentario Sumar Sumar, Podemos, IU и другие левые партии
GEH Bildu Grupo Parlamentario EH Bildu EH Bildu (баскская левая партия)
GJxCAT Grupo Parlamentario Junts per Catalunya Junts per Catalunya (каталонские националисты)
GV (EAJ-PNV) Grupo Vasco (EAJ-PNV) EAJ-PNV (Баскская националистическая партия)
GR Grupo Republicano ERC (Esquerra Republicana de Catalunya)
GMx Grupo Mixto Разные небольшие партии (например, BNG, CUP, CC, UPN)
library(readr)

data <- read_csv("dataset.csv") 
data
# A tibble: 324,070 × 7
   Sesion Fecha      TextoExpediente                Asiento Diputado Grupo Voto 
    <dbl> <chr>      <chr>                            <dbl> <chr>    <chr> <chr>
 1     79 21/11/2024 Moción consecuencia de interp…    2511 Santana… GMx   No   
 2     79 21/11/2024 Moción consecuencia de interp…    2508 Recas M… GSUM… No   
 3     79 21/11/2024 Moción consecuencia de interp…       4 Gil de … GSUM… No   
 4     79 21/11/2024 Moción consecuencia de interp…    2402 Martíne… GSUM… No   
 5     79 21/11/2024 Moción consecuencia de interp…    2401 Guijarr… GSUM… No   
 6     79 21/11/2024 Moción consecuencia de interp…    2505 Cofiño … GSUM… No   
 7     79 21/11/2024 Moción consecuencia de interp…    2204 Santiag… GSUM… No   
 8     79 21/11/2024 Moción consecuencia de interp…    2406 Rivera … GSUM… No   
 9     79 21/11/2024 Moción consecuencia de interp…    2203 Micó Mi… GSUM… No   
10     79 21/11/2024 Moción consecuencia de interp…    2305 Ibáñez … GSUM… No   
# ℹ 324,060 more rows

Библиотеки

library(tidyverse)
library(igraph)
library(ggraph)

Подготовка матрицы

Из датасета я взяла только необходимую мне информацию: текст темы голосования, группу, голос (отфильтровано до бинарного значения). Также я упростила названия парламентских групп до сокращенных названий главенствующих партий.

party_names <- c(
  "GP" = "PP",
  "GS" = "PSOE",
  "GVOX" = "Vox",
  "GSUMAR" = "Sumar",
  "GEH Bildu" = "Bildu",
  "GJxCAT" = "Catalunya",
  "GV (EAJ-PNV)" = "PNV",
  "GR" = "ERC",
  "GMx" = "Mixto"
)

data <- data |> 
  mutate(
    Grupo = recode(Grupo, !!!party_names)
  ) |> 
  select(TextoExpediente, Grupo, Voto) |> 
  filter(Voto == "Sí" | Voto == "No")

Так как в нижней палате испанского Парламента разное количество представителей каждой партии, я сгруппировала голоса депутатов сначала по вопросам, затем по партиям, и посчитала процент представителей каждой партии, ответивших “да” в голосовании. Получился показатель, отражающий процент одобрения партии конкретного предложения, выставленного на парламентское голосование.

tmp <- data |> 
  group_by(TextoExpediente, Grupo) |> 
  summarise(Count_Yes = sum(Voto == "Sí"), 
            Total_Votes = n()) |> 
  mutate(Percent = round(Count_Yes / Total_Votes * 100)) |> 
  select(-Count_Yes, -Total_Votes)

tmp
# A tibble: 2,497 × 3
# Groups:   TextoExpediente [303]
   TextoExpediente                                                 Grupo Percent
   <chr>                                                           <chr>   <dbl>
 1 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… Bildu     100
 2 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… Cata…     100
 3 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… Mixto     100
 4 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… PNV       100
 5 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… PP        100
 6 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… PSOE      100
 7 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… Sumar     100
 8 Actas del XXVII Congreso de la Unión Postal Universal (UPU), a… Vox         0
 9 Acuerdo Global de Transporte Aéreo entre los Estados miembros … Mixto     100
10 Acuerdo Global de Transporte Aéreo entre los Estados miembros … PNV       100
# ℹ 2,487 more rows

Затем я сформировала матрицу попарного сходства. Значение на пересечении партий - это число “мэтчей” между партиями (“мэтч” случается в том случае, когда примерно одинаковый процент (отличается не более, чем на 10%) голосовавших ответил “да” на один и тот же вопрос)

tmp <- data |> 
  group_by(TextoExpediente, Grupo) |> 
  summarise(Count_Yes = sum(Voto == "Sí"), 
            Total_Votes = n()) |> 
  mutate(Percent = round(Count_Yes / Total_Votes * 100)) |> 
  select(-Count_Yes, -Total_Votes)

tmp_filtered <- tmp |> 
  group_by(TextoExpediente) |> 
  filter(!all(Percent == 100)) |> 
  ungroup()

tmp2 <- tmp_filtered |> 
  group_by(TextoExpediente) |> 
  summarise(pairs = list(tidyr::crossing(Grupo1 = Grupo, Grupo2 = Grupo))) |> 
  unnest(pairs) |> 
  filter(Grupo1 != Grupo2) |> 
  left_join(tmp |> select(TextoExpediente, Grupo, Percent) |> rename(Percent1 = Percent), 
            by = c("TextoExpediente", "Grupo1" = "Grupo")) |> 
  left_join(tmp |> select(TextoExpediente, Grupo, Percent) |> rename(Percent2 = Percent), 
            by = c("TextoExpediente", "Grupo2" = "Grupo")) |> 
  mutate(Match = ifelse(abs(Percent1 - Percent2) <= 10, 1, 0)) |> 
  select(TextoExpediente, Grupo1, Grupo2, Percent1, Percent2, Match)

matrix <- tmp2 |> 
  group_by(Grupo1, Grupo2) |> 
  summarise(Match_Sum = sum(Match)) |> 
  pivot_wider(names_from = Grupo2, values_from = Match_Sum, values_fill = list(Match_Sum = 0))

Итоговая матрица:

matrix
# A tibble: 9 × 10
# Groups:   Grupo1 [9]
  Grupo1    Catalunya   ERC Mixto   PNV    PP  PSOE Sumar   Vox Bildu
  <chr>         <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Bildu           155   203   101   160    50   169   197    12     0
2 Catalunya         0   159    96   158    73   153   158    34   155
3 ERC             159     0   104   159    52   167   191    14   203
4 Mixto            96   104     0   100    65   114   115    21   101
5 PNV             158   159   100     0    72   183   174    33   160
6 PP               73    52    65    72     0    77    65   139    50
7 PSOE            153   167   114   183    77     0   197    38   169
8 Sumar           158   191   115   174    65   197     0    20   197
9 Vox              34    14    21    33   139    38    20     0    12

Построение графа

Матрица, конечно, хороша для наглядности, но для построения графа я все сверну обратно в таблицу.

parties <- tmp2 |> 
  group_by(Grupo1, Grupo2) |> 
  summarise(weight = sum(Match))

parties
# A tibble: 72 × 3
# Groups:   Grupo1 [9]
   Grupo1    Grupo2    weight
   <chr>     <chr>      <dbl>
 1 Bildu     Catalunya    155
 2 Bildu     ERC          203
 3 Bildu     Mixto        101
 4 Bildu     PNV          160
 5 Bildu     PP            50
 6 Bildu     PSOE         169
 7 Bildu     Sumar        197
 8 Bildu     Vox           12
 9 Catalunya Bildu        155
10 Catalunya ERC          159
# ℹ 62 more rows
parties_g <- graph_from_data_frame(parties, directed = FALSE)

ggraph(parties_g, layout = "fr", maxiter = 100) +
  geom_edge_link(aes(width = weight, alpha = weight, color = weight)) +
  geom_node_label(aes(label = name, fill = "sienna1"), color = "white") +
  scale_edge_width(range = c(0.5, 3)) +
  scale_edge_alpha(range = c(0.5, 1)) +
  scale_edge_color_gradient(low = "yellow", high = "darkred") + 
  theme_void() +
  theme(legend.position = "none") 

На графе мы видим, что партии PP и Vox часто голосуют не так, как остальные испанские партии. Это не удивительно, ведь они представляют правый политический фланг в Испании, формируют оппозицию нынешнему правительству.

Чтобы разглядеть различия между остальными партиями я построю граф, исключив PP и Vox.

tmp_filtered <- tmp_filtered |> 
  group_by(TextoExpediente) |> 
  filter(Grupo != "PP") |> 
  filter(Grupo != "Vox") |> 
  ungroup()

tmp2 <- tmp_filtered |> 
  group_by(TextoExpediente) |> 
  summarise(pairs = list(tidyr::crossing(Grupo1 = Grupo, Grupo2 = Grupo))) |> 
  unnest(pairs) |> 
  filter(Grupo1 != Grupo2) |> 
  left_join(tmp |> select(TextoExpediente, Grupo, Percent) |> rename(Percent1 = Percent), 
            by = c("TextoExpediente", "Grupo1" = "Grupo")) |> 
  left_join(tmp |> select(TextoExpediente, Grupo, Percent) |> rename(Percent2 = Percent), 
            by = c("TextoExpediente", "Grupo2" = "Grupo")) |> 
  mutate(Match = ifelse(abs(Percent1 - Percent2) <= 10, 1, 0)) |> 
  select(TextoExpediente, Grupo1, Grupo2, Percent1, Percent2, Match)

parties <- tmp2 |> 
  group_by(Grupo1, Grupo2) |> 
  summarise(weight = sum(Match)) |> 
  filter(weight > 0)

parties_g <- graph_from_data_frame(parties, directed = FALSE)

ggraph(parties_g, layout = "fr", maxiter = 100) +
  geom_edge_link(aes(width = weight, alpha = weight, color = weight)) +
  geom_node_label(aes(label = name, fill = "sienna1"), color = "white") +
  scale_edge_width(range = c(0.5, 3)) +
  scale_edge_alpha(range = c(0.5, 1)) +
  scale_edge_color_gradient(low = "yellow", high = "darkred") + 
  theme_void() +
  theme(legend.position = "none") 

Выводы

На основе построенных графов можно увидеть две противостоящие коалиции:

  1. Правые (PP, Vox) - оппозиция
  • PP (Partido Popular) – традиционная консервативная правая партия
  • Vox – ультраправая партия
  1. Левые и регионалисты (PSOE, Sumar, Bildu, ERC, Basque Nationalists) - в настоящий момент находятся у власти
  • PSOE (Partido Socialista Obrero Español) – главная социал-демократическая партия Испании
  • Sumar – коалиция левых партий
  • PNV (Partido Nacionalista Vasco) – баскские националисты
  • ERC (Esquerra Republicana de Catalunya) – каталонские левые республиканцы
  • EH Bildu – радикальная левая баскская коалиция

Также мы видим выбивающуюся Grupo Mixto - это парламентская группа, в которую входят разрозненные депутаты от мелких партий. У них разные идеологии, они голосуют по-разному и несистемно, поэтому их связь с левым политическим блоком слабее.

Junts per Catalunya (JxCat) – правая каталонская националистическая партия. Ее связь с левым правящим блоком немного слабее из-за расхождений в области экономической политики (здесь они ближе к правым PP и Vox)

Итак, построение графа позволило нам продемонстрировать тот факт, что голосование депутатов в Конгрессе отражает идеологическую позицию и принадлежность депутатов к своим партиям. Также мы четко видим различие в голосовании между противоборствующими политическими блоками.