{"id":321,"date":"2018-05-27T09:01:30","date_gmt":"2018-05-27T07:01:30","guid":{"rendered":"https:\/\/benedict-witzenberger.de\/wordpress\/?p=321"},"modified":"2018-05-26T20:57:26","modified_gmt":"2018-05-26T18:57:26","slug":"r-shiny-statt-javascript","status":"publish","type":"post","link":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/","title":{"rendered":"Shiny statt Javascript"},"content":{"rendered":"<p><strong>Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit &#8222;Shiny&#8220; klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.<\/strong><!--more--><\/p>\n<p>Zun\u00e4chst ein Gest\u00e4ndnis: Ich habe Probleme mit Javascript. Leider kann ich nicht genau begr\u00fcnden, warum \u2013 denn im Grunde finde ich die Sprache hervorragend in ihrer Flexibilit\u00e4t. Aber irgendwie komme ich mit der Syntax nicht ganz klar. Mein Gl\u00fcck: Ich darf oft mit super Entwicklern zusammenarbeiten, die JS im Schlaf schreiben k\u00f6nnen. Ich kann mich dann darauf konzentrieren, die Daten im Vorfeld bereitzustellen mit Python oder R.<\/p>\n<p>Immer wieder kommt es aber vor, dass wir Datenjournalisten den Fachredakteuren aus verschiedenen Ressorts gro\u00dfen Datenmengen zug\u00e4nglich machen m\u00fcssen. W\u00e4hrend wir am schnellsten w\u00e4ren, wenn wir die Analysen einfach in R-Code schreiben k\u00f6nnen, brauchen die Kollegen was einfacheres. Und trotzdem sollten sie zu eigenen Erkenntnissen kommen k\u00f6nnen. Exceltabellen sind da oft nicht die ideale L\u00f6sung.<\/p>\n<p>In einem meiner letzten Gro\u00dfprojekte zu den <a href=\"http:\/\/www.sueddeutsche.de\/thema\/Wanderungsbewegungen\" target=\"_blank\" rel=\"noopener\">Wanderungsbewegungen in M\u00fcnchen<\/a> 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\u00fcr die Zukunft andere M\u00f6glichkeiten ausprobieren. Da kommt mir ein Einfall von neulich ganz recht, um mal Shiny auszuprobieren. denn ich will meine Daten selbst interaktiv bekommen.<\/p>\n<h2>Was ist Shiny?<\/h2>\n<p>Shiny ist zun\u00e4chst Mal ein Paket f\u00fcr R. Es erm\u00f6glicht, interaktive Web-Apps direkt in R zu programmieren, ohne dass die Daten von R rausgespeichert werden m\u00fcssen und dann mit Javascipt (oder anderen Sprachen) weiterverarbeitet werden. Shiny verspricht viel Funktionalit\u00e4t: Ausgabe als HTML-Dateien, flexible Layouts und Themes, eigenes CSS oder interaktive htmlwidgets (auch eine R-Bibliothek).<\/p>\n<h2>Meine Idee: Ein WM-Planer<\/h2>\n<p>Ich stand neulich vor einem Problem: Ich wollte einen Termin Anfang Juli planen. Zwar hatte ich auf dem Schirm, dass da Fu\u00dfball-WM ist. Aber wann genau Deutschland spielt, musste ich erst m\u00fchsam ergooglen. Meine Idee soll das vereinfachen: Eine kleine App mit zwei Auswahlfeldern f\u00fcr alle Mannschaften und ihr Abschneiden in der Vorrunde. Daraus l\u00e4sst sich der komplette Weg ins Finale ableiten. Und es ist ein einfaches Beispiel f\u00fcr den Einsatz von Shiny mit ein wenig Interaktivit\u00e4t und einer \u00fcberschaubaren Datenmenge.<\/p>\n<h2>Die Vorarbeit<\/h2>\n<p>An die Daten zu kommen ist kein Problem. Es gibt zahlreiche M\u00f6glichkeiten, den WM-Spielplan maschinenlesbar zu bekommen, etwa als <a href=\"https:\/\/github.com\/lsv\/fifa-worldcup-2018\" target=\"_blank\" rel=\"noopener\">JSON<\/a> oder als <a href=\"https:\/\/fixturedownload.com\/results\/fifa-world-cup-2018\" target=\"_blank\" rel=\"noopener\">CSV<\/a>.<\/p>\n<p>Ich habe mir die Daten als CSV heruntergeladen und in R vorbereitet. Dazu geh\u00f6rt zum Beispiel, das Datum in das richtige Format f\u00fcr die interne Datenverarbeitung umzuwandeln (als sogenanntes POSIXct). Das geht gut mit der Bibliothek <a href=\"https:\/\/cran.r-project.org\/web\/packages\/lubridate\/vignettes\/lubridate.html\" target=\"_blank\" rel=\"noopener\">lubridate<\/a>. Au\u00dferdem musste ich aus den Daten die jeweiligen Gruppen extrahieren (nicht &#8222;Group XYZ&#8220;, sondern nur &#8222;XYZ&#8220;). Das klappt gut mit dem Package <a href=\"https:\/\/cran.r-project.org\/web\/packages\/stringr\/vignettes\/stringr.html\" target=\"_blank\" rel=\"noopener\">stringR<\/a> und dessen Funktion <a href=\"https:\/\/www.rdocumentation.org\/packages\/stringr\/versions\/1.3.1\/topics\/str_extract\" target=\"_blank\" rel=\"noopener\">str_extract()<\/a>. Der &#8222;spektakul\u00e4rste&#8220; Teil ist das Umwandeln vom sogenannten &#8222;langen Format&#8220; der Daten in das &#8222;weiten Format&#8220;. Beim langen Format steht jede Beobachtung mit nur einer Variable in einer Zeile, die Daten werden quasi \u00fcbereinander gestapelt. Beim weiten Format steht jede Spalte f\u00fcr eine Variable und jede Zeile f\u00fcr eine Beobachtung. Das funktioniert hier besser. Diese Umwandlung erledigt das Paket <a href=\"https:\/\/blog.rstudio.com\/2014\/07\/22\/introducing-tidyr\/\" target=\"_blank\" rel=\"noopener\">tidyR<\/a> f\u00fcr uns. Mit der Funktion <a href=\"https:\/\/www.rdocumentation.org\/packages\/tidyr\/versions\/0.8.0\/topics\/spread\" target=\"_blank\" rel=\"noopener\">spread()<\/a>.<\/p>\n<pre class=\"toolbar-overlay:false lang:r decode:true \">library(tidyverse)\r\nlibrary(lubridate)\r\n\r\ndata &lt;- read.csv(\"match_schedule.csv\")\r\n\r\ndata$Group &lt;- as.character(data$Group)\r\ndata$Home.Team &lt;- as.character(data$Home.Team)\r\ndata$Away.Team &lt;- as.character(data$Away.Team)\r\ndata$Date &lt;- dmy_hm(data$Date, tz=\"Europe\/Berlin\")\r\n\r\ndata %&gt;%\r\nmutate(Group = str_extract(Group, \"[A-Z]$\")) %&gt;%\r\nselect(-Result) -&gt; data\r\n\r\nteams &lt;- unique(filter(data, Round.Number %in% c(1,2,3))$Home.Team)\r\n\r\ndata %&gt;%\r\ngather(key = \"Place\", value = \"Team\", Home.Team:Away.Team) -&gt; data_long\r\n\r\ndata_vorrunde &lt;- list()\r\nfor (i in seq_along(teams)) {\r\n\r\ndata_long %&gt;%\r\nfilter(Team == teams[i]) %&gt;%\r\ngroup_by(Team) %&gt;%\r\nselect(-c(Location, Place)) %&gt;%\r\nspread(Round.Number, Date) -&gt; d_current\r\n\r\ndata_vorrunde[[i]] &lt;- d_current\r\n}\r\n\r\ndata_vorrunde &lt;- bind_rows(data_vorrunde)\r\ncolnames(data_vorrunde) &lt;- c(\"Group\", \"Team\", \"Erste Runde\", \"Zweite Runde\", \"Dritte Runde\")<\/pre>\n<p>Die Vorarbeit ist beendet. An dieser Stelle speichere ich die Daten in R und mache kurz mit Excel weiter, um dort die Daten f\u00fcr die m\u00f6glichen Wege durch die Finals einzuf\u00fcgen? Warum? Weil es schneller geht. Ich k\u00f6nnte theoretisch Filter bauen, um immer genau die Spiele zu treffen, bei denen ich bestimmte Daten f\u00fcr 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\u00fcgig unterwegs.<\/p>\n<p>Ich suche mir also alle Spielpaarungen (unter anderem der <a href=\"http:\/\/www.kicker.de\/news\/fussball\/wm\/spiele\/weltmeisterschaft\/2018\/spieltag.html\" target=\"_blank\" rel=\"noopener\">Kicker hat da eine gute \u00dcbersicht<\/a>), die die gleichen Termine haben (Gruppenerste von der einen Gruppe, Gruppenzweite von einer anderen) und f\u00fcge die in die noch leeren Spalten ein, au\u00dferdem kopiere ich die Spiele und f\u00fcge die Trennung &#8222;Gruppenerster\/Gruppenzweiter&#8220; als Variable ein. Gespeichert wird das Ergebnis in Excel als CSV und dann wieder in R geladen.<\/p>\n<p>In R muss ich leider die Spalten wieder ins richtige Datumsformat bringen. CSVs speichern das nicht ab. (Ich k\u00f6nnte mir das also ganz am Anfang sparen) Damit ich das nicht f\u00fcr 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):<\/p>\n<pre class=\"lang:r decode:true \">data_edited %&gt;%\r\nmutate_at(.vars = c(3:10), .funs = dmy_hm, tz=\"Europe\/Berlin\") -&gt; data_edited<\/pre>\n<p>Danach bringe ich die bisher noch englischen Teamnamen mit einem W\u00f6rterbuch f\u00fcr die deutschen Begriffe zusammen. So haben wir hinterher die Teams in der deutschen Schreibweise:<\/p>\n<pre class=\"toolbar-overlay:false lang:r decode:true \">countries %&gt;% read_csv2(\"countries.csv\")\r\n \r\ndata_edited %&gt;%\r\n left_join(countries, by = c(\"Team\" = \"Englisch\")) -&gt; data_edited<\/pre>\n<p>Das Ergebnis geht jetzt an die App. Ich speichere es als RDS-Datei. Ein R-Format.<\/p>\n<h2>Die Shiny-App<\/h2>\n<p>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 \u00fcbersichtlicher. Ein guter Startpunkt f\u00fcr die Entwicklung mit Shiny ist das <a href=\"http:\/\/shiny.rstudio.com\/tutorial\/\" target=\"_blank\" rel=\"noopener\">Tutorial bei RStudio<\/a>, oder der <a href=\"https:\/\/www.datacamp.com\/courses\/building-web-applications-in-r-with-shiny\" target=\"_blank\" rel=\"noopener\">Kurs bei Datacamp<\/a> ($). Es ist anfangs ein bisschen kompliziert, geht aber nach einer kurzen Einarbeitungszeit ganz gut von der Hand.<\/p>\n<p>Zun\u00e4chst werden \u2013 wie bei R \u00fcblich \u2013 die ben\u00f6tigten Pakete geladen. Au\u00dferdem lade ich hier unsere Daten von eben als Variable <em>d<\/em>.<\/p>\n<p>Die beiden Shiny-Komponenten sind die sogenannte UI und der Server. Beide muss ich definieren, und am Ende zur App kombinieren.<\/p>\n<h3>Die UI<\/h3>\n<p class=\"\">In der Variable ui definiere ich das Aussehen der App. Vor allem, welche Eingabe und Ausgabefelder es geben wird. Au\u00dferdem kann ich hier einstellen, wie das Seitenlayout funktioniert und das Theme der App (mit der Einstellung theme = &#8222;&#8220;).<\/p>\n<p>Die vorgefertigten Layouts basieren auf Bootstrap. Einem Layout, das von Twitter entwickelt wurde, und sich an die Displaygr\u00f6\u00dfe anpasst. Grunds\u00e4tzlich k\u00f6nnten wir aber auch eigene Layouts verwenden.<\/p>\n<p>Das UI besteht aus verschiedenen Inputs, mit denen die Nutzer unsere Daten ver\u00e4ndern k\u00f6nnen. Eine Auswahl:<\/p>\n<ul>\n<li>checkboxInput &#8211; Einfachauswahl \u00fcber Checkbox<\/li>\n<li>checkboxGroupInput &#8211; Mehrfachauswahl mit Checkboxen<\/li>\n<li>dateInput &#8211; Datum (geht auch mit dateRangeInput)<\/li>\n<li>fileInput &#8211; L\u00e4dt eine Datei<\/li>\n<li>numericInput &#8211; ein Nummerneingabefeld<\/li>\n<li>textInput &#8211; ein Textfeld<\/li>\n<li>radioButtons &#8211; Checkboxen mit runden Radiobuttons<\/li>\n<li>selectInput &#8211; Auswahl \u00fcber eine Dropdown-Liste. Benutzen wir hier.<\/li>\n<li>sliderInput &#8211; Auswahl \u00fcber einen Slider<\/li>\n<li>submitButton \/ actionButton &#8211; k\u00f6nnen eine Funktion zugewiesen bekommen<\/li>\n<\/ul>\n<p>Wir definieren unsere UI mit zwei SelectInputs \u2013 Dropdown-Listen. Auch im HTML werden die mit dem Tag &#8222;Select&#8220; definiert, daher kommt wohl der Begriff in Shiny. Das Theme der App ist &#8222;United&#8220;, der Titel &#8222;WM 2018-Planer&#8220;.<\/p>\n<p>In der Sidebar lege ich die Dropdown-Men\u00fcs fest: SelectInput Nummer eins hei\u00dft &#8222;teamname&#8220;, womit ich ihn sp\u00e4ter im Server ansprechen kann. Hier kann man auch dem Men\u00fc einen eigenen Titel geben, dazu muss man die Auswahlm\u00f6glichkeiten definieren und man kann einen Wert davon als Startwert festlegen. Das gleiche mache ich mit den Vorrundenergebnissen.<\/p>\n<p>Im sogenannten &#8222;MainPanel&#8220; definiere ich den Output (mehr dazu unten) und schreibe einen helpText rein, der erkl\u00e4rt, was die App macht.<\/p>\n<pre class=\"toolbar-overlay:false lang:r decode:true\">ui &lt;- shinyUI(fluidPage(theme = shinytheme(\"united\"),\r\n  \r\n  # Application title\r\n  titlePanel(\"WM 2018-Planer\"),\r\n  \r\n  # Sidebar mit zwei Selectboxen \r\n  sidebarLayout( # Hier beginnt das Layout\r\n    sidebarPanel(\r\n      selectInput(\"teamname\",\r\n                  \"Team\", choices = unique(d$Deutsch), selected = \"Deutschland\"), \r\n# Hier wird der erste Input definiert. teamname ist die Variable, auf die wir im Server zugreifen\r\n      selectInput(\"vorrunde\",\r\n                  \"Vorrundenergebnis\", choices = unique(d$Status), selected = 1)\r\n# Hier wird der zweite Input definiert\r\n    ),\r\n    \r\n    mainPanel(\r\n      helpText(\"Welche Zeiten sollte man sich f\u00fcrs WM-Schauen freihalten?\"),\r\n      htmlOutput(outputId = \"dates\") \r\n# hier wurde das Output definiert, das wir in der Serverfunktion berechnen\r\n    )\r\n  )\r\n))<\/pre>\n<h3>Der Server<\/h3>\n<p>Im Server gebe ich die Berechnungen ein, die die App ausf\u00fchren soll. Au\u00dferdem lege ich fest, welche Ausgabe zu meinen in der UI definierten Bereichen ausgegeben wird.<\/p>\n<p>Wie bei den Inputs, gibt es auch hier Unterschiede bei den Outputs:<\/p>\n<ul>\n<li>klassische Tabellen<\/li>\n<li>Data Table &#8211; interaktive Tabellen<\/li>\n<li>Fotos<\/li>\n<li>Graphen<\/li>\n<li>Konsolenausgaben<\/li>\n<li>Text<\/li>\n<li>HTML<\/li>\n<\/ul>\n<p>Weitere Infos gibt es in dem sehr \u00fcbersichtlichen <a href=\"https:\/\/shiny.rstudio.com\/images\/shiny-cheatsheet.pdf\" target=\"_blank\" rel=\"noopener\">Shiny-Cheatsheet<\/a> [PDF].<\/p>\n<p>Ich nutze in meinem Beispiel einen HTML-Output, weil ich da flexibel HTML ausgeben kann, wie ich es m\u00f6chte. Ich mache es mir hier sehr einfach, indem ich alles einzeln als HTML-Code einf\u00fcge. Theoretisch k\u00f6nnte ich mit Shiny auch auf die einzelnen HTML-Tags zugreifen.<\/p>\n<p>So sieht der Server-Bereich dann aus:<\/p>\n<pre class=\"toolbar-overlay:false lang:r decode:true \">server &lt;- function(input, output) {\r\n   \r\n   output$dates &lt;- renderUI({ \r\n# Hier wird festgelegt welches Element aus der UI wir ver\u00e4ndern\r\n     \r\n     d %&gt;% \r\n       filter(Deutsch == input$teamname &amp; Status == input$vorrunde) -&gt; d_current \r\n# Hier ist die Berechnung mit den Daten, jedesmal wenn etwas neues ausgew\u00e4hlt wird.\r\n     \r\n     \r\n     \r\n     HTML( # Ab hier erfolgt die Ausgabe als reines HTML\r\n       paste0(\"&lt;h2&gt;\", input$teamname, \"&lt;\/h2&gt;\"),\r\n       \r\n       paste0(\"Die &lt;b&gt;Vorrundenspiele&lt;\/b&gt; in Gruppe \", d_current$Group, \" finden statt am: \", \"&lt;br&gt;&lt;br&gt;\"),\r\n       paste(format(d_current$`Erste Runde`, format = \"%a., %d.%m.%y %H:%M Uhr\"), \r\n             format(d_current$`Zweite Runde`, format = \"%a., %d.%m.%y %H:%M Uhr\"),\r\n             format(d_current$`Dritte Runde`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       \r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       \r\n       paste0(\"Wird \", input$teamname, \" \", input$vorrunde, \" \u2013 dann finden die folgenden Spiele so statt:&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       \r\n       paste(\"&lt;b&gt;Achtelfinale&lt;\/b&gt;:\", format(d_current$`Achtel`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Viertelfinale&lt;\/b&gt;:\", format(d_current$`Viertel`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Halbfinale&lt;\/b&gt;:\", format(d_current$`Halb`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Spiel um Platz Drei&lt;\/b&gt;:\", format(d_current$`Platz Drei`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Finale&lt;\/b&gt;:\", format(d_current$`Finale`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;i&gt;&amp;copy; 2018 Benedict Witzenberger&lt;\/i&gt;\")\r\n       )\r\n     \r\n   })\r\n}<\/pre>\n<h3>Beides zusammen<\/h3>\n<p>Ist dann nur noch eine Zeile:<\/p>\n<pre class=\"\">shinyApp(ui = ui, server = server)<\/pre>\n<p>Fertig ist die App. Die n\u00e4chste Frage ist dann: Wo spiele ich sie aus? Grunds\u00e4tzlich gibt es zwei M\u00f6glichkeiten. Ich kann die vorgefertigte Hostingl\u00f6sung von RStudio benutzen \u2013 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\u00fcr muss ich auf meinem Webserver Shiny installieren. RStudio hat daf\u00fcr eine <a href=\"https:\/\/www.rstudio.com\/products\/shiny\/shiny-server\/\" target=\"_blank\" rel=\"noopener\">Open Source-Variante des Shiny Servers<\/a>.<\/p>\n<p>F\u00fcr unseren Fall reicht aber das kostenlose Hosting von RStudio. Direkt im Programm RStudio kann ich die App mit meinem Account dort verkn\u00fcpfen und sie &#8222;deployen&#8220; (hochladen).<\/p>\n<p>Die komplette Shiny-App sieht dann so aus:<\/p>\n<pre class=\"toolbar:2 toolbar-overlay:false lang:r decode:true\">library(shiny)\r\nlibrary(shinythemes)\r\nlibrary(dplyr)\r\n\r\nd &lt;- readRDS(\"Daten_final.rds\")\r\n\r\n# Define UI for application that draws a histogram\r\nui &lt;- shinyUI(fluidPage(theme = shinytheme(\"united\"),\r\n  \r\n  # Application title\r\n  titlePanel(\"WM 2018-Planer\"),\r\n  \r\n  # Sidebar with a slider input for number of bins \r\n  sidebarLayout(\r\n    sidebarPanel(\r\n      selectInput(\"teamname\",\r\n                  \"Team\", choices = unique(d$Deutsch), selected = \"Deutschland\"),\r\n      selectInput(\"vorrunde\",\r\n                  \"Vorrundenergebnis\", choices = unique(d$Status), selected = 1)\r\n    ),\r\n    \r\n    mainPanel(\r\n      helpText(\"Welche Zeiten sollte man sich f\u00fcrs WM-Schauen freihalten?\"),\r\n      htmlOutput(outputId = \"dates\")\r\n    )\r\n  )\r\n))\r\n\r\n\r\n# Define server logic required to draw a histogram\r\nserver &lt;- function(input, output) {\r\n   \r\n   output$dates &lt;- renderUI({\r\n     \r\n     d %&gt;% \r\n       filter(Deutsch == input$teamname &amp; Status == input$vorrunde) -&gt; d_current\r\n     \r\n     \r\n     \r\n     HTML(\r\n       paste0(\"&lt;h2&gt;\", input$teamname, \"&lt;\/h2&gt;\"),\r\n       \r\n       paste0(\"Die &lt;b&gt;Vorrundenspiele&lt;\/b&gt; in Gruppe \", d_current$Group, \" finden statt am: \", \"&lt;br&gt;&lt;br&gt;\"),\r\n       paste(format(d_current$`Erste Runde`, format = \"%a., %d.%m.%y %H:%M Uhr\"), \r\n             format(d_current$`Zweite Runde`, format = \"%a., %d.%m.%y %H:%M Uhr\"),\r\n             format(d_current$`Dritte Runde`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       \r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       \r\n       paste0(\"Wird \", input$teamname, \" \", input$vorrunde, \" \u2013 dann finden die folgenden Spiele so statt:&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       \r\n       paste(\"&lt;b&gt;Achtelfinale&lt;\/b&gt;:\", format(d_current$`Achtel`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Viertelfinale&lt;\/b&gt;:\", format(d_current$`Viertel`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Halbfinale&lt;\/b&gt;:\", format(d_current$`Halb`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Spiel um Platz Drei&lt;\/b&gt;:\", format(d_current$`Platz Drei`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste(\"&lt;b&gt;Finale&lt;\/b&gt;:\", format(d_current$`Finale`, format = \"%a., %d.%m.%y %H:%M Uhr\"), sep = \"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;br&gt;\"),\r\n       paste0(\"&lt;i&gt;&amp;copy; 2018 Benedict Witzenberger&lt;\/i&gt;\")\r\n       )\r\n     \r\n   })\r\n}\r\n\r\n# Run the application \r\nshinyApp(ui = ui, server = server)<\/pre>\n<p>&nbsp;<\/p>\n<p>Und so sieht das Ergebnis aus: <a href=\"https:\/\/munichrocker.shinyapps.io\/WM-Planer\/\" target=\"_blank\" rel=\"noopener\">Der WM-Planer<\/a> (Das Laden dauert einen Moment)<\/p>\n<p><a href=\"https:\/\/munichrocker.shinyapps.io\/WM-Planer\/\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-349\" src=\"https:\/\/benedict-witzenberger.de\/wordpress\/wp-content\/uploads\/2018\/05\/wm-planer-screenshot.png\" rel='magnific' alt=\"\" width=\"1200\" height=\"646\" srcset=\"https:\/\/benedict-witzenberger.de\/wordpress\/wp-content\/uploads\/2018\/05\/wm-planer-screenshot.png 1200w, https:\/\/benedict-witzenberger.de\/wordpress\/wp-content\/uploads\/2018\/05\/wm-planer-screenshot-300x162.png 300w, https:\/\/benedict-witzenberger.de\/wordpress\/wp-content\/uploads\/2018\/05\/wm-planer-screenshot-768x413.png 768w, https:\/\/benedict-witzenberger.de\/wordpress\/wp-content\/uploads\/2018\/05\/wm-planer-screenshot-1024x551.png 1024w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit &#8222;Shiny&#8220; klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.<\/p>\n","protected":false},"author":1,"featured_media":325,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[8],"tags":[33,15,32],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Shiny statt Javascript &bull; Benedict Witzenberger<\/title>\n<meta name=\"description\" content=\"Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit &quot;Shiny&quot; klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Shiny statt Javascript &bull; Benedict Witzenberger\" \/>\n<meta property=\"og:description\" content=\"Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit &quot;Shiny&quot; klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/\" \/>\n<meta property=\"og:site_name\" content=\"Benedict Witzenberger\" \/>\n<meta property=\"article:published_time\" content=\"2018-05-27T07:01:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-05-26T18:57:26+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/benedict-witzenberger.de\/wordpress\/wp-content\/uploads\/2018\/05\/shinyApp-Titelbild.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1244\" \/>\n\t<meta property=\"og:image:height\" content=\"1244\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Benedict Witzenberger\" \/>\n<meta name=\"twitter:card\" content=\"summary\" \/>\n<meta name=\"twitter:creator\" content=\"@munichrocker\" \/>\n<meta name=\"twitter:site\" content=\"@munichrocker\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Benedict Witzenberger\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"12\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#website\",\"url\":\"https:\/\/benedict-witzenberger.de\/wordpress\/\",\"name\":\"Benedict Witzenberger\",\"description\":\"#ddj-Blog and personal portfolio page\",\"publisher\":{\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/benedict-witzenberger.de\/wordpress\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"de\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/\",\"url\":\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/\",\"name\":\"Shiny statt Javascript &bull; Benedict Witzenberger\",\"isPartOf\":{\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#website\"},\"datePublished\":\"2018-05-27T07:01:30+00:00\",\"dateModified\":\"2018-05-26T18:57:26+00:00\",\"description\":\"Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit \\\"Shiny\\\" klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.\",\"breadcrumb\":{\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Startseite\",\"item\":\"https:\/\/benedict-witzenberger.de\/wordpress\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Shiny statt Javascript\"}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/\"},\"author\":{\"name\":\"Benedict Witzenberger\",\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae\"},\"headline\":\"Shiny statt Javascript\",\"datePublished\":\"2018-05-27T07:01:30+00:00\",\"dateModified\":\"2018-05-26T18:57:26+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/\"},\"wordCount\":1463,\"publisher\":{\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae\"},\"keywords\":[\"Fu\u00dfball-WM\",\"R\",\"Shiny\"],\"articleSection\":[\"Werkstatt\"],\"inLanguage\":\"de\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae\",\"name\":\"Benedict Witzenberger\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a0a634cefbade1a34310db77ccc3fe5d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a0a634cefbade1a34310db77ccc3fe5d?s=96&d=mm&r=g\",\"caption\":\"Benedict Witzenberger\"},\"logo\":{\"@id\":\"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/image\/\"},\"url\":\"https:\/\/benedict-witzenberger.de\/wordpress\/author\/benedict\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Shiny statt Javascript &bull; Benedict Witzenberger","description":"Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit \"Shiny\" klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/","og_locale":"de_DE","og_type":"article","og_title":"Shiny statt Javascript &bull; Benedict Witzenberger","og_description":"Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit \"Shiny\" klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.","og_url":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/","og_site_name":"Benedict Witzenberger","article_published_time":"2018-05-27T07:01:30+00:00","article_modified_time":"2018-05-26T18:57:26+00:00","og_image":[{"width":1244,"height":1244,"url":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-content\/uploads\/2018\/05\/shinyApp-Titelbild.png","type":"image\/png"}],"author":"Benedict Witzenberger","twitter_card":"summary","twitter_creator":"@munichrocker","twitter_site":"@munichrocker","twitter_misc":{"Verfasst von":"Benedict Witzenberger","Gesch\u00e4tzte Lesezeit":"12\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebSite","@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#website","url":"https:\/\/benedict-witzenberger.de\/wordpress\/","name":"Benedict Witzenberger","description":"#ddj-Blog and personal portfolio page","publisher":{"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/benedict-witzenberger.de\/wordpress\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"de"},{"@type":"WebPage","@id":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/","url":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/","name":"Shiny statt Javascript &bull; Benedict Witzenberger","isPartOf":{"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#website"},"datePublished":"2018-05-27T07:01:30+00:00","dateModified":"2018-05-26T18:57:26+00:00","description":"Mit Javascript wird das Internet interaktiv. Das kann auch bei Datenanalysen wichtig sein. Mit \"Shiny\" klappt sowas auch in R \u2013 ganz ohne Javascriptkenntnisse.","breadcrumb":{"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Startseite","item":"https:\/\/benedict-witzenberger.de\/wordpress\/"},{"@type":"ListItem","position":2,"name":"Shiny statt Javascript"}]},{"@type":"Article","@id":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/#article","isPartOf":{"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/"},"author":{"name":"Benedict Witzenberger","@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae"},"headline":"Shiny statt Javascript","datePublished":"2018-05-27T07:01:30+00:00","dateModified":"2018-05-26T18:57:26+00:00","mainEntityOfPage":{"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/2018\/05\/27\/r-shiny-statt-javascript\/"},"wordCount":1463,"publisher":{"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae"},"keywords":["Fu\u00dfball-WM","R","Shiny"],"articleSection":["Werkstatt"],"inLanguage":"de"},{"@type":["Person","Organization"],"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/ec6b0c6e16ce10af4a73b4dafefeceae","name":"Benedict Witzenberger","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/a0a634cefbade1a34310db77ccc3fe5d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a0a634cefbade1a34310db77ccc3fe5d?s=96&d=mm&r=g","caption":"Benedict Witzenberger"},"logo":{"@id":"https:\/\/benedict-witzenberger.de\/wordpress\/#\/schema\/person\/image\/"},"url":"https:\/\/benedict-witzenberger.de\/wordpress\/author\/benedict\/"}]}},"_links":{"self":[{"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/posts\/321"}],"collection":[{"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/comments?post=321"}],"version-history":[{"count":10,"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/posts\/321\/revisions"}],"predecessor-version":[{"id":363,"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/posts\/321\/revisions\/363"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/media\/325"}],"wp:attachment":[{"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/media?parent=321"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/categories?post=321"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benedict-witzenberger.de\/wordpress\/wp-json\/wp\/v2\/tags?post=321"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}