Unsplash

Lösungen

Vorwarnung

Achtung! Im Folgenden werden die Lösungen für das achte Projekt präsentiert. Falls du das Projekt noch nicht vollständig bearbeitet hast, nutze zunächst die Tipps. Sofern dir die Tipps für einen Teil nicht geholfen haben, kannst du die Lösungen dafür benutzen, einen Schritt weiterzukommen und beim nächsten Abschnitt weiterzumachen.

Abschnitt 1 - Daten herunterladen

Variable auswählen

Abschnitt anzeigen

Bevor du anfängst, lädst du am besten die Pakete, welche du für dieses Projekt benötigst:

#install.packages("httr")
#install.packages("jsonlite")
#install.packages('OData')
#install.packages('data.table')
library(data.table)
library(OData)
library(httr)
library(jsonlite)

Im ersten Schritt musst du dir dann eine Variable aussuchen. Dafür lädst du zunächst die Daten der Indikatoren herunter.

Indikatoren <- retrieveData('https://ghoapi.azureedge.net/api/Indicator')

Da wir uns die verschachtelte Liste nur schwer anzeigen lassen können, wandeln wir sie in ein Dataframe um.

Indi <- as.data.frame(Indikatoren)

Somit bekommen wir ein sehr langes Dataframe, bei dem in der ersten Zeile der Variablenname steht, in der zweiten Zeile die Beschreibung zu der Variable und in der dritten Zeile die Sprache.

Wenn du dir die Indikatoren lieber als Matrix anzeigen lassen willst, musst du zunächst den Befehl unlist anwenden.

a <- unlist(Indikatoren)
ma <- as.matrix(a)

Hier hast du nun eine Spalte, in der erst der Vairablenname steht, darunter steht die Beschreibung zu der Variable und wiederum darunter steht die Sprache zu der Variable, welche meistens Englisch ist.

Ich werde im Folgenden mit der Variable WHOSIS_000001 zur Lebenserwartung arbeiten, aber es sollte mit jeder anderen Variable genauso funktionieren.

Daten über die API herunterladen

Abschnitt anzeigen

Die Daten, mit denen wir arbeiten wollen, können wir auf die gleiche Art und Weise herunterladen, wie die Indikatoren: mit dem Befehl retrieveData. Das Einzige, was wir für den Befehl benötigen, ist die richtige URL.

Für die Daten der WHO ist der grundlegende Befehl https://ghoapi.azureedge.net/api. Da ich die Variable mit den Daten zur Lebenserwartung laden möchte, füge ich dem Link ein / + WHOSIS_000001 an. Damit sieht der vollständige Link folgendermaßen aus: https://ghoapi.azureedge.net/api/WHOSIS_000001 und kann zum Laden der Daten verwendet werden.

data <- retrieveData('https://ghoapi.azureedge.net/api/WHOSIS_000001')

Diese Daten liegen nun wieder in einer verschachtelten Liste vor. Wenn wir uns die Variable value anschauen, sehen wir jedoch, dass es sich diesmal nicht um character, sondern um weitere Listen in der Liste handelt. Für diese Art von verschachtelten Listen gibt es einen einfachen Befehl aus dem data.table-Paket, welches mit dem Befehl rbindlist verschachtelte Listen in einfache Dataframes konvertiert. Dabei musst du nur beachten, dass du fill= TRUE setzt.

data1 <- rbindlist(data$value, fill = T)
Somit haben wir ein vollständiges Dataframe, mit dem wir nun die Grafiken und das User Interface erstellen können.

Abschnitt 2 - Interaktive Grafiken

Übertragen der Daten von Excel in R Studio

Hier findest du eine kurze Erklärung, wie man Datensätze von Excel in R Studio überführt. Wenn du diesen Schritt erledigt hast, kehre zurück zu Abschnitt 2 dieses Projektes und probiere dich an der Erstellung von Grafiken mit plotly.

Abschnitt anzeigen Bevor du mit dem Einlesen der Daten beginnen kannst, solltest du dir das Dokument anschauen. Es enthält 6 Tabellen: “Tabelle1” bis “Tabelle6”.

  • Tabelle 1 ist leer.
  • Tabelle 2 beinhaltet Variablennamen und deren Erläuterung
  • Tabelle 3 führt alle Indikatoren und deren Bedeutung (z.B. WHOSIS_000001 = ‘Life expectancy at birth (years)’)
  • Tabelle 4 stellt den gesamten Datensatz dar
  • Tabelle 5 nur die Daten von Männern aus 2011 und
  • Tabelle 6 nur die Daten von Männern

Das heißt, dass du hier ausschließlich Tabelle 4 in R laden solltest. Dafür kann man das Paket readxl mit dem Befehl read_excel verwenden. Mit den Argumenten innerhalb dieses Befehls musst du dann nur noch den Dateipfad der Excel-Tabellen, die Behandlung von fehlenden Werten und den Namen des gewünschten Sheets (“Tabelle4”) angeben und das Ergebnis einem Objekt zuweisen. Achte hierbei darauf, dass sich das Excel-Dokument in deiner Working Directory befindet.

#install.packages("readxl")
library (readxl)
data1 <- read_excel("GHO-Daten.xlsx", na = "NA", sheet = "Tabelle4")

