gluecko.se


Etwas coolere ggplots erstellen

TidyTuesday Baden-Württemberg

Leider muss ich zugeben, dass meine bisherige Beteiligung am TidyTuesday weit hinter meinen Erwartungen zurück geblieben ist. Bisher habe ich lediglich einmal mitgemacht und das ist auch schon Monate her.

Natürlich überfliege ich trotzdem jede Woche unzählige Tweets und erfreue mich an tollen Grafiken zu verschiedenen Themen. So wie beispielsweise an dieser super-coole Visualisierung, die wirklich Lust darauf gemacht hat, mal wieder was mit Karten zu machen.

Und da ich mit ein bisschen Glück demnächst wieder Vollzeit-Baden-Württemberger werde, werden wir uns nicht Pizza-Restaurants in NYC sondern was aus dem lokaleren Datenbestand anschauen.

In diesem Blog-Post möchte ich zeigen, wie sich Visualisierungen in meinem Workflow entwickeln, wie ich von ggplot() + geom_point() zu etwas halbwegs ansehnlichem komme. Dass manche Schritte dabei ein Fall für @accidential aRt sind, versteht sich vermutlich von selbst 😉

Daten sammeln

Lange Rede - kurzer Sinn. Ich habe also auf die Webseite des Statistischen Landesamtes aufgerufen und mir einfach den erstbesten Datensatz runtergeladen, der Informationen auf Kreis-Ebene zur Verfügung stellt. Wir werden heute also etwas zur Bevölkerungsdichte in Baden-Württemberg lernen.

Damit wir die Daten auch hübsch visualisieren könne, habe ich mir darüber hinaus vom Landesamt für Geoinformation und Landentwicklung das entsprechende Shapefile heruntergeladen.

Da die Daten sauber vorbereitet sind, können wir sie mit wenigen Zeilen einlesen und in das gewünschte Format bringen.

# --------------------------------
# notwendige Packete laden
# --------------------------------
library(tidyverse)
library(sf)

# --------------------------------
# Dateipfade definieren
# --------------------------------
file_bevoelkerung <- "link/to/Bevoelk_I_Flaeche_j.csv"
file_shapes <- "link/to/AX_Gebiet_Kreis.shp"

# --------------------------------
# Daten einlesen und aufbereiten
# --------------------------------
EINWOHNER.Kreise <- 
  read_delim(
    file = file_bevoelkerung, 
    locale = locale(encoding = readr::guess_encoding(file_bevoelkerung) %>% select(encoding) %>% unlist()),
    delim = ";", 
    skip = 20,
    col_names = c(
      "ID.Regionaleinheit",
      "AmtlicherGemeindeschluessel",
      "PLZ",
      "Regionalname",
      "Stichtag",
      "Bevoelkerung_insgesamt",
      "Gemeindegebiet_ha",
      "Bevoelkerungsdichte_EW_km2"
    )
  ) %>% 
  filter(
    Stichtag == "31.12.2018",
    ID.Regionaleinheit == "KR"
  ) %>% 
  left_join(
    y = read_sf(file_shapes),
    by = c("AmtlicherGemeindeschluessel" = "Schlüssel")
  )

Basis-Plot erstellen

Nachdem wir die Daten eingelesen und aufbereitet haben, sollten wir uns anschauen was ggplot() vorschlägt. Ein klarer Vorteil ist, dass selbst dieser erste Versuch schon relativ ansehnlich ist. Aber da geht natürlich noch mehr …

# --------------------------------
# Basisplot erstellen
# --------------------------------
p <- ggplot(data = EINWOHNER.Kreise) + 
  geom_sf(aes(fill = Bevoelkerungsdichte_EW_km2))
p


Basis-Plot

All hail the Spätzle!

Theoretisch könnten wir hier aufhören. Wir haben eine Karte auf der die Landkreise entsprechend ihrer Bevölkerungsdichte eingefärbt sind. Es wird auf einen Blick deutlich, dass in Stuttgart sehr viele Menschen auf wenig Raum wohnen, während es im Nord- und Südosten eher ruhig zugeht.

Nun wollten wir ja aber einen etwas cooleren Plot erstellen. Dazu ändern wir als erstes die Farben. Und weil die Landesfarbe halt spätzle-gelb ist, nehmen wir das als Basis. Von coolors.co lasse ich mir einen dazu passenden Rot-Ton erzeugen und definiere so die beiden Extremwerte meiner Farbskala.

# --------------------------------
# Farben definieren
# --------------------------------
colour_spaetzlegelb <- "#FFFEF9"
colour_rangemax <- "#AA4465"

# --------------------------------
# Plot aktualisieren
# --------------------------------
p <- p + 
  scale_fill_gradient(low = colour_spaetzlegelb, high = colour_rangemax)
p


Neue Farben

Make the theme great again!

