Shiny statt Javascript

R-ShinyApp Titelbild

Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit „Shiny“ klappt sowas auch in R – ganz ohne Javascriptkenntnisse.

Zunächst ein Geständnis: Ich habe Probleme mit Javascript. Leider kann ich nicht genau begründen, warum – denn im Grunde finde ich die Sprache hervorragend in ihrer Flexibilität. Aber irgendwie komme ich mit der Syntax nicht ganz klar. Mein Glück: Ich darf oft mit super Entwicklern zusammenarbeiten, die JS im Schlaf schreiben können. Ich kann mich dann darauf konzentrieren, die Daten im Vorfeld bereitzustellen mit Python oder R.

Immer wieder kommt es aber vor, dass wir Datenjournalisten den Fachredakteuren aus verschiedenen Ressorts großen Datenmengen zugänglich machen müssen. Während wir am schnellsten wären, wenn wir die Analysen einfach in R-Code schreiben können, brauchen die Kollegen was einfacheres. Und trotzdem sollten sie zu eigenen Erkenntnissen kommen können. Exceltabellen sind da oft nicht die ideale Lösung.

In einem meiner letzten Großprojekte zu den Wanderungsbewegungen in München habe ich sehr viel mit R Markdown-Dokumenten gearbeitet, in denen die Redakteure Tabellen zu verschiedenen Themen durchsuchen konnten. Das war gut, aber ich will für die Zukunft andere Möglichkeiten ausprobieren. Da kommt mir ein Einfall von neulich ganz recht, um mal Shiny auszuprobieren. denn ich will meine Daten selbst interaktiv bekommen.

Was ist Shiny?

Shiny ist zunächst Mal ein Paket für R. Es ermöglicht, interaktive Web-Apps direkt in R zu programmieren, ohne dass die Daten von R rausgespeichert werden müssen und dann mit Javascipt (oder anderen Sprachen) weiterverarbeitet werden. Shiny verspricht viel Funktionalität: Ausgabe als HTML-Dateien, flexible Layouts und Themes, eigenes CSS oder interaktive htmlwidgets (auch eine R-Bibliothek).

Meine Idee: Ein WM-Planer

Ich stand neulich vor einem Problem: Ich wollte einen Termin Anfang Juli planen. Zwar hatte ich auf dem Schirm, dass da Fußball-WM ist. Aber wann genau Deutschland spielt, musste ich erst mühsam ergooglen. Meine Idee soll das vereinfachen: Eine kleine App mit zwei Auswahlfeldern für alle Mannschaften und ihr Abschneiden in der Vorrunde. Daraus lässt sich der komplette Weg ins Finale ableiten. Und es ist ein einfaches Beispiel für den Einsatz von Shiny mit ein wenig Interaktivität und einer überschaubaren Datenmenge.

Die Vorarbeit

An die Daten zu kommen ist kein Problem. Es gibt zahlreiche Möglichkeiten, den WM-Spielplan maschinenlesbar zu bekommen, etwa als JSON oder als CSV.

Ich habe mir die Daten als CSV heruntergeladen und in R vorbereitet. Dazu gehört zum Beispiel, das Datum in das richtige Format für die interne Datenverarbeitung umzuwandeln (als sogenanntes POSIXct). Das geht gut mit der Bibliothek lubridate. Außerdem musste ich aus den Daten die jeweiligen Gruppen extrahieren (nicht „Group XYZ“, sondern nur „XYZ“). Das klappt gut mit dem Package stringR und dessen Funktion str_extract(). Der „spektakulärste“ Teil ist das Umwandeln vom sogenannten „langen Format“ der Daten in das „weiten Format“. Beim langen Format steht jede Beobachtung mit nur einer Variable in einer Zeile, die Daten werden quasi übereinander gestapelt. Beim weiten Format steht jede Spalte für eine Variable und jede Zeile für eine Beobachtung. Das funktioniert hier besser. Diese Umwandlung erledigt das Paket tidyR für uns. Mit der Funktion spread().

Die Vorarbeit ist beendet. An dieser Stelle speichere ich die Daten in R und mache kurz mit Excel weiter, um dort die Daten für die möglichen Wege durch die Finals einzufügen? Warum? Weil es schneller geht. Ich könnte theoretisch Filter bauen, um immer genau die Spiele zu treffen, bei denen ich bestimmte Daten für Achtel-, Viertel- und Halbfinale eintragen will. Denn meine Daten bisher enthalten logischerweise nur die Gruppenspiele, noch keine Finalspiele. Doch die kann ich auch schon vorhersehen, je nachdem, ob die Teams Gruppenerster oder Gruppenzweiter werden. Aber das dauert in R etwa genauso lange, wie die Arbeit in Excel. Da ich das nur einmal machen muss, bin ich mit einer optischen Tabellenverarbeitung auch zügig unterwegs.

Ich suche mir also alle Spielpaarungen (unter anderem der Kicker hat da eine gute Übersicht), die die gleichen Termine haben (Gruppenerste von der einen Gruppe, Gruppenzweite von einer anderen) und füge die in die noch leeren Spalten ein, außerdem kopiere ich die Spiele und füge die Trennung „Gruppenerster/Gruppenzweiter“ als Variable ein. Gespeichert wird das Ergebnis in Excel als CSV und dann wieder in R geladen.

