Kuka allekirjoittaa kenen aloitteen? Eduskunnan yhteistyöverkostot

Eduskunta jakaumana, osa 11: allekirjoitusverkosto

Lakialoitteilla on usein useita allekirjoittajia. Kun katsomme, ketkä allekirjoittavat toistensa aloitteita yhden vaalikauden aikana, syntyy verkosto, joka paljastaa puoluerajat ylittävän yhteistyön ja tiiviit ryhmittymät.

eduskunta
avoin data
verkostoanalyysi
igraph
Author

Kristian Vepsäläinen

Published

10.9.2026

Uutiskoukku: kuka tekee yhteistyötä kenen kanssa?

Puolue on ilmeisin jakolinja eduskunnassa. Mutta aloitteiden allekirjoitukset paljastavat toisen kartan: kuka on valmis laittamaan nimensä kenenkin aloitteeseen. Se ei aina noudata puoluerajoja.

Näytä koodi
library(tidyverse); library(here); library(qs2); library(scales)
library(igraph); library(ggraph); library(tidygraph)
select <- dplyr::select; filter <- dplyr::filter   # igraph/MASS-maskaus pois
set.seed(20270418)
pal <- c(punainen="#e63946", turkoosi="#2a9d8f", oranssi="#f4a261",
         laivasto="#1d3557", sininen="#457b9d")
# Yhteisövärit: tarpeeksi sävyjä, generoidaan paletista (ei "insufficient values")
yhteiso_varit <- function(n) grDevices::colorRampPalette(unname(pal))(n)
theme_set(theme_minimal(base_size = 13) + theme(plot.title = element_text(face="bold")))

DATA <- here("data", "eduskunta")
init <- qs2::qd_read(file.path(DATA, "vaski_init.qs"))
mp   <- qs2::qs_read(file.path(DATA, "mp.qs"))

KAUSI_ALKU <- 2023L   # rajaus yhteen vaalikauteen -> luettava verkosto
KYNNYS     <- 5L      # säilytä vain parit, jotka esiintyvät vähintään näin usein
MAX_ALLEK  <- 20L     # massa-aloitteet pois: ne ovat ryhmäkuria, eivät yhteistyötä

Verkoston rakentaminen

Yhteistyön yksikkö on yhteinen allekirjoitus: kaksi edustajaa yhdistyy, kun he allekirjoittavat saman aloitteen. Kolme tietoista rajausta pitävät verkoston luettavana ja mittaavat aitoa yhteistyötä, eivät rakenteellisia artefakteja:

  • Yksi vaalikausi (2023–2027): ~200 edustajaa tuhansien sijaan, jolloin rakenne erottuu eikä hukkaannu “hairballiin”.
  • Massa-aloitteet pois (yli 20 allekirjoittajaa): kymmenien nimien aloitteet ovat ryhmäkuria, ja ne loisivat keinotekoisia klikkejä.
  • Kynnys yhteyksille: säilytämme vain parit, jotka toistuvat — satunnainen yksi yhteinen aloite ei ole yhteistyötä.
Näytä koodi
allek <- init |> filter(tyyppi %in% c("LA","TPA"), vpvuosi >= KAUSI_ALKU) |>
  select(id, allekirjoittajat) |>
  mutate(n_allek = map_int(allekirjoittajat, length)) |>
  filter(n_allek >= 2, n_allek <= MAX_ALLEK) |>
  select(id, allekirjoittajat) |> unnest(allekirjoittajat) |>
  filter(!is.na(allekirjoittajat)) |> rename(henkilo_nro = allekirjoittajat)
stopifnot("Allekirjoituksia ei löytynyt kaudelta" = nrow(allek) > 100)

# Parit per aloite -> painotetut reunat
parit <- allek |> group_by(id) |>
  summarise(p = list(as_tibble(t(combn(sort(unique(henkilo_nro)), 2))) |>
                       set_names(c("a","b"))), .groups = "drop") |>
  unnest(p)
reunat <- parit |> count(a, b, name = "paino") |> filter(paino >= KYNNYS)
stopifnot("Kynnyksen jälkeen ei reunoja — laske KYNNYS" = nrow(reunat) > 0)

# Vain solmut, joilla jää reuna; puolue väritietona
kaytetyt <- unique(c(reunat$a, reunat$b))
solmut <- tibble(henkilo_nro = kaytetyt) |>
  left_join(mp |> select(henkilo_nro, nimi, ryhma_nyt), by = "henkilo_nro") |>
#korjataan kaksi nimiea oikeisiin puolueisiin
  mutate(ryhma_nyt = case_when(henkilo_nro == 1387 ~ 'Vihreä eduskuntaryhmä',
                               henkilo_nro == 1400 ~ 'Vasemmistoliiton eduskuntaryhmä',
                               T ~ ryhma_nyt))

g <- tbl_graph(nodes = solmut, edges = reunat |> rename(from = a, to = b),
               directed = FALSE) |>
  activate(nodes) |>
  mutate(yhteiso = as.factor(group_louvain(weights = paino)),
         aste = centrality_degree(weights = paino))

