Ist es nicht cool, die Daten, die man mühsam aufbereitet hat, schön zu visualisieren anstatt nur langweilige Tabellen zu erstellen? Und das bitteschön interaktiv, dann kann man tiefer in die Daten einsteigen. Genau das zeige ich Euch in diesem R Shiny Tutorial. Wir erstellen zusammen eine Shiny App in R, und das ist ganz easy.

Was ist R Shiny?

Shiny ist eigentlich nur ein Package für R, aber es ermöglicht uns, interaktive Dashboards in R zu erstellen, die dann als Webseite im Browser angezeigt werden. Wenn man Webseite hört, denkt man an HTML- und CSS-Programmierung. Und ja, ein paar Grundlagen zu wissen ist nicht verkehrt, aber das benötigte Wissen hält sich wirklich in Grenzen.

Um mal einen Eindruck zu bekommen, was Shiny Apps so alles können, solltet ihr die Shiny Gallery besuchen, die einige wirklich tolle Showcases beinhaltet.

Da Shiny ein R-Package ist, programmiert Ihr also Euer Dashboard einfach in R. Dazu teilt man das Skript in einen Anzeige- und einen Interaktionsteil auf, aber dazu mehr im nächsten Abschnitt. Will man dann das Programm starten, drückt man einfach auf „Run App“ und schon sieht man es im Browser.

Shiny wurde von RStudio entwickelt und ist dementsprechend gut in die RStudio IDE integriert. Ich empfehle also auf jeden Fall, RStudio zu benutzen, wenn Ihr eine Shiny App programmieren wollt. Natürlich geht es aber auch in anderen Entwicklungsumgebungen wie VS Code.

Das Deployment, also das Veröffentlichen in der Cloud, so dass andere Benutzer darauf zugreifen können, kann durchaus etwas komplexer sein, das hängt aber vom Anbieter ab. Für den Anfang gibt es aber eine einfache, kostenlose Möglichkeit. Mehr dazu am Ende von diesem R Shiny Tutorial, also dranbleiben.

Gibt es Alternativen zu R Shiny?

In R nicht wirklich, da ist Shiny einfach die beste Möglichkeit, um Dashboard-Apps zu erstellen. Die Python-Community schielt sogar ein wenig neidisch auf dieses tolle R-Package. Ähnliche Funktionalitäten gibt es in Plotly Dash, das in Python recht weit verbreitet ist, aber auch in R und Julia läuft. In meinem Einsteiger-Tutorial zu Plotly Dash in Python erfährst Du mehr darüber.

Natürlich gibt es auch noch Anbieter, bei denen man sich mehr oder weniger per Point & Click seine Dashboards zusammenstellt. Die grafischen Oberflächen zum Erstellen der Dashboards sind Fluch und Segen. Es ist wirklich einfach und schnell, sich damit ein Dashboard zu bauen. Aber will man eine spezielle Interaktivität oder Verhaltensweise haben, muss man sich durch viele Optionen wühlen und häufig irgendwelche Tricks einsetzen, damit es klappt. Auch die Wartbarkeit ist eher bescheiden: Wo war nochmal die Option? Von Automatisierung, Modularisierung und Versionskontrolle ganz zu schweigen.

Mittels Shiny oder Plotly Dash ist das Erstellen der grafischen Elemente zwar etwas mühselig, aber dafür hat man es schön übersichtlich im Skript, kann Dinge in Funktionen auslagern, hat Git als Versionskontrolle usw.

Hier trotzdem eine Liste der wichtigsten Dashboard-Anbieter:

  • Microsoft PowerBI: Eher im Business Intelligence angesiedelt, daher ein bisschen behäbig und nicht so wirklich schön damit zu arbeiten. Ich habe aber auch schon tolle Dashboards in PowerBI gesehen. Die Desktop-Version ist kostenlos und da PowerBI Bestandteil des Office-Pakets ist, wird es in vielen Firmen eingesetzt.
  • QlikSense: Eine interessante Kombination von mächtigem Lade-Skript und interaktiver Oberfläche. D.h. in QlikSense kann man die Datentransformation und -bearbeitung gut in einem Skript, welches beim Aktualisieren ausgeführt wird, erledigen. Sind die Daten dann in der gewünschten Form, kommt Dashboard und die Interaktivitäten darauf. Man muss sich ein wenig daran gewöhnen, aber dann ist diese Kombination wirklich cool.
  • Tableau: Ich kenne es zu wenig, als dass ich wirklich eine Meinung dazu habe. Tableau galt lange Zeit als der Platzhirsch und ist insbesondere in den USA weit verbreitet. Die Visualisierungen sollen sehr gelungen sein.
  • Grafana: Eine tolle kostenlose Software und bei der IT sehr beliebt. Tatsächlich wird Grafana viel im Monitoring-Bereich eingesetzt, denn Echtzeit-Zeitreihen sind der Schwerpunkt.

 

