Trulli
from www.cartoonimpact.com

Überblick

In diesem Practical wirst du das Dashboard der letzten Aufgabe weiter ausbauen, interaktiver und dynamischer gestalten. Am Ende wirst du dieses Dashboard erstellen.

Am Ende des Practicals wirst du wissen wie man:

  1. In leaflet Popupfenster einfügt
  2. Ein flexdashboard in eine shiny Webapp umwandelt.
  3. Statische in reaktive Grafiken umwandelt.

Aufgaben

A - Setup

  1. Öffne dein TheRBootcamp R project. Es sollte die Ordner 1_Data, 2_Assets und 3_Markdown enthalten.

  2. Lade dieses Markdown Skript herunter und speichere es in deinen 3_Markdown Ordner.

  3. Öffne das eben heruntergeladene Markdown file. Es enthält das fertige Dashboard der vorherigen Aufgabe. Arbeite mit diesem File weiter, um sicherzustellen, dass alles korrekt implementiert ist.

B - shiny

  1. Wandle das Markdown Dokument in eine shiny App um, indem du runtime: shiny im YAML Header, direkt unter social: ["twitter", "facebook"] einfügst und speicherst. Ab jetzt wird beim Rendern eine App geöffnet, die deine R-Session als Server verwendet.

  2. Im ersten R Chunk deines Dokuments ändere das setup in der Chunk Definition des setup Chunks in global. Damit wird dein Code, der in diesem Chunk enthalten ist, nur einmal beim Starten der App geladen und so die Performanz verbessert.

  3. Ist die App jetzt bereits dynamisch und reaktiv?

  4. Nein, du musst zuerst Inputs definieren. Füge vor dem Titel der ersten Seite (vor Übersicht der Airbnbs in Berlin {data-orientation=rows}) eine neue Seite via eine Zeile Gleichheitszeichen und nenne diese Inputs.

  5. Hinter das Inputs (aber auf derselben Zeile), schreibe {.sidebar}. Damit wird eine Sidebar definiert, die über alle drei Seiten der App fix auf der linken Seite bestehen bleibt.

  6. Fülle nun deine Sidebar. Erstelle unterhalb des Inputs Titels (genauer unterhalb der Gleichheitszeichen) einen neuen Code Chunk.

  7. In diesen Chunk fülle jetzt über die folgenden Tasks vier Inputmöglichkeiten. Füge zunächst mit dem untenstehenden Code einen Slider ein. Im ersten Argument wird die inputID spezifiziert, unter welcher der Input, also der mit dem Slider ausgewählte Wert, später ansteuerbar ist. Das zweite Argument spezifiziert das label, also den Titel des Sliders. Setze die inputID auf "Schlafzimmer", und das label auf "Minimum Anzahl Schlafzimmer". Die anderen Argumente spezifizieren Minimum, Maximum, und Standardwert.

# Auswahl Airbnbs über Schlafzimmer
sliderInput("XXX", 
            "XXX", 
            min = 0, 
            max = 12,
            value = 0)
  1. Direkt darunter, spezifiziere einen zweiten Slider, mit dem der “Maximale Preis pro Nacht” gesteuert wird. Da die Preisverteilung schief ist, wird hier der 10er Logarithmus (log10) verwendet. Setze die inputID auf "Preis", und das label auf "Max Preis pro Nacht (log):". Das zusätzliche step Argument spezifiziert die minimalen Schritte, in denen der Slider bewegt werden kann. Setze es auf .01.
# Preis pro Nacht
sliderInput("XXX", 
            "XXX", 
            min = 0, max = ceiling(max(log10(airbnb$Preis), na.rm = TRUE)),
            value = ceiling(max(log10(airbnb$Preis), na.rm = TRUE)),
            step = XXX)
  1. Als drittes, spezifiziere Checkboxen, mit denen die Stadtteile ausgewählt werden können. Verwende dazu die checkboxGroupInput() Funktion. Setze die inputID auf "Stadtteile", und das label auf "Wähle Stadtteile:". Das choices Argument definiert, wie viele Checkboxes angezeigt werden sollen, und welche Werte diese annehmen können. Im selected Argument wird definiere, welche Checkboxes standardmässig angewählt werden (hier alle).
