Tipps
Vorwarnung
Achtung! Im Folgenden erhältst du ein paar Tipps, die dir bei der Problemlösung zu den jeweiligen Aufgaben helfen sollen. Diese können auch Teile der Lösung enthalten, benutze sie daher nur dann, wenn du bei einer Aufgabe nicht mehr weiterkommen solltest!
Tipps zu Teil 1: Darstellung der Suchanfragen verschiedener Parteien
Damit klar ist, wann du die Tipps zu diesem Teil des Projekts einsetzen kannst, sind diese in vier Abschnitte unterteilt. Diese Abschnitte werden im Folgenden auch in den Lösungen verwendet. Aus diesem Grund empfehlen wir, dass du beim Bearbeiten des Projekts nach den folgenden Schritten vorgehst, wenn du mit den Tipps arbeiten möchtest. Dabei kann es jedoch vorkommen, dass einige Schritte zunächst nicht zu 100% nachvollziehbar sind. Das liegt unter anderem daran, dass viele Schritte des Aufbaus der App nicht von Anfang an bedacht werden können, sodass sie in einem natürlichen Arbeitsprozess etwas ungeordneter vorkommen würden. Die Schilderung der Erarbeitung dieses Projekts in natürlicher Reihenfolge macht jedoch wenig Sinn, da es in den Tipps und den Lösungen einer gewissen Struktur der Arbeitsschritte bedarf. Um also jegliches Chaos zu vermeiden, solltest du dir schon vor der Verwendung der Tipps Gedanken darüber machen, was für die einzelnen Anforderungen in der Problemstellung alles benötigt werden könnte und wie diese Anforderungen im Einzelnen umzusetzen wären.
Mein Vorschlag für ein strukturiertes Vorgehen:
- Bereite die beiden Datensätze
p1_wide
undp1_long
so auf, dass sie zur Verwendung in den Diagrammen geeignet sind. - Erstelle die einzelnen Diagramme, die in der Problemstellung gefordert sind.
- Erstelle das User Interface (
ui
); den Teil der App, der für die Anwender:innen sichtbar sein wird. - Erstelle den Server (
server
), der die Hintergrundprozesse der App beinhaltet und reaktive Inhalte erzeugt.
Zu jedem dieser Schritte wirst du im Folgenden einen Tipp erhalten, in dem die inhaltlichen Anforderungen explizit ausgeführt werden.
Tipp 1 - Aufbereitung der Daten
Sobald man die beiden Datensätze p1_wide
und p1_long
heruntergeladen hat, sollte man sich deren Aufbau anschauen. Der relevanteste Unterschied beider Datensätze besteht darin, dass die Prozentangaben der einzelnen Parteien bei p1_long
in einer Spalte vorliegen, während bei p1_wide
für jede Partei eine eigene Spalte vorliegt. Dadurch ergibt sich eine nötige Erweiterung des Datensatzes für die Erstellung der Diagramme: Bei p1_long
musst du eine neue Variable mit den kumulierten Prozentangaben für die einzelnen Parteien erstellen. Dafür kannst du dir mal die tapply
-Funktion anschauen.
R
erkennt die Datumsangaben nicht als solche an. Um das zu erreichen, muss man jeweils eine neue Datumsvariable erstellen, in der das Datum von der Klasse factor
in die Klasse POSIXct
umgewandelt wird. Da es in diesem Projekt schwerpunktmäßig nicht um die Arbeit mit Datensätzen geht, kannst du diese Umwandlungen aus den Lösungen von Projekt 1 übernehmen.Tipp 2 - Erstellung der Diagramme
Auch bei der Erstellung der Diagramme kannst du große Teile aus Projekt 1 übernehmen. Die Diagramme 1 und 2 können beinahe unverändert aus den Lösungen von Projekt 1 übernommen werden. Nur die Diagramme 3 und 4 (das Balkendiagramm und das Kuchendiagramm) musst du neu erstellen. Verwende dafür der Einfachheit halber den Datensatzp1_wide
in Verbindung mit ggplot2
. Falls du damit nicht zurechtkommen solltest, kannst du auch mit den beiden Funktionen barplot
und pie
arbeiten; diese weisen jedoch gewisse Nachteile auf.Tipp 3 - User Interface
Überlege dir, welche Input-Fenster für die App benötigt werden. Zur Hilfe kannst du hier eine kleine Übersicht der Input-Funktionen sehen:
In jedem Fall benötigst du Inputs für einfachen Text, eine Datumsspanne, Checkboxen und einen Button.
Ein weiterer wichtiger Punkt für das User Interface ist die sogenannte navbarPage
. Innerhalb dieser Funktion bestimmst du mit einzelnen tabsetPanel
-Befehlen die Inhalte der verschiedenen Unterseiten. Schau dir dafür einfach die Hilfefunktion in R
an oder suche im Internet nach weiterer Hilfe.
Zusatz: Sofern du alle Bestandteile im Einzelnen erstellen konntest, besteht nun die Frage, wie diese möglichst ästhetisch angeordnet und formatiert werden können. Da shiny
auf Basis von HTML funktioniert, können dir hier auch Kenntnisse von CSS (Cascading Style Sheets - " gestufte Gestaltungsbögen") weiterhelfen. Schau dir dafür gerne auch Tutorials zu CSS an oder suche explizit nach der Umsetzung bestimmter Gestaltungsideen.
An einigen Stellen im User Interface kannst du verschiedenste Gestaltungsvorschriften einsetzen, deren Gültigkeit vom gesamten Dokument bis auf die kleinste Einheit reichen kann. Derart lassen sich verschieden spezifische Optimierungen ästhetischer Natur überall im R
-Skript vornehmen. Im Folgenden siehst du zwei beispielhafte Befehle:
##Beispiel 1 - Stellung im Skript relevant
style = "background: #337ab7; color: white;" #bestimmt je nach Stellung im Skript Hintergrundfarbe und Schriftfarbe
##Beispiel 2 - Allgemeine Style-Vorschrift am Anfang des Dokuments
tags$head(
tags$style(".navbar {background-color: #eded00;}", #bestimmt Hintergrundfarbe der "navbar"
".navbar-default .navbar-brand {color: black; background-color: #ffaa00}")) #bestimmt Hintergrundfarbe und Schriftfarbe des Navbar-Titels
Tipp 4 - Server
Beim Server geht es darum, die Outputs der App zu erstellen und dabei die Inputs mit einzubeziehen. Die bereits erstellten Diagramme musst du dafür zunächst in eine render
-Funktion einfügen und dann anhand der zuvor definierten Bezeichnungen im User Interface den richtigen Outputs zuweisen. Eine kurze Erklärung dazu erhältst du hier.
Für das Einbeziehen der Inputs in die Diagramme benötigst du Wissen darüber, wie die eingegebenen Daten in den Input-Variablen vorliegen (abrufen kannst du die Inputs über input$inputId
). Der Titel wird als normaler Text vorliegen, weshalb man input$Titel
einfach an die Stelle des Titels setzen kann. Der Input der Datumsspanne wird aus zwei Daten im Format “yyyy-mm-dd” bestehen (input$Datum[1]
und input$Datum[2]
). Damit R
diese auch als Datum erkennen kann, müssen diese in das POSIXct
-Format umgewandelt werden (das geht einfach mit asPOSIXct
). Dann kann man mit zwei einfachen größer (>)/kleiner (<) Bedingungen die erwünschte Datumsspanne aus dem Datensatz herausfiltern. Hierfür braucht man die reactiveValues
-Funktion zum Erstellen eines reaktiven Datensatzes (als Ausgangspunkt für Veränderungen anhand des Inputs und als Default-Datensatz zur Erstellung der Diagramme beim Öffnen der App) und die observeEvent
-Funktion, damit die Aktualisierung des verwendeten Datensatzes erst nach dem Betätigen des Action-Buttons geschieht. Ist diese Veränderung vorgenommen, muss man auch den in den Diagrammen verwendeten Datensatz verändern (dort muss der reaktive Datensatz eingesetzt werden). Denk dabei daran, dass in den Diagrammen mit zwei Datensätzen gearbeitet wird, weshalb du zwei reaktive Datensätze erstellen musst. Als letztes musst du noch den Input der zu verwendenden Parteien in die Veränderung der reaktiven Datensätze (innerhab der observeEvent
-Funktion) einbeziehen. Innerhalb von input$Parteien
liegen die ausgewählten Parteien als Faktoren vor (z.B. CDU
), weshalb du mithilfe einer is.element
-Bedingung die ausgewählten Parteien aus dem Ursprungsdatensatz herausfiltern kannst.
shinyApp(ui, server)
ausgeführt werden. Doch damit diese App auch formal bereit zum Hochladen wäre, muss man alle anderen ausgeführten Befehle ab dem Download der beiden Datensätze p1_long
und p1_wide
in die server
-Funktion einbeziehen. Das führt dazu, dass beim Ausführen der App alle Veränderungen an den Datensätzen automatisch ein Mal ausgeführt werden. Man könnte denken, dass dies zu Problemen führen könnte, da diese Befehle keine reaktiven Inputs beinhalten. Doch da im server
nur jene Befehle wiederholt ausgeführt werden, die auf Veränderungen in den Inputs reagieren, bedeutet das im Umkehrschluss, dass die Veränderungen in den Datensätzen p1_long
und p1_wide
direkt beim Start der App vorgenommen und nicht wiederholt werden.Tipps zu Teil 2: Sudokus lösen
Auch bei diesem Teil des Projekts wird das Vorgehen beim Erstellen eines Interfaces zum Lösen von Sudokus in mehrere Schritte gegliedert. Es beginnt mit den Vorbereitungen bzw. der Überarbeitung der Sudoku-Ausgabe aus Projekt 5. Der nächste Abschnitt behandelt die Erstellung des User Interfaces und als letztes wird der Server erstellt. Möglicherweise werden dabei im ersten Schritt einige Veränderungen vorgeschlagen, deren Sinn erst im weiteren Verlauf des Projekts klar wird. Lasse dich davon nicht verwirren und versuche dich daran, die vorgeschlagenen Schritte umzusetzen.
Tipp 1 - Vorbereitungen/Sudoku-Ausgabe
Der erste Tipp behandelt einige notwendige sowie auch optionale Vorbereitungen. Dabei geht es in erster Linie um die Anpassung der Sudoku-Ausgabe für die App. Wenn man die Abbildung aus Projekt 5 übernimmt, hat man ein Sudoku-Gitter mit farbig hinterlegten Zahlen und der Überschrift “Sudoku”. Als Abbildung allein ergibt das vielleicht Sinn, doch für die App sollten einige Veränderungen daran vorgenommen werden:
- Die Beschriftung der Achsen sollte wieder hinzugefügt werden.
- Die Legende auf der rechte Seite sollte entfernt werden.
- Das Sudoku benötigt keinen Titel (dieser sitzt in der App an einer anderen Stelle).
- Die Abstände des Sudokus zu den Rändern der Abbildung können mit dem
mar
-Argument in derpar
-Funktion angepasst werden. - Die Größe der Zahlen in den Feldern kann vergrößert werden.
Tipp 2 - User Interface
Dieser Tipp beschäftigt sich mit der Frage “Wie soll die App aufgebaut sein und welche Bestandteile soll diese implizieren?”. Im User Interface muss entschieden werden, mit welchen Inputs das Einsetzen von Zahlen und das Hochladen eines eigenen Sudokus umgesetzt werden sollen. Das Einsetzen von Zahlen gestaltet sich nicht so einfach wie in der analogen Form, bei der man “einfach” eine Zahl in ein Kästchen schreibt. In einer digitalen Form muss angegeben werden, an welcher Position die ausgewählte Zahl eingesetzt werden soll. Aus diesem Grund braucht man hier neben einem Zahlen-Input zwei weitere Zahlen-Inputs für Zeilen und Spaltenangabe jeweils mit einem Wertebereich von 1 bis 9 sowie auch einen Action-Button für das tatsächliche Einsetzen der Zahl. Für das Hochladen eines eigenen Sudokus braucht man darüber hinaus einen Input für Dateien. Damit es dabei zu keinen Problemen kommt, kann man hier auch einen weiteren Action-Button einbauen, der später steuert, ob und wann genau die hochgeladene Datei als Quelle für die Abbildung übernommen werden soll.
Als zweiter Punkt geht es im User Interface darum, die benötigten Inputs ansehnlich anzuordnen, sodass auch eine optisch ansprechende App entsteht. Dafür benutzen wir in diesem Fall das übergeordnete Layout pageWithSidebar
, das aus sidebarPanel
und mainPanel
(+ optional einem titlePanel
) besteht. Darüber hinaus kann die App auch durch weitere Style-Vorgaben verschönert werden, die man mithilfe der Formatierungssprache CSS in R
einbinden kann. Beispiele dafür siehst du hier:
##Beispiel 1 - Stellung im Skript relevant
style = "background: #337ab7; color: white;" #bestimmt je nach Stellung im Skript Hintergrundfarbe und Schriftfarbe
##Beispiel 2 - Allgemeine Style-Vorschrift am Anfang des Dokuments
tags$head(
tags$style(".navbar {background-color: #eded00;}", #bestimmt Hintergrundfarbe der "navbar"
".navbar-default .navbar-brand {color: black; background-color: #ffaa00}")) #bestimmt Hintergrundfarbe und Schriftfarbe des Navbar-Titels
Tipp 3 - Server
Schritt 1: Allgemeine Funktionalität
Zunächst braucht man ein Beispiel-Sudoku, das abgebildet wird, wenn die App geöffnet wird und noch kein eigenes Sudoku geladen wurde. Anhand von diesem Sudoku können wir auch die Funktionalität des Einsetzens der Zahlen überprüfen und die Funktion einfügen, die bestimmt, dass vorgegebene Zahlen nicht verändert werden können. Dafür muss man den Datensatz (also das Sudoku) laden und als reaktiven Datensatz abspeichern, indem man die reactiveValues
-Funktion verwendet. Nun liegt das Sudoku im ursprünglichen Objekt (z.B. BeispielSudoku
) und im reaktiven Objekt (z.B. rv$data
) vor, anhand dessen nun die Abbildung erstellt werden soll.
Da erst auf Knopfdruck eine Zahl an die gewählte Position gesetzt werden soll, nutzt man nun eine observeEvent
-Funktion, um das reaktive Objekt (z.B. rv$data
) und die einzusetzende Zahl an der gewählten Position zu erweitern. Diese Zahl soll jedoch nur dann an der gewählten Position eingesetzt werden können, wenn an der gewählten Position im Ausgangssudoku (z.B. BeispielSudoku
) keine vorgegebene Zahl steht. Das kann man mithilfe einer if
-Funktion implizieren, da über den gesamten Lösungsprozess hinweg das Ausgangssudoku (BeispielSudoku
) nicht verändert wird und deshalb als Objekt für den Abgleich fungieren kann.
Schritt 2: Implikation der Upload-Funktion
Möchte man jedoch sein eigenes Sudoku in diesem Interface lösen, muss das Ganze erweitert werden. Unter anderem braucht man eine weitere observeEvent
-Funktion, um das hochgeladene Sudoku in die Abbildung zu übertragen. Dafür muss man sich jedoch den Inhalt des dazugehörigen Inputs genauer anschauen. Wie die Input-Variable aufgebaut ist, kannst du hier nachschauen. Es zeigt sich, dass die Input-Variable unter input$inputId$datapath
den Dateipfad des geladenen Sudokus abspeichert, mithilfe dessen man im Folgenden das Sudoku in die Sitzung laden kann (einfach Sudoku <- readRDS(input$inputId$datapath)
). Ebenso wie beim Beispiel-Sudoku benötigt man dieses geladene Sudoku nun einmal als “festes” Objekt für den Abgleich mit dem Ausgangssudoku und einmal als reaktives Objekt (in rv$data
) für die Erstellung der Abbildung. Nun muss man mit if
-Funktionen festlegen, wie das Einsetzen von Zahlen erfolgen soll, wenn kein Sudoku hochgeladen wurde, und wie es ablaufen soll, wenn ein eigenes Sudoku hochgeladen wurde.
server
-Funktion einfügen. Dadurch werden die Veränderungen automatisch einmal beim Start der App ausgeführt und für die gesamte Sitzung beibehalten.