Länderfinanzausgleich: 30 Jahre in einer animierten Grafik (1988 – 2018)

Länderfinanzausgleich 1988 bis 2018. Animierte Grafik, erstellt mit R, gganimate und ggplot2

Wie kann man die Beträge, die die Bundesländer im Rahmen des Länderfinanzausgleichs zahlten oder erhielten, in einer Grafik darstellen, sodass Veränderungen im Zeitverlauf deutlich werden? Hier eine animierte Grafik, die den Zeitraum von 1988, also kurz vor der Wende, bis 2018 abbildet:

Länderfinanzausgleich 1988 bis 2018. Animierte Grafik, erstellt mit R, gganimate und ggplot2
Länderfinanzausgleich 1988 bis 2018. Animierte Grafik, erstellt mit R, gganimate und ggplot2
Anklicken für eine etwas größere Version; R-Code am Ende des Beitrags

Länderfinanzausgleich: Entwicklungen 1988 bis 2018

In diesen 30 Jahren gab es mehrere spannende Entwicklungen. Besonders einschneidend war natürlich die Wiedervereinigung 1990. 1995 wurden die ostdeutschen Bundesländer Brandenburg, Sachsen, Sachsen-Anhalt, Thüringen zusammen mit (dem wiedervereinigten) Berlin in den Länderfinanzausgleich einbezogen. Sie befinden sich überwiegend am Ende der Rangfolge, d. h. sie zählen zu den Bundesländern, die die höchsten Beträge empfangen. Niedersachsen, Bremen und in letzter Zeit vor allem Nordrhein-Westfalen durchbrechen dieses Muster und zählen zeitweise ebenfalls zu stark ausgeprägten Empfängerländern.

Der Einschnitt 1995 wird hervorgehoben, indem die Animation hier pausiert. So kann sich der Betrachter etwas besser auf die veränderte Datenlage einstellen. Eine weitere Pause wurde am Ende eingefügt, sodass das aktuellste Jahr länger zu sehen ist – sozusagen die (vorläufige) „Abschlusstabelle“.

Bayerns „Aufstieg“ und Nordrhein-Westfalens „Abstieg“

Zwei Bundesländer habe ich fett hervorgehoben, um ihre entgegengesetzte Entwicklung etwas besser erkennbar zu machen:

  • Bayern wurde 1989 erstmals zum Geberland, nach zwei Jahren ohne Zahlungen (weder Geber- noch Empfängerland 1987 und 1988). Somit erfolgte der Schritt vom Empfänger- zum Geberland nach insgesamt vier Jahrzehnten, von 1950 an gerechnet, dem Beginn des Länderfinanzausgleichs der Bundesrepublik.
  • Nordrhein-Westfalen verzeichnete besonders auffällige Schwankungen: Es pendelte zwischen dem ersten Platz (größter Einzahler 1995, als die ostdeutschen Bundesländer hinzukamen) und dem vorletzten (15.) Platz (zweitgrößter Empfänger 2017).

Technische Umsetzung der Animation: R, gganimate, ggplot2

Technisch wurde die Animation mit R (Version 3.5.3) und hauptsächlich den Erweiterungspaketen gganimate (Version 1.0.3) und ggplot2 (Version 3.1.1) umgesetzt.

Thomas Lin Pedersen (seit 2018 bei RStudio) hat die Betreuung von gganimate von David Robinson übernommen; in seiner lockeren Art erklärte er die Bedingung: dass er die API (User-Schnittstelle) komplett neu schreiben darf. So geschehen … In einer Präsentation sagte er sinngemäß: Egal, was ihr bisher mit gganimate gemacht habt – ich garantiere Euch, dass es nicht mehr funktionieren wird. Das nennt man wohl breaking changes. Betrifft auch meinen früheren Beitrag Animierte Visualisierungen: Treemaps zum US-Haushaltsdefizit und zum Strommix in Deutschland. Dort habe ich das zweite Beispiel (Treemaps zum Strommix) mit der alten gganimate-Version erstellt.

Die neue Version ist jedenfalls weitaus leistungsfähiger geworden. Die alte hat ggplot2 einfach eine Ästhetik frame hinzugefügt, mit der man eine Variable angeben konnte, nach der sich die einzelnen Bilder der Animation unterscheiden sollten – oft eine Zeitvariable. Die neue baut auf einer deutlich ausgefeilteren Theorie auf, „Grammar of Animation“ – hier erklärt auf der useR2018 in Brisbane.