Anpassung des Datensatzes

Hier wird der geladene Datensatz von unbrauchbaren Spalten bereinigt, die Variablen neu benannt und zwei neue Variablen hinzugefügt. Prinzipiell kannst du dadurch den Datensatz übersichtlicher machen aber auch die Struktur der Daten kennenlernen.

Abschnitt anzeigen Natürlich kann man sich mithilfe der Standard-Pakete von R bereits einen guten Überblick über die Struktur des Datensatzes verschaffen. So könnten beispielsweise folgende Befehle verwendet werden:

str(data1) # gibt den Typ jeder Variable aus ("character", "string", "integer", "numeric") 

table(data1$IndicatorCode) # Tabellen zu den Ausprägungen der gewählten Variablen
table(data1$SpatialDimType) # ermöglicht Überblick über Anzahl der Ausprägungen und deren Häufigkeit
table(data1$TimeDimType)
table(data1$TimeDim)

View(data1) # öffnet den gesamten Datensatz in einem neuen Fenster

Wir haben uns im ersten Schritt jedoch dafür entschieden, mit dem tidyverse-Paket zu arbeiten. Dieses bietet einige Vorteile im Umgang mit Datensätzen. So kann man den Datensatz in ein neues Format (tibble) umwandeln, das die Darstellung des Datensatzes standardmäßig übersichtlicher gestaltet.

#install.packages("tidyverse")
library(tidyverse)
## ── Attaching packages ────────────────────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2     ✓ purrr   0.3.4
## ✓ tibble  3.0.3     ✓ dplyr   1.0.2
## ✓ tidyr   1.1.2     ✓ stringr 1.4.0
## ✓ readr   1.3.1     ✓ forcats 0.5.0
## ── Conflicts ───────────────────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::between()   masks data.table::between()
## x dplyr::filter()    masks stats::filter()
## x dplyr::first()     masks data.table::first()
## x purrr::flatten()   masks jsonlite::flatten()
## x dplyr::lag()       masks stats::lag()
## x dplyr::last()      masks data.table::last()
## x purrr::transpose() masks data.table::transpose()
data1 <- as_tibble(data1)
data1
## # A tibble: 9,486 x 23
##        Id IndicatorCode SpatialDimType SpatialDim TimeDimType TimeDim Dim1Type
##     <dbl> <chr>         <chr>          <chr>      <chr>         <dbl> <chr>   
##  1 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2000 SEX     
##  2 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2000 SEX     
##  3 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2000 SEX     
##  4 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2001 SEX     
##  5 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2001 SEX     
##  6 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2001 SEX     
##  7 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2002 SEX     
##  8 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2002 SEX     
##  9 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2002 SEX     
## 10 1.56e7 WHOSIS_000001 COUNTRY        RWA        YEAR           2003 SEX     
## # … with 9,476 more rows, and 16 more variables: Dim1 <chr>, Dim2Type <lgl>,
## #   Dim2 <lgl>, Dim3Type <lgl>, Dim3 <lgl>, DataSourceDimType <lgl>,
## #   DataSourceDim <lgl>, Value <chr>, NumericValue <dbl>, Low <lgl>,
## #   High <lgl>, Comments <chr>, Date <dttm>, TimeDimensionValue <chr>,
## #   TimeDimensionBegin <dttm>, TimeDimensionEnd <dttm>

Jetzt können wir mit einem Befehl und ohne Zwischenschritte direkt alle Spalten entfernen, die nicht weiter von Nutzen sind. Das ermöglicht der Pipe-Befehl %>% aus dem dplyr-Paket. %>% umgeht Zwischenschritte, indem es die zuvor erstellten “Zwischenwerte” direkt als erstes Argument in die darauf folgende Funktion einsetzt. Hier wird bspw. zuerst das Objekt data1 ausgewählt; in der folgenden Funktion select_if wird data1 als erstes Argument eingesetzt und modifiziert; das Ergebnis davon wird schließlich in der letzten Funktion (select) weiter verwertet und abschließend verändert. (Für die Bedeutung und Funktionsweise der einzelnen Funktionen hierin, nutze die R interne Hilfe oder das Internet.)

data2 <- data1 %>%
  select_if(function(col) !is.logical(col)) %>%
  select(-c(TimeDimType, Dim1Type, Date, TimeDimensionValue, TimeDimensionBegin, TimeDimensionEnd))
data2
## # A tibble: 9,486 x 9
##        Id IndicatorCode SpatialDimType SpatialDim TimeDim Dim1  Value
##     <dbl> <chr>         <chr>          <chr>        <dbl> <chr> <chr>
##  1 1.56e7 WHOSIS_000001 COUNTRY        RWA           2000 MLE   43.0 
##  2 1.56e7 WHOSIS_000001 COUNTRY        RWA           2000 FMLE  48.4 
##  3 1.56e7 WHOSIS_000001 COUNTRY        RWA           2000 BTSX  45.7 
##  4 1.56e7 WHOSIS_000001 COUNTRY        RWA           2001 MLE   43.5 
##  5 1.56e7 WHOSIS_000001 COUNTRY        RWA           2001 FMLE  49.5 
##  6 1.56e7 WHOSIS_000001 COUNTRY        RWA           2001 BTSX  46.5 
##  7 1.56e7 WHOSIS_000001 COUNTRY        RWA           2002 MLE   46.0 
##  8 1.56e7 WHOSIS_000001 COUNTRY        RWA           2002 FMLE  51.5 
##  9 1.56e7 WHOSIS_000001 COUNTRY        RWA           2002 BTSX  48.8 
## 10 1.56e7 WHOSIS_000001 COUNTRY        RWA           2003 MLE   47.4 
## # … with 9,476 more rows, and 2 more variables: NumericValue <dbl>,
## #   Comments <chr>