Die ersten Schritte zum Erstellen einer R Shiny App

Zuerst müsst Ihr Shiny wie jedes extra R-Package installieren. Das geht über install.packages(“shiny”).

Als nächstes erstellen wir ein Shiny Projekt. Also File -> New Project -> New Directory -> Shiny Web Application ausgewählt und den neuen Verzeichnisnamen und dessen Stammverzeichnis eingegeben.

In dem Verzeichnis wurde nun eine Datei app.R angelegt, welche als Ausgangspunkt für unsere eigene App dient und schon ein paar Demoelemente enthält. Schaut Euch erstmal an, was die Shiny App macht, klickt dafür auf „Run App“ rechts über dem Editorfenster.

Die Default App in RStudio, wenn man ein Shiny Projekt erstellt
In der Konsole seht ihr, dass dieser Button einfach den Befehl runApp() ausführt.
R Konsole zum Starten einer Shiny App mittels runApp

Es wird ein lokaler Webserver gestartet. Also ein Dienst, der auf Eurem Rechner läuft und eben die Shiny Webapp bereitstellt.

Dann öffnet sich ein RStudio-Browserfenster mit der Shiny Applikation. Ihr könnt aber auch die Adresse http://127.0.0.1:3906 im Browser Eurer Wahl eingeben. 127.0.0.1 ist die Adresse von Eurem Rechner und 3906 der Port, auf dem die App erreichbar ist.

Demo App von Shiny: Histogramm zu den Old Faithful Geysir Daten

So oder so ähnlich sollte das dann aussehen. Nach der Überschrift ist links ein Schieberegler, der die Anzahl Unterteilungen für das Histogramm auf der rechten Seite steuert. Schauen wir uns den zugehörigen Code an.

Zweigeteilter Aufbau einer R Shiny App

Jede Shiny App besteht aus drei Teilen, der Benutzeroberfläche (UI = User Interface), dem Server, der für die Interaktionen zuständig ist und schließlich der Aufruf der shinyApp. Statt einer Datei app.R kann man übrigens auch die beiden ersten Teile trennen und eine Datei ui.R und eine Datei server.R anlegen.

So sieht der UI-Teil aus:

# Define UI for application that draws a histogram
ui <- fluidPage(
 
    # Application title
    titlePanel("Old Faithful Geyser Data"),
 
    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),
 
        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot")
        )
    )
)

Also, das UI besteht aus einer sogenannten fluidPage, welche aus Zeilen und diese wiederum aus Spalten besteht. In diesem Fall ist die erste Zeile der Titel mittels titlePanel. In der zweiten Zeile definieren wir dann ein sidebarLayout, welches aus einem sidebarPanel und einem mainPanel besteht. Im sidebarPanel ist der Slider und im mainPanel das Histogramm.

Sidebar-Layout des Dashboards: oben der Titel, links die Sidebar und rechts die Grafik bzw. der Hauptteil

In dieser Art lassen sich alle möglichen Strukturen realisieren: mehrere Seiten, Tabs, verschachtelte Zeilen/Spalten usw.

Die Server-Funktion der Shiny App

Im Server-Abschnitt müssen wir den Slider mit dem Plot verknüpfen bzw. den Plot überhaupt erstmal definieren. Denn im UI-Abschnitt steht ja nur plotOutput("distPlot"). Dabei ist distPlot der Name/die Identifikation des Plots.