Thomas Lin Pedersen erklärt die „Grammatik der Animation“ anhand der neuen gganimate-Version – useR2018, Brisbane

Eine wesentliche Verbesserung zur alten Version besteht darin, dass Zwischen-Zustände interpoliert werden können. Stehen beispielsweise Daten nur in 5-Jahres-Schritten zur Verfügung, die in der Animation zu manch groben Sprüngen führen würden, dann kann gganimate für sanfte Übergänge sorgen, indem es Zwischenwerte berechnet (das Konzept heißt easing). (Zwischenzeitlich konnte man das mit Pedersens tweenr-Paket tun – nun ist es in gganimate integriert, sodass man sich darum nicht mehr selbst kümmern muss.)

In der obigen Animation zum Länderfinanzausgleich enthält die .gif-Datei deutlich mehr Bilder als Jahre, um die Verschiebungen optisch besser zu kennzeichnen. Der Effekt heißt bounce-in-out, die Codezeile lautet:

ease_aes('bounce-in-out')

Das neue gganimate bietet eine Reihe von Easing-Alternativen: quadratic, cubic, quartic, circular, exponential usw. (siehe Dokumentation: ?ease_aes).

Das aes erinnert an die Ästhetiken von ggplot2 , an die gganimate anschließt. Insgesamt wurden 1000 Bilder (frames) berechnet; 15 pro Sekunde; das Jahr 2018 wird 100 Mal gezeigt. Das Jahr 1995 wird 5x so lang gezeigt wie die anderen Jahre. Setzt man die Zahl der frames zu niedrig an, dann geht der easing-Effekt verloren.

Elemente des Storytellings und Design-Aspekte

Ich habe versucht, den Betrachter nicht mit der Fülle an Informationen allein zu lassen, sondern einige Entwicklungen hervorzuheben. Interpretationen sind immer subjektive Entscheidungen … Elemente des Storytelling sind hier:

  • Die Hervorhebung der beiden Bundesländer Bayern und Nordrhein-Westfalen (fett)
  • Die Betonung der Jahre 1995 (Aufnahme der ostdeutschen Bundesländer) und 2018 (letzter Stand) durch die Pausen
  • Der Anmerkungstext

Einen weiteren Design-Ratschlag habe ich berücksichtigt: unnötige visuelle Elemente zu entfernen. Durch die Beschriftungen innerhalb der Diagramm-Fläche kommt die Darstellung ohne Achsenbeschriftungen (Text, Zahlen, Strichmarkierungen) aus. Auch die Rasterlinien habe ich entfernt. So sieht die Grafik weniger wie ein typisches ggplot2-Diagramm aus und man erkennt nicht sicher, um welches Theme es sich handelt (es ist das Standard-Theme theme_grey, mit benutzerdefinierten Anpassungen).

R-Code zur Animation

Hier der R-Code. Die Daten stammen von Wikipedia. Ich habe aus Zeitgründen (und da ich nicht den Anspruch hatte, einen voll-automatisierten Ablauf zu basteln) einige kleine Nachbearbeitungen in Excel gemacht (das Minuszeichen war ein langer Dash, den R nicht als numerisches Minuszeichen erkannte! / Entfernen der Anmerkungsziffern von den Jahreszahlen). Der Code beginnt mit dem Laden der Excel-Daten, die hier auf Github zu finden sind.

library(tidyverse)

daten <- readxl::read_excel("Länderfinanzausgleich.xlsx",
na = c("", "NA", "./."))
daten <- daten %>%
mutate(Volumen = str_remove_all(Volumen, "± ")) %>%
mutate_if(is.character, str_remove_all, "\.") %>%
mutate_if(is.character, as.numeric)
daten <- daten %>%
gather(-Jahr, key = Bundesland, value = Finanzausgleich) %>%
arrange(Jahr, desc(Finanzausgleich)) %>%
mutate(Typ = ifelse(Finanzausgleich < 0, "Geberland", "Empfängerland")) %>%
mutate(Typ = fct_rev(Typ)) %>%
mutate(Finanzausgleich = -Finanzausgleich) %>%
filter(Bundesland != "Volumen")