Nun hast du den Datensatz erfolgreich auf 9 Spalten reduziert. Diese kannst du zur besseren Übersicht umbenennen:

names(data2) <- c('DatID', 'VarID', 'SpatialDimType', 'COUNTRYCODE', 'YEAR',
                  'SEX', 'LIFE_EXPECTANCY','L_E2EXACT', 'COMMENT')

Dir ist vielleicht aufgefallen, dass zwei Variablen die Lebenserwartung beinhalten: LIFE_EXPECTANCY und L_E2EXACT. Aus diesem Grund lohnt sich ein genauerer Blick auf beide Variablen - Kann man beide für ein Diagramm verwenden oder ist irgendeine Variable besser/schlechter geeignet oder gar ungeeignet? Ein Blick auf die Variablenart könnte darüber Aufschluss liefern:

str(data2$LIFE_EXPECTANCY)
##  chr [1:9486] "43.0" "48.4" "45.7" "43.5" "49.5" "46.5" "46.0" "51.5" ...
str(data2$L_E2EXACT)
##  num [1:9486] 43 48.4 45.7 43.5 49.5 ...
range(data2$L_E2EXACT)
## [1] 33.22747 87.14502

Wie sich zeigt, ist die Variable LIFE_EXPECTANCY ein character. Das heißt, dass R diese Variable nicht als Zahl behandelt, weshalb diese für Abbildungen ungeeignet ist. L_E2EXACT hingegen liegt im Format numeric vor. Im weiteren Verlauf müssen wir deshalb diese Variable zur Erstellung der Abbildungen verwenden.

Jetzt kannst du dich daran setzen, die zwei neuen Variablen zu erstellen. Im aktuellen Zustand stehen unter der Variable COUNTRYCODE nur die Codes der verschiedenen Länder. Diese sind jedoch teilweise nicht intuitiv zuzuordnen, weshalb wir noch eine neue Variable (COUNTRY) erstellen, die die ausgeschriebenen Ländernamen beinhaltet. Glücklicherweise sind die Ländercodes weit verbreitet und können deshalb einfach mit dem ISOcodes-Paket in ihre zugehörigen Ländernamen umgewandelt werden.

#install.packages("ISOcodes")
library(ISOcodes)

(Anmerkung: Da die for-Schleife einen Fehler hervorruft, können wir die folgenden Befehle an dieser Stelle nicht ausführen. Wenn du das selbst in deiner R-Sitzung ausführst, wirst du merken, dass die neue Variable COUNTRY trotz des Fehlers vollständig mit Werten befüllt wurde. Hiernach gilt es für dich, die Gründe für den Fehler zu ermitteln. Dabei helfen die darauf folgenden 4 Befehle.)

for (i in 1:9438){
  data2$COUNTRY[i] <- ISO_3166_1$Name[ISO_3166_1$Alpha_3 == data2$COUNTRYCODE[i]]
}

table(table(data2$COUNTRY)) #Warum gibt es zu allen Ländern 51 Einträge und zu einem 204?
#A: Welches Land hat 204 Einträge? --> Ruanda
data2 %>% .$COUNTRY %>% table() %>% which.max()
#B: Woran liegt das? Ein Fehler? --> JA!
data2 %>% filter(COUNTRY == "Rwanda") %>% select(COUNTRYCODE) %>% table()
#C: Wie viele Daten gibt es wirkich zu Ruanda?
data2 %>% filter(COUNTRYCODE == "RWA") %>% select(COUNTRY) %>% table()

Wie sich zeigt, ist bei der Umwandlung der Ländercodes ein Fehler aufgetreten - und ab diesem Punkt im Datensatz wurde in jeder Zeile “der erste Wert” eingesetzt (siehe A). Schaut man sich die falsch formatierten Ländercodes (bzw. die zugehörigen Zeilen) an, so fällt auf, dass es sich hierbei um selbstkreierte Regionen der WHO handelt (siehe B). Zudem war der erste Wert aus irgendeinem Grund Ruanda, obwohl die Länder ansonsten alphabetisch geordnet vorliegen. Es zeigt sich, dass zu Ruanda mehr Daten zur Verfügung stehen, als zu allen anderen Ländern (normal: 17 Jahre * 3 Geschlechter = 51 Datenpunkte)(siehe C).

Daraus lässt sich schließen, dass die ersten 48 Zeilen zu Ruanda irgendwie fehl am Platz sind und gelöscht werden können. Außerdem müssen die Ländercodes zu den WHO-Regionen händisch umgewandelt werden:

data2 <- data2[-c(1:48), ] #um die ersten Ruanda-Zeilen zu löschen