# Auswahl der zu Plottenden Stadtteile
checkboxGroupInput("XXX", 
                   "XXX",
                   choices = sort(unique(airbnb$Stadtteil)),
                   selected = sort(unique(airbnb$Stadtteil)))
  1. Füge zum Schluss noch einen Auswahlinput ein. Mit der Funktion selectInput wird ein Dropdown-Menu erstellt, aus dem ein Wert ausgewählt wird. choices definiert die möglichen Werte, selected den Standardwert. Setze die Werte "viridis", "cividis", "magma", und "inferno", beim choices Argument ein. Das sind die Namen vierer Farbpaletten. Definiere "viridis" als Standardwert.
# Farbpalette
selectInput("Farbpalette", "Wähle Farbpalette:",
            choices = c("XXX", "XXX", "XXX", "XXX"),
            selected = "XXX")
  1. Bevor du nun das Dokument ausführen kannst, musst du zuerst die Plots anpassen. In shiny müssen R outputs, wie Plots, explizit mit der render*() Funktionen gerendert werden. Gehe zuerst zum Code Chunk, in dem sich der leaflet code befindet und packe den gesamten leaflet Code dieses Chunks in die renderLeaflet({}) Funktion, zwischen die geschwungenen Klammern, wie unten angezeigt.
renderLeaflet({
  
  LEAFLET CODE
  
})
  1. Gehe nun zum nächsten Code Chunk, in dem die Häufigkeitsverteilung als plotly Grafik erstellt wird. Packe auch hier den Code des gesamten Chunks in die renderPlotly({}) Funktion.
renderPlotly({
  
  PLOTLY CODE
  
})
  1. Gehe zum nächsten Code Chunk, in dem das Histogram der Gesamtratings definiert wird. Wiederum, packe den Code des gesamten Chunks in die renderPlot({}) Funktion.
renderPlot({
  
  PLOT CODE
  
})
  1. Gehe zum nächsten Code Chunk, in dem der gauge der mittleren Gesamtratings definiert wird. Wiederum, packe den Code des gesamten Chunks in die renderGauge({}) Funktion.
renderGauge({
  
  GAUGE CODE
  
})
  1. Wiederhole die Schritte B14 und B15 auch noch für die letzten Chunks (zweimal die Plots und zweimal die Gauges, in abwechselnder Reihenfolge).

  2. Nun kannst du dein Dokument ausführen. Passiert etwas, wenn du die Inputwerte veränderst?

  3. Der Grund weshalb nichts passiert ist, dass zwar Inputs definiert werden, diese aber bei den Plots nicht vorkommen. Du musst also den Code der Plots anpassen. Um, zum Beispiel, die Auswahl der Stadtteile nicht in jedem Plot durchführen zu müssen, können wir reaktive Objekte erstellen, also Objekte, die mit jeder Veränderung der Inputwerte upgedatet werden. Die grundsätzliche Syntax ist wie folgt:

DYNAMISCHES_OBJEKT <- reactive({
  MANIPULATION EINES OBJEKTES BASIEREND AUF INPUT
})
  1. Die reactive() Funktion unten erstellt ein reaktives Objekt und führt den in der Funktion geschriebenen Code jedes Mal aus, wenn sich einer der darin enthaltenen Inputwert verändert. Der untenstehende Code definiert zwei reaktive Objekte airbnb_dynamic - eine reaktive Version des airbnb Datensatzes, gefiltert nach Stadtteil, Schlafzimmer, und dem 10er Logarithmus von Preis - sowie farben_dynamic und ein reaktives Objekt, welches basierend auf der ausgewählten Farbpalette (aus dem viridis Paket) und der Anzahl ausgewählter Stadtteile einen Vektor von Farbwerten zurückgibt. Kopiere den Code und füge ihn ganz unten in den R Chunk in dem die Inputs definiert werden (der zweite R Chunk im Dokument) ein.
