Einführung in R: Datentypen und Funktionen

, , 2021

```{r setup, include=FALSE} ## include this at top of your RMarkdown file for pretty output ## make sure to have the printr package installed: install.packages('printr') knitr::opts_chunk$set(echo = TRUE, results = TRUE, message = FALSE, warning = FALSE) #library(printr) ``` # 1. Einleitung Im ersten Tutorial habt ihr erste Einblicke in fortgeschrittene Funktionen und spezialisierte Pakete zur Durchführung von Textanalysen bekommen. Es gibt weitere unzählige Pakete, die zur Lösung von ebenso vielen Problemen oder zur Vereinfachung des täglichen *work flows* programmiert wurden. Die meisten Pakete enhalten detallierte Anleitungen, so genannte Vignetten, die euch Schritt für Schritt zeigen, wie die Pakete zu benutzen sind (siehe z.B. die Vignette zu quanteda: , oder die drei Vignetten zu ggplot2: ). Es gibt aber auch Situationen, in denen keines der existierenden Pakete eine geeignete Lösung für ein spezielles Problem bietet (z.B. wenn ihr spezielle Daten für eure Analysen bereinigen und aufbereiten müsst). Es ist daher wichtig, dass ihr die Grundlagen von `R` kennenlernt. In diesem zweiten Teil unserer Einführung in `R` lernt ihr die grundlegenden Datentypen, Datenstrukturen und die Logik von Funktionen kennen. Zur praktischen Übung der hier diskutierten Aspekte findet ihr Aufgaben auf [meiner Homepage](https://phimeyer.github.io/teaching/QTA_IPW). # 2. Datentypen Einzelwerte können verschiedene Datentypen einnehmen. Die grundlegendsten Datentypen in `R` sind: * numerisch - *numeric* (Zahlen) * Buchstaben - *character* (Text) * Faktoren - *factors* (kategorische Daten, die die Einteilung in mehrere Klassen ermöglichen) * logisch - *logic* (Wahr oder Falsch, Größer oder Kleiner, Gleich oder Ungleich) Darüber hinaus gibt es spezielle Typen z.B. für Datums-/Uhrzeitwerte: * Datum (Kalenderdaten) oder POSIXlt (Kalenderdaten und Uhrzeiten) **Achtung**: Im folgenden werde ich die englischen Begriffe verwenden. Das wird euch die Suche nach Problemlösungen im Internet deutlich erleichtern. ### 2.1. Numeric Zahlen. Nicht mehr und nicht weniger. Zahlen sind vor allem für mathematische Prozesse notwendig. Hier ein paar Beispiele: ```{r eval=T} x <- 51 # ordne eine Nummer dem Namen x zu class(x) # kontrolliere den Datentyp von x x + 3 x / 2 log(x) # Logarithmus von x sqrt(x) # Wurzel von x ``` ### 2.2. Character Der Character-Datentyp umfasst Buchstaben, Wörter, Sätze oder ganze Texte (Bücher, Aufsätze, Gerichtsurteile, Pressemitteilungen, Gesetzestexte, Parteiprogramme you name it). ```{r eval=T} x <- "Politikwissenschaft in Hannover" # Ordne Wörter dem Namen x zu class(x) # kontrolliere den Datentyp/die Klasse von x ``` Es wichtig, dass ihr die Datentypen mit denen ihr arbeitet immer kontrolliert. Falsch zugeordnete Datentypen können eine Analyse um Stunden verlängeren. Im obigen Beispiel ist x der Name, dem der Text "Politikwissenschaft in Hannover" zugeordnet wurde. `R` erkennt character-values erst als solche an, wenn die Eingabe mit Anführungszeichen angegeben wurde. ```{r eval=T} x # der Wert der dem Namen x zugeordnet wurde "x" # der Text/der Buchstabe "x" # Bitte versucht man den folgenden Code in eurer Konsole durchzuführen: x <- Politikwissenschaft in Hannover ``` Mit Textdaten kann man natürlich keine mathematischen Formeln berechnen. Die Kombination eines falschen Datentyps mit einer Funktion führt im Allgemeinen zu einer Fehlermeldung: ```{r eval=F} sum(x) # berechne die Summe von x ``` ```## [1] Error in sum(x): invalid 'type' (character) of argument ``` Spoiler alert: Fehlermeldungen gehören zum "`R`-Feeling"! Ihr werdet deswegen Verzweifeln! Aber keine Angst... `R` ist **open-source** und deswegen hat die `R`-Community für alle Probleme eine Antwort, ihr müsst nur im Internet danach suchen. Es ist wichtig, diese Art von Fehlern zu erkennen, denn, wie gesagt, Fehlermeldungen werden euch euer ganzes `R`-Leben lang begleiten. Möglicherweise wurden Daten importiert, in denen eine Spalte ein Wort enthält, die eigentlich eine Zahl enthalten soll. In diesem Fall betrachtet `R` die Spalte als *character*-Klasse. Achtung: Ihr könnt auch Zahlen als *character* ausdrücken! Dafür verwendet ihr lediglich Anführungszeichen, z.B. "23". Andererseits könnt ihr *numerics* nur als Zahlen und nicht als *character* ausdrücken! Das Umwandeln von einem Datentyp in einen anderen geschieht mit der as.-Methode: ```{r eval=T} x <- "999" x <- as.numeric(x) # transformiert character in numeric x y <- 999 y <- as.character(y) # transformiert numeric in character y z <- "Nein! Das funktioniert nicht" z <- as.numeric(z) # versucht vergeblich character in numeric zu transformiert z ``` ### 2.3. Zusätzliche Datentypen Zusätzliche Hinweise für die Arbeit mit Faktoren (*factor*), logischen Werten (*logic*) und Daten und Uhrzeiten (*date*) findet ihr am Ende dieses Dokuments. # 3. Datenstrukturen In SPSS oder Excel werden Daten immer in einem Datensatz organisiert, wobei die Zellen in Reihen und Spalten angeordnet sind. Typischerweise stellen die Reihen die Fälle/Beaobachtungen dar (z.B. Staaten, TeilnehmerInnen, Zeitungsartikel) und die Spalten repräsentieren die Variablen (z.B. Staatsform, Alter, Datum). Für die meisten Analysen mit `R` ist das auch das empfohlene Datenformat. Hierfür existiert in `R` die `data.frame`-Struktur. Ein wichtiger Unterschied besteht jedoch darin, dass es in `R` möglich und oft auch sinnvoll ist, verschiedene Datenstrukturen/ Datenformate zu kombinieren. Um zu verstehen, wie ein `data.frame` in `R` funktioniert, ist es unerlässlich zu verstehen, dass ein `data.frame` eine Sammlung von Vektoren ist. Aus diesem Grund werden wir jetzt kurz Vektoren behandeln. Anschließend werden wir uns den `data.frames` zuwenden. Zusätzlich werden wir uns auch mit Matrizen und Listen beschäftigen. ### 3.1. Vektor Ein Vektor in `R` ist eine Folge von einem oder mehreren Werten desselben Datentyps. Aus sozialwissenschaftlicher Sicht ist als ein Vektor dem, was wir als Variable bezeichnen, sehr ähnlich. Ihr könnt einen Vektor in `R` mit c(...) benennen, wobei zwischen den Klammern die Werte durch Kommata getrennt werden. Die Anzahl der Werte bestimmt die Länge des Vektors. Ein Vektor kann jeden der genannten Datentypen beinhalten (*numeric*, *character*, *factor*, *logic*, *date*). ```{r eval=T} v1 <- c(1, 2, 10, 15) # ein numericher Vektor mit einer Länge von 4 v2 <- c("a", "b", "b") # character Vektor mit einer Länge von 3 v3 <- 1:10 ## ein numericher Vektor mit einer Länge von 10 mit den Werten von 1 bis 10 (der Doppelpunkt gibt R den Befehl Zahlen im definierten Bereich von X bis X verwenden.) ``` ```{r eval=T} v1 v2 v3 ``` Kombiniert ihr verschiedene Datentypen in einem Vektor, verwendet `R` immer den Typen mit dem kleinsten gemeinsamen Nenner. Das wird als impliziter Zwang (implicit coercion) bezeichnet und `R` folgt grundsätzlich der folgenden Reihenfolge: * logical `->` integer `->` numeric `->` complex `->` character Beispielsweise haben wir vorhin gesehen, dass eine Zahl als *character* ausgedrückt werden kann, ein Text jedoch nicht als *numeric*. Wenn wir also beide Typen in einem Vektor kombinieren, wandelt `R` alle *numerics* in *character* um. ```{r eval=T} v1 <- c(1, 2, "c") # Ein vector mit einer Länge von 3 class(v1) ``` Da Vektoren nur einen Datentyp beinhalten können, können wir mit ihnen auch nur datentypspezifische Operationen durchführen. Wir können mit ihnen also im Grunde auf die gleiche Weise arbeiten wie mit Einzelwerten. Tatsächlich sind Einzelwerte eigentlich nur Vektoren mit einer Länge von 1. Wenn wir zum Beispiel einen *numeric* Vektor haben, der von `R` auch als *numeric* Vektor bezeichnet/erkannt wird, können wir Berechnungen durchführen. Zum Beispiel: ```{r eval=T} x <- c( 1, 2, 3, 4, 5) y <- c(10,20,30,40,50) x + y # für 2 Vektoren mit der selben Länge berechnet R paarweise (1 + 10, 2 + 20, etc.) x + 10 # für einen Vektor und einen Einzelwert wird letzterer immer wieder für die Rechnung verwendet (1 + 10, 2 + 10, etc.) ``` #### 3.1.1. Auswählen von Elementen aus Vektoren Es gibt zwei gängige Methoden, um ein bestimmtes Element oder einen Bereich von Elementen aus einem Vektor auszuwählen. Die eine besteht darin, die Indizes (Positionen) der Elemente in eckigen Klammern hinter dem Vektornamen anzugeben. Beachtet bitte, dass die Indizes selbst als numerischer Vektor angegeben werden: ```{r eval=T} x <- c('a','b','c','d','e','f','g') # ein character Vektor mit einer Länge von 7 x[5] # das fünfte Element auswählen x[c(1,3)] # das erste und dritte Element auswählen x[2:5] # die Elemente zwei bis fünf auswählen ``` Ihr bestimmt die Reihenfolge in der ihr eure Indizes auswählt. Natürlich könnt ihr die Auswahl von Indizes auch wiederholen, was z.B. zum sortieren von Daten nützlich sein kann: ```{r eval=T} x[5:1] # Elemente in den Positionen 5 bis 1 auswählen x[c(5,5,5)] # das fünfte Elemnt mehrmals auswählen ``` Ihr könnt auch eine negative Auswahl treffen, um so alles außer den von euch angegebenen Elementen auszuwählen: ```{r eval=T} x[-5] # jedes Element außer dem fünften auswählen x[-c(1,3)] # jedes Element mit Ausnahme des ersten und dritten auswählen ``` Die zweite Möglichkeit ist die Verwendung eines logischen Vektors. Weitere Informationen über logische Vektoren findet ihr am Ende dieses Dokuments. Hier geht es vor allem darum, zu verstehen, dass ein logischer Vektor nur die Werte FALSE und TRUE hat. Verwenden wir einen logischen Vektor um Werte auszuwählen, dann müssen beide die gleiche Länge haben. Alle Werte, für die der logische Vektor WAHR ist, werden dann ausgewählt. In diesem Beispiel sind die ersten drei Werte WAHR, und so werden die ersten drei Werte aus dem Vektor x ausgewählt ```{r eval=T} x[c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE)] ``` Warum ist das nützlich? Diese spezielle Art Elemente auszwählen werdet ihr wahrscheinlich **nie** nutzen. **Aber** dieses Beispiel soll dabei helfen zu verstehen, wie logische Vergleiche genutzt werden können. Beispielsweise können wir alle Werte in einem Vektor mit einem einzigen Wert vergleichen. Hier erstellen wir einen Vektor mit dem Namen "jahre" und vergleichen die Werte dieses Vektors mit dem Wert 2018. Wir verwenden `==` also logischen Operator. ```{r eval=T} jahre <- c(2017,2034,2018,2020,2012,2025,2030,2014,2033) jahre == 2018 ``` Wir können auch > für einen Größer-als-Vergleich oder >= für einen Größer-Gleich-Vergleich verwenden. ```{r eval=T} jahre > 2018 jahre >= 2018 ``` Jetzt können wir den logischen Vektor (TRUE / FALSE) zur Auswahl von Werten verwenden. ```{r eval=T} auswahl <- jahre >= 2018 # der Vergleich generiert einen logischen Vektor (Größer-Gleich) und speichert ihn im Objekt "auswahl" jahre[auswahl] # wählt Werte mit Hilfe des logischen Vektors in "auswahl" aus ``` Es ist nicht notwendig dem logischen Vektor zunächst einen Namen zu geben (im Beispiel war es der Name "auswahl"). Wir können den Vergleich direkt zur Wertauswahl verwenden. ```{r eval=T} jahre[jahre >= 2018] # selber Effekt, aber effektiver (da weniger Code) ``` ### 3.2. Data.frame / Datensätze Ein `data.frame` ist eine Sammlung von Vektoren mit **gleicher Länge** (!!), die als Spalten miteinander verbunden sind. Um einen `data.frame` zu erstellen, verwenden wir die Funktion `data.frame()`. Wir geben die Vektoren im folgenden Format ein: `column_name = Vektor` **,** `column_name = Vektor` **,** `column_name = Vektor` (die Kommata sind für eine erfolgreiche Erstellung eines Datensatzes in `R` zentral!). Hier erstellen wir einen `data.frame` für Daten aus einem fiktiven Experiment. ```{r eval=T} d <- data.frame(id = 1:10, bedingung = c("E", "E", "C", "C", "C", "E", "E", "E", "C", "C"), geschlecht = c("M", "M", "W", "M", "W", "W", "W", "M", "M", "W"), alter = c( 17, 19, 22, 18, 16, 21, 18, 17, 26, 18), wert_t1 = c(8.0, 6.0, 7.5, 6.8, 8.0, 6.4, 6.0, 3.2, 7.3, 6.8), wert_t2 = c(8.3, 6.4, 7.7, 6.3, 7.5, 6.4, 6.2, 3.6, 7.0, 6.5)) d ``` Die Datenstruktur zeigt, dass es eine Beziehung zwischen den Elementen in den Spaltenvektoren gibt. Mit anderen Worten: jede Reihe stellt einen Fall dar. In unserem Beispiel sind diese Fälle TeilnehmerInnen, und die Spalten repräsentieren: * die Identifikationsnummer für jede_n TeilnehmerIn (*id*) * die Versuchsbedingung (*bedinung*) (E = Experiment, C = Kontrollgruppe) * demographische Variablen: *Geschlecht* und *Alter*. * Testergebnisse zu zwei Zeitpunkten: *wert_t1* und *wert_t2* #### 3.2.1. Auswählen von Reihen, Spalten und Elementen Da `data.frames` sowohl Reihen als auch Spalten haben, können wir beide zur Datenauswahl verwenden. Ähnlich wie bei der Auswahl in Vektoren verwenden wir eckige Klammern. Der Unterschied besteht darin, dass bei `data.frames` die eckigen Klammern aus zwei Teilen bestehen, die durch ein Komma getrennt sind. Syntax | Bedeutung ------ | ------------------------------------------ d[i,j] | Reihe (i) und Spalten (j) auswählen d[i, ] | nur Reihe (i) auswählen aber alle Spalten nutzen d[ ,j] | nur Spalte (j) auswählen aber alle Reihen nutzen Die Auswahl für Reihe (i) und Spalten (j) funktioniert genauso wie die Auswahl von Vektoren. Ihr könnt entwender *numeric* oder *logic* Vektorentypen verwenden. Entsprechend könnt ihr auch Vergleiche verwenden. Darüber hinaus gibt es zwei spezielle Möglichkeiten Spalten zu selektieren. Die eine ist, dass j ein Reihennvektor mit Spaltennamen sein kann. Die andere verwendet das Dollarzeichen (`$`). Syntax | Bedeutung ----------------------------| ------------------------------------------ d[ ,c(“alter”, “wert_t1”)] | Spalten mit den Namen “alter” and “wert_t1” auswählen d$id | die Spalte mit den Namen "id" auswählen #### Die Auswahl von Spalten Schauen wir uns das mal an unserem Beispiel an: ```{r eval=T} # Das Auswählen einer einzelnen Spalte liefert einen Vektor d[,1] # die erste Spalte nach Index auswählen d[,"id"] # die id-Spalte auswählen d$id # mit dem Dollarzeichen die id-Spalte auswählen # Die Auswahl mehrerer Spalten gibt einen data.frame zurück d[,1:2] # die ersten beiden Spalten nach Indizes auswählen d[,c("id","alter")] # die Spalten "id" und "Alter" mit ihren Namen auswählen d[,-1] # jede Spalte außer der ersten auswählen ``` Wie bereits erwähnt, können wir die Auswahl von Spalten auch mittels eines logischen Vektors durchführen. Aber dazu später mehr! #### Die Auswahl von Reihen Das Auswählen von Reihen ist praktisch identisch: ```{r eval=T} d[1:5,] # die ersten fünf Reihen des Datensatzes ``` Ein sehr nützlicher Trick ist, dass man alle Spalten für Vergleiche verwenden kann. So können wir zum Beispiel in der Spalte "Geschlecht" alle Elemente nachschlagen, die den Wert "M" (männlich) haben und diese zur Auswahl von Reihen verwenden: ```{r eval=T} d[d$geschlecht == "M", ] ``` Wir können auch logische Operatoren kombinieren, um so eine Auswahl über mehrere Spalten vorzunehmen. Aber dazu später mehr ;-). Im Moment ist es für euch nur wichtig, dass wir den Operator & (AND) verwenden können. Damit zeigen wir `R` an, dass zwei Vergleiche aufeinmal WAHR sein können (`d$geschlecht == "W" AND d$alter == 21`). ```{r eval=T} d[d$geschlecht == "W" & d$alter == 21, ] # Teilnehmerinnen die 21 Jahre alt sind d[d$wert_t1 < d$wert_t2,] # TeilnehmerInnen die einen höhren Wert zum zweiten Messzeitpunkt erreicht haben ``` #### Auswahl von Reihen **und** Spalten Wir können die Reihen- und Spaltenauswahl kombinieren. Gleiches gilt natürlich auch für die verschiedenen Auswahlmethoden. ```{r eval=T} d[d$geschlecht == "W", "wert_t1"] # "wert_t1" für weibliche Teilnehmerinnen d[d$geschlecht == "W",]$wert_t1 # identisch, aber hier wird im ersten Schritt der Datensatz unterteilt (subset) und dann wird die Reihe ausgewählt d$wert_t1[d$geschlecht == "W"] # identisch, aber erst wird die Spalte ausgewählt und dann der Datensatz unterteilt ``` ### 3.3. Subsetting/Unterteilen, Hinzufügen und Ändern von Daten Mit den Auswahltechniken haben wir jetzt gelernt, wie wir eine Teilmenge der Daten auswählen können (subsetting). Teilmengen können wir natürlich auch eigene Namen zuweisen: ```{r eval=T} experimentelle_gruppe <- d[d$bedingung == "E",] # wir wählen nur die Beobachtungen aus, welche die Versuchsbedingung E aufweisen experimentelle_gruppe demographie <- d[, c('id','geschlecht','alter')] # wir wählen nur die demographischen Variablen in unserem Datensatz aus demographie ``` Wir können jetzt auch weitere Spalten hinzufügen. Wenn es sich um einen einzelnen Wert handelt, wird der Wert für die gesamte Spalte wiederholt. Zum Beispiel fügen wir eine Dummy-Variable für männlich hinzu, die wir zunächst auf 0 setzen. ```{r eval=T} d$männlich = 0 d ``` Wenn wir nun diesen Wert für alle männlichen Teilnehmer auf 1 ändern wollen, können wir einfach die Auswahl verwenden, um diese Spalte nur für männliche Teilnehmer zu erhalten und dann dieser Auswahl den Wert 1 zuweisen. ```{r eval=T} d$männlich[d$geschlecht == "M"] = 1 # wenn im Datensatz (d) die Variable Geschlecht den Wert M aufweist, dann schreibe den Wert 1 in die Variable "männlich" d ``` Wir können Berechnungen mit den Spalten eines Datensatzes durchführen und so die vorhandenen Spalten überschreiben. Nehmen wir zum Beispiel an, dass wir die Werte für die zwei Untersuchungszeitpunkte auf einer Skala von 1 bis 100 haben wollen. Dafür können wir die Spalten einfach mit 10 multiplizieren: ```{r eval=T} d$wert_t1 <- d$wert_t1 * 10 # beachtet bei diesem Befehl, dass wir den Datensatz (d) nennen und die gewünschte Variable (wert_t1 bzw. wert_t2) mittels des Dollarzeichen ansteuern d$wert_t2 <- d$wert_t2 * 10 d ``` Natürlich können wir auch neue Variablen erstellen. Das hat den Vorteil, dass wir die Ursprungswerte für wert_t1 bzw. _t2 behalten können und die transformierten Werte in einer zweiten Variable speichern. ```{r eval=T} d$wert_t1 <- d$wert_t1 / 10 # Wiederherstellung der Ursprungswerte d$wert_t2 <- d$wert_t2 / 10 d$wert_t1_trans <- d$wert_t1 * 10 # in diesem Fall nehmen wir einen neuen Namen für unsere Variable (wert_t1_trans), kopieren die Werte von wert_t1 und multiplizieren diese mit 10 d$wert_t2_trans_trans <- d$wert_t2 * 10 d ``` ### 3.4. Andere gängige Datenstrukturen Es gibt weitere Datenstrukturen, wie z. B. eine Matrix und eine Liste. Pakete können auch neue Klassen zum Organisieren und Manipulieren von Daten bereitstellen, wie z. B. die Dokument-Feature-Matrix (dfm) von Quanteda, welche wir im ersten Tutorial kurz kennengelernt haben. # 4. Funktionen Machen wir jetzt den nächsten Schritt und gehen zu Funktionen mit mehreren Argumenten über. Schauen wir uns als Beispiel die euch bereits bekannte Funktion `dfm()` aus dem quanteda-Paket an. Damit wir auf diese Funktion zugreifen können, müssen wir zunächst `library(quanteda)` ausführen. Damit teilen wir `R` mit, dass das Paket und die dort programmierten Funktionen jetzt nutzen möchten. **Achtung**: Ihr müsst das Paket natürlich vorher installiert haben. Falls das noch nicht geschehen ist oder ihr inzwischen den Computer gewechselt habt, dann müsst ihr zuerst `install.packages("quanteda")` ausführen. Führt jetzt bitte den folgenden Code in eurem `R`-Skript aus. Dann wird sich automatisch unten rechts eine Hilfestellung mit Beschreibungen der Funktion öffnen. ```{r eval=T} library(quanteda) ?dfm ``` Der Titel und die Beschreibung fassen die Aufgaben der Funktion bereits treffend zusammen: "Create a document-feature matrix Description Construct a sparse document-feature matrix, from a character, corpus, tokens, or even other dfm object." Sehen wir uns jetzt den Abschnitt "Usage" an. Hier sehen wir, dass zwischen den Klammern mehrere Argumente angegeben werden. Diese Argumente werden im Abschnitt "Arguments" detalliert erklärt. Diese Beschreibungen findet ihr auch im Internet, zum Beispiel hier: . Beachtet bitte, dass allen Argumenten (mit Ausnahme der Variable x, die euer Input in die Funktion darstellt) ein Wert in der Form `argument = value` zugewiesen wird. Das Argument `tolower` hat den Wert TRUE, `stem` hat den Wert FALSE, usw. Das sind die Standardwerte für diese Argumente, welche solange gelten bis ihr sie persönlich verändert. Auf diese Weise können wir die Funktion `dfm()` mit den Standardeinstellungen verwenden, indem wir nur das Argument x eingeben. ```{r eval=T} beispiel_text = c("Ich studiere", "PoWi in Hannover") dfm(x = beispiel_text) ``` Wenn wir diese zwei Zeilen Code ausführen, werden wir eine Matrix mit den Häufigkeiten der einzelnen Wörter für jeden Text erhalten. Beachtet bitte zwei Sachen. Erstens, `c("Ich studiere", "PoWi in Hannover")` kreiert zwei *character* Vektoren, die quanteda dann als zwei Texte ansieht, Zweitens. Die Wörter "Ich", "PoWi" und "Hannover" werden in der Document-Feature-Matrix kleingeschrieben. Das ist der Fall, weil das Argument `tolower` (dessen Aufgabe mit "convert all features to lowercase" beschrieben wird) standardmäßig TRUE ist. Argumente, die keinen Standardwert haben, wie z. B. x in der Funktion `dfm()`, sind obligatorisch. Wenn ihr jetzt die folgende Codezeile ausführt, dann erhaltet ihr die Fehlermeldung, dass das Argument "x" fehlt und keinen Standardwert hat. ```{r eval=F} dfm() ``` In den meisten Fällen werdet ihr zusätzlich zu den obligatorischen Argumenten noch bestimmte Argumente verändern bzw. angeben um ein gewünschtes Ergebniss zu erzielen. Hierfür gibt es zwei Möglichkeiten: * Ihr verwendet die gleiche Reihenfolge, wie sie auf der Help-Seite (?dfm) angegeben ist * Ihr nutzt die Eigennamen der Argumente (dann ist die Reihenfolge egal, mit Ausnahme von x das immer am Anfang stehen muss) * Möglichkeit 1: ```{r eval=T} dfm(x = beispiel_text, FALSE, TRUE) ``` In der Ausgabe sehen wir die Wörter "Ich", "studier", "PoWi", "in" und "Hannov". "studier" und "Hannov" sehen deswegen so komisch aus, weil wir das Argument `stem` mit einem TRUE versehen haben. Stemming bricht die Wörter auf seine Wortstämme runter, um so die Anzahl der möglichen Wortvarienten mit gleicher Bedeutung zu reduzieren. Was sehen wir noch? Wir sehen das die Wörter Großbuchstaben haben. Das rührt daher, dass wir das `tolower` Argument auf FALSE gesetzt haben. Die Großbuchstaben wurden also nicht mehr in Kleinbuchstaben umgewandelt. Dieser Weg ist anfällig für Fehler und er verringert die Replizierbarkeit eures Codes. Wer merkt sich schon die Reihenfolge von Funktions-Argumenten? Daher ist es empfehlenswert, dass ihr Befehle namentlich angebt. * Möglichkeit 2: ```{r eval=T} dfm(x = beispiel_text, tolower = FALSE, stem= TRUE) ``` Dieser Weg - namentliche Nennung der Argumente - ist expliziter und sicherer. Andererseits kann es aber auch unnötig langatmig sein, alle Argumente anzugeben. Zum Beispiel `x = beispiel_text`. Daher können wir beide Ansätze kombinieren, indem wir die Argumente auf der linken Seite (d. h. die ersten und oft obligatorischen Argumente) der Reihe nach übergeben und die Argumente auf der rechten Seite namentlich benennen. ```{r eval=T} dfm(beispiel_text, tolower = FALSE, stem= TRUE) ``` Welchen Ansatz ihr auch folgt, es ist wichtig das ihr eine gewisse Konsistenz in eurem Code habt. Ich müsst immer im Hinterkopf haben, dass euer Code von anderen gelesen und verstanden werden muss. Eine gute allgemeine Regel ist, obligatorische Argumente (wie x in der dfm-Funktion) ohne Namen zu übergeben, aber alle optionalen Argumente (die eine Voreinstellung haben, wie `tolower` und `stem`) namentlich anzusteuern. Ein zweiter grundsätzlicher Hinweis, der das Verständis eures Codes erheblich vereinfacht, ist, dass ihr alle eure Schritte kurz und bündig kommentiert. Das macht ihr mit dem `#`-Zeichen (`R` erkennt alles nach einem `#` nicht als Code bzw. Befehl an): ```{r eval=T} dfm(beispiel_text, tolower = TRUE, stem= FALSE) # dfm mit bespiel_text bilden; alle buchstaben in kleinbuchstaben verwandeln, kein stemming ``` # 4. Zusätzliche Anweisungen Jetzt wenden wir uns noch einigen zusätzlichen Datentypen, Strukturen und Funktionen zu. Hier wird es jetzt auch etwas komplizierter. Ihr solltet daher vor allem den ersten Teil dieses Tutoriums wirklich gut verstehen. ### 4.1. Zusätzliche Datentypen #### Faktoren Ein Faktor in `R` ist eine Reihe von beschrifteten Zahlen. Das ist vor allem für kategoriale Variablen nützlich, wie z.B. den Bildungsgrad (Fachhochschulreife, Hochschulreife) oder den Medientyp (Wochenzeitung, Tageszeitung). ```{r eval=T} x <- c("Die Zeit", "Die Welt", "die tageszeitung", "Süddeutsche Zeitung" ,"die tageszeitung", "Die Zeit", "Süddeutsche Zeitung") x ``` Wir haben nun eine Folge von *character* Werten. Das Besondere ist, dass sich einige Werte wiederholen. In diesem Fall ist es am besten, wenn ihr euch die einzelnen Werte ("Die Zeit", "Die Welt" etc.) als Etikett vorstellt. Das wird klarer, wenn wir x in einen *factor* transformieren. ```{r eval=T} x <- as.factor(x) x ``` Zwei Dinge haben sich geändert. Erstens sehen wir, dass es jetzt eine Zeile gibt, auf der "Levels: ..." steht, in der die eindeutige Bezeichnungen angezeigt werden. Zweitens, die Anführungszeichen sind verschwunden. Das liegt daran, dass es sich nicht mehr um *character* sondern um *factor* Werte handelt. Das bedeutet: x ist jetzt eine Folge von Zahlen geworden und jede Zahl verweist auf eine Beschriftung. ```{r eval=T} as.numeric(x) # Anzeige der Nummer levels(x) # Anzeige der Level / Label ``` Wenn euch das verwirrt, dann ist das zum jetzigen Zeitpunkt ganz normal! Die Vorteile von Faktoren werden später deutlich. Vor allem wenn ihr damit anfangt, mit bestimmten Arten von Analysen und Visualisierungen zu arbeiten, und wenn ihr sehr große Datensätze verwendet (numerische Werte benötigen weniger Speicher als Zeichenwerte, was der Vorteil von *factor* Werten ist). Es ist auch vollkommen legitim, wenn ihr zu dem Schluss gekommen seid, dass ihr Faktoren lästig findet und zukünftig einfach bei *character* Werten bleiben werdet. Das Problem ist aber, dass `R` die Neigung hat seinen Benutzern *factor* Werte aufzuzwingen. Zum Beispiel denkt `R` beim importieren von externen Datensätzen, dass *character* Spalten mit Volltexten von Gerichtsurteilen besser als *factor* zu interpretieren sind. Das ist natürlich falsch und man den auch selten daran genau das zu kontrollieren, aber es passt leider relativ häufig. Solltet ihr einmal Probleme mit Faktoren haben und wirklich lieber *character* Werte verwenden, könnt ihr einfach den Befehl `as.character()` nutzen: ```{r eval=T} x <- as.character(x) x ``` #### Logische Daten Logische Datentypen haben nur zwei Werte: TRUE und FALSE (die mit T und F abgekürzt werden können). Ihr werdet in euren Daten selten auf logische Werte stoßen, aber ihr werdet logische Operatoren nutzen um eure Daten zu unterteilen und zu transformieren. Ich empfehle euch, dass ihr die folgende Seite besucht, durchlest und mit eigenen Code nachverfolgt: * #### Datum/Uhrzeit Daten gehören nicht zu den grundlegenden Datentypen in `R`. In der sozialwissenschaftlichen Forschung arbeiten wir jedoch oft mit Kalenderdaten und Uhrzeiten. Dies erfordert einen speziellen Datentyp, denn es gibt viele Einschränkungen und Probleme, wenn wir versuchen, Datums- und Zeitangaben als Zeichen- oder numerische Werte auszudrücken. Zwei der gebräuchlichsten Datumsklassen in `basic R` (d. h. ohne zusätzliche Pakete) sind `Date`, das nur Kalenderdaten verarbeitet, und `POSIXlt`, das sowohl Kalenderdaten als auch Uhrzeiten verarbeitet. Da sich der grundlegende Umgang mit den unterschiedlichen Daten- und Zeittypen in `R` ähnelt, fokussieren wir uns im Folgenden auf `POSIXlt`. Generall brauchen wir nur zwei Funktionen um die meisten Daten/Zeit-Aufgaben zu bewältigen (two functions to rule them all): `strptime()` und `strftime()`. * strptime (string parse time) erzeugt einen Datums-/Zeitwert aus einem Zeichenwert * strftime (string format time) extrahiert Teile eines Datums/einer Uhrzeit aus einem Datums-/Zeitwert Im Wesentlichen handelt es sich um einen String (*character* Werte). Dieser String enthält das Datumsformat, wobei spezielle Platzhalter verwendet werden um bestimmte Teile des Datums anzugeben. Diese Platzhalter sind immer ein Prozentzeichen `%`, gefolgt von einem Buchstaben. Die am häufigsten verwendeten Platzhalter sind: Platzhalter | Datums Aspekt ------ | ------------------------------------------ %Y | Jahr mit Jahrhundert (2001, 2010) %m | Monat als Dezimalzahl (01, 02,..., 06,..., 12) %d | Tag als Dezimalzahl (01, 02, 03, 04,..., 11, 12) %H | Stunde als Dezimalzahl (00, 01, 03, 04,..., 22, 23, 24) %M | Minute als Dezimalzahl (01, 02, 03, 04,..., 57, 58, 59) %S | Sekunde als Dezimalzahl (01, 02, 03, 04,..., 57, 58, 59) Mit diesen Platzhaltern können wir verschiedene Datumsformate beschreiben. Zum Beispiel können wir mit der Funktion `strptime` ein Datum in einen Datumstyp (POSIXlt) transformieren (*parsen*). ```{r eval=T} strptime('2010-01-01 20:00:00', format = '%Y-%m-%d %H:%M:%S') strptime('01/01/2010', format = '%m/%d/%Y') strptime('2010 any 01 format 01 goes', format = '%Y any %m format %d goes') ``` Mit der Funktion `strftime()` können wir dieselben Formatstrings verwenden, um bestimmte Teile aus einem Datums-/Zeitwert extrahieren. Hier erstellen wir zuerst ein POSIXlt-Datum mit `strptime` und verwenden dann `strftime`, um Teile zu extrahieren. Beachtet das alle Teile als *character* zurückgegeben werden, auch wenn es sich um einzelne Zahlen handelt. ```{r eval=T} x <- strptime('2010-06-01 20:00:00', format = '%Y-%m-%d %H:%M:%S') strftime(x, '%Y') strftime(x, 'Jahr %Y Woche %W') strftime(x, 'Heute ist %A') ## die Sprache der Werktage ist abhängig von euren lokalen Einstellungen in R ``` Eine vollständige Dokumentation aller Möglichkeiten von `strptime` und allen anderen Funktionen erhaltet ihr, wenn ihr ein Fragezeichen vor den Funktionsnamen setzt und als Code ausführt. Die Hilfeseite sollte sich im Fenster unten rechts in `RStudio` öffnen. ```{r eval=F} ?strptime ?strftime ``` Wenn ihr weiter mit Datums- und Zeitoperationen rumspielen wollt, dann empfehle ich euch das Paket `lubridate`. Die Verwendung wird in diesem (Online-) Kapitel in "R for Data Science" ausführlich erklärt: . Und natürlich gibt es auch ein Cheat Sheet (wie für sehr viele andere Pakete, ihr müsst nur danach suchen): #### Vergleiche Am häufigsten werdet ihr in `R` mit logischen Werten konfrontiert, wenn ihr einen Vergleich zwischen zwei Werten oder Vektoren anstellt. Es gibt 6 Arten von operator-basierenden Vergleichen: Operator | Bedeutung ------ | ------------------------------------------ x < y | x ist kleiner als y x > y | x ist größer als y x <= y | x ist kleiner oder gleich y x >= y | x ist größer als oder gleich y x == y | x ist gleich y x != y | x ist nicht gleich y Das Ergebnis eines Vergleiches zweier Werte ist ein logischer Wert ```{r eval=T} 5 < 10 5 < 2 ``` Das Ergebnis eines Vergleichs von zwei Vektoren ist ein logischer Vektor. Nehmen wir zum Beispiel an, dass wir die durchschnittlichen Mathematiknoten von 5 SchülerInnen im Jahr 1 und im Jahr 2 haben. Wir wollen kontrollieren, ob ihre Noten gesunken sind (d. h. die Noten im Jahr 1 war höher als im Jahr 2). Hier sehen wir, dass nur für den/die erste_n SchülerIn die Note gesunken ist (von 6 in Jahr 1 auf 5 in Jahr 2), also ist nur der erste Wert WAHR. ```{r eval=T} note_jahr_1 <- c(6,6,7,7,4) note_jahr_2 <- c(5,6,8,7,6) note_jahr_1 > note_jahr_2 ``` Wenn wir einen Vektor mit einem einzelnen Wert vergleichen, wird jeder Wert des Vektors mit diesem Wert verglichen. Das ist eine Kernmechanik hinter der Auswahl und Unterteilung von Daten. Im folgenden Beispiel werden wir nachschlagen, welche TeilnehmerInnen mindestens 18 Jahre alt sind. ```{r eval=T} jahr <- c(17,34,12,20,12,25,30,14,33) jahr >= 18 ``` Schließlich gibt es noch einen weiteren Operator, der einen logischen Wert oder Vektor erzeugt, obwohl es sich nicht um einen Vergleich im eigentlichen Sinne handelt. Operator | Bedeutung ------ | ------------------------------------------ x %in% y | der/die Wert(e) in x existieren auch in y Dies ist zum Beispiel nützlich, wenn wir nach mehreren Werten suchen wollen. Zum Beispiel suchen hier hier nach Philipp und Christoph in unserer Namensliste. ```{r eval=T} name <- c("Anton","Marie","Philipp","Katrin","Christoph") name %in% c("Philipp", "Christoph") ``` #### Logische Operatoren Bei einem logischen Wert (oder Vektor) können wir die logischen Operatoren & (AND), | (OR) und ! (NOT) nutzen. In der folgenden Tabelle sind x und y beides logische Werte. Operator | Bedeutung | Ergebnis ------ | ------------------------------------------ | x & y | x AND y | nur WAHR, wenn sowohl x als auch y WAHR sind x I y | x OR y | x ODER Y sind WAHR !x | NOT x | das Gegenteil von x, also nur WAHR, wenn x FALSCH ist. Bitte kopiert euch den folgenden Code und schaut welches Ergebnis `R` nach der Ausführung ausgibt: ```{r eval=F} TRUE & TRUE TRUE & FALSE FALSE & FALSE TRUE | TRUE TRUE | FALSE FALSE | FALSE !TRUE !FALSE TRUE & !FALSE TRUE & (!TRUE | TRUE) a <- c(T, T, F, F, F, T) b <- c(T, F, F, F, T, T) a & b ``` # 5. Methoden und generische Funktionen ### Ein Hinweis zu 'Methoden' und 'generischen Funktionen' Einige Funktionen sind generische Funktionen, die abhängig von der Eingabe, mit der sie verwendet werden, unterschiedliche Methoden nutzen. Abgesehen von den technischen Details gibt es eine zentrale Sache, die ihr mit eurem aktuellen Wissensstand wissen solltet. Eine Methode ist eine Funktion, die mit einem bestimmten Objekt verbunden ist. Zum Beispiel funktioniert das *subsetting* (Unterteilen/Einteilen) eines Vektors anders als das *subsetting* eines `data.frame` oder einer Matrice. Trotzdem braucht ihr nur eine Funktion namens `subset()`, damit ihr alle Datentypen *subsetten* könnt. In `R` ist die Funktion `subset()` daher eine generische Funktion, die sich je nach Art der Eingabe unterschiedlich verhält. Die Art der Eingabe an die subset()-Funktion bestimmt also auch, welche Art von Argumenten verwendet werden kann. Mehr dazu verrät die Dokumentation. ```{r eval=F} ?subset ``` In der Beschreibung sehen wir, dass `subset()` auf Vektoren, Matrizen oder `data.frames` angewendet werden kann. Der Abschnitt "Usage" enthält daher verschiedene Versionen, für verschiedene S3-Methoden (ignoriert "S3" vorerst), die mit verschiedenen Arten von Eingaben verbunden sind. Die allgemeine Form ist `subset(x, ...)`, was zeigt, dass subset immer ein Argument x benötigt, und bei den Argumenten sehen wir, dass x das "object to be subsetted" ist. Wir sehen dann drei Methoden: default, 'matrix' und 'data.frame'. * Default wird verwendet, wenn x weder eine Matrice noch ein `data.frame` ist (z. B. ein Vektor). In diesem Fall ist das einzige Argument die Teilmenge, also der Ausdruck (z. B. x > 10), mit dem eine Auswahl getroffen wird. * Wenn die Eingabe eine "matrice" (Matrix) ist, gibt es zwei zusätzliche Argumente: select und drop. Es macht Sinn, dass diese für Vektoren nicht zur Verfügung stehen, da beide nur relevant sind, wenn es mehrere Spalten gibt. select wird für die Auswahl von Spalten verwendet, und drop kann verwendet werden, damit `subset()` einen Vektor (statt einer Matrice) zurückgibt, wenn nach dem subsetting nur eine Zeile oder Spalte übrig bleibt. * Wenn die Eingabe ein `data.frame` ist, werden die gleichen Argumente wie für 'matrix' verwendet (aber intern arbeitet die Methode anders) # 6. Weitere Lektüre Wenn ihr mehr über die Grundlagen von `R` erfahren wollt, dann empfehle ich: * Das (kostenlose Online-)Buch R for Data Science: * Die Teilnahme an einer der vielen (kostenlosen) Online-Einführungen, wie sie z. B. von DataCamp () oder Code School () angeboten werden. Außerdem kann es sinnvoll sein, sich einige der Cheat Sheets zu besorgen, die auf der RStudio-Website gesammelt sind. Für die Grundlagen von `R` ist das Cheat Sheet zu `Base R` besonders nützlich (). # 7. Hinweis Bitte vergesst nicht die Aufgaben zu machen, die ich euch als `R`-Skript mit dem namen `excercises_basicR_commands.R` hochgeladen habe.