for (i in 1:9438){
  if (data2$COUNTRYCODE[i] == "AFR"){data2$COUNTRY[i] <- "African Region"}
  else if (data2$COUNTRYCODE[i] == "AMR"){data2$COUNTRY[i] <- "Region of the Americas"}
  else if (data2$COUNTRYCODE[i] == "EMR"){data2$COUNTRY[i] <- "Eastern Mediterranean Region"}
  else if (data2$COUNTRYCODE[i] == "EUR"){data2$COUNTRY[i] <- "European Region"}
  else if (data2$COUNTRYCODE[i] == "GLOBAL"){data2$COUNTRY[i] <- "GLOBAL"}
  else if (data2$COUNTRYCODE[i] == "SEAR"){data2$COUNTRY[i] <- "South-East Asian Region"}
  else if (data2$COUNTRYCODE[i] == "WPR"){data2$COUNTRY[i] <- "Western Pacific Region"}
  else {data2$COUNTRY[i] <- ISO_3166_1$Name[ISO_3166_1$Alpha_3 == data2$COUNTRYCODE[i]]}
}

table(table(data2$COUNTRY))
## 
##  15  51 
##   7 183

Wie die Tabelle zeigt, wurden die Ländercodes in COUNTRYCODE nun erfolgreich in die vollständigen Länder-/Regionenbezeichnungen in der Variable COUNTRY umgewandelt. Das sieht man daran, dass nun zu jedem Land 51 Datenpunkte vorliegen und zu allen Regionen 15.

Jetzt können wir uns der zweiten neuen Variable widmen: den Kontinenten. Auch hierfür gibt es ein passendes Paket in R, das anhand der Länderbezeichnungen (COUNTRY) den Kontinent ausgeben kann. Dieses Paket heißt genau wie die benötigte Funktion countrycode.

#install.packages('countrycode')
library(countrycode)
data2 <- data2 %>% mutate(
    CONTINENT = countrycode(
      sourcevar = data2$COUNTRY,
      origin = "country.name",
      destination = "continent",
      nomatch = NA))

Zur weiteren Überprüfung kann man nun noch folgende Befehle ausführen:

table(data2$CONTINENT) #Gibt Auskunft darüber, wie häufig welcher Kontinent eingefügt wurde
## 
##   Africa Americas     Asia   Europe  Oceania 
##     2754     1683     2397     1989      510
table(data2$COUNTRY[data2$CONTINENT == "Oceania"]) #Zeigt alle Länder vom Kontinent Ozeanien an
## 
##                       Australia                            Fiji 
##                              51                              51 
##                        Kiribati Micronesia, Federated States of 
##                              51                              51 
##                     New Zealand                Papua New Guinea 
##                              51                              51 
##                           Samoa                 Solomon Islands 
##                              51                              51 
##                           Tonga                         Vanuatu 
##                              51                              51
table(data2$CONTINENT)/51 #So erhält man die Anzahl der Länder pro Kontinent, zu denen Daten der WHO vorliegen
## 
##   Africa Americas     Asia   Europe  Oceania 
##       54       33       47       39       10

Somit ist dieser Abschnitt abgeschlossen. Der Datensatz wurde erfolgreich von unnötigen Zeilen und Spalten bereinigt, die Variablen wurden neu benannt und die zwei neuen Variablen wurden erstellt. Hiernach solltest du außerdem einen weitreichenden Überblick über den Datensatz haben, ohne ihn dir Zeile für Zeile anschauen zu müssen.

Grafiken erstellen mit plotly

Jetzt, wo die Daten geladen und vorbereitet wurden, können wir uns dem Erstellen der Grafiken widmen. In der Problemstellung haben wir hierfür fünf verschiedene Vorschläge gegeben. Diese steigern sich in ihrer Komplexität und werden im Folgenden nach und nach durchgegangen. Grafik 1 soll hierbei eine generelle Einführung in die Struktur von plotly-Abbildungen darstellen und die Möglichkeit aufzeigen, Abbildungen auf Basis von ggplot2 nur mit einer Funktion (ggplotly) in plotly-Abbildungen umzuwandeln.

Lade dir dafür zunächst einmal das plotly-Paket:

#install.packages("plotly")
library(plotly)

Grafik 1 - Ein Standard-Plot Das Ziel des ersten Plots ist es, ein Liniendiagramm mit drei Linien zu erstellen. Diese drei Linien sollen dabei die drei Ausprägungen der Gendervariable repräsentieren (MLE, FMLE und BTSX). Das bedeutet im Umkehrschluss, dass wir den Datensatz auf ein Land reduzieren müssen. Hierfür verwenden wir weiterhin dplyr-Funktionen aus dem tidyverse:

