Kartor över valdistrikt i R

valet
Author

Filip Wästberg

Published

September 19, 2022

Det har gått mer än en vecka sedan valet och resultatdata har nu börjat publiceras på valmyndigheten.se. I det här inlägget tänkte jag visa hur vi kan använda swemaps2 för att analysera valresultatet på kommunal nivå.

Till att börja med kan vi ladda ner valresutlatet som finns i en Excel på valmyndigheten.se

library(readxl)
library(tidyverse)

download.file("https://www.val.se/download/18.14c1f613181ed0043d567ae/1663009000443/valresultat-riksdagen-preliminar-jamforande-statistik.xlsx",
              destfile = "data/valresultat-riksdagen-preliminar-jamforande-statistik.xlsx")

valresultat <- read_excel("data/valresultat-riksdagen-preliminar-jamforande-statistik.xlsx", sheet = 3) |> 
  janitor::clean_names() 

valresultat
# A tibble: 4,350 × 9
   riksdagsvalkrets kommu…¹ parti roste…² andel…³  diff diff_a…⁴ roste…⁵ andel…⁶
   <chr>            <chr>   <chr>   <dbl>   <dbl> <dbl>    <dbl>   <dbl>   <dbl>
 1 Västra Götaland… Ale     Mode…    3451  0.182   -188 -0.00588    3639  0.188 
 2 Västra Götaland… Ale     Cent…     994  0.0525  -364 -0.0177     1358  0.0703
 3 Västra Götaland… Ale     Libe…     631  0.0333  -283 -0.0139      914  0.0473
 4 Västra Götaland… Ale     Kris…    1065  0.0563  -147 -0.00642    1212  0.0627
 5 Västra Götaland… Ale     Arbe…    5687  0.301    286  0.0211     5401  0.279 
 6 Västra Götaland… Ale     Väns…    1259  0.0665  -219 -0.00993    1478  0.0765
 7 Västra Götaland… Ale     Milj…     664  0.0351   -95 -0.00417     759  0.0393
 8 Västra Götaland… Ale     Sver…    4864  0.257    563  0.0346     4301  0.223 
 9 Västra Götaland… Ale     Övri…     307  0.0162    39  0.00236     268  0.0139
10 Västra Götaland… Ale     Gilt…   18922  1       -408 NA         19330  1     
# … with 4,340 more rows, and abbreviated variable names ¹​kommunnamn,
#   ²​roster_2022, ³​andel_2022, ⁴​diff_andel, ⁵​roster_2018, ⁶​andel_2018

Vi ser att det i kolumnen parti finns en del värden som inte är partier, ex. Röstberättigande.

valresultat |> 
  group_by(parti) |> 
  summarise(roster_2022 = sum(roster_2022)) |> 
  arrange(desc(roster_2022))
# A tibble: 15 × 2
   parti                                  roster_2022
   <chr>                                        <dbl>
 1 Röstberättigade                            7772120
 2 Valdeltagande vallokaler                   6320963
 3 Giltiga Röster                             6227229
 4 Arbetarepartiet-Socialdemokraterna         1897965
 5 Sverigedemokraterna                        1282352
 6 Moderaterna                                1187350
 7 Centerpartiet                               417851
 8 Vänsterpartiet                              414604
 9 Kristdemokraterna                           333327
10 Miljöpartiet de gröna                       314579
11 Liberalerna (tidigare Folkpartiet)          286503
12 Övriga anmälda partier                       92698
13 Ogiltiga röster - blanka                     59218
14 Ogiltiga röster - övriga                     31137
15 Ogiltiga röster - inte anmälda partier        3379

Vi är bara intresserade av de stora riksdagspartierna, så vi filtrerar bort värden som inte är partier.

library(stringr)
valresultat_partier <- valresultat |> 
  filter(!(parti %in% c("Röstberättigade",
                        "Valdeltagande vallokaler",
                        "Giltiga Röster")) &
           !str_detect(parti, "Ogiltiga"))

För att kunna redovisa resultatet på en karta behöver vi knyta det till ett geografiskt objekt. Det kan vi enkelt göra genom att joina vår data.frame med municipality som är ett dataset i swemaps2.

library(swemaps2)
valresultat_kommun <- swemaps2::municipality |> 
  left_join(
    valresultat_partier, by = c("kn_namn" = "kommunnamn")
  )

valresultat_kommun
Simple feature collection with 2602 features and 12 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 1230810 ymin: 6137234 xmax: 1880916 ymax: 7669583
Projected CRS: RT90 2.5 gon V
# A tibble: 2,602 × 13
   kn_kod kn_namn ln_kod ln_namn                  geometry riksd…¹ parti roste…²
   <chr>  <chr>   <chr>  <chr>          <MULTIPOLYGON [m]> <chr>   <chr>   <dbl>
 1 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Mode…    5043
 2 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Cent…    1461
 3 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Libe…    1126
 4 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Kris…    1194
 5 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Arbe…    7389
 6 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Väns…    1805
 7 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Milj…     894
 8 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Sver…    5205
 9 0114   Upplan… 01     Stockh… (((1620218 6599562, 1618… Stockh… Övri…     450
10 0115   Vallen… 01     Stockh… (((1637371 6601120, 1636… Stockh… Mode…    5272
# … with 2,592 more rows, 5 more variables: andel_2022 <dbl>, diff <dbl>,
#   diff_andel <dbl>, roster_2018 <dbl>, andel_2018 <dbl>, and abbreviated
#   variable names ¹​riksdagsvalkrets, ²​roster_2022

Jag är intresserad av hur det gått för Moderaterna i Skåne så jag filtrerar ut Skåne och moderaterna.

valresultat_skane <- valresultat_kommun |> 
  filter(parti == "Moderaterna" & str_detect(tolower(ln_namn), "skåne"))

Nu har vi valresultat knutet till geografiska objekt och kan visualisera det.

library(scales)

Attaching package: 'scales'
The following object is masked from 'package:purrr':

    discard
The following object is masked from 'package:readr':

    col_factor
plot_skane <- valresultat_skane |> 
  ggplot(aes(fill = diff_andel)) +
  geom_sf() +
  scale_fill_viridis_c(option = "magma", label = scales::percent) +
  theme_swemap2()

plot_skane

För att göra visualiseringen lite mer informativ kan vi skapa en dataframe där vi har de kommuner där Moderaterna tappat mest och där det ökat mest.

top_skane <- valresultat_skane |> 
  slice_max(diff_andel, n = 3)

bottom_skane <- valresultat_skane |> 
  slice_min(diff_andel, n = 3)

labels_skane <- bind_rows(top_skane, bottom_skane)

För att skapa snygga labels använder jag ggrepel. Vi ser att Bromölla är den kommun där moderaterna gått framåt. Annars ser de ut att tappa i de flesta kommuner.

library(ggrepel)
plot_skane <- plot_skane +
  geom_label_repel(
    data = labels_skane,
    aes(label = kn_namn, geometry = geometry),
    stat = "sf_coordinates",
    min.segment.length = 0,
    color = "black",
    fill = "white"
  ) 

plot_skane

Till sist ändrar jag titel och lite andra parametrar för att få en mer tilltalande visualisering:

plot_skane +
  labs(
    title = "Förändring andel röster för Moderaterna",
    subtitle = "2018 till 2022",
    fill = "Skillnad",
    caption = "Källa: Valmyndigheten och SCB"
  )