# kreiere reaktives airbnb Objekt
airbnb_dynamic <- reactive({

    airbnb %>%
      filter(Stadtteil %in% c(input$Stadtteile) &
               Schlafzimmer >= as.numeric(input$Schlafzimmer) &
               log10(Preis) <= as.numeric(input$Preis)) 
  
    
})

farben_dynamic <- reactive({
  ## Farbpalette vorbereiten
  # Anzahl Stadtteile
  n_stadtteile <- length(input$Stadtteile)
  # Definiere n_stadtteile verschiedene Farben aus der viridis palette
  switch(input$Farbpalette,
         viridis = viridis(n_stadtteile),
         cividis = cividis(n_stadtteile),
         magma = magma(n_stadtteile),
         inferno = inferno(n_stadtteile))
})
  1. Um die reaktive Version der airbnb Daten in den Plots und Gauges zu verwenden, ersetze airbnb in den Definitionen der Karte (dem leaflet Plot), der Häufigkeitsverteilung (dem plotly Plot), der Histogramme (dem ggplot2 Plot), und der Gauges (also alle die Teile, die du vorhin in eine render*() Funktion gepackt hast) durch airbnb_dynamic(). Dabei sind die Klammern nach airbnb_dynamic unbedingt nötig; das ist eine spezielle Syntax der reaktiven Objekte.

  2. Da du das farben_dynamic() Objekt vorgängig definiert hast, wird das farben Objekt nicht mehr benötigt. Lösche dessen Definition oberhalb des Codes für die Häufigkeitsverteilung:

### LÖSCHE ALLES VON HIER
# Anzahl Stadtteile
n_stadtteile <- length(unique(airbnb$Stadtteil))
# Definiere n_stadtteile verschiedene Farben aus der viridis palette
farben <- viridis(n_stadtteile)
### BIS HIER
  1. Ersetze ausserdem im Code der Häufigkeitsverteilung (dem plotly Plot) das Objekt farben durch farben_dynamic(). Du findest das Objekt in der Zeile scale_color_manual(values = farben) +.

  2. Klicke auf Run Document. Nun solltest du steuern können, welche Daten und welcher Stadtteile gezeigt werden, sowie welche Farbpalette im Plot auf Seite 2 verwendet wird. Cool oder?

C - leaflet 2

  1. Nun ist es an der Zeit, die Karte noch etwas mehr aufzupeppen! Die Datenpunkte sollen nach Stadtteil eingefärbt werden, Popupfenster sollen Informationen über das jeweilige Airbnb anzeigen, und eine Legende soll zeigen, welche Farbe welchem Stadtteil entspricht. Gehe zum leaflet Code, der die Karte erstellt.

  2. Beginne mit der Einfärbung der Punkte. Mit der colorFactor() Funktion des leaflet Pakets kannst du Farbwerte auf Levels einer kategorialen Variable (wie Stadtteil) mappen. Füge den untenstehenden Code vor dem Code der den leaflet Plot erstellt (aber innerhalb von renderLeaflet({})) ein.

dom <- airbnb_dynamic() %>% 
  pull(Stadtteil) %>% unique()