filter(data2, COUNTRY == "Germany")
## # A tibble: 51 x 11
##     DatID VarID SpatialDimType COUNTRYCODE  YEAR SEX   LIFE_EXPECTANCY L_E2EXACT
##     <dbl> <chr> <chr>          <chr>       <dbl> <chr> <chr>               <dbl>
##  1 1.82e7 WHOS… COUNTRY        DEU          2000 MLE   75.0                 75.0
##  2 1.82e7 WHOS… COUNTRY        DEU          2001 MLE   75.4                 75.4
##  3 1.82e7 WHOS… COUNTRY        DEU          2002 MLE   75.6                 75.6
##  4 1.82e7 WHOS… COUNTRY        DEU          2003 MLE   75.7                 75.7
##  5 1.82e7 WHOS… COUNTRY        DEU          2004 MLE   76.4                 76.4
##  6 1.82e7 WHOS… COUNTRY        DEU          2005 MLE   76.5                 76.5
##  7 1.82e7 WHOS… COUNTRY        DEU          2006 MLE   76.9                 76.9
##  8 1.82e7 WHOS… COUNTRY        DEU          2007 MLE   77.1                 77.1
##  9 1.82e7 WHOS… COUNTRY        DEU          2008 MLE   77.3                 77.3
## 10 1.82e7 WHOS… COUNTRY        DEU          2009 MLE   77.4                 77.4
## # … with 41 more rows, and 3 more variables: COMMENT <chr>, COUNTRY <chr>,
## #   CONTINENT <chr>

Wie man sehen kann, wurde der Datensatz nun auf 51 Zeilen reduziert. Alle diese Zeilen enthalten Daten aus Deutschland. Um mit diesem modifizierten Datensatz weiterarbeiten zu können, müssen wir diesen jedoch NICHT in einem neuen Objekt speichern. Hier können wir einfach wieder den Pipe-Operator %>% verwenden. Zur Erstellung der Abbildung verwendet man nun den plot_ly-Befehl (siehe ?plot_ly für weitere Hilfe). Ohne weitere Anpassungen sieht der Code dann folgendermaßen aus:

filter(data2, COUNTRY == "Germany") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~SEX,
    type = "scatter",
    mode = "lines+markers")
## Warning: `arrange_()` is deprecated as of dplyr 0.7.0.
## Please use `arrange()` instead.
## See vignette('programming') for more help
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.

Wenn man möchte kann man hier jetzt noch mit dem colors-Argument die Farben der drei Linien anpassen. Das kann man bspw. manuell machen, indem man einfach drei Farben nennt. In diesem Fall ist das auch noch nicht zu aufwändig - bei mehr als 10 benötigten Farben könnte das jedoch problematisch werden. In solchen Fällen kann man beispielsweise die colorRampPalette-Funktion verwenden. Diese Funktion funktioniert derart, dass man 2 oder mehr Farben angibt, aus denen die Funktion dann einen Farbverlauf bildet (diese Funktion speichert man in einem Objekt ab, bspw. CRP). Aus diesem Farbverlauf kann man dann eine beliebige Anzahl an Farbabstufungen ziehen, indem man hinter dem Farbobjekt (CRP) in Klammern angibt, wie viele Farben benötigt werden. Das könnte jetzt erst einmal etwas verwirrend sein, deshalb hier ein Beispiel:

CRP <- colorRampPalette(c('red', 'blue'))
filter(data2, COUNTRY == "Germany") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~SEX,
    type = "scatter",
    mode = "lines+markers",
    colors = CRP(3))

Das Objekt bzw. die Funktion CRP() hat in diesem Fall dafür gesorgt, dass aus einem Farbverlauf von rot und blau eine dritte Farbe gezogen wurde. Diese eine Farbe liegt ‘genau zwischen rot und blau’ und ist somit eine 50:50-Mischung aus diesen beiden Farben - deshalb ist die dritte Farbe violett.

Eine weitere Möglichkeit zur Erstellung dieses Plots ist das ggplot2-Paket. Die daraus entstandene Abbildung kann dann mit der ggplotly-Funktion direkt in eine plotly-Abbildung umgewandelt werden. Dafür bedarf es keiner weiteren Pakete, da ggplot2 bereits im tidyverse enthalten ist. Ein ähnliches Liniendiagramm mit ggplot2 ließe sich demnach folgendermaßen erstellen:

#install.packages("ggthemes")
library(ggthemes)  # aus diesem Paket kommt das Color-Theme `theme_stata`
ggplot1 <- filter(data2, COUNTRY == "Germany") %>%
  ggplot(aes(x = YEAR, y = L_E2EXACT, group = SEX)) +
    geom_line(aes(colour = SEX)) +      # Liniendiagramm
    xlab('TIME') +                      # Beschriftung x-Achse
    ylab('LIFE EXPECTANCY') +           # Beschriftung y-Achse
    ggtitle('LIFE EXPECTANCY DEVELOPMENT IN GERMANY - GENDER COMPARISON') +    # Überschrift
    scale_color_manual(values = CRP(3)) +  # Zuweisung Farbe-Geschlecht
    theme_stata()
ggplot1  # Abruf des gespeicherten Plots

Die Umwandlung in eine plotly-Abbildung geschieht dann einfach folgendermaßen:

ggplotly(ggplot1)
## Warning: `group_by_()` is deprecated as of dplyr 0.7.0.
## Please use `group_by()` instead.
## See vignette('programming') for more help
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.

Wie man sehen kann, ergibt sich ein sehr ähnlicher Plot - einzig und allein mit dem Unterschied, dass der ggplot bereits an einigen Stellen modifiziert wurde (bspw. xlab('TIME') für die x-Achsenbeschriftung und theme_stata() für das Aussehen von Achsen, Hintergrund und Gitterlinien). Die Frage ist: Geht das auch direkt mit plotly oder macht es sogar mehr Sinn den Weg über ggplot2 zu gehen?