Das sieht schon besser (aber noch nicht gut) aus. Als nächstes passen wir das Theme an, entfernen die Legende, den grauen Hintergrund und die Achsen, deren Zahlenwerte aktuell eher verwirrend als hilfreich sind.

# --------------------------------
# Plot aktualisieren
# --------------------------------
p <- p + 
  theme(
    
    # Achsen entfernen -----------
    axis.line = element_blank(), 
    axis.title = element_blank(),
    axis.ticks = element_blank(),
    axis.text = element_blank(),

    # Hintergrund anpassen -------
    panel.background = element_rect(fill = colour_spaetzlegelb),
    plot.background = element_rect(fill = colour_spaetzlegelb),
    
    # Legende entfernen ----------
    legend.position = "none"
  )
p


Neues Theme

Reden ist Silber, Annotierung ist Gold!

So langsam wird es doch. Es fehlt aber noch ein bisschen Kontext. Also fügen wir neben der eigentlich Grafik einen Titel und eine kurze Zusammenfassung der Ergebnisse ein. Das hilft die Hauptaussage der Visualisierung noch einmal (in prosa) hervorzuheben.

# --------------------------------
# Untertitel verfassen
# --------------------------------
plot_subtitle <- str_c(
  "Die Karte zeigt die Bevölkerungsdichte nach Kreisen in Baden-Württemberg.",
  "Am dünnsten besiedelt ist der Main-Tauber-Kreis. Hier wohnen 101 Personen pro km².",
  "Mehr als 30x mehr Personen pro Quadratkilomenter wohnen in der Landeshauptstadt.",
  sep = " "
)

# --------------------------------
# Quellen definieren
# --------------------------------
plot_caption <- str_c(
  "Daten: Statistisches Landesamt Baden-Württemberg;",
  "Karten: Landesamt für Geoinformation und Landentwicklung",
  "Grafik: https://gluecko.se",
  sep = " "
)

# --------------------------------
# Notwendige Koordinaten speichern
# --------------------------------
coord_xmin <- sf::st_bbox(EINWOHNER.Kreise$geometry)$xmin
coord_xmax <- sf::st_bbox(EINWOHNER.Kreise$geometry)$xmax
coord_ymin <- sf::st_bbox(EINWOHNER.Kreise$geometry)$ymin
coord_ymax <- sf::st_bbox(EINWOHNER.Kreise$geometry)$ymax

# --------------------------------
# Plot aktualisieren
# --------------------------------
p <- p + 
  annotate("text", label = "Bevölkerungsdichte nach Kreisen", x = .7 * coord_xmin, y = 1.01 * coord_ymax, size = 6, hjust = 0) +
  annotate("text", label = str_wrap(plot_subtitle, 40), x = .7 * coord_xmin, y = coord_ymax, size = 3, hjust = 0) +
  annotate("text", label = str_wrap(plot_caption, 40), x = .7 * coord_xmin, y = coord_ymax, size = 3, hjust = 0, vjust = 2.5)
p


Mit Titel und Zusammenfassung

Look here!

Na das sieht doch jetzt echt nach was aus! Wir sollten allerdings noch ein paar Kreise explizit hervorheben. Extremfälle bieten sich an - fügen wir also noch Hinweise auf die Kreise mit der höchsten bzw. niedrigsten Bevölkerungsdichte ein.

# --------------------------------
# Centroid berechnen
# --------------------------------
get_centroid <- function(name){
  coords <- 
    EINWOHNER.Kreise %>% 
    filter(Name %in% name) %>% 
    select(geometry) %>% 
    st_as_sf() %>% 
    st_centroid() %>% 
    unlist()
  
  if(length(coords)>2){
    warning("More than one centroid found!")
  }
  
  returnData <- 
    tribble(
      ~"Name", ~"x", ~"y",
      name, coords[1], coords[2]
    )
  
  return(returnData)
}

# --------------------------------
# Notwendige Koordinaten speichern
# --------------------------------
coord_Stuttgart_xlabel <- 1.05 * coord_xmin
coord_Stuttgart_ylabel <- 1.0045 * get_centroid("Stuttgart")$y
coord_Mannheim_xlabel <- 1.1 * coord_xmin
coord_Mannheim_ylabel <- 1.005 * get_centroid("Mannheim")$y
coord_Karlsruhe_xlabel <- 1.1 * coord_xmin
coord_Karlsruhe_ylabel <- 1.005 * get_centroid("Karlsruhe")$y
coord_MainTauberKreis_xlabel <- 0.95 * coord_xmax
coord_MainTauberKreis_ylabel <- 1.005 * get_centroid("Main-Tauber-Kreis")$y
coord_Sigmaringen_xlabel <- 0.98 * coord_xmax
coord_Sigmaringen_ylabel <- 1.005 * get_centroid("Sigmaringen")$y
coord_NeckarOdenwaldKreis_xlabel <- 0.82 * coord_xmax
coord_NeckarOdenwaldKreis_ylabel <- 1.0115 * get_centroid("Neckar-Odenwald-Kreis")$y