In R muss ich leider die Spalten wieder ins richtige Datumsformat bringen. CSVs speichern das nicht ab. (Ich könnte mir das also ganz am Anfang sparen) Damit ich das nicht für alle Spalten einzeln machen muss, nutze ich mutate_at(), das hier in den Spalten 3 bis 10 arbeitet (also diese Spalten modifiziert, wie ich das uns .funs eingebe):

Danach bringe ich die bisher noch englischen Teamnamen mit einem Wörterbuch für die deutschen Begriffe zusammen. So haben wir hinterher die Teams in der deutschen Schreibweise:

Das Ergebnis geht jetzt an die App. Ich speichere es als RDS-Datei. Ein R-Format.

Die Shiny-App

Eine Shiny-App besteht aus zwei Komponenten. Man kann die in zwei verschiedene Dateien schreiben, man kann sie aber auch kombinieren, was ich in diesem Beispiel mache. Das ist hier übersichtlicher. Ein guter Startpunkt für die Entwicklung mit Shiny ist das Tutorial bei RStudio, oder der Kurs bei Datacamp ($). Es ist anfangs ein bisschen kompliziert, geht aber nach einer kurzen Einarbeitungszeit ganz gut von der Hand.

Zunächst werden – wie bei R üblich – die benötigten Pakete geladen. Außerdem lade ich hier unsere Daten von eben als Variable d.

Die beiden Shiny-Komponenten sind die sogenannte UI und der Server. Beide muss ich definieren, und am Ende zur App kombinieren.

Die UI

In der Variable ui definiere ich das Aussehen der App. Vor allem, welche Eingabe und Ausgabefelder es geben wird. Außerdem kann ich hier einstellen, wie das Seitenlayout funktioniert und das Theme der App (mit der Einstellung theme = „“).

Die vorgefertigten Layouts basieren auf Bootstrap. Einem Layout, das von Twitter entwickelt wurde, und sich an die Displaygröße anpasst. Grundsätzlich könnten wir aber auch eigene Layouts verwenden.

Das UI besteht aus verschiedenen Inputs, mit denen die Nutzer unsere Daten verändern können. Eine Auswahl:

  • checkboxInput – Einfachauswahl über Checkbox
  • checkboxGroupInput – Mehrfachauswahl mit Checkboxen
  • dateInput – Datum (geht auch mit dateRangeInput)
  • fileInput – Lädt eine Datei
  • numericInput – ein Nummerneingabefeld
  • textInput – ein Textfeld
  • radioButtons – Checkboxen mit runden Radiobuttons
  • selectInput – Auswahl über eine Dropdown-Liste. Benutzen wir hier.
  • sliderInput – Auswahl über einen Slider
  • submitButton / actionButton – können eine Funktion zugewiesen bekommen

Wir definieren unsere UI mit zwei SelectInputs – Dropdown-Listen. Auch im HTML werden die mit dem Tag „Select“ definiert, daher kommt wohl der Begriff in Shiny. Das Theme der App ist „United“, der Titel „WM 2018-Planer“.

In der Sidebar lege ich die Dropdown-Menüs fest: SelectInput Nummer eins heißt „teamname“, womit ich ihn später im Server ansprechen kann. Hier kann man auch dem Menü einen eigenen Titel geben, dazu muss man die Auswahlmöglichkeiten definieren und man kann einen Wert davon als Startwert festlegen. Das gleiche mache ich mit den Vorrundenergebnissen.

Im sogenannten „MainPanel“ definiere ich den Output (mehr dazu unten) und schreibe einen helpText rein, der erklärt, was die App macht.

Der Server

Im Server gebe ich die Berechnungen ein, die die App ausführen soll. Außerdem lege ich fest, welche Ausgabe zu meinen in der UI definierten Bereichen ausgegeben wird.

Wie bei den Inputs, gibt es auch hier Unterschiede bei den Outputs:

  • klassische Tabellen
  • Data Table – interaktive Tabellen
  • Fotos
  • Graphen
  • Konsolenausgaben
  • Text
  • HTML

Weitere Infos gibt es in dem sehr übersichtlichen Shiny-Cheatsheet [PDF].

Ich nutze in meinem Beispiel einen HTML-Output, weil ich da flexibel HTML ausgeben kann, wie ich es möchte. Ich mache es mir hier sehr einfach, indem ich alles einzeln als HTML-Code einfüge. Theoretisch könnte ich mit Shiny auch auf die einzelnen HTML-Tags zugreifen.

So sieht der Server-Bereich dann aus:

Beides zusammen

Ist dann nur noch eine Zeile:

Fertig ist die App. Die nächste Frage ist dann: Wo spiele ich sie aus? Grundsätzlich gibt es zwei Möglichkeiten. Ich kann die vorgefertigte Hostinglösung von RStudio benutzen – in der Gratisvariante reicht das zum Testen aus. Will ich mehr Traffic oder mehrere Apps parallel, muss ich einen besseren Tarif buchen. Variante zwei ist das Selberhosten. Dafür muss ich auf meinem Webserver Shiny installieren. RStudio hat dafür eine Open Source-Variante des Shiny Servers.

Für unseren Fall reicht aber das kostenlose Hosting von RStudio. Direkt im Programm RStudio kann ich die App mit meinem Account dort verknüpfen und sie „deployen“ (hochladen).

Die komplette Shiny-App sieht dann so aus:

 

Und so sieht das Ergebnis aus: Der WM-Planer (Das Laden dauert einen Moment)