Diese Frage ist nicht pauschal zu beantworten. Für einige simplere Plots sind die Ergebnisse von ggplot2 und plotly kaum unterscheidbar, weshalb in diesem Fall einfach die Empfehlung lautet: Nutze das Paket, das dir besser liegt. Doch an einigen Stellen und Typen von Plots stößt ggplot2 in der Kombination mit den Funktionen von plotly an seine Grenzen (dazu mehr im Abschnitt zum zweiten Plot ).

Nun stellt sich noch die Frage, wie man die angesprochenen Formatierungen mit plotly umsetzt. Hierfür gibt es einen einfachen Befehl: layout, mit dem alle graphischen Anpassungen getätigt werden können. Auch hier bietet es sich an, mit %>% die erstellte Abbildung direkt in die layout-Funktion zu überführen. Im folgenden werden wir dir beispielhaft die Formatierung von theme_stata mit plotly nachempfinden - für eine breite Auswahl an Formatierungsmöglichkeiten kannst du dich auf dieser Website über alle Layout-Anpassungen informieren. Hinter den einzelnen Zeilen findest du jeweils kurz erklärt, was genau diese bewirken.

filter(data2, COUNTRY == "Germany") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~SEX,
    type = "scatter",
    mode = "lines+markers",
    colors = CRP(3)) %>%
  layout(
    # Hier bestimmen wir die Hintergrundfarbe des gesamten Papiers - mit `plot_bgcolor` kann man weiterhin auch die Hintergrundfarbe des Plots bestimmen
    paper_bgcolor = "#eaf2f3",
    # Mit dem Argument `legend` passen wir die Legende an - das geschieht in einer Liste (`list()`)
    legend = list(
      font = list( # Anpassung der Schrift der Legende
        family = "sans-serif", # Schriftfamilie
        size = 12,             # Schriftgröße
        color = "#000"),       # Schriftfarbe
      bgcolor = "#E2E2E2",     # Hintergrundfarbe der Legende
      bordercolor = "#0a0a0a", # Rahmenfarbe der Legende
      borderwidth = 2,         # Dicke des Rahmens der Legende 
      yanchor = "center",      # Anker für y-Wert in nächster Zeile
      y = 0.5),                # Position der Legende auf der y-Achse (Bedeutung abhängig von Ankersetzung)
    # Mit `annotations` kann man Text in die Abbildung einfügen.
    annotations = list(
      text = "GERMANY",        # dieser Text soll eingefügt werden
      font = list(             # Hier wird wiederum die Schrift angepasst
        family = "Arial, Helvetica, sans-serif", # Schriftfamilie
        size = 18,             # Schriftgröße
        color = "black"),      # Schriftfarbe
      xref = "paper",          # Positionierung auf der x-Achse
      xanchor = "center",
      x = 0.5,
      yref = "paper",          # Positionierung auf der y-Achse
      yanchor = "bottom",
      align = "center",
      y = 0.95,
      showarrow = FALSE        # soll der Text mit einem Pfeil abgedruckt werden - default:TRUE
    ),
    # Mit `xaxis` kann man Anpassungen an der x-Achse vornehmen.
    xaxis = list(
      title = "Time (in Years)", # Titel der x-Achse
      gridcolor = "white",       # Farbe der Gitterlinien der x-Achse (vertikale Gitterlinien)
      gridwidth = 0,             # Dicke der Gitterlinien der x-Achse (vertikale Gitterlinien)
      tickcolor = "black",       # Farbe der Ticks an der x-Achse (Striche zu der Skalierung)
      tickwidth = 1,             # Dicke der Ticks an der x-Achse (Striche zu der Skalierung)
      linecolor = "black",       # Farbe der Linie der x-Achse (an dieser Linie ist die Beschriftung)
      linewidth = 1              # Dicke der Linie der x-Achse (an dieser Linie ist die Beschriftung)
    ),
    # Mit `yaxis` kann man Anpassungen an der y-Achse vornehmen. (siehe `xaxis`)
    yaxis = list(
      title = "Life Expectancy",
      gridcolor = "#eaf2f3",
      gridwidth = 1,
      tickcolor = "black",
      tickwidth = 1,
      linecolor = "black",
      linewidth = 1
    )
  )

Grafik 2 - Ein Plot mit veränderter hover-Info Dieser zweite Plot unterscheidet sich grundsätzlich kaum vom ersten. Aus diesem Grund können wir das Grundgerüst erst einmal übernehmen:

filter(data2, COUNTRY == "Germany") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~SEX,
    type = "scatter",
    mode = "lines+markers",
    colors = CRP(3))

An diesem Grundgerüst müssen wir nun an drei Stellen Veränderungen vornehmen:

  1. Die zugrundeliegenden Daten beschränken sich nun nicht nur auf ein Land, sondern auf einen Kontinent UND eine Ausprägung auf der Gendervariable. Das heißt, dass die Filter-Bedingung in der ersten Zeile angepasst werden muss.
  2. Die Farbe der einzelnen Linien hängt außerdem nicht mehr von der Variable SEX ab. Stattdessen soll in diesem Liniendiagramm jedes Land (COUNTRY) durch eine andere Linie repräsentiert werden.
  3. In Folge dessen muss auch die Anzahl der Farben verändert werden, die aus dem Farbverlauf CRP() gezogen werden. Hier muss stattdessen die Anzahl der in den WHO-Daten erfassten Ländern des ausgewählten Kontinents verwendet werden.