# Define server logic required to draw a histogram
server <- function(input, output) {
 
    output$distPlot <- renderPlot({
        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)
 
        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
}

Wir definieren also, dass distPlot die Ausgabe von renderPlot ist. Innerhalb der renderPlot-Funktion nutzen wir input$bins als Parameter für das Histogramm. Wir können darauf zugreifen, weil wir den Slider im UI als „bins“ benannt haben (sliderInput("bins",…)).

Das erste eigene interaktives Dashboard in R erstellen

Wir wollen ein kleines Aktiendashboard bauen. Dazu gibt es links eine Sidebar mit Auswahl des Zeitraums und der anzuzeigenden Aktie. Rechts soll dann das zugehörige Liniendiagramm oder eine Tabelle angezeigt werden. Um zu zeigen, dass auch mehrere Seiten möglich sind, bauen wir auch noch eine zweite Seite ein, die lassen wir aber erstmal leer.

So soll das Ganze aussehen:

Aktienchart als Dashboard in R. In der Sidebar links lassen sich Zeitraum und Aktien wählen, rechts ist ein Linienchart mit der Kursentwicklung zu sehen

Zuerst erzeugen wir ein neues, leeres Projekt. In dem Ordner erstellen wir die Dateien ui.R und server.R. Wir wählen also die Variante mit zwei Dateien.

Außerdem brauchen wir noch ein paar Packages, und zwar quantmod, gglot2, dplyr, DT und shinythemes. Mit Quantmod können wir Aktiendaten von Yahoo Finance laden, ggplot2 ist für Grafik, dplyr für Datentransformation (falls Du dplyr noch nicht kennst, schau mal in mein dplyr Tutorial rein), DT für interaktive Tabellen und schließlich shinythemes, um dem Dashboard ein anderes Farbschema überzustülpen. Prinzipiell funktioniert hier übrigens jedes Bootstrap-Theme, wir bleiben aber erstmal bei den in dem Package vorhandenen.

 

Mehrseitiges Layout einer Shiny App mittels navbarPage

Fangen wir mit der Datei ui.R an. Hier definieren wir uns die zwei Seiten als tabPanels und fügen diese am Ende mittels navbarPage zusammen. Seite 2 ist, wie gesagt, nur ein Dummy, den wir nicht weiter beachten. Seite 1 hat das Sidebar-Layout, bestehend aus sidebarPanel und mainPanel, das wir schon aus dem Standard-Beispiel kennen.

Im sidebarPanel definieren wir ein Datums-Picker, um den Zeitraum festzulegen. Dafür wollen wir eigentlich dateRangeInput verwenden, dieses Input-Element legen wir aber erst in der server.R-Datei fest. Daher gibt es hier nur den Platzhalfter uiOutput. Der Grund dafür ist, dass wir Variablen aus der server-Datei benutzen wollen und diese sind erstmal nicht in der ui.R verfügbar. UiOutput ist übrigens gut dafür geeignet, dynamische Inhalte zu erstellen, doch dazu später mehr.

uiOutput("ui_datumswahl")

Das zweite Element der Sidebar ist eine Gruppe von Checkboxen, mit denen man die Aktien-Symbole an- und abwählen kann. Das geht über ein checkboxGroupInput, aber wir müssen es wieder in die server.R schieben, da wir die Variable symbols benötigen.

uiOutput("ui_aktienwahl")

Und so sieht dann unsere gesamte ui.R aus:

library(shinythemes)
library(DT)
 
seite1 <- tabPanel("Seite 1",
                   sidebarLayout(
                     sidebarPanel(
                       uiOutput("ui_datumswahl"),
		       uiOutput("ui_aktienwahl")                       
                     ),
                     mainPanel(
                       tabsetPanel(type = "tabs",
                                   tabPanel("Chart", plotOutput("chart")),
                                   tabPanel("Tabelle", DTOutput("table")))
                     )
                   ))
 
 
seite2 <- tabPanel("Seite 2",
                   h1("TODO"))
 
ui <- navbarPage("Mein Dashboard",
                 theme = shinytheme("darkly"),
                 seite1,
                 seite2
)

 

Die Server-Seite unserer Shiny App

Zuerst laden wir die benötigten Packages und definieren uns drei Parameter. Symbols ist ein Vektor mit den Aktien-Symbolen, die von Yahoo Finance geholt werden soll. Zudem gibt es noch das Start- und Enddatum.

library(quantmod)
library(ggplot2)
library(dplyr)
 
symbols <- c("AAPL","NFLX","AMZN","MSFT")
start_datum = as.Date("2019-01-01")
end_datum = Sys.Date()

Im nächsten Schritt laden wir die benötigten Daten herunter und transformieren diese in einen data.frame, der für Tabelle und Chart taugt. Ich habe das ziemlich ineffizient mit einer for-Schleife programmiert, aber so ist es leicht nachzuvollziehen. Als erstes holen wir uns die Symbole, diese werden von Quantmod als Zeitreihe unter dem Symbolnamen abgelegt. Wir deklarieren uns einen leeren data.frame, den wir dann in jedem Schleifendurchgang mit den Daten eines Symbols ergänzen.

## Daten von Yahoo Finance holen ###################################
getSymbols(symbols, from = start_datum, to = end_datum)
df <- data.frame(Symbol=character(),
                 Datum=as.Date(character()),
                 Open=numeric(),
                 High=numeric(),
                 Low=numeric(),
                 Close=numeric(),
                 Volume=numeric(),
                 Adjusted=numeric())
for (s in symbols) {
  temp <- data.frame(Symbol=s, Datum=index(get(s)), get(s))
  colnames(temp) <- c("Symbol","Datum","Open","High","Low","Close","Volume","Adjusted")
  df <- rbind(df,temp)
}

Code, der nicht in der server-Funktion steht, wird einmalig beim Start der R Shiny App ausgeführt.

Und schließlich folgt die eigentliche Server-Funktion. Wir haben vier Elemente: den Date-Picker, die Aktien-Checkboxen, das Liniendiagramm und die Tabelle.

Der Date-Picker ist ein dateRangeInput mit ein paar Parametern, die relativ selbsterklärend sind. Eigentlich wollen wir ihn ja in der UI haben, also kapseln wir ihn über renderUI.

output$ui_datumswahl <- renderUI(
    dateRangeInput("datumswahl",
                   label = "Zeitauswahl:",
                   start = start_datum,
                   end = end_datum,
                   min = start_datum,
                   max = end_datum,
                   format = "dd.mm.yyyy",
                   language = "de",
                   separator = "bis")
  )

Das Gleiche machen wir für die Aktien-Checkboxen

output$ui_aktienwahl <- renderUI(
    checkboxGroupInput("aktienwahl",
                        label="Aktienauswahl:",
                       symbols,
                       selected=symbols[1])
  )

Das Liniendiagramm wird über renderPlot und einen ggplot-Chart realisiert. Dazu nehmen wir den Datensatz, filtern ihn auf die ausgewählten Symbole (input$aktienwahl) und ausgewählten Zeitraum. Damit keine Fehlermeldung bei nicht gewähltem Zeitraum auftritt, habe ich noch per ifelse das minimale Anfangs- bzw. maximale Enddatum ergänzt.

# Das Liniendiagramm mit Filter abhängig von den Inputs
output$chart <- renderPlot({
     df %>%
       filter(Symbol %in% input$aktienwahl,
              Datum >= ifelse(length(input$datumswahl[1])==0,start_datum,input$datumswahl[1]),
              Datum <= ifelse(length(input$datumswahl[2])==0,end_datum,input$datumswahl[2])) %>%
     ggplot(aes(x = Datum, y = High, color = Symbol)) +
      geom_line()
  })

Das vierte und letzte Element ist die Tabelle, ebenfalls mit dem identischen Filter wie der Chart versehen. Wichtig hierbei ist der Parameter style = „bootstrap“, denn ansonsten wird die Tabelle nicht an das gewählte dunkle shinytheme angepasst und man kann die Überschriften nicht lesen.

# Datentablle als DT
output$table <- renderDT({
     df %>%
       filter(Symbol %in% input$aktienwahl,
              Datum >= ifelse(length(input$datumswahl[1])==0,start_datum,input$datumswahl[1]),
              Datum <= ifelse(length(input$datumswahl[2])==0,end_datum,input$datumswahl[2])) %>%
     datatable(style = "bootstrap", options = list(pageLength = 20))
  })

Damit haben wir alles soweit fertig. Hier sind die Dateien server.R und ui.R auch nochmal zum Herunterladen. Das ist doch schon mal ziemlich cool, oder?

Dashboard in R Shiny erstellen: links die Sidebar, rechts ein Linienchart

Ausblick: Möglichkeiten von Shiny

Dieses Tutorial gibt natürlich nur einen kleinen Einblick in die glänzende Shiny-Welt. Es gibt tausende Möglichkeiten und wahrscheinlich habt Ihr auch schon Ideen, wie man unsere kleine App erweitern könnte. Also mir fällt jedenfalls noch eine Menge ein, zum Beispiel:

  • Abspeichern der Daten in einer kleinen Datenbank (z.B. SQLite) bzw. noch besser eine Trennung in Datenholen (dann täglich automatisiert) und dem Dashboard.
  • Verschiedene Charttypen (z.B. Candlesticks)
  • Zusammenfassende Statistiken zu den ausgewählten Aktien
  • Tabelle im Responsive Design

Und dann gibt es ja auch noch Performance-Überlegungen in Shiny. Welche Elemente müssen reactive sein, welche nicht, …

Deployment

Und schließlich das Deployment einer Shiny App, also das Hochladen in die Cloud, damit man es von jedem Rechner aus ansehen kann. Mit shinyapp.io könnt ihr kostenlos die ersten Schritte machen, muss dann aber recht schnell monatlich etwas zahlen. Shinyapp.io ist der bequemste Weg. Aber wenn ihr Zugang zu einem Linux-Server habt, könnt ihr darauf auch selber einen Shiny Server installieren. Wollt Ihr Eure Applikation bei anderen Anbietern hosten, dann ist es am besten, die App in einen Docker-Container zu packen. Das ist eigentlich ganz einfach, aber dazu ein anderes Mal mehr.

 

So, und nun ran an die Dashboards und viel Spaß beim Coden,

Euer Holger