# --------------------------------
# Plot aktualisieren
# --------------------------------
p <- p + 
  # Top 1: Stuttgart -------------  
  annotate("segment", x = coord_Stuttgart_xlabel, y = coord_Stuttgart_ylabel, xend = get_centroid("Stuttgart")$x,  yend = get_centroid("Stuttgart")$y) + 
  annotate("label", label = "Stuttgart\n3.062 EW/km²", x = coord_Stuttgart_xlabel, y = coord_Stuttgart_ylabel) + 
  
  # Top 2: Mannheim --------------
  annotate("segment", x = coord_Mannheim_xlabel, y = coord_Mannheim_ylabel, xend = get_centroid("Mannheim")$x, yend = get_centroid("Mannheim")$y) + 
  annotate("label", label = "Mannheim\n2.134 EW/km²", x = coord_Mannheim_xlabel, y = coord_Mannheim_ylabel) + 
  
  # Top 3: Karlsruhe -------------
  annotate("segment", x = coord_Karlsruhe_xlabel, y = coord_Karlsruhe_ylabel, xend = get_centroid("Karlsruhe")$x, yend = get_centroid("Karlsruhe")$y) + 
  annotate("label", label = "Karlsruhe\n1.805 EW/km²", x = coord_Karlsruhe_xlabel, y = coord_Karlsruhe_ylabel) + 

  # Bottom 1: Main-Tauber-Kreis --
  annotate("segment", x = coord_MainTauberKreis_xlabel, y = coord_MainTauberKreis_ylabel, xend = get_centroid("Main-Tauber-Kreis")$x, yend = get_centroid("Main-Tauber-Kreis")$y) +
  annotate("label", label = "Main-Tauber-Kreis\n101 EW/km²", x = coord_MainTauberKreis_xlabel, y = coord_MainTauberKreis_ylabel) + 

  # Bottom 2: Sigmaringen --------
  annotate("segment", x = coord_Sigmaringen_xlabel, y = coord_Sigmaringen_ylabel, xend = get_centroid("Sigmaringen")$x, yend = get_centroid("Sigmaringen")$y) +
  annotate("label", label = "Sigmaringen\n109 EW/km²", x = coord_Sigmaringen_xlabel, y = coord_Sigmaringen_ylabel) +

  # bottom 3: Neckar-Odenwald-Kreis --
  annotate("segment", x = coord_NeckarOdenwaldKreis_xlabel, y = coord_NeckarOdenwaldKreis_ylabel, xend = get_centroid("Neckar-Odenwald-Kreis")$x, yend = get_centroid("Neckar-Odenwald-Kreis")$y) +
  annotate("label", label = "Neckar-Odenwald-Kreis\n127 EW/km²", x = coord_NeckarOdenwaldKreis_xlabel, y = coord_NeckarOdenwaldKreis_ylabel)
p


Finale Visualisierung

Eine große Version der Grafik und den vollständigen Code am Stück gibt es - wie immer - bei GitHub.


r tidytuesday baden-wuerttemberg ttbw open-source


Reply to this post

I used to have a comments box on this blog, but found that maintaining another system and dealing with the cookies is cumbersome. So, if you have any thoughts on what I wrote in this article, please write me an email and we can have a conversation about it.


If you enjoyed reading this, you may also like ...

  • OSM Daten als SVG exportieren
    OSM2XML Ich möchte euch heute ein cooles Projekt vorstellen. Der Hase nutzt die Zeit während seines Spezialprojektes sehr effektiv und hat am Wochenende ein Python-Projekt auf GitHub geladen. Mit dem ...
  • Hugo mit Git auf dem uberspace nutzen
    Vorgeschichte Vor gar nicht allzu langer Zeit bin ich mit diesem Blog von Ghost zu Jekyll gewechselt. Ghost hat mir eigentlich sehr gut getaugt, auch wenn ich nicht der größte Fan davon war, dass blog...
  • Komplexe CSV-Dateien einlesen
    Natürlich gibt es einfachere Dinge, als Daten in R einzulesen. Und trotzdem ist es zumindest seit der Einführung des tidyverse und den damit propagierten tidy data principles sehr viel einfacher gewor...
  • EXIF Daten unter Ubuntu auslesen
    <p>Heute nur ganz kurz, weil ich es gerade gesucht, <a href="https://mikebeach.org/2011/12/17/how-to-retrieve-exif-data-from-the-command-line-in-ubuntu-linux/">gefunden</a> und vermutlich morgen wiede...
  • Lebenslauf mit Markdown erstellen
    Zum Glück müssen die meisten von uns nicht jeden Tag den eigenen Lebenslauf zu Papier bringen. Wann genau war ich nochmal bei Firma X? Wie lang dauerte das Praktikum in München? Und wie war nochmal de...

© 2023 gluecko.se - Good to know - RSS
build with 💚 and Hugo, hosted on uberspace