# Erstelle Farbpalette
pal <- colorFactor(farben_dynamic(),
                   domain = dom)
  1. Das so erstellte pal Objekt kann jetzt in der addCircleMarker() Funktion verwendet werden. Allerdings wird hier eine etwas eigene Syntax verwendet. Füge ein neues Argument color in die addCircleMarker() Funktion ein und setze es auf ~pal(Stadtteil) Die Tilde ~ ersetzt das Gleichheitszeichen nicht, sondern gibt leaflet lediglich an, dass hier unterschiedliche Werte für die verschiedenen Airbnbs benutzt werden.

  2. Führe die App aus. Jetzt sollten die Punkte nach Stadtteil eingefärbt sein.

  3. Im Moment ist aber noch nicht ersichtlich, welche Farbe zu welchem Stadtteil gehört. Es braucht eine Legende. Das geht in leaflet ganz einfach mit der addLegend() Funktion. Dabei muss eine mit colorFactor() erstellte Farbpalette, sowie die Werte nach denen die Marker eingefärbt werden sollen, als Argumente spezifiziert werden. Füge den untenstehenden Code direkt nach der addCircleMarker() Funktion ein, sodass die Pipe (%>%) direkt nach der schliessenden Klammer von addCircleMarker() (also auf derselben Zeile) steht.

 %>% 
  # Füge Legende hinzu
  addLegend(pal = pal, values = ~Stadtteil, opacity = 1)
  1. Das wars schon, jetzt wird auch eine Legende angezeigt. Der letzte Schritt sind die Popupfenster, die Informationen über die Airbnbs anzeigen. Dazu musst du den Code an zwei Stellen anpassen. Zuerst musst du zwischen airbnb_dynamic() %>% und leaflet() %>% eine Zeile mit dem untenstehenden Code einfügen. Der erste Teil erstellt dabei eine Variable superhost, die "Ja" und "Nein" anstelle von TRUE und FALSE angibt. Der zweite Teil erstellt eine Variable info, die Informationen aus unterschiedlichen Variablen mithilfe der paste0() Funktion zusammenfügt. Dabei wird etwas HTML verwendet, damit die Informationen später sauber formatiert untereinander dargestellt werden.
  mutate(
    superhost = case_when(isTRUE(Host_superhost) ~ "Ja",
                          TRUE ~ "Nein"),
    info = paste0("Unterkunft: <b>", Unterkunftsart,
                  "</b><br>Schlafzimmer: <b>", Schlafzimmer,
                  "</b><br>Superhost: <b>", superhost,
                  "</b><br>Preis pro Nacht: <b>", Preis, "&euro;</b>")
  ) %>% 
  1. Die neu erstellte Variable info muss jetzt noch in addCircleMarkers() aufgerufen, und der angezeigte Text etwas vergrössert werden. Füge dazu die Argumente label = ~lapply(info, HTML) und labelOptions = labelOptions(textsize = "15px") in der addCircleMarkers() Funktion hinzu. lapply(info, HTML) wendet die HTML() Funktion auf jedes Element von info an, was nötig ist, das der Text von leaflet als HTML gerendert wird.

  2. Führe die App erneut aus. Jetzt sollte alles klappen. Cool oder?

X - Uncharted territory - Here be dragons

Das wars mit den vorgegebenen Aufgaben. In diesem Teil wirst du dein Dashboard auf eigene Faust erweitern und uns am Ende kurz zeigen, was du gemacht hast.

Untenstehend findest du einige Vorschläge, wie du dein Dashboard erweitern könntest. Versuche eigenständig weitere Features einzubauen. Dazu kannst du auf die Vorschläge unten zurückgreifen, aber auch gerne eigene Ideen umsetzen; je nach dem was dich am meisten reizt.

  1. Schaue dir die Popupfenster der beiden Plots auf den Seiten 1 und 2 an. Gibt es zusätzliche Dinge die du angezeigt haben möchtest? Das können weitere Variablen, neu kreierte Variablen oder konditionale Variablen sein. Versuche diese zu implementieren.

  2. leaflet ermöglicht es auch Icons anstatt nur Punkte zu plotten. Eine Anleitung findest du hier (siehe dazu auch das Beispiel in den Slides zu Dashboards I). Du könntest zum Beispiel versuchen, Marker mit unterschiedlichen Icons zu verwenden. Zum Beispiel könntest du ein bestimmtes Icon für Superhosts verwenden und ein weiteres für ganz schlechte Airbnbs, und ein weiteres für den Rest. Eine weiter Möglichkeit wäre, unterschiedliche Icons pro Unterkunftsart zu wählen.

  3. Gestern hast du im Bericht eine Regression gerechnet. Du könntest eine vierte Seite in das Dashboard einbauen, in der du z.B. Inputs für ein Regressionsmodell definierst und dir den jeweiligen Output als Tabelle anzeigen lasst. Zusätzlich könntest du einen Plot mit den vorhergesagten und tatsächlichen Werten einbauen. Hier findest du eine Übersicht der möglichen Inputformate.

  4. Keine der obigen Optionen spricht dich an (oder vielleicht hast du ja bereits alle gemacht)? Überlege dir, wie du das Dashboard weiter aufpeppen kannst. Das kann das Styling betreffen, die Inputs, die angezeigte Information etc… Wir sind gespannt auf deine Kreationen!