Im Gegensatz zur Datenvorlage wollte ich Geberländer mit positiven Geldbeträgen darstellen, Empfängerländer mit negativen. In den Daten wird zur Erläuterung zwischen Geber- und Empfängerländern differenziert, auch wenn diese Variable nicht in der Animation verwendet wird. Sie wird jedoch zur Färbung eines statischen Plots herangezogen.

daten %>% 
filter(Jahr == 2018) %>%
mutate(Bundesland = fct_inorder(Bundesland)) %>%
ggplot(aes(x = Bundesland, y = Finanzausgleich)) +
geom_bar(stat = "identity", aes(fill = Typ), width = 0.6) +
labs(y = "Finanzausgleich in Mio. Euro",
title = "Länderfinanzausgleich 2018",
caption = "Datenquelle: https://de.wikipedia.org/wiki/Länderfinanzausgleich#Finanzvolumen") +
coord_flip() +
scale_fill_manual(name = "", values = c("steelblue1", "peachpuff4")) +
theme(legend.position = "top",
text = element_text(size = 14))
Länderfinanzausgleich 2018. R / ggplot2

Für die Animation habe ich einige Stunden experimentiert, bis ich bei der obigen Version herauskam. Zunächst hatte ich die Reihenfolge der Bundesländer statisch gelassen, in der Sortierung von 2018. Das sah zu langweilig aus – ich wollte die veränderten Rangfolgen sichtbar machen. Hier der Code für die finale Version:

library(gganimate)
farben <- tmaptools::get_brewer_pal("Dark2", n = 16)

Die Farben waren eine Herausforderung. Da die Bundesländer „an sich“ keine Sortierung aufweisen, sollte es eine qualitative Skala sein (nicht im Sinne von Qualität, sondern im Sinne von unsortierten Kategorien). Das hervorragende R-Paket RColorBrewer bietet gut bewährte Skalen, wobei die qualitativen Skalen wie Accent, Dark, Pastel, Set nur zwischen 8 und 12 verschiedene Farben beinhalten. 16 deutlich unterscheidbare Farben für 16 Bundesländer zu finden ist nicht so einfach. Meine Lösung greift auf das sehr nützliche Paket tmaptools zurück, das die oben genannten Paletten mit einem Einzeiler erweitern kann (get_brewer_pal). [Die hier genannten Pakete müssen natürlich installiert sein; ggf. mit install.packages(„Paket“) installieren.]

Für die Pause 1995 habe ich keine gganimate-Funktion gefunden. Gut möglich, dass es mit einer Umsetzung über transition_events() statt transition_states() ginge – bin für Hinweise dankbar. Meine Lösung ist nicht besonders elegant und bedient sich eines „Hacks“: Das Jahr 1995 wird in den Daten dupliziert.

y1995_1 <- daten %>% 
filter(Jahr == 1995) %>%
mutate(Jahr = 1995.1,
Rang = rank(-Finanzausgleich))
y1995_2 <- daten %>%
filter(Jahr == 1995) %>%
mutate(Jahr = 1995.2,
Rang = rank(-Finanzausgleich))
y1995_3 <- daten %>%
filter(Jahr == 1995) %>%
mutate(Jahr = 1995.3,
Rang = rank(-Finanzausgleich))
y1995_4 <- daten %>%
filter(Jahr == 1995) %>%
mutate(Jahr = 1995.4,
Rang = rank(-Finanzausgleich))

animdaten <- daten %>%
filter(Jahr > 1987) %>%
na.omit() %>%
group_by(Jahr) %>%
mutate(Rang = rank(-Finanzausgleich, ties.method = "first")) %>%
ungroup() %>%
bind_rows(y1995_1, y1995_2, y1995_3, y1995_4) %>%
arrange(Jahr)

Leider geht es nicht ganz direkt – exakt gleiche Jahre werden zusammengefasst, sodass hier keine Pause entstünde. Ich habe daher Jahre 1995.1, 1995.2, 1995.3, 1995.4 erstellt – die Animation wird nur durch neue, bisher nicht vorhandene Einträge verlängert. Der Betrachter merkt nichts davon, da die Daten identisch sind und im Diagrammtitel das Jahr ganzzahlig gerundet wird. gganimate bietet dafür die interne Variable {closest_state} in Verbindung mit transition_states (siehe unten).