Anmerkung: Wir haben hier eine andere vorgefertigte Farbpalette verwendet, da jene aus rot und blau kaum Differenzierungen ermöglicht.

filter(data2, SEX == "BTSX" & CONTINENT == "Europe") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~COUNTRY,
    type = "scatter",
    mode = "lines+markers",
    colors = viridisLite::magma(nrow(filter(data2, CONTINENT == "Europe"))/51))

Jetzt stellt sich noch die Frage: Wie passe ich hier die hover-Info an? Momentan sieht diese folgendermaßen aus:

Das ist jedoch noch nicht optimal und Bedarf einiger Bearbeitung, denn zum einen liefert diese Info nicht den maximal möglichen Informationsgehalt und zum anderen sieht die Info auch optisch nicht sehr ansprechend aus. Mit ggplotly stößt man dabei an Grenzen - zumindest haben wir keinen Weg gefunden, die hover-Info hier verlässlich zu modifizieren.

Mit plotly direkt ist dies jedoch relativ leicht und kann auf mehrere Wege bewerkstelligt werden. Man kann beispielsweise die beiden Argumente text und hoverinfo verwenden. Hinter text fügt man dann einfach in paste0() den gewünschten hover-Infotext mit Formatierungen ein und macht dann hoverinfo = "text", um diesen Text als hoverinfo einzufügen. Das kann dann beispielsweise so aussehen:

filter(data2, SEX == "BTSX" & CONTINENT == "Europe") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~COUNTRY,
    type = "scatter",
    mode = "line",
    text = paste0("<b><i>Data from the Global Health Observatory (GHO)</i></b>",
                  "<br><br><b>Country:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$COUNTRY,
                  "<br><b>Country-Code:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$COUNTRYCODE,
                  "<br><b>Life Expectancy:</b> ", round(filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$L_E2EXACT, digits = 3),
                  "<br><b>Year:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$YEAR,
                  "<br><b>Sex:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$SEX,
                  "<br><b>Continent:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$CONTINENT),
    hoverinfo = "text",
    colors = viridisLite::magma(nrow(filter(data2, CONTINENT == "Europe"))/51)) %>%
  layout(
    paper_bgcolor = "#eaf2f3",
    legend = list(
      font = list(
        family = "sans-serif",
        size = 12,
        color = "#000"),
      bgcolor = "#E2E2E2",
      bordercolor = "#0a0a0a",
      borderwidth = 2,
      yanchor = "center",   
      y = 0.5),             
    annotations = list(
      text = "EUROPE",
      font = list(
        family = "Arial, Helvetica, sans-serif",
        size = 18,
        color = "black"),
      xref = "paper",
      yref = "paper",
      yanchor = "bottom",
      xanchor = "center",
      align = "center",
      x = 0.5,
      y = 0.95,
      showarrow = FALSE
    ),
    xaxis = list(
      title = "Time (in Years)",
      gridcolor = "white",
      gridwidth = 0,
      tickcolor = "black",
      tickwidth = 1,
      linecolor = "black",
      linewidth = 1
    ),
    yaxis = list(
      title = "Life Expectancy",
      gridcolor = "#eaf2f3",
      gridwidth = 1,
      tickcolor = "black",
      tickwidth = 1,
      linecolor = "black",
      linewidth = 1
    )
  )

Damit ist auch der zweite Plot erfolgreich erstellt. Es sollte deutlich werden, dass die Möglichkeit, plotly und ggplot2 zu verbinden, sehr nützlich sein kann, jedoch mit Einschränkungen verbunden ist - insbesondere bezogen auf durch plotly gegebene Funktionen wie die hoverinfo. Aus diesem Grund werden alle Abbildungen in unseren Lösungen im Folgenden direkt mit plotly erstellt.

Grafik 3 - Mehrere Plots Die dritte Abbildung soll dazu dienen, dass du dich mit der Möglichkeit auseinandersetzt, mehrere Plots in einer Abbildung darzustellen. Damit kann man beispielsweise mehrere Gruppen von Daten miteinander vergleichen. Um das umzusetzen, braucht man nur die subplot-Funktion, die einfach mehrere plotly-Abbildungen als Argumente annimmt und dann zusammenfügt. In der Problemstellung wurden dafür zwei mögliche Beispiele genannt:

  1. ein Gendervergleich mit mehreren Ländern in einem Plot (Bsp.: Europa)
  2. ein Ländervergleich mit allen drei Genderbezeichnungen in einem Plot

Dafür erstellt man zunächst jeden Plot einzeln und speichert diese jeweils in einem Objekt ab, um diese Objekte in der subplot-Funktion wieder verwenden zu können. Erstellen wir also zunächst einmal einen der Plots für Beispiel 1 - diesen können wir zufälligerweise einfach aus dem Abschnitt ‘Grafik 2’ übernehmen (nur Anpassung der annotation: In diesem Vergleich sind alle Datem aus Europa. Diese Info kann also eine allgemeine Überschrift auf der Ebene der subplot-Funktion liefern. Stattdessen ist hier für den einzelnen Plot die Ausprägung der Gendervariable von Interesse):