Verkosto ja yhteisöt

Etsimme tiiviit ryhmittymät yhteisödetektiolla (Louvain) ja väritämme solmut yhteisön mukaan — se on tarina. Puolue jää lisätiedoksi seuraavaan kuvaan. Käytämme painotettua Kamada–Kawai-asettelua, joka erottelee ryppäät paremmin kuin tiheälle verkolle tukkoinen voimasuunnattu asettelu.

Näytä koodi
n_yht <- nlevels(pull(g, yhteiso) )
ggraph(g, layout = "kk", weights = paino) +
  geom_edge_link(aes(width = paino), alpha = .10, color = "grey55") +
  geom_node_point(aes(color = yhteiso, size = aste)) +
  scale_edge_width(range = c(.1, 1.5), guide = "none") +
  scale_size(range = c(1.5, 8), guide = "none") +
  scale_color_manual(values = yhteiso_varit(n_yht), guide = "none") +
  labs(title = "Eduskunnan allekirjoitusverkosto (2023–2027)",
       subtitle = "Väri = tiivis yhteistyöryhmä (Louvain), koko = kytkeytyneisyys") +
  theme_graph()

Näytä koodi
# Sama verkosto, väri puolueen mukaan — vertaa: noudattavatko yhteisöt puoluerajoja?
n_pue <- n_distinct(pull(g, ryhma_nyt))
ggraph(g, layout = "kk", weights = paino) +
  geom_edge_link(alpha = .08, color = "grey60") +
  geom_node_point(aes(color = ryhma_nyt, size = aste)) +
  scale_size(range = c(1.5, 8), guide = "none") +
  scale_color_manual(values = yhteiso_varit(n_pue), na.value = "grey70") +
  labs(title = "Sama verkosto puolueväreillä",
       subtitle = "Jos yhteisöt eivät seuraa värejä, yhteistyö ylittää puoluerajat",
       color = NULL) +
  theme_graph()

Kuinka puoluerajaista yhteistyö on? Nollamalli

Onko puoluerajat ylittäviä yhteyksiä enemmän vai vähemmän kuin sattumalta odottaisi? Vertaamme havaittua osuutta permutaatio-nollamalliin, jossa puolueleimat sekoitetaan satunnaisesti.

Näytä koodi
e <- reunat |>
  left_join(mp |> select(henkilo_nro, ra = ryhma_nyt), by = c("a"="henkilo_nro")) |>
  left_join(mp |> select(henkilo_nro, rb = ryhma_nyt), by = c("b"="henkilo_nro")) |>
  filter(!is.na(ra), !is.na(rb))
havaittu <- mean(e$ra != e$rb)

puolueet <- mp |> filter(henkilo_nro %in% c(e$a, e$b)) |> distinct(henkilo_nro, ryhma_nyt)
perm <- replicate(1000, {
  sek <- puolueet |> mutate(r = sample(ryhma_nyt))
  e2 <- e |> left_join(sek |> select(henkilo_nro, ra2 = r), by = c("a"="henkilo_nro")) |>
    left_join(sek |> select(henkilo_nro, rb2 = r), by = c("b"="henkilo_nro"))
  mean(e2$ra2 != e2$rb2)
})
tibble(havaittu_ylitys = round(havaittu,3),
       nollamalli_ka = round(mean(perm),3),
       p = round(mean(perm <= havaittu),3),
       tulkinta = if_else(havaittu < mean(perm),
                          "Vähemmän puoluerajan ylityksiä kuin sattumalta (yhteistyö puolueen sisäistä)",
                          "Enemmän puoluerajan ylityksiä kuin sattumalta"))
# A tibble: 1 × 4
  havaittu_ylitys nollamalli_ka     p tulkinta                                  
            <dbl>         <dbl> <dbl> <chr>                                     
1           0.135         0.797     0 Vähemmän puoluerajan ylityksiä kuin sattu…
NoteMiksi nollamalli?

Pelkkä puoluerajan ylittävien yhteyksien osuus ei kerro mitään ilman vertailukohtaa — se riippuu puolueiden koosta ja aloitteiden määrästä. Permutaatio-nollamalli antaa sen, mitä sattumalta odottaisi, jolloin havaittu luku saa merkityksen.

Mitä tämä tarkoittaa päättäjälle

  • Yhteistyö on verkosto, ei puoluelista. Allekirjoitukset paljastavat rakenteen, jota puoluejako ei näytä — mutta vasta oikein rajattuna.
  • Rajaus on osa analyysia. Massa-aloitteet ja koko historia tuottaisivat tukkoisen möykyn; yksi kausi ja kynnys tekevät rakenteen näkyväksi.
  • Verkkomittari vaatii nollamallin. Ilman satunnaisvertailua “30 % ylittää puoluerajan” on luku ilman merkitystä.

TipTehdäänkö teidän datallenne sama?

Verkostoanalyysi ja yhteisödetektio — asiakkaista, toimijoista tai riskeistä — oikein rajattuna ja nollamallilla validoituna. kristianvepsalainen.com

Datalähde: Eduskunnan avoin data, VaskiData (CC BY 4.0).