Keine schöne Lösung – habe gegen den Programmier-Grundsatz verstoßen, statt mehrfachem Copy & Paste eine Funktion zu schreiben. Auch der Rang (die y-Variable) müsste nur 1x berechnet werden, und ich müsste nur 1x nach Jahr filtern. Hab mehr Zeit auf die Animation selbst verwendet …

Die Animation entsteht mit einem ggplot-Aufruf, der um die transition_states()-Angabe erweitert wird: gganimate erstellt für jedes Jahr eine eigene Grafik.

anim4 <- animdaten %>% 
ggplot(aes(x = -Rang, y = Finanzausgleich, fill = Bundesland, color = Bundesland)) +
geom_tile(aes(y = Finanzausgleich / 2, height = Finanzausgleich, width = 0.9),
alpha = 0.8, color = NA) +
annotate("text", x = -10, y = 2000,
label = paste("Bayern wurde 1989 zum Geberland\n",
"(1992 nochmals Empfänger).\n\n",
"1995 wurden die ostdeutschen Bundesländer\n",
"Berlin, Brandenburg, Sachsen,\n",
"Sachsen-Anhalt und Thüringen in den\n",
"Länderfinanzausgleich aufgenommen.\n\n",
"Ab dieser Zeit stieg\n",
"das Transfervolumen deutlich an.\n\n",
"Auffällig die starken Bewegungen\n",
"Nordrhein-Westfalens zwischen\n",
"Rang 1 (1995) und Rang 15 (2017)."),
size = 4.5, hjust = 0) +
coord_flip(clip = "off", expand = TRUE) +
geom_text(aes(y = -5000,
label = paste0(Bundesland, ": ", Finanzausgleich), hjust = 1,
fontface = ifelse(Bundesland == "Nordrhein-Westfalen" | Bundesland == "Bayern", "bold", "plain")),
size = 5) +
scale_fill_manual(values = farben) +
scale_color_manual(values = farben) +
scale_y_continuous(limits = c(-12000, 13000)) +
guides(color = FALSE, fill = FALSE) +
labs(title = "Länderfinanzausgleich {round(as.numeric(as.character({closest_state})), 0)}",
subtitle = "in Mio. Euro; positive Werte: Geber; negative Werte: Empfänger",
caption = paste("Animation von 1988 bis 2018, mit Pause 1995\n",
"Erstellt mit R 3.5.3, gganimate 1.0.3, ggplot2 3.1.1,", Sys.Date(), "\n",
"Datenquelle: https://de.wikipedia.org/wiki/Länderfinanzausgleich#Finanzvolumen")) +
theme(text = element_text(size = 16),
axis.title = element_blank(),
axis.ticks = element_blank(),
axis.text = element_blank(),
panel.grid = element_blank()) +
transition_states(Jahr, transition_length = 1, state_length = 1, wrap = FALSE) +
ease_aes('bounce-in-out')

Der Übergang und der neue Zustand werden gleich lang dargestellt (transition_length, state_length). wrap = FALSE stellt sicher, dass das Jahr 2018 gut lesbar stehen bleibt und nicht gleich wieder in das erste Jahr der Animation (1988) übergleitet.

Ich verwende geom_tile statt geom_bar oder geom_col für die sich „schwebend“ umsortierenden Balken. Inspiration von einer Frage bei stackoverflow.

Eine Animation wird daraus durch die animate-Funktion; eine Datei im Arbeitsverzeichnis liefert save_animation.

anim4 <- animate(anim4, fps = 15, end_pause = 100, nframes = 1000,
rewind = FALSE, width = 700, height = 700)
anim4
save_animation(anim4, file = "Länderfinanzausgleich4.gif")
rm(farben, animdaten, y1995_1, y1995_2, y1995_3, y1995_4)

Überzeugt die Animation?

Länderfinanzausgleich 1988 bis 2018. Animierte Grafik, erstellt mit R, gganimate und ggplot2
Länderfinanzausgleich 1988 bis 2018. Animierte Grafik, erstellt mit R, gganimate und ggplot2

2 Gedanken zu „Länderfinanzausgleich: 30 Jahre in einer animierten Grafik (1988 – 2018)“

Freue mich über Kommentare!