p1 <- filter(data2, SEX == "BTSX" & CONTINENT == "Europe") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~COUNTRY,
    type = "scatter",
    mode = "line",
    text = paste0("<b><i>Data from the Global Health Observatory (GHO)</i></b>",
                  "<br><br><b>Country:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$COUNTRY,
                  "<br><b>Country-Code:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$COUNTRYCODE,
                  "<br><b>Life Expectancy:</b> ", round(filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$L_E2EXACT, digits = 3),
                  "<br><b>Year:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$YEAR,
                  "<br><b>Sex:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$SEX,
                  "<br><b>Continent:</b> ", filter(data2, SEX == "BTSX" & CONTINENT == "Europe")$CONTINENT),
    hoverinfo = "text",
    colors = viridisLite::magma(nrow(filter(data2, CONTINENT == "Europe"))/51)) %>%
  layout(
    paper_bgcolor = "#eaf2f3",
    legend = list(
      font = list(
        family = "sans-serif",
        size = 12,
        color = "#000"),
      bgcolor = "#E2E2E2",
      bordercolor = "#0a0a0a",
      borderwidth = 2,
      yanchor = "center",   
      y = 0.5),             
    annotations = list(
      text = "Both Sexes",
      font = list(
        family = "Arial, Helvetica, sans-serif",
        size = 18,
        color = "black"),
      xref = "paper",
      yref = "paper",
      yanchor = "bottom",
      xanchor = "center",
      align = "center",
      x = 0.5,
      y = 0.95,
      showarrow = FALSE
    ),
    xaxis = list(
      title = "Time (in Years)",
      gridcolor = "white",
      gridwidth = 0,
      tickcolor = "black",
      tickwidth = 1,
      linecolor = "black",
      linewidth = 1
    ),
    yaxis = list(
      title = "Life Expectancy",
      gridcolor = "#eaf2f3",
      gridwidth = 1,
      tickcolor = "black",
      tickwidth = 1,
      linecolor = "black",
      linewidth = 1
    )
  )
p1

Somit haben wir den ersten Plot (p1) zu der Ausprägung BTSX (= Both Sexes) bereits erstellt. Für p2 und p3 müssen wir nun einfach den Filter in der filter-Funktion und der hoverinfo verändern und die annotation auf den Gendervergleich anpassen. Ist das getan, sind die einzelnen Plots fertig und wir können uns mit der subplot-Funktion befassen. Da das Erstellen der einzelnen Plots an dieser Stelle sehr repetitiv wäre, werden wir dir den Code dazu nicht zwangsläufig präsentieren. Falls du Probleme hast oder die genauen Anpassungen anschauen möchtest, kannst du einfach den folgenden Unterabschnitt ausklappen.

Erstellen von p2 und p3

p2: SEX = “MLE”

p2 <- filter(data2, SEX == "MLE" & CONTINENT == "Europe") %>%
  plot_ly(
    x = ~YEAR,
    y = ~L_E2EXACT,
    color = ~COUNTRY,
    type = "scatter",
    mode = "line",
    text = paste0("<b><i>Data from the Global Health Observatory (GHO)</i></b>",
                  "<br><br><b>Country:</b> ", filter(data2, SEX == "MLE" & CONTINENT == "Europe")$COUNTRY,
                  "<br><b>Country-Code:</b> ", filter(data2, SEX == "MLE" & CONTINENT == "Europe")$COUNTRYCODE,
                  "<br><b>Life Expectancy:</b> ", round(filter(data2, SEX == "MLE" & CONTINENT == "Europe")$L_E2EXACT, digits = 3),
                  "<br><b>Year:</b> ", filter(data2, SEX == "MLE" & CONTINENT == "Europe")$YEAR,
                  "<br><b>Sex:</b> ", filter(data2, SEX == "MLE" & CONTINENT == "Europe")$SEX,
                  "<br><b>Continent:</b> ", filter(data2, SEX == "MLE" & CONTINENT == "Europe")$CONTINENT),
    hoverinfo = "text",
    colors = viridisLite::magma(nrow(filter(data2, CONTINENT == "Europe"))/51)) %>%
  layout(
    paper_bgcolor = "#eaf2f3",
    legend = list(
      font = list(
        family = "sans-serif",
        size = 12,
        color = "#000"),
      bgcolor = "#E2E2E2",
      bordercolor = "#0a0a0a",
      borderwidth = 2,
      yanchor = "center",
      y = 0.5),
    annotations = list(
      text = "Male",
      font = list(
        family = "Arial, Helvetica, sans-serif",
        size = 18,
        color = "black"),
      xref = "paper",
      yref = "paper",
      yanchor = "bottom",
      xanchor = "center",
      align = "center",
      x = 0.5,
      y = 0.95,
      showarrow = FALSE
    ),
    xaxis = list(
      title = "Time (in Years)",
      gridcolor = "white",
      gridwidth = 0,
      tickcolor = "black",
      tickwidth = 1,
      linecolor = "black",
      linewidth = 1
    ),
    yaxis = list(
      title = "Life Expectancy",
      gridcolor = "#eaf2f3",
      gridwidth = 1,
      tickcolor = "black",
      tickwidth = 1,
      linecolor = "black",
      linewidth = 1
    )
  )
p2