Datensatz

Der airbnb.csv Datensatz enthält Zahlen zu 9868 Berliner Airbnbs

Variable Beschreibung
Preis Preis pro Nacht
Erstellungsdatum Eröffnungsdatum des Airbnbs
Unterkunftsart Appartement, Loft, House, etc.
Schlafplätze Anzahl Schlafplätze
Schlafzimmer Anzahl Schlafzimmer
Badezimmer Anzahl Badezimmer
Reinigungsgebühr Reinigungsgebühr
Verfügbarkeit_90Tage
Viertel In welchem Viertel befindet sich das Airbnb
Stadtteil In welchem Stadtteil befindet sich das Airbnb
Breitengrad Breitengrad
Längengrad Längengrad
Host_id Host id
Host_seit Erfahrung des Hosts
Host_antwortzeit Host Antwortzeit
Host_antwortrate Host Antwortrate
Host_superhost Superhost Ja/Nein
Host_anzahl Anzahl Gäste
Rating_gesamt Gesamtrating
Rating_genauigkeit Genauigkeitsrating
Rating_sauberkeit Sauberkeitsrating
Rating_checkin Checkinrating
Rating_kommunikation Kommunikationsrating
Rating_lage Lagerating
Rating_wertigkeit Wertigkeitsrating
Küche Küche vorhanden TRUE/FALSE
Wifi WLAN vorhanden TRUE/FALSE
TV TV vorhanden TRUE/FALSE
Kaffeemaschine Kaffeemaschine vorhanden TRUE/FALSE
Geschirrspüler Geschirrspüler vorhanden TRUE/FALSE
Terrasse_Balkon Terrasse/Balkon vorhanden TRUE/FALSE
Badewanne Badewanne vorhanden TRUE/FALSE
Check_in_24h 24h Check-In vorhanden TRUE/FALSE

Funktionen

Paket

Paket Installation
tidyverse install.packages("tidyverse")
flexdashboard install.packages("flexdashboard")
plotly install.packages("plotly")
leaflet install.packages("leaflet")
viridis install.packages("viridis")
htmltools install.packages("htmltools")
shiny install.packages("shiny")

Funktionen

Funktion Paket Beschreibung
gauge() flexdashboard Erstelle einen gauge.
renderGauge() flexdashboard Rendere Gauges in shiny
valueBox() flexdashboard Erstelle eine value Box
ggplotly() plotly Konvertiere ggplot2 Objekt in plotly Objekt
renderPlotly() plotly Rendere plotly Plot in shiny
leaflet() leaflet Initiiere leaflet Karte
setView() leaflet Definiere Anfangsfenster
addTiles() leaflet Zeige Karte
addCircleMarkers() leaflet Füge Kreisförmige Marker auf die Karte
renderLeaflet() leaflet Rendere leaflet Plot in shiny
reactive() shiny Initiiere reaktives Objekt
XXXInput() shiny Definiere Inputwidget. Siehe hier für eine Übersicht der möglichen Inputformate.

Materialien