Dieses Buch ist immer noch "Work in Progress", aber eines Tages soll es "das perfekte R Buch für blutige Anfänger" werden.
Dieses Buch ist für all diejenigen, die blutige Anfänger in Programmierung oder Data Science sind. Bis zum Ende des Buches werden dir all diese Punkte näher gebracht:
- Programmieren
- Erstellung von hübschen und einfach verständlichen Statistiken
- 3D Grafen und Statistiken mit Animationen
- Verschiedene Methoden der Analyse von schriftlichen Werken
Falls du ein blutiger Anfänger sein solltest, dann lohnt es sich über das Kapitel Grundlagen zu gehen, sonst nutze dieses Buch gerne als Nachschlagewerk.
Ziel dieses Buches ist es, ein Verständnis dafür zu entwickeln, wie wir mit R Texte analysieren um wertvolle Erkenntnisse aus großen Textmengen zu extrahieren. Wir werden verschiedene Techniken und Methoden kennenlernen, die in der Textanalyse angewendet werden, um Muster und Zusammenhänge zu erkennen.
Warum R?
R wurde ursprünglich von Statistikern für statistische Analysen entwickelt und bleibt bis heute die bevorzugte Wahl vieler Statistiker.
Während es nicht die beste Programmiersprache ist, um Systeme oder Applikationen zu erstellen, erleichtert die Syntax von R das Erstellen komplexer statistischer Modelle mit nur wenigen Codezeilen und ist auch perfekt optimiert für den Data Science Bereich, außerdem kann man viel erreichen ohne der beste Programmierer sein zu müssen.
Warum nicht Python?
Pyhton gewinnt in den letzten Jahren mehr und mehr Popularität in Data Science. Python ist eine "multi purpose language". "Multi purpose language" bedeuetet, dass man Python für viele verschiedene Usecases verwenden kann und es nicht für nur einen Bereich spezialisiert ist, im Gegensatz zu R, welches hauptsächlich nur für Data Science gut geeignet ist.
Der Vorteil von Python ist, dass der Code sehr an der normalen englischen Sprache erinnert und somit einfach verständlich ist, auch für diejenigen, die noch nie programmiert haben.
Dadurch dass man Python auch gut für Web- oder Systementwicklung verwenden kann, lohnt es sich Python zu lernen. Vorallem gewinnt Python immer mehr an Popularität in der Data Science Community.
Der Vorteil von R ist, dass es speziell für Data Science entworfen worden ist. Wenn du Data Science lernen möchtest und tiefer in die Materie einsteigen möchtests, dann ist R perfekt, sonst würde ich schon fast empfehlen, für diejenigen, die keinen guten Grund haben R zu benutzen und in mehr Themen als nur Data Science eintauchen möchten, sich Python anzuschauen.
License
Dieses Werk ist lizensiert unter GNU Free Documentation License Version 1.3, 3 November 2008
Installation
Um in R programmieren zu können, müssen wir zwei Programme installieren. Einmal den R selber und Rstudio.
Rstudio ist eine IDE (Abkürzung für "Integrated development environment"). Du kannst dir eine IDE wie ein Word Programm vorstellen, nur spezialisiert fürs Programmieren. RStudio bietet uns viele Features an, die es uns einfacher machen werden, für R Code zu schreiben.
R installieren:
Um R zu installieren, besuche die R Project Website und lade die aktuelle Version für dein Betriebssystem herunter.
RStudio installieren:
Um RStudio zu installieren, besuche die posit Website und klicke auf den Button "Download RStudio for <DEIN BETRIEBSSYSTEM>".
Einführung in Rstudio
TODO erklaere die UI
TODO erklaere wie man ein Projekt erstellt
TODO erklaere wie man eine Datei erstellt
TODO erklaere wie man diesen Code Snippet in Rstudio ausfuehrt
print("Hello World")
wo speichert man dies?
Einführung in R Interpreter
Was ist ein "Interpreter"?
Ein Interpreter ist ein Programm, das Code liest und ausführt. Stellen dir vor, du liest ein Rezept und führst jeden Schritt sofort aus, anstatt das ganze Rezept zuerst auswendig zu lernen.
Das ist im Grunde, was ein Interpreter macht: Er liest den Code (das Rezept), den ein Programmierer geschrieben hat, Zeile für Zeile (Schritt für Schritt) und führt ihn direkt aus. Er übersetzt den Code in Maschinensprache, während das Programm läuft, sodass der Computer ihn verstehen und ausführen kann.
Im Fall von R ist der Interpreter das, was dein R-Code Zeile für Zeile nimmt und ihn so umsetzt, dass dein Computer verstehen kann, was zu tun ist. Jedes Mal, wenn du einen Befehl in R eingibst, nimmt der Interpreter diesen Befehl, analysiert ihn und führt die entsprechende Aktion aus.
Dies nennt man auch "just in time".
Was ist "JIT"?
JIT steht für "Just-In-Time" und bezeichnet eine Art von Compiler, der versucht, die Geschwindigkeit der Codeausführung zu verbessern. Ein JIT-Compiler übersetzt den Code nicht vor der Ausführung des Programms in Maschinencode (wie ein traditioneller Compiler), sondern genau in dem Moment, wenn der Code ausgeführt werden muss. Dies geschieht "just-in-time", daher der Name.
Der Prozess ist so, als würdest du ein Rezept nicht Schritt für Schritt durchgehen, sondern sich ein paar Schritte merken und diese dann schnell hintereinander ausführen, anstatt sie jedes Mal neu zu lesen. Der JIT-Compiler optimiert den Code, indem er häufig genutzte Teile des Codes im Voraus kompiliert und speichert, so dass sie schneller ausgeführt werden können, wenn sie wieder benötigt werden.
Ein JIT-Compiler kann beispielsweise Teile eines R-Programms, die oft ausgeführt werden, in eine optimierte Form übersetzen und diesen optimierten Code ausführen, anstatt den ursprünglichen R-Code jedes Mal neu zu interpretieren. Dadurch wird die Ausführung des Programms schneller, insbesondere bei wiederholten Berechnungen oder großen Datenmengen.
Variablen
Variablen, den Begriff kennen wir schon von der Mathematik.
Es bedeutet, dass ein Wert variabel ist, also sich verändern kann.
Zum Beispiel die Variable x in y = x + 2 ist ein Platzhalter für einen oder
beliebig vielen Werten.
Auch so sind in der Programmierung Variablen als eine Art "Platzhalter" oder viel mehr "Zwischenspeicher" für einen Wert, den wir selber bestimmen müssen, zu verstehen.
Aber durch Theorie allein kann man nur schwer lernen oder etwas verstehen, also lass uns eine Variable erstellen, die mein jetziges Alter "zwischenspeichert".
my_age <- 25
Ich habe hier eine Varible mit den Namen my_age erstellt
(in Programmierfachchinesisch nennt man das auch "deklarieren").
my_age bekommt den Wert 25 zugewiesen.
Um in R eine Variable zu deklarieren, dürfen wir das <- nicht vergessen,
dadurch weiß R überhaupt, dass my_age eine Variable ist.
Ohne das <- kann der R-Interpreter
unser Code nicht verstehen und
wir würden einen Error bekommen.
Wenn ich jetzt mein Alter wissen möchte
(weil ich es wieder einmal vergessen habe, was mir oft passiert...),
dann kann ich darauf zugreifen, indem
ich in einer späteren Zeile im Skript einfach nur my_age schreibe oder
in der R Konsole my_age mit ENTER eingebe.
my_age <- 25
my_age # printet "25" in der R Konsole
Überall wo wir jetzt my_age brauchen, können wir,
anstelle immer wieder 25 zu schreiben, my_age schreiben.
Der Vorteil ist, wenn wir unser Code anpassen möchte für einen anderen User,
der jünger ist, dann müssen wir nur die Zeile mit my_age <- 25 anpassen.
Die meisten Programmiersprachen nutzen das = Zeichen,
um Variablen zu deklarieren. Natürlich geht das auch in R:
my_age <- 25
my_age = 25 ## '=' ist das selbe wie '<-'
<- und = sind besondere Zeichen, damit R versteht, dass wir eine Variable erstellen wollen.
Machen wir einen Fehler und versuchen eine Variable mit -> zu deklarieren,
dann bekommen wir einen Error in der R-Konsole.
Wie in einer Fremdsprache, wenn wir Fehler in der Grammatik machen, kann es sein, dass man uns nicht verstehen kann. Genau so gibt es auch in Programmiersprachen "Grammatik", damit der Computer weiß, was wir machen möchten. Diese "Grammatik" nennt man in Programmierfachchinesisch auch Syntax.
Datentypen
Neben Zahlen können wir auch Variablen für Text, Listen, Tabellen und viele, viele andere Dinge erstellen.
In diesen Kapitel gehen wir Schritt für Schritt über die wichtigsten Datentypen von R und wie man mit diesen arbeiten kann.
Hier schonmal eine kleine Übersicht von all den Datentypen, die ich Dir näher bringen werde:
| Datentyp | Ein-Wort-Erklärung |
|---|---|
| Number | Zahl |
| Boolean | Wahr oder Falsch |
| Character | Text |
| Vector | Liste an Werten |
| Matrix | zwei-dimensionale Liste |
| List | Multityp-liste |
| Factor | Kategorien |
| DataFrame | Tabelle |
Number
Einer der wohl wichtigsten und offensichtlichsten Datentypen einer Programmiersprache ist die Nummer oder Zahl.
In R heißen Nummern und Zahlen Number.
Im vorherigen Kapitel haben wir die Variable my_age erstellt,
die die "Number" 25 zugewiesen bekommen hat.
Wie in der Mathematik können wir mit Numbers auch rechnen:
Plus +
x <- 5
y <- 3.5
z <- x + y
z ## Console: [1] 8.5
Minus -
x <- 5
y <- 3.5
z <- x - y
z ## Console: [1] 1.5
Mal *
x <- 5
y <- 3.5
z <- x * y
z ## Console: [1] 17.5
Geteilt /
x <- 5
y <- 3.5
z <- x / y
z ## Console: [1] 1.428571
Hoch ^
x <- 5
y <- 3.5
z <- x ^ y
z ## Console: [1] 279.5085
Modulus %%
Der Modulus %% wird verwendet, um den Rest einer Division zu berechnen.
Kannst du dich vielleicht noch erinnern,
wie du in der Grundschule geteilt rechnen gelernt hast und
am Ende der Rechnung den Rest angegeben hast,
z.b. 5 / 2 = 2 Rest 1.
Der Modulus funktioniert genau so, nur dass wir nur den Rest bekommen!
Also 5 %% 2 gibt uns die Zahl 1 oder 11 %% 3 ergibt 2.
Zum Beispiel können wir den Modulus verwenden,
um zu erkennen ob eine Zahl gerade oder ungerade ist,
denn wenn x %% 2 null ergibt, dann ist die Zahl durch zwei teilbar
und wenn es eins ergibt, dann eben nicht.
5 %% 2 ## Console: [1] 1 Die Zahl ist ungerade
8 %% 2 ## Console: [1] 0 Die Zahl ist gerade
Übungsaufgaben
Hier ein paar Übungsaufgaben, die du nun lösen kannst, um deine Programmierskills auf die Probe zustellen!
-
Berechne die Tage anhand des Alters einer Person.
-
Berechne wie viele Sekunden x Stunden hat, wo x eine beliebige Zahl ist.
-
Ermittle anhand des Geburtsjahres wie alt eine Person ist
-
Ermittle den Rest einer Division von zwei Zahlen
-
Berechne den Flächeninhalt eines Dreiecks (a: 5cm, b: 5cm, c: 10cm)
Boolean
Den nächsten Datentyp, den wir lernen werden, ist der Boolean.
Ein Boolean kann nur zwei mögliche Werte haben kann:
"wahr" also TRUE oder "falsch" also FALSE.
Stelle dir einen Boolean wie einen Lichtschalter vor,
der entweder ein- oder ausgeschaltet sein kann.
In diesem Fall ist "eingeschaltet" TRUE und "ausgeschaltet" FALSE.
In der Programmierung sind Booleans nützlich, um Entscheidungen zu treffen oder den Ablauf eines Programms zu steuern. Zum Beispiel kann man Booleans verwendet, um zu bestimmen, ob ein Kunde erlaubt ist von unserem Onlinestore Alkohol zu kaufen oder ob der User sein Programm in Light oder Dark Theme eingestellt hat.
is_light_theme <- TRUE
user_can_buy_alcohol <- FALSE
Anstelle immer TRUE oder FALSE schreiben zu müssen,
können wir es auch abkürzen mit T oder F.
is_r_cool <- T
is_progamming_boring <- F
Es gibt überhaupt kein Unterschied zwischen TRUE und T sowie FALSE und F.
Benutze das, was dir besser gefällt.
Ganz nach dem Motto "Whatever floats your boat!"
Logische Operatoren
Logische Operatoren sind spezielle Symbole, die in der Programmierung verwendet werden, um logische Vergleiche zwischen zwei oder mehreren Ausdrücke zu machen.
Uff, die Erklärung war viel zu kompliziert, nicht wahr?
In anderen Worten: ein Logischer Operator ist wie eine Behauptung, die entweder wahr oder falsch sein kann.
Wenn ich behaupte, dass die Corona Impfung ein Versuch von Bill Gates sei,
die Menschheit zu unterjochen, dann ist die Aussage entweder TRUE oder FALSE.
Obwohl wenn man auf Facebook geht, es für manche Menschen TRUE ist...
Hier eine Tabelle von allen Logischen Operatoren, die es in R gibt. Einige werden dir von der Mathematik bekannt sein:
| Operator | Bedeutung |
|---|---|
| && | und |
| || | oder |
| == | gleich |
| != | nicht gleich |
| ! | nicht |
| < | kleiner als |
| > | größer als |
| <= | kleiner gleich |
| >= | größer gleich |
Aber keine Sorge, ich gehe über jeden Operator und gebe dir ein Beispiel, wie man es benutzen könnte.
&& und
T && F ## Console: [1] FALSE
Der "und"-Operator überprüft ob die beiden Werte, links und rechts, TRUE sind.
Zum Beispiel wir wollen überprüfen ob ein Kunde, der Alkohol kaufen möchte, über 18 ist und einen Ausweis mit sich trägt. Nur wenn beides der Fall ist, können wir ihm im guten Gewissen Alkohol verkaufen:
is_adult <- TRUE
has_passport <- FALSE
is_adult && has_passport ## Console: [1] FALSE
Aber wenn ein Kunde unseren Onlineshop besucht und beides erfüllt, können wir endlich Profit mit unserem Alk machen.
is_adult <- TRUE
has_passport <- TRUE
is_adult && has_passport ## Console: [1] TRUE
|| oder
T || F ## Console: [1] TRUE
Der "oder"-Operator überprüft ob links oder rechts TRUE ist.
Nur eine der beiden Seiten muss TRUE sein, damit die Aussage als TRUE gilt.
Zum Beispiel in einer Dating App möchte ich mit Personen gematched werden, die entweder Hunde oder Katzen mögen.
likes_cats <- TRUE
likes_dogs <- FALSE
likes_cats || likes_dogs ## Console: [1] TRUE
== gleich
T == F ## Console: [1] FALSE
Der "gleich"-Operator überprüft ob die linke Seite identisch mit der rechten Seite ist. Wir können auch Zahlen und alle Datentypen, die wir noch lernen werden, damit vergleichen.
my_age <- 25
your_age <- 28
my_age == your_age ## Console: [1] FALSE
your_age <- 25 ## Überschreibe your_age mit 25
my_age == your_age ## Console: [1] TRUE
!= nicht gleich
T != F ## Console: [1] TRUE
Das selbe wie beim "gleich" können wir auch hier machen, aber umgedreht mit dem "nicht-gleich"-Operator. Dieser checkt ob die linke Seite nicht gleich mit der rechten Seite ist.
my_age <- 25
your_age <- 28
my_age != your_age ## Console: [1] TRUE
your_age <- 25 ## Überschreibe your_age mit 25
my_age != your_age ## Console: [1] FALSE
! nicht
!T ## Console: [1] FALSE
Der "nicht"-Operator tut einen "boolischen" Wert (TRUE oder FALSE) umdrehen.
So wie wir es oben im Beispiel sehen können, wo TRUE mit ! zu FALSE umkonvertiert wird.
Schauen wir uns weitere Beispiel des "nicht"-Operator an!
!TRUE ## Console: [1] FALSE
!T ## Console: [1] FALSE
!FALSE ## Console: [1] TRUE
!F ## Console: [1] TRUE
!42 ## Console: [1] FALSE
!0 ## Console: [1] TRUE
!-42 ## Console: [1] FALSE
Du fragst dich jetzt bestimmt warum !0 TRUE ist und warum !42 sowie !-42 FALSE ist.
In vielen Programmiersprachen wird die Zahl 0 als FALSE behandelt,
während alle anderen Zahlen (sowohl positive als auch negative) als TRUE behandelt werden.
Nun, da alle anderen Zahlen (außer 0) als TRUE gelten, sind sowohl 42 als auch -42 wahr.
Wenn wir den "nicht"-Operator auf 42 und -42 anwenden, kehren wir ihre Werte um. Daher werden sowohl !42 als auch !-42 zu FALSE.
Wir können auch sebstverständlich den "nicht"-Operator an Variablen verwenden.
is_r_cool <- FALSE
!is_r_cool ## Console: [1] TRUE
< kleiner als
42 < 24 ## Console: [1] FALSE
Das "kleiner als"-Symbol wird verwendet, um zwei Werte miteinander zu vergleichen.
Wenn der Wert auf der linken Seite des Symbols kleiner ist als der Wert auf der rechten Seite,
dann ist die Aussage TRUE oder andernfalls FALSE.
> größer als
42 > 24 ## Console: [1] TRUE
Das selbe auch beim "größer als"-Symbol.
<= kleiner gleich
42 <= 24 ## Console: [1] FALSE
Das "kleiner gleich"-Symbol tut nicht nur TRUE geben, wenn die linke Seite kleiner ist als die rechte,
sondern auch wenn die linke Seite gleich der rechten Seite ist.
42 <= 43 ## Console: [1] TRUE
42 <= 42 ## Console: [1] TRUE
42 <= 41 ## Console: [1] FALSE
>= größer gleich
42 >= 24 ## Console: [1] TRUE
Das selbe auch beim "größer gleich"-Symbol.
42 >= 43 ## Console: [1] FALSE
42 >= 42 ## Console: [1] TRUE
42 >= 41 ## Console: [1] TRUE
Im nächsten Kapitel werden wir all diese Operatoren bzw. Symbole verwenden,
um mit Hilfe von if und else in unserem Programm Entscheidungen treffen
zu können.
if und else
Mit if und else können wir innerhalb unseres Programmes Entscheidungen treffen,
um eine Aktion auszuführen, wenn eine bestimmte Bedingung erfüllt ist
und eine andere Aktion, wenn die Bedingung nicht erfüllt ist.
number <- 5
if (number %% 2 == 0) {
print("number is even")
} else {
print("number is odd")
}
Nach dem if-Schlüsselwort folgt eine ()-Klammer. Innerhalb dieser Klammer müssen wir
eine Aussage schreiben, die sich entweder zu TRUE oder FALSE auflöst.
In der {}-Klammer, direkt danach, kommt der Code der ausgeführt wird, wenn die Aussage
TRUE ist. Wenn aber die Aussage FALSE ist wird die nächste {}-Klammer ausgeführt, die
mit else markiert worden ist.
Also nochmal einfacher erklärt mit einem Beispiel:
if (this_is_true) {
do_this()
} else {
do_that()
}
Im Beispiel von ganz oben wird "number is even" in der Console ausgegeben, wenn der Rest von number
geteilt durch zwei null ist, also die Zahl gerade ist und wenn dies nicht der Fall ist,
dann wird "number is odd" ausgegeben.
Wir können auch mehrere if's hintereinander reihen um mehrere Bedingungen Schritt für zu überprüfen.
if (this_is_true) {
do_x()
} else if (or_this_is_true) {
do_y()
} else {
do_z()
}
Lass uns ein Beispiel schreiben, bei dem in der Console eine Nachricht geschrieben wird, wenn die Zahl positiv, negativ oder null ist.
number <- -7
if (number < 0) {
print("Number is negative.")
} else if (number > 0) {
print("Number is positive.")
} else {
print("Number is null.")
}
Als erstes checken wir ob die Zahl kleiner als null ist, dann printen wir "Number is negative" und
wenn die Zahl größer als null ist, dann printen wir "Number is positive".
Wenn beides nicht der Fall ist, also beide Aussagen FALSE sind dann wird der else Block ausgeführt und
logisch können wir ab diesen Punkt von ausgehen, dass die Zahl 0 sein muss!
Wir sind aber nicht verpflichtet ein else zu verwenden am Ende einer if-Kette.
do_x()
if (this_is_true) {
do_y()
}
do_z()
So können wir zum Beispiel erst x machen und wenn die if-Aussage TRUE ist,
dann machen wir auch y, aber wenn es FALSE ist, naja, dann machen wir halt y nicht
und ungeachtet der if Aussage, machen wir z als nächstes.
Übungsaufgaben
-
Hier ein paar Übungsaufgaben mit
ifundelse: -
Teile eine Zahl durch eine andere und prüfe, ob das Ergebnis eine ganze Zahl ist.
-
Überprüfe, ob eine Zahl positiv, negativ oder null ist und gib eine entsprechende Meldung aus.
-
Überprüfe, ob eine Zahl gerade oder ungerade ist
-
Vergleiche zwei Zahlen und gib an, welche größer ist.
-
Überprüfe, ob eine Zahl in einem bestimmten Bereich liegt (z.B. zwischen 10 und 20).
-
Schreibe eine Bedingung, die prüft, ob eine Zahl durch eine andere teilbar ist.
-
Schreibe eine Bedingung, die überprüft, ob eine Zahl ein Vielfaches von 10 ist.
-
Schreibe ein Programm, welches aus einer Punktzahl (0-100) eine Note (1-6) zuweist
-
Erkenne aus 3 Zahlen welche davon die größte ist und welche davon die kleinste ist
-
Empfehle aufgrund des Alters einer Person einen Film
switch
switch funktioniert wie ein Entscheidungsbaum,
der eine bestimmte Aktion aus einer Liste von Optionen ausführt,
basierend auf dem Wert einer Variable oder eines Ausdrucks.
Stell dir vor, du hast eine Box mit verschiedenen Früchten und du möchtest wissen, welche Farbe eine bestimmte Frucht hat. Zum Beispiel du listest die verschiedenen Früchte auf und ordnest ihnen ihre jeweiligen Farben zu, sprich Apfel = "Rot", Banane = "Gelb", Orange = "Orange" und so weiter.
fruit = "banana"
switch(fruit,
"apple" = print("red"),
"banana" = print("yellow"),
"orange" = print("orange"),
"pear" = print("green"))
In diesem Beispiel, wird je nachdem welchen Wert fruit hat, der jeweilige print Befehl ausgeführt.
An erster Stelle der () Klammern des switch Befehls kommt die Variable, die wir "matchen" wollen,
gefolgt von allen Möglichkeiten, die wir abdecken wollen, getrennt mit einem Komma.
Bei jeder Möglichkeit (auch "case" genannt) kommt nach dem = Zeichen weiterer R-Code, der ausgeführt
werden soll.
Wenn fruit in dem Fall einen anderen Wert hat als vom switch abgedeckt wird, dann wird kein Code
ausgeführt. Natürlich können wir auch einen "default case" innerhalb der switch definieren.
grade = "A"
switch(grade,
"A" = print("Great"),
"B" = print("Good"),
"C" = print("Ok"),
"D" = print("Bad"),
"F" = print("Terrible"),
print("No Such Grade"))
Hier tuen wir je nach Note (A-F) den jeweiligen Begriff in die Konsole "printen".
Wenn wir aber jetzt grade den Wert Z geben, wird "No Such Grade" in der
Konsole "geprintet".
switch mit Zahlen
Anstelle von Text lass uns versuchen ein switch Statement mit Zahlen zu schreiben!
grade <- 2
switch(grade,
6 = print("Did you study at all?"),
5 = print("You need to study more"),
4 = print("Well next time will be better"),
3 = print("Good"),
2 = print("Great"),
1 = print("Awesome"),
print("No such grade"))
Ohoh, wenn ich diesen Code ausführe, dann bekomme ich einen Error:
Error: unexpected '=' in:
"switch(grade,
6 ="
> Error: unexpected ',' in " 5 = print("You need to study more"),"
> Error: unexpected ',' in " 4 = print("Well next time will be better"),"
> Error: unexpected ',' in " 3 = print("Good"),"
> Error: unexpected ',' in " 2 = print("Great"),"
> Error: unexpected ',' in " 1 = print("Awesome"),"
> Error: unexpected ')' in " print("No such grade"))"
Wir dürfen die " Zeichen bei den Möglichkeiten nicht vergessen:
grade <- 2
switch(grade,
"6" = print("Did you study at all?"),
"5" = print("You need to study more"),
"4" = print("Well next time will be better"),
"3" = print("Good"),
"2" = print("Great"),
"1" = print("Awesome"),
print("No such grade"))
Sieht gut aus? Dann führen wir den Codeabschnitt mal aus:
[1] "You need to study more"
Wowowow, die Note war 2! Was ist hier passiert?
switch mit Zahlen funktioniert ein bisschen anders.
Anstelle die Variable grade mit den jeweiligen Zweigen zu vergleichen,
um dann den richtigen Code auszuführen, wird der x'te Zweig ausgeführt.
In diesem Fall ist grade 2 also wird der Zweig
"5" = print("You need to study more") ausgeführt.
Die "5" wird dabei von R komplett ignoriert. Wir könnten den selben Code auch so schreiben:
grade <- 6
message <- switch(grade,
"Did you study at all?",
"You need to study more",
"Well next time will be better",
"Good",
"Great",
"Awesome")
print(message)
Zwei Sachen sind hier zu bemerken: Die " und = wurde entfernt und gleichzeitig
auch die print Befehle, sonst würden wir einen Syntaxerror bekommen.
Anstelle dass jetzt mit print Befehl etwas in der R-Konsole geschrieben wird,
wird der jeweilige Text zurückgegeben und in der Variable message gespeichert,
um es dann in der nächsten Zeile in der Konsole zu "printen".
Wir werden das "zurückgeben" noch im Kapitel 7.3 genauer kennen lernen,
keine Sorge, wenn du es noch nicht so ganz verstanden hast, was hier passiert.
Jetzt müssen wir nur noch die Reihenfolge ändern, damit auch der richtige Zweig ausgeführt wird:
grade <- 6
message <- switch(grade,
"Awesome",
"Great",
"Good",
"Well next time will be better",
"You need to study more",
"Did you study at all?")
print(message)
ODER wir könnten die Variable grade mit as.character() zu Text konvertieren,
um den alten obrigen Code zu reparieren:
grade <- 2
switch(as.character(grade),
"6" = print("Did you study at all?"),
"5" = print("You need to study more"),
"4" = print("Well next time will be better"),
"3" = print("Good"),
"2" = print("Great"),
"1" = print("Awesome"),
print("No such grade"))
Ja manchmal können Lösungen auch so einfach sein, aber wir als Programmierer kommen meist nicht immer sofort auf die "beste" Lösung und deswegen ist irgendeine Lösung manchmal besser als sich stundenlang den Kopf zu zerbrechen.
Übungsaufgaben
Hier ein paar Übungsaufgaben mit switch:
-
Schreibe ein Programm, dass mit Hilfe eines switch Statements aus einer Note (1-6) die Punktzahl (0-100) zuweist
-
Erkenne anhand einer Zahl zwischen 1-7 um welchen Wochentag es sich handelt
-
Erkenne anhand einer Zahl zwischen 1-12 um welchen Monat es sich handelt
Character
Der Datentyp von Text wird in R auch character genannt.
Viele Programmiersprachen benutzen dafür das Wort string
(die englische Übersetzung von "Zeichenkette").
Damit der R-Interpreter Text auch als character erkennen kann,
müssen wir den Text mit " oder ' einkapseln.
Beispieltext mit ":
greetings <- "Hello"
oder mit ':
greetings <- 'Hello'
aber, wenn wir die " oder ' vergessen, dann bekommen einen Error:
greetings <- Hello ## Console: Error: object 'Hello' not found
R denkt, dass Hello eine Variable ist, aber natürlich gibt es Hello als Variable
nicht, weswegen wir den Error object 'Hello' not found bekommen.
Ein character kann auch ein Satz sein, ergo Leerzeichen innerhalb der " oder ' sind erlaubt!
greetings <- "Hello world!"
Wie dir vielleicht aufgefallen ist, haben wir die " Syntax auch schon beim
print oder switch Befehl benutzt.
Wichtige Funktionen für Characters
Für Characters gibt es viele nützliche Funktionen. Zum Beispiel:
paste
Der paste Befehl tut mehrere Characters miteinander verbinden.
Innerhalb der () Klammern können wir so viele Textsnippets reinpacken wie wir wollen,
aber an letzter Stelle brauchen wir ein sep="", um paste zu sagen wie die Characters
zusammengeklebt werden sollen.
Bei Default ist sep ein Leerzeichen. Wenn ein Leerzeichen für dein Fall okay ist,
dann kannst du es sogar weg lassen.
paste("Mann", "Bär", "Schwein") ## Console: [1] "Mann Bär Schwein"
paste("Mann", "Bär", "Schwein", sep="") ## Console: [1] "MannBärSchwein"
paste("Mann", "Bär", "Schwein", sep="-") ## Console: [1] "Mann-Bär-Schwein"
paste("Mann", "Bär", "Schwein", sep=" und ") ## Console: [1] "Mann und Bär und Schwein"
substr
Der substr Befehl tut einen Teil eines Characters herausschneiden, anhand zwei Zahlen für den Anfangs- und Endpunkt.
animal <- "Mannbärschwein"
substr(x=animal, start=5, stop=7) ## [1] "bär"
Hier eine Beschreibung der Paramter
| Parameter | Datentyp | Beschreibung |
|---|---|---|
| x | character | Der Character von dem du einen Teil rausschneiden willst |
| start | number | Anfangspunkt |
| stop | number | Endpunkt |
sub
Mit dem sub Befehlt können wir den ersten Text, der dem pattern matched,
innerhalb eines Characters austauschen bzw. ersetzen.
animal <- "Mannbärschwein"
sub(x=animal, pattern="schwein", replacement="gans") # Console: [1] "Mannbärgans"
| Parameter | Datentyp | Beschreibung |
|---|---|---|
| x | character | Input |
| pattern | character | Character der ausgetauscht werden soll innerhalb x |
| replacement | character | Ersatztext der pattern ersetzen soll |
gsub
Wenn wir mehrer Vorkommnise vom pattern haben und du willst alle mit einem neuen Text austauschen,
dann musst du gsub verwenden. Das tolle an gsub ist, dass es von der Benutzung her genauso ist wie sub.
gsub(x="Chicken Chicken Goose", pattern="Chicken", replacement="Goose") # Console: [1] "Goose Goose Goose"
Übungsaufgaben
-
Weise einer Variable einen Text zu und gib ihn aus.
-
Verbinde zwei Strings miteinander.
Vector
Vektoren (oder "Vector" auf Englisch) sind Listen, die aus mehreren Einträge bestehen.
Stell dir einen Vektor wie eine Einkaufsliste vor.
In dieser Liste können viele verschiedene Sachen drinne stehen,
wie z.B. "Ketchup" "Tomaten" "Paprika" etc....
Um einen Vector in R zu erstellen benutzen wir den c Befehl.
c steht in diesem Fall für combine, weil mehrere Items zu einer Liste (einem Vektor) kombiniert werden.
Innerhalb der () Klammer kommen mit einem Komma getrennt alle Werte rein,
die wir in einem Vektor vereinen wollen.
vec <- c("Ketchup", "Tomaten", "Paprika", "Banane")
vec ## Console: [1] "Ketchup" "Tomaten" "Paprika" "Banane"
Wichtig zu wissen ist es, dass innerhalb eines Vektors nur ein Datentyp erlaubt ist. Hier zum ein Beispiel ein Vektor, der nur aus Zahlen besteht:
vec <- c(23, 63, 39, 35, 84, 58)
vec ## Console: [1] 23 63 39 35 84 58
Wir können aber nicht gleichzeitig Zahlen und Text in einen Vektor speichern,
beziehungsweise können wir das doch,
aber R tut automatisch alle numbers zu characters konvertieren,
damit unser Code ohne Error ausgeführt werden kann.
vec <- c(1, 2, 4, "A", 5, "B")
vec ## Console: [1] "1" "2" "4" "A" "5" "B"
Wie wir sehen können, hat R automatisch für uns die Zahlen
innerhalb der Klammern von c zu character konvertiert.
Man kann auch mit c mehrer Vectoren miteinander kombinieren.
vec <- c(
c(1, 2, 3),
c(4, 5, 6)
)
vec ## Console: [1] 1, 2, 3, 4, 5, 6
Indexing
Wenn wir jetzt wissen wollen was an einer bestimmten Stelle in unserer Einkaufsliste steht,
brauchen wir die [] Klammern.
Innerhalb der [] Klammern kommt dann die Zahl für welchen Index wir den Wert haben wollen.
Für die erste Stelle ist der Index 1 und für die zweite 2 und so weiter.
shopping_list <- c("Ketchup", "Tomaten", "Paprika", "Banane")
shopping_list[1] # Console: [1] "Ketchup"
shopping_list[3] # Console: [1] "Paprika"
Wenn wir die Werte von 1 bis 3 haben wollen,
dann können wir innerhalb der [] Klammern auch einen Vektoren mit den Indexen
(oder auch Indizen in richtigeren Deutsch genannt), die wir haben wollen reinschreiben.
shopping_list[c(1, 2, 3)] # Console: [1] "Ketchup" "Tomaten" "Paprika"
shopping_list[c(1, 3)] # Console: [1] "Ketchup" "Paprika"
Wie du sehen kannst können wir auch nur den 1. und 3. Index bekommen, wenn wir die 2 weglassen.
Wir hätten aber auch c(1, 2, 3) abkürzen können.
R gibt uns für sogenannte ranges eine bequeme Syntax.
shopping_list[1:3] # Console: [1] "Ketchup" "Tomaten" "Paprika"
Die Syntax 1:3 erstellt einen Vektor mit den Zahlen 1, 2, 3.
Wie groß die Range sein soll, können wir selber bestimmen,
so würde zum Beispiel 1:100 einen Vektor von 1 bis 100 erstellen.
Du könntest auch den Vektor selber per Hand erstellen: c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ..., 100)
oder du wenn du kein Masochist bist, dann kannst du auch einfach nur 1:100 eintippen.
Wir sind aber nicht beschränkt auf positive Zahlen eine negative Range ist auch möglich.
So würde -100:100 einen Vektor mit den Zahlen zwischen -100 bis +100 erstellen.
Was passiert jetzt, wenn wir eine zu große Range bekommen wollen als shopping_list hat?
shopping_list[1:5] # Console: [1] "Ketchup" "Tomaten" "Paprika" "Banane" NA
Wie wir sehen können, wenn wir einen Wert von einem Index uns holen,
der noch nicht existiert, bekommen wir NA, "Not Available", zurück.
Indexe ausschließen
Wir können angeben welchen Index oder welche Indexe wir haben wollen, aber wir können auch das
Gegenteil und zwar angeben welche Indexe wir nicht haben wollen, indem wir innerhalb der []
Klammern eine Minuszahl eingeben.
shopping_list[-1] # Console: [1] "Tomaten" "Paprika" "Banane"
Um mehrere Indexe auszuschließen, dann müssen wir sicher gehen,
dass der Vektor oder die Range, die wir innerhalb der [] Klammern schreiben, nur aus Minus zahlen besteht.
Denn bedenke eine Range -1:2 gibt uns einen Vektor -1 0 1 2.
Als nächses zeige ich dir verschiedenen Methoden, wie man ein negative Ranges erstellen kann. Du musst dir nicht alle merken, außer natürlich mindestens eine.
shopping_list[-1:-2] # Console: [1] "Paprika" "Banane"
shopping_list[-c(1, 2)] # Console: [1] "Paprika" "Banane"
shopping_list[-(2:4)] # Console: [1] "Ketchup"
shopping_list[-c(2:4)] # Console: [1] "Ketchup"
Ein Vector erweiteren
Wir können auch einen Vektor erweitern, indem wir an der nächsten freie Stelle nach und nach Werte zuweißen:
shopping_list[5] <- "Orangensaft"
shopping_list # Console: [1] "Ketchup" "Tomaten" "Paprika" "Banane" Orangensaft"
Wenn wir einen Index wieder entfernen wollen, dann müssen wir einen kleinen Trick anwenden.
Mit einer Minuszahl als Index bekommen wir alles bis auf den Index. Was wir bekommen ist ein
komplett neuer Vektor, nur halt ohne den Index den wir angegeben haben als Minuszahl, mach Sinn oder?
Diesen Vektor können wir dann benutzen, um den originalen Wert von shopping_list zu überschreiben:
# Gibt uns einen neuen Vektor mit: [1] "Ketchup" "Tomaten" "Paprika" "Banane"
shopping_list[-5]
# Aber noch ist shopping_list: [1] "Ketchup" "Tomaten" "Paprika" "Banane" Orangensaft"
shopping_list
# Jetzt überschreiben wir shopping_list mit den neuen Vektor
shopping_list <- shopping_list[-5]
# Et voilá
shopping_list # Console: [1] "Ketchup" "Tomaten" "Paprika" "Banane"
Mathe mit Vektoren
Mit Vektoren zu rechnen ist genauso einfach wie im Kapitel 5.1. Number.
Plus +, Minus -, Geteilt /, Mal *, Hoch ^, Modulus %% alles können wir auch mit
zwei oder mehreren Vektoren benutzen.
a <- c(1, 2, 3)
b <- c(4, 5, 6)
Plus +
a + b # Console: [1] 5 7 9
Minus -
a - b # Console: [1] -3 -3 -3
Geteilt /
a / b # Console: [1] 0.25 0.40 0.50
Mal *
a * b # Console: [1] 4 10 18
Hoch ^
a ^ b # Console: [1] 1 32 729
Modulus %%
b %% a # Console: [1] 0 1 0
Siehe im Kapitel 5.1 wie der Modulus Operator funktioniert.
Rechnen mit Zahlen
Wir können auch ohne Probleme einen Vektor plus, minus, geteilt, usw. einer Zahl nehmen, dann wird jede Zahl im Vektor mit der anderen Zahl verrechnet.
a + 2 # Console: [1] 3 4 5
a / 2 # Console: [1] 0.5 1.0 1.5
Rechnen mit unterschiedlich langen Vektoren
Wenn wir zwei Vektoren, die unterschiedlich lang sind, miteinander verrechnen, dann passiert etwas komisches:
c(1, 2, 3, 4) + c(1, 2) # Console: [1] 2 4 4 6
Was wir hier sehen können ist, dass R automatisch den zweiten Vektor verlängert hat.
Aus c(1, 2, 3, 4) + c(1, 2) wurde c(1, 2, 3, 4) + c(1, 2, 1, 2).
Wenn wir zwei unterschiedlich lange Vektoren in R miteinander verrechnen, sei es plus, minus, geteilt usw., dann wird automatisch der kürzere Vektor verlängert damit beide Vektoren gleich lang sind.
Dabei muss einer der beiden Vektoren das gerade Vielfache des anderen sein, sonst bekommen wir einen Error:
vec1 <- c(1, 2, 3)
vec2 <- c(1, 2, 3, 4)
vec1 + vec2
Warning message:
In vec2 + vec1 :
longer object length is not a multiple of shorter object length
Einfache Zahl vs. Zahlen-Vektor
Schauen wir einmal hinter den Kulissen, warum sich Zahlen und Vektoren bei mathematischen Berechnungen nicht unterscheiden.
In R ist jeder Wert und jede Variable, die wir erstellen gleichzeitig auch ein Vektor. Zum Beispiel wenn wir eine Variable erstellen mit der Zahl 420, dann haben wir auch einen Vektor mit nur einem Wert erstellt.
x <- 420
is.numeric(x) # Console: [1] TRUE
is.vector(x) # Console: [1] TRUE
Die Funktion is.numeric und is.vector zeigt uns, dass x eine Zahl und auch ein Vektor ist.
Jetzt wo wir wissen, dass x ein Vector ist, können wir doch bestimmt auch die [] Klammern
benutzen, oder nicht? Richtig!
x[1] # Console: [1] 420
x[2] # Console: [1] NA
Und wenn wir im Kopf behalten, dass R automatisch versucht die Länge des kürzeren Vektors dem anderen
anzupassen, bekommen wir ein Einblick wie R c(1, 2, 3) + 1 hinter den Kulissen berechnet wird:
# Aus:
c(1, 2, 3) + 1
# wird:
c(1, 2, 3) + c(1, 1, 1)
Übung: Rechnen mit Vektoren
Ich habe meine gesamten Einnahmen und Ausgaben im letzen Jahr aufgezeichnet und möchte jetzt berechnen, ob ich über meine Verhältnisse gelebt habe oder doch etwas sparen konnte.
| Monat | Einnahmen | Ausgaben |
|---|---|---|
| Januar | 1450 | 1200 |
| Februar | 1300 | 1100 |
| März | 1500 | 1800 |
| April | 1450 | 2000 |
| Mai | 1450 | 1000 |
| Juni | 1500 | 1895 |
| Juli | 1600 | 1500 |
| August | 1400 | 2000 |
| September | 1450 | 1800 |
| Oktober | 1450 | 1200 |
| November | 1500 | 1200 |
| Dezember | 2100 | 1500 |
Lass uns diese Tabelle in R-Code übersetzen:
income <- c(1450, 1300, 1500, 1450, 1450, 1500,
1600, 1400, 1450, 1450, 1500, 2100)
expenses <- c(1200, 1100, 1800, 2000, 1000, 1895,
1500, 2000, 1800, 1200, 1200, 1500)
Um jetzt herauszufinden in welchen Monat ich plus oder minus gemacht habe,
kann ich income - expenses nehmen.
income - expenses # Console: [1] 250 200 -300 -550 450 -395 100 -600 -350 250 300 600
sum
Das gibt mir schonmal einen groben Eindruck,
ob ich plus oder minus im letzten Jahr gemacht habe,
aber wie viel genau?
Die sum Funktion gibt uns die Summe aller Werte eines numerischen Vektors:
sum(income - expenses) # Console: -45
Ich habe wohl doch nicht so gut gespart, wie ich erst gedacht habe...
median
Mit der median Funktion können wir herausfinden
was der Durchschnitt meiner Einnahmen und Ausgaben war.
median(income) ## Console: [1] 1450
median(expenses) ## Console: [1] 1500
Wie wir sehen habe ich letztes Jahr im Durchschnitt pro Monat 1450 EUR verdient, aber 1500 EUR ausgegeben...
Filtering
Innerhalb der [] Klammern nach einem Vektor können wir auch eine logische Aussage eintippen.
Was bedeutet das überhaupt? Nun wenn wir zum Beispiel einen numerischen Vektor von 1 bis 100 haben,
aber wir nur die Werte von den geraden Indexen haben wollen und alle ungeraden rausfiltern wollen, dann können wir
innerhalb der [] Klammern eben diese logische Abfrage "ist Zahl gerade?" eingeben.
Kannst du dich noch erinnern, wie wir erkennen konnten, ob eine Zahl gerade oder ungerade ist? Ein kleiner Tipp,
bevor wir zur Lösung kommen, wir brauchen den %% Modulus Operator.
irgendeine_zahl %% 2 == 0 # [1] TRUE oder [1] FALSE
Wenn der Rest durch zwei einer Zahl gleich null ist, dann wissen wir, dass die Zahl durch zwei teilbar ist und somit eine gerade Zahl sein muss.
Wie wir gelernt haben im vorherigen Kapitel
können wir irgendeine_zahl auch ohne Probleme mit einem Vektor austauschen!
one_hundred <- 1:100
one_hundred %% 2 == 0 # [1] FALSE TRUE FALSE ...
%% 2 wird jetzt für jeden einzelnen Wert im Vector ausgeführt und wir überprüfen
ob das Ergebnis gleich null ist. Wenn ja, dann bekommen wir TRUE und wenn nicht, dann bekommen
wir FALSE, somit haben wir am Ende einen Vektor, der nur aus boolischen Werten besteht.
Aber wie hilft uns das jetzt beim filtern eines Vektors weiter?
Eben diesen "boolischen" Vektor können wir jetzt in die [] Klammern schreiben,
um alles rauszufiltern, was an dem jeweiligen Index FALSE ist.
numbers <- 1:100
even_numbers <- numbers[numbers %% 2 == 0]
even_numbers # [1] 2 4 6 8 10 12 14 16 18 20 ...
Wie wir erkennen können, bedeutet TRUE, behalte den Wert an diesem Index und FALSE bedeutet,
verwerfe den Wert an diesem Index.
Das selbe Prinzip wie im Kapitel -, wo wir mit Hilfe einer Minuszahl Werte ausschließen konnten,
so können wir mit einem Boolean-Vektor Werte in einem anderen Vektor ausschließen.
Polizeihund Beispiel
Wenn du es bis jetzt nicht wirklich verstanden hast, wie das Filtern mit Vektoren funktioniert, keine Sorge, du bist definitiv nicht alleine. Deswegen versuche ich es nochmal bildlischer zu beschreiben.
Stell dir vor du bist Polizist und hast einen Polizeihund. Der Polizeihund ist trainiert eine Gruppe von Menschen zu erschnüffeln. Wie er diese Gruppe erkennen kann, müssen wir im vorher erklären. Unser Polizeihund ist zufällig auch ein Mathegenie und kann erkennen ob Zahlen größer oder kleiner sind. Perfekt! Einen besseren Hund hätten wir uns nicht vorstellen können. Unser Übeltäter ist über 40 Jahre alt. In der Gruppe haben wir insgesamt 100 Personen. Jede dieser Personen ist im Alter von 1-100 und jedes Alter gibt es nur einmal.
In R-Sprache ausgedrückt haben wir eine range von 1:100. Wir wissen der Übeltäter ist >40 Jahre alt.
Unserem Polizeihund müssen wir jetzt nur die Gruppe, also den Vector mit der logischen Aussage überreichen.
gruppe <- 1:100
gruppe > 40 # Console: [1] FALSE FALSE FALSE FALSE ... TRUE TRUE ...
Unser Polizeihund geht jetzt reih um und sagt TRUE oder FALSE bei jeder Person, die über 40 ist.
Ja unser Polizeihund ist nicht nur ein Mathegenie, sondern kann auch reden. Wir sind mitgegangen
und haben eine chronologische Liste gemacht, an welcher Position unser Hund TRUE und an welcher Stelle
er FALSE gesagt hat. Diese Liste bringt uns nichts. Sie ist zu schwer zu lesen und unsere Kollegen
werden keine Ahnung haben, was die Bedeutung dieser ist. Zum Glück haben einen hoch talentierten
Praktikanten, der für uns noch einmal durch die Gruppe an Menschen geht und jeden nach Hause schickt,
den unser Hund als FALSE identifiziert hat. Damit unser Praktikant auch richtig arbeiten kann,
müssen wir ihm die Gruppe als auch die Liste vom Polizeihund überreichen:
gruppe <- 1:100
police_dog_list <- gruppe > 40 # Console: [1] FALSE FALSE FALSE ... TRUE TRUE ...
gruppe[police_dog_list] # Console: [1] 41 42 43 44 45 ...
Wir können uns eine Zeile Code sparen, indem wir den Praktikanten und Polizeihund gleichzeitig arbeiten lassen:
gruppe <- 1:100
gruppe[gruppe > 40] # Console: [1] 41 42 43 44 45 ...
Et voliá. Um sicher zu gehen, dass du das Konzept mit den Polizeihund auch richtig verstanden hast, probiere es mit anderen Vektoren aus und gebe dem "Polizeihund" auch andere Aussagen, nach dem er und der Praktikant filtern können. Probiere auch andere logische Operatoren aus, die wir im Kapitel 5.2.1 gelernt haben!
Filtern mit %in%
Der %in% Operator wird dann verwendet, wenn wir genau wissen, wonach was wir suchen.
Zum Beispiel, anstelle dass unser Polizeihund nach einer logischen Aussage filtert, geben wir ihm jetzt eine Liste mit den Namen der Verbrecher. Unser Polizeihund geht jetzt über eine Liste an Personen und schickt alle Heim, die nicht auf der Vebrecherliste stehen.
group_of_people <- c("Marc", "Michael", "Niklas", "Paul", "Andre")
suspects <- c("Marc", "Paul")
group_filtered_by_suspects <- group_of_people[group_of_people %in% suspects]
group_filtered_by_suspects # Console: [1] "Marc" "Paul"
Aus unserer Gruppe an Personen group_of_people holen wir uns nur diejenigen heraus,
die auch im Vektor suspects vermerkt sind.
Oft müssen wir aber auch das Gegenteil machen, also wir wollen bestimmte Werte
rausfiltern bzw. entfernen. Wenn wir den ! nicht Operator mit den %in% Operator kombinieren,
können wir bestimmte Werte von einem Vektor entfernen.
invitations <- c("Thomas", "Bernd", "Lukas", "Joshua")
declined <- c("Joshua", "Bernd")
invitations <- invitations[!invitations %in% declined]
invitations # Console: [1] "Thomas" "Lukas"
Hier habe ich eine Liste an Leuten, denen ich eine Einladung zur meiner Party geschickt habe,
aber leider haben Joshua und Bernd abgesagt und deshalb werde ich sie aus meiner Eingeladenen
(invitations) Liste entfernen, um ein besser Plan zu haben, wer alles kommt und wer nicht.
invitations %in% declined gibt mir einen Vektor, der nur aus TRUE und FALSE besteht.
Diesen Vektor tue ich dann mit ! umkehren, also alle TRUEs werden FALSE und umgedreht.
Aufgrund diesen TRUE/FALSE Vektor kann ich nun invitations filtern und mit mit dem
neu-gefilterten Vektor den alten überschreiben.
Gehen über noch ein weiteres Beispiel mit %in%.
Wir haben von einer Schule eine Liste mit den Alter aller Schüler bekommen.
Bei all denen, die nicht ihr Alter angegeben haben, wurde eine 0 eingetragen.
Mit den %in% Operator können wir jetzt all diese Nullen rausfiltern.
ages <- c(24, 18, 19, 20, 34, 0, 17, 19, 0, 39)
ages <- ages[!ages %in% 0]
ages # Console: [1] 24 18 19 20 34 17 19 39
Named Vectors (Benannte Vektoren)
In R haben wir auch dir Möglichkeit jedem einzelnen Wert an einem Index einen eigenen Namen zu geben. Dies kann uns helfen die Daten, die wir einem Vektor zuweisen, besser einorden zu können.
Lass uns einen Vektor erstellen, der die Stunden, wie viel ich in der Woche gearbeitet habe, hat.
workhours <- c(8, 8, 10, 8, 6, 0, 0)
Der Variablenname workhours hilft uns dabei zu verstehen, was unser Ziel ist,
aber es wird schwieriger nachzuvollziehen, wenn wir z.B. die Stunden von Freitag
wissen wollen.
workhours[5] # Console: [1] 6
Wir können jetzt im Nachhinein jedem Index einen eigenen Namen geben!
names(workhours) <- c("Monday", "Tuesday", "Wednesday", "Thursday","Friday", "Saturday", "Sunday")
Und jetzt können wir, anstelle workhours[5] zu schreiben, auch workhours["Friday"] schreiben!
Der names Funktion können wir einen neuen Vektor zuweisen, der den Namen der jeweiligen Indexen
entsprechen soll. Dies heißt, dass der Name für den Index 1 von workhours jetzt "Monday" ist und
von 2 "Tuesday" usw. In der Documentation der Funktion, die wir mit dem Befehl ? names
in der R-Konsole bekommen steht folgendes: Functions to get or set the names of an object.
Wir müssen aber nicht immer einen named vector so erstellen:
workhours <- c(8, 8, 10, 8, 6, 0, 0)
names(workhours) <- c("Monday", "Tuesday", "Wednesday", "Thursday","Friday", "Saturday", "Sunday")
Wir können auch schon beim erstellen des workhours Vektors Namen für den jeweiligen Index mit geben:
workhours <- c("Monday"=8,
"Tuesday"=8,
"Wednesday"=10,
"Thursday"=8,
"Friday"=6,
"Saturday"=0,
"Sunday"=0)
Apropos dies würde auch ohnen den " Zeichen klappen:
workhours <- c(Monday=8,
Tuesday=8,
Wednesday=10,
Thursday=8,
Friday=6,
Saturday=0,
Sunday=0)
Jetzt können wir in den [] Klammern Zahlen oder einer Namen eingeben:
workhours[1] # Console: Monday 8
workhours["Monday"] # Console: Monday 8
Vergesse aber nicht die " Zeichen, weil sonst denkt R, dass Monday eine Variable ist
und wenn diese nicht existiert, bekommen wir einen Error.
Japp. R ist sehr eigen, wenn es darum geht, wann " Optional oder Pflicht sind.
Übungsaufgaben
-
Erstelle einen Vektor mit 5 Zahlen und gib ihn aus.
-
Wähle das 3. Element aus einem Vektor aus und gib es aus.
-
Lösche ein Element aus einem Vektor.
-
Kombiniere zwei Vektoren
-
Erstelle einen Vektor mit einer Range von 1 bis 1.000.000.
-
Aus diesen Vektor finde heraus wie viele davon durch 7 teilbar sind. (Tipp: Mit
lengthbekommt man die Länge eines Vektors)
Matrix
Matrizen sind zwei-dimensionale Vektoren. Stell dir eine Matrix wie eine Excel Tabelle mit Zeilen (rows) und Spalten (columns) vor. Genau wie bei Vektoren, können auch Matrizen nur einen Datentyp in sich tragen.
Mit der matrix() Funktion können wir eine neue Matrix erstellen.
Innerhalb der () Klammern kommt ein Vektor mit den Daten für die Matrix.
x <- matrix(c(1, 2, 3, 4))
x
Dadurch haben wir eine ein-spaltige Matrix mit 4 Zeilen erstellt:
[,1]
[1,] 1
[2,] 2
[3,] 3
[4,] 4
Wir können auch ohne Probleme eine Matrix mit characters (booleans oder jeden anderen beliebigen Datentyp) erstellen:
y <- matrix((c("a", "b", "c", "d")))
y
[,1]
[1,] "a"
[2,] "b"
[3,] "c"
[4,] "d"
Wir können auch bestimmen wie viele Spalten (columns) und Zeilen (rows) unsere Matrix haben soll.
x <- matrix(c(1, 2, 3, 4),
2,
2)
x
[,1] [,2]
[1,] 1 3
[2,] 2 4
Hier haben wir drei Werte der matrix Funktion gegeben:
Die Daten (data), die Anzahl der Zeilen (nrow) un die Anzahl der Spalten (ncol).
In R können wir jeden dieser Werte, die wir innerhalb einer Funktion schreiben,
auch mit deren richtigen Namen zuweisen:
x <- matrix(data = c(1, 2, 3, 4),
nrow = 2,
ncol = 2)
Was der "richtige Name" ist, können wir herausfinden, indem wir in die R-Konsole ein ?
gefolgt mit den Funktionsname eintippen. Probiere es einmal aus mit ? matrix!
Anstelle irgendwelche Zahlen oder Vektoren in den () Klammern zu schreiben,
können wir mit paramter = x genau sagen welchen Parameter wir welchen Wert zuweisen.
Wenn wir das so machen, dann ist auch die Reihenfolge egal,
weil R es automatisch für uns erkennen kann.
Matrix erstellen byrow
Beim erstellen einer Matrix wird Spalte für Spalte mit Daten gefüllt.
x <- matrix(data = (c("one", "two", "three", "four")),
nrow = 2,
ncol = 2)
x
[,1] [,2]
[1,] "one" "three"
[2,] "two" "four"
Wenn wir das nicht wollen, dann können wir der matrix Funktion
auch sagen, dass die Matrix Zeile für Zeile befüllt werden soll.
x <- matrix(data = c("one", "two", "three", "four"),
nrow = 2,
ncol = 2,
byrow = TRUE)
x
[,1] [,2]
[1,] "one" "two"
[2,] "three" "four"
Matrix erweitern
Mit rbind und cbind können wir eine schon erstellte Matrix erweitern.
Um eine neue Zeile (row) einer Matrix hinzuzufügen, müssen wir die rbind Funktion benutzen.
rbind steht in diesem Fall "binde" (also füge hinzu) eine neue "row".
x <- matrix(data = c("one", "two", "three", "four"),
nrow = 2,
ncol = 2)
[,1] [,2]
[1,] "one" "three"
[2,] "two" "four"
x <- rbind(x, c("five", "six"))
[,1] [,2]
[1,] "one" "three"
[2,] "two" "four"
[3,] "five" "six"
das selbe mit cbind aber um eine Spalte (column) hinzufügen:
x <- matrix(data = c("one", "two", "three", "four"),
nrow = 2,
ncol = 2)
[,1] [,2]
[1,] "one" "three"
[2,] "two" "four"
x <- cbind(x, c("five", "six"))
[,1] [,2] [,3]
[1,] "one" "three" "five"
[2,] "two" "four" "six"
rbind und cbind akzeptieren eine beliebig große Anzahl an Vektoren
oder Matrixen, um daraus eine neue Matrix zu erstellen,
deswegen vergesse nicht deine Matrix-Variable, in diesem Fall x,
zu überschreiben.
Named Matrices (Benannte Matrizen)
Genauso wie wir es bei Vektoren gelernt haben oder es auch Excel Tabellen möglich ist, können wir "Tabellen" erstellen, deren Zeilen und Spalten benannt sind.
Das ist vorallem sehr interessant, wenn wir mit komplexen Daten zu tun haben, wie zum Beispiel einen Notenspiegel:
# Definiere die Matrix-Daten
point_matrix <- matrix(c(85, 90, 88, 92, 78, 80),
nrow = 2,
ncol = 3,
byrow = TRUE)
# Definiere die Zeilen- und Spaltennamen
rownames(point_matrix) <- c("Alice", "Bob")
colnames(point_matrix) <- c("Math", "Science", "English")
point_matrix
Math Science English
Alice 85 90 88
Bob 92 78 80
Ich hoffe dieses Beispiel hilft, um zu verstehen wie man "named matrices" benutzen könnte, aber für solche Fälle werden meist DataFrames bevorzugt, aber keine Sorge wir werden in Kapitel 5.8 DataFrame alles über DataFrames lernen!
Indexing
Matrizen sind eigentlich nichts anderes als Vektoren, aber nur zwei-dimensional.
Sprich, um ein Wert von einer Matrix zu bekommen,
schreibt man zwei Zahlen in die [] Klammern.
Die erste Zahl für die Zeile (row) und die zweite Zahl für die Spalte (column).
x <- matrix(data = 1:16,
nrow = 4,
ncol = 4)
# x[row, column]
x[1, 2] # Console: [1] 3
Um eine komplette Zeile zu bekommen, lässt du einfach die zweite Zahl leer. Du musst aber trotzdem das Komma stehen lassen:
x[1,] # Console: [1] 1 3
Um eine komplette Spalte zu bekommen, machst du das selbe nur umgedreht:
x[,1] # Console: [1] 1 2
Wie im Kapitel 5.4.1 Indexing von Vektoren besprochen,
kann man auch eine Range oder
einen Vektor in die [] Klammern schreiben, um, in diesen Fall, mehrere Zeilen oder Spalten
aufeinmal zu bekommen.
So bekommen wir Zeile 3 und 4 mit allen Spalten:
x[3:4,]
[,1] [,2] [,3] [,4]
[1,] 3 7 11 15
[2,] 4 8 12 16
Und so bekommen wir die Spalte 3 und 4 mit allen Zeilen:
x[,3:4]
[,1] [,2]
[1,] 9 13
[2,] 10 14
[3,] 11 15
[4,] 12 16
Genauso wie bei Vektoren, können wir auch mit einer Minuszahl Zeilen oder Spalten ausschließen.
x[-4, -4]
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
Zu guter letzt zeige ich dir noch wie du Werte einer Matrix überschreiben kannst.
Um einen einzige Stelle zu überschreiben:
x[1, 1] = 420
[,1] [,2] [,3] [,4]
[1,] 420 5 9 13
[2,] 2 6 10 14
[3,] 3 7 11 15
[4,] 4 8 12 16
Oder eine komplette Reihe:
# Eine komplette Zeile überschreiben:
x[1,] = c(420, 420, 420, 420)
# Eine komplette Spalte überschreiben:
x[,4] = c(68, 68, 68, 68)
[,1] [,2] [,3] [,4]
[1,] 420 420 420 68
[2,] 2 6 10 68
[3,] 3 7 11 68
[4,] 4 8 12 68
Mathe mit Matrizen
Mit Matrizen zu rechnen ist nahezu identisch wie mit Vektoren im Kapitel 5.4.1. Mathe mit Vektoren.
Lass zwei identische 3x3 Matrizen erstellen, um damit zu rechnen.
a <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), 3, 3)
b <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), 3, 3)
a:
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
b:
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
Plus +
a + b
[,1] [,2] [,3]
[1,] 2 8 14
[2,] 4 10 16
[3,] 6 12 18
Minus -
a - b
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 0 0 0
[3,] 0 0 0
Geteilt /
a / b
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
[3,] 1 1 1
Mal *
a * b
[,1] [,2] [,3]
[1,] 1 16 49
[2,] 4 25 64
[3,] 9 36 81
Um eine echte mathematische Multiplizierung von Matrizen durchzuführen,
benutzen wir %*% anstelle *:
a %*% b
[,1] [,2] [,3]
[1,] 30 66 102
[2,] 36 81 126
[3,] 42 96 150
Hoch ^
a ^ b
[,1] [,2] [,3]
[1,] 1 256 823543
[2,] 4 3125 16777216
[3,] 27 46656 387420489
Modulus %%
a %% b
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 0 0 0
[3,] 0 0 0
Rechnen mit Zahlen
Genau so wie bei Vektoren, können wir auch einzelne Zahlen mit Matrizen verrechnen.
a + 10
[,1] [,2] [,3]
[1,] 11 14 17
[2,] 12 15 18
[3,] 13 16 19
Rechnen mit Vektoren
Sogar Matrix + Vektor geht ohne Probleme:
a + c(10, 20, 30)
[,1] [,2] [,3]
[1,] 11 14 17
[2,] 22 25 28
[3,] 33 36 39
Wichtige Funktionen um mit Matrizen zu rechnen
diag
sum
min
max
dim
t
as.vector
rowSums
colSums
rowMeans
colMeans
Filtering
Das Filtern von Matrizen funktioniert genau gleich wie das Filtern von Vektoren in Kapitel 5.4.3. Filtering. Wenn wir aber eine Matrix filtern, dann bekommen wir einen neuen Vektor:
a <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), 3, 3)
Output von a:
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
Wir filtern nur die ungeraden Zahlen aus der Matrix a heraus:
a[a %% 2 == 0] # Console: [1] 2 4 6 8
a[a %% 2 == 0] gibt uns einen Vektor, welches nur aus den geraden
Zahlen der Matrix a besteht.
Wir können auch den %in% Filter-operator benutzen:
a[!a %in% c(7, 8, 9)] # Console: [1] 1 2 3 4 5 6
Übungsaufgaben
-
Erstelle aus einer Multiplikationstabelle (das Einmaleins von eins bis zehn) eine Matrix.
-
Mach aus dieser Mutliplikationstabellen-Matrix eine Multiplikationstabellen-Matrix, die von 10, 20, 30 bis 100 geht.
-
TODO etwas mit filtern
List
Listen sind wie Vektoren, die in der Lage sind mehrer verschiedene Datentypen aufeinmal in sich zu tragen. Alles ist in einer Liste erlaubt, sogar Vektoren oder noch mehr Listen. Egal was, wirklich alles ist erlaubt.
Kannst du es in einer Variable packen?
Dann darfst du es auch in eine Liste packen!
Um eine neue Liste zu erstellen, brauchen wir die list Funktion:
my_list <- list(c(1, 2, 3),
"R ist cool!",
420,
TRUE)
Wenn wir my_list in der R-Konsole eingeben, dann bekommen wir diesen Output:
[[1]]
[1] 1 2 3
[[2]]
[1] "R ist cool!"
[[3]]
[1] 420
[[4]]
[1] TRUE
Die Doppel [[]] Klammer mit einer Zahl drinne,
sagt uns am welchen Index dieser Wert in unserer Liste ist.
Indexing
Wie bei Vektoren, benutzen wir die [] Klammern, um einen Wert von einer Liste zu bekommen.
my_list[2]
[[1]]
[1] "R ist cool!"
Was auffällt ist, dass wir etwas mehr als nur [1] "R ist cool!" bekommen.
Der Grund hierfür ist, dass wir nicht nur den Text "R ist cool" sondern
eine Liste mit der Länge eins bekommen haben.
typeof(my_list[2]) # Console: [1] "list"
Die typeof Funktion sagt uns, dass my_list[2] von Typ list ist.
Um den tatsächlichen Wert zu bekommen, müssen wir zwei [[]] Klammern benutzen.
my_list[[2]] # Console: [1] "R ist cool!"
typeof(my_list[[2]]) # Console: [1] "character"
Natürlich können wir auch einen Wert an einem Index überschreiben oder einen neuen hinzufügen:
my_list[[5]] <- c(18, 17, 48)
[[1]]
[1] 1 2 3
[[2]]
[1] "R ist cool!"
[[3]]
[1] 420
[[4]]
[1] TRUE
[[5]]
[1] 18 17 48
Oder entfernen, indem wir NULL, also nichts, zuweisen:
my_list[5] <- NULL
[[1]]
[1] 1 2 3
[[2]]
[1] "R ist cool!"
[[3]]
[1] 420
[[4]]
[1] TRUE
Named Lists (Benannte Listen)
Wie bei Vektoren oder Matrizen, haben wir auch bei Listen die Möglichkeit Indexe einen Namen zu geben:
my_list <- list(numbers=c(1, 2, 3),
message="R ist geil!",
is_cool=TRUE)
$numbers
[1] 1 2 3
$message
[1] "R ist geil!"
$is_cool
[1] TRUE
Anstelle Zahlen für den Index können wir auch die Namen, die wir definiert haben, benutzen:
my_list[["message"]] # Console: [1] "R ist geil!"
Wir können auch die [[]] mit einem $ austauschen:
my_list$message # Console: [1] "R ist geil!"
Das $ funktioniert aber nur mit dem Namen und nicht mit einem Index.
my_list$2 würde uns folgenden Error geben:
Error: unexpected numeric constant in "my_list$2"
Wir können auch eine Liste mit einem neuen Namen erweitern. Zum Beispiel lass ein weiteren Wert mit den Namen "admin" hinzufügen:
my_list$admin <- "marc"
$numbers
[1] 1 2 3
$message
[1] "R ist geil!"
$is_cool
[1] TRUE
$admin
[1] "marc"
Übungsaufgaben
Factor
Faktoren sind Kategorien, die uns helfen Datenmengen, zu sortieren, in zum Beispiel Geschlecht, Herkunftsland oder Elektro-/Verbrennermotoren usw. Die Möglichkeiten sind endlos, da wir unsere komplett eigenen Faktoren erstellen können.
Einen Faktor erstellt man aus einem Vektor mithilfe der factor Funktion:
# Erstelle einen Vektor mit 10 `yes` und 15 `no`
data <- c(rep("yes", 10),
rep("no", 15))
data_factor <- factor(data)
data_factor ist nur ein Faktor mit 2 Kategorien, in R auch Levels genannt,
aber wir können uns die Levels auch einmal anschauen:
levels(data_factor) # Console: [1] "no" "yes"
Wir können aber auch beim erstellen des Faktors die Levels bestimmen:
data_factor <- factor(data,
levels=c("yes", "no", "maybe"))
levels(data_factor) # Console: [1] "yes" "no" "maybe"
Falls wir im nachhinein unzufrieden sein sollten, können wir auch die Levels überschreiben:
levels(data_factor) <- c("yes", "no", "unknown")
levels(data_factor) # Console: [1] "yes" "no" "unknown"
Oder einen neuen Level hinzufügen:
levels(data_factor) <- c("yes", "no", "unknown", "maybe")
levels(data_factor) # Console: [1] "yes" "no" "unknown" "maybe"
Mit Faktoren können wir dann ganz einfach erkennen,
wie viele yes und no wir in unserer data haben.
Die summary Funktion gibt uns allerlei Informationen
zu verschiedenen Daten und bei Faktoren bekommen wir wie
oft ein Level (eine Kategorie) vorhanden ist.
summary(data_factor)
yes no unknown maybe
10 15 0 0
Wenn wir nicht benutzte Level automatisch entfernen wollen,
dann können wir die droplevels Funktion benutzen:
data_factor <- droplevels(data_factor)
levels(data_factor) # Console: [1] "yes" "no"
Ordered Factor
Neber Faktoren können wir auch "sortierte" Faktoren erstellen, die eine Hierachie oder Gewichtung darstellen können.
Stell dir vor du hast Umfragedaten, auf denen Fragen die einzelnen Befragten "very low", "low", "medium", "high" oder "very high" antworten konnten. Unser menschliches Gehirn sortiert diese Kategorien automatisch ein. "very low" ist ganz unten und "very high" dementsprechend ganz oben.
Um auch nach diesem Prinzip Faktoren zu erstellen,
müssen wir beim erstellen den Paramter ordered auf TRUE setzen:
## Erstelle einen Vektor mit jeweils 5 "very low", "low", "medium", ...
questionnaire <- rep(c("very low", "low", "medium", "high", "very high"), 5)
## Erstelle aus dem `questionnaire`
questionnaire_factor <- factor(questionnaire,
levels=c("very low", "low", "medium", "high", "very high"),
ordered=TRUE)
Wichtig ist es innerhalb der factor Funktion nicht nur ordered auf TRUE zu setzen,
sondern auch levels in der Reihenfolge von niedrig zu hoch setzen.
In diesen Beispiel von "very low" bis "very high".
questionnaire_factor
Levels: very low < low < medium < high < very high
Vom Output, wenn wir questionnaire_factor in der R-Konsole eingeben,
können wir die Einstufung der Gewichtung der Kategorien erkennen.
Zum Vergleich habe ich hier nocheinmal questionnaire_factor ohne ordered = TRUE erstellt:
Levels: very low low medium high very high
Faktoren werden vorallem im Kapitel 8. Plots und Kapitel 9. ggplot interessant, wenn es darum geht eigene Grafen zu erstellen.
DataFrame
Ich habe schon im Kapitel 5.5. Matrix gesagt, dass Matrizen wie Excel Tabellen sind, aber die tatsächlichen Excel Tabellen sind DataFrames.
DataFrame ist ein mächtiger Datentyp in R, der sehr oft für Datenanalyse benutzt wird. In DataFrames können wir etliche Daten in etlichen Kategorien in etlichen Datentypen speichern, um daraufhin Grafen oder Statistiken erstellen zu können.
| Item | Price | In stock | Inhouse-brand |
|---|---|---|---|
| Juicy Apples | 1.99 | 7 | true |
| Bananas | 1.89 | 52 | false |
| ... | ... | ... | ... |
Um einen DatenFrame zu erstellen, benutzt man die data.frame Funktion:
name <- c("Marc", "Thomas", "Leon", "Rüdiger")
age <- c(25, 23, 26, 26)
child <- c(FALSE, TRUE, FALSE, TRUE)
df <- data.frame(name, age, child)
Der Output von der Varibale df sieht wie folgt aus:
name age child
1 Marc 25 FALSE
2 Thomas 23 TRUE
3 Leon 26 FALSE
4 Rüdiger 26 TRUE
Die data.frame Funktion erkennt automtisch anhand der Variablennamen,
die wir innerhalb der () Klammern schreiben,
dass die 3 Spalten die Name: name, age, child haben sollen,
aber wir können die Namen der Spalten auch selber bestimmen:
df <- data.frame("Vorname" = name,
"Alter" = age,
"Hat_Kinder" = child)
Vorname Alter Hat_Kinder
1 Marc 25 FALSE
2 Thomas 23 TRUE
3 Leon 26 FALSE
4 Rüdiger 26 TRUE
Indexing
Wie Matrizen sind auch DataFrames zwei-dimensional,
also benutzen wir hier auch zwei Zahlen innerhalb der [] Klammern.
Lass uns zunächst den DataFrame erstellen:
name <- c("Marc", "Thomas", "Leon", "Rüdiger")
age <- c(25, 23, 26, 26)
child <- c(FALSE, TRUE, FALSE, TRUE)
df <- data.frame(name, age, child)
name age child
1 Marc 25 FALSE
2 Thomas 23 TRUE
3 Leon 26 FALSE
4 Rüdiger 26 TRUE
Wie bei Matrizen, um das Alter von Leon zu bekommen,
geben wir innerhalb der [] Klammern erst die Zeile (row)
und dann die Spalte (column) ein.
df[3, 2]
[1] 26
Wir können aber auch den Namen der Spalte verwenden, anstelle einer Zahl:
df[3, "age"]
[1] 26
Mit Vektoren als Indexe können wir auch mehrer Zeilen und Spalten bekommen. Im Falle von DataFrames haben wir auch die Möglichkeit einen Vektor mit den Namen für die Spalten zu verwenden.
df[c(2, 4), c("name", "child")]
name child
2 Thomas TRUE
4 Rüdiger TRUE
TODO
df[3,]
df[3]
df[,3]
df[,"child"]
Ähnlich wie bei Listen können wir auch bei DataFrames $ und [[]] benutzen,
um alle Werte einer Spalte (column) als Vektor zu bekommen.
df$name # Console: [1] "Marc" "Thomas" "Leon" "Rüdiger"
df[["name"]] # Console: [1] "Marc" "Thomas" "Leon" "Rüdiger"
df[[1]] # Console: [1] "Marc" "Thomas" "Leon" "Rüdiger"
DataFrame erweitern
Genauso wie wir es in Kapitel 5.5.1,
können wir DataFrames mit rbind und cbind erweitern.
Lass dafür unsere df Variable, die wir vorher erstellt haben erweitern:
name age child
1 Marc 25 FALSE
2 Thomas 23 TRUE
3 Leon 26 FALSE
4 Rüdiger 26 TRUE
rbind
Mit rbind können wir eine weitere Zeile (row) hinzufügen.
rbind(df,
c("Lennard", 38, TRUE))
name age child
1 Marc 25 FALSE
2 Thomas 23 TRUE
3 Leon 26 FALSE
4 Rüdiger 26 TRUE
5 Lennard 38 TRUE
cbind
cbind(df,
"height" = c(185, 175, 178, 182))
name age child height
1 Marc 25 FALSE 185
2 Thomas 23 TRUE 175
3 Leon 26 FALSE 178
4 Rüdiger 26 TRUE 182
Vergesse nicht den alten Wert von df zu überschreiben oder
in einer neuen Variable zu speichern, sonst geht all unsere
Arbeit verloren.
Wichtige Funktionen
DataFrame sortieren
Sowie in Excel können wir auch ein df sortieren:
sort(people$age) # -> [1] 23 25 26 26 38
order(people$age) # -> [1] 2 1 3 4 5
order returnt die indexe der Zeilen sortiert nach dem Alter um eine sortierte df zu bekommen:
people[order(people$age),] ## für aufsteigend nach dem alter
people[order(people$age, decreasing = TRUE),] ## absteigend
dim
dim(customerData) # gette dimensions -> 5 (rows) 4 (columns)
colnames und rownames
colnames(people) ## gette column names
rownames(people) ## gette row names
ncol und nrow
ncol(people) ## Zahl der column
nrow(people) ## Zahl der rows
subset
subset(people, height > 179) ## filtering
people[people$height > 179,] ## macht das selbe wie subset
str und summary
str(people) ## printed "str"ucture eines R-Objektes in diesem Fall
summary(people) ## gibt uns "result summaries"
fivenum
fivenum(people) ## Tukey's five nummer summary (minimum, lower-hinge, median, upper-hinge, maximum)
DataFrame als Datei abspeichern
printet es alle wichtigen Infos des people df
write.csv(people, "people.csv") ## speicher df als csv Datei auf dem Filesystem ab
write.csv(people, "people.csv", row.names=FALSE) ## um die Zeilennummern zu entfernen
people <- read.csv("people.csv") ## um eine csv Datei als df zu lesen/laden
colSums
Schleifen
In so ziemlich jeder Programmiersprache gibt es Schleifen oder auch "Loops" auf Englisch genannt.
Schleifen ermöglichen es Dir, bestimmte Aktionen mehrmals wiederholen zu lassen (in Programmierfachchinesisch wird ein solche Wiederholung einer Schleife auch "Iteration" genannt). Dies ist besonders nützlich, wenn du Code hast, der viele Male hintereinander durchgeführt werden muss und Du nicht 100-mal hintereinander den selben Code copy-pasten willst.
In R gibt es drei verschiedene Arten von Schleifen, die für verschiedene Usecases verwendet werden:
for
Die for Schleife gibt es so ziemlich in jeder Programmiersprache.
Kurzgesagt funktioniert sie so: Für die Variable i in einer Sequenz führe Code aus.
In R-Code sieht es ungefähr so aus:
for (i in sequence) {
## ...code
}
Um die for Schleife besser verstehen zu können,
müssen wir uns ein Beispiel anschauen:
for (i in 1:10) {
print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
i ist die Variable, die jeden Wert innerhalb einer Sequenz animmt.
In diesem Beispiel ist i beim ersten mal durchlaufen der Schleife 1
und dann beim zweiten Mal 2 und so weiter bis 10.
i nimmt dabei pro Durchlauf jeden einzelnen Wert der Sequenz 1:10 an.
Wir sind nicht gezwungen unsere Schleifen-Variable i zu nennen.
Wir können unsere Schleifenvariable so benennen,
wie es gerade am besten zu unserem Code passt.
Für jeden Durchlauf wird der Code innerhalb der {} Klammern ausgeführt,
deswegen sehen wir in der Konsole 1 bis 10 je in einer Zeile.
Der Code innerhalb der {} kann so lang sein, wie wir wollen.
Lass uns zum Beispiel eine for Schleife schreiben,
die von 1-12, für die Monate in einem Jahr, geht und
in die Konsole schreibt, ob es ein Sommer oder Winter Monat ist:
for (month in 1:12) {
if (month >= 6) {
print(paste("Summer, month", month))
} else {
print(paste("Winter, month", month))
}
}
[1] "Winter, month 1"
[1] "Winter, month 2"
[1] "Winter, month 3"
[1] "Winter, month 4"
[1] "Winter, month 5"
[1] "Summer, month 6"
[1] "Summer, month 7"
[1] "Summer, month 8"
[1] "Summer, month 9"
[1] "Summer, month 10"
[1] "Summer, month 11"
[1] "Summer, month 12"
next und break
Innerhalb einer Schleife können wir auch next und break benutzen.
next tut jeden weiteren Code, der kommen würde, überspringen und
geht direkt in die nächste Schleifeniteration.
## Schleife von 1 bis 10
for (i in 1:10) {
## Wenn es durch 3 teilbar ist, dann überspringe
if (i %% 3 == 0) {
next
}
## Sonst printe die Zahl in der Konsole
print(i)
}
[1] 1
[1] 2
[1] 4
[1] 5
[1] 7
[1] 8
[1] 10
break bricht eine Schleife sofort ab und es wird keine weiter Iteration und kein
weiterer Code der Schleife ausgeführt.
## Schleife von 1 bis 10
for (i in 1:10) {
## Wenn i gleich 5 ist, dann breche die Schleife ab
if (i == 5) {
break
}
## Sonst printe die Zahl in der Konsole
print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
mit Zahlen-Vektoren
Im vorherigen Kapitel haben wir schon Vektoren als Sequenz verwendet,
denn bedenke 1:10 ist nichts anders als c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).
Aber wir können auch natürlich ohne Probleme jeden beliebigen Vektor benutzen
um über diesen zu iterieren.
Zum Beispiel können wir über einen Zahlenvektor mit verschieden Zahlen gehen und
jede Zahl addieren
und das Ergebnis in einer neuen summe Variable speichern.
## Der Zahlenvektor
numbers <- c(1, 43, 56, 134, 1230, 34, 5, 20)
## Das Ergebnis der Addition. Beginnt bei null.
summe <- 0;
## Gehe über numbers Vektor
for (number in numbers) {
## Überschreibe `summe` mit dem alten Wert von `summe` plus `number`
summe = number + summe
}
## Gebe das Ergebnis in der Konsole aus
print(summe)
[1] 1523
Das selbe macht auch die sum Funktion, also können wir ganz einfach unser Code testen:
sum(numbers)
[1] 1523
mit String-Vektoren
Vektoren können nicht nur aus Zahlen bestehen.
Es gibt auch Vektoren die aus Strings oder Booleans bestehen.
Egal aus welchen Datentyp der Vektor auch besteht,
wir können alle Arten von Vektoren mit einer for Schleife verwenden.
Wir müssen nur sicher gehen, dass unser Code keinen Error wirft,
denn bedenke man kann nicht plus, minus oder geteilt mit Strings nehmen.
## character/string Vektor
animals <- c("koala", "cat", "dog", "panda")
for (animal in animals) {
print(animal) ## Gebe das Tier in die Konsole aus
}
[1] "koala"
[1] "cat"
[1] "dog"
[1] "panda"
mit Listen
Über Listen kann man auch Schleifen, aber hier müssen wir vorsichtig sein! Während bei einem Vektor nur ein Datentyp erlaubt ist, ist in einer Liste alles erlaubt. Zahlen, Vektoren, Listen, Booleans, alles kann hier auftauchen. Über Listen zu Schleifen kann also zu sehr unstabilen Code führen, der schnell crashen kann, wenn wir nicht 100%ig sicher sein können, dass unser Code auch für den jeweiligen Datentyp funktioniert.
my_list <- list(c(5, 2, 4),
"cat",
"dog",
TRUE,
3,
c("koala", "panda", "rabbit"))
for (item in my_list) {
print(item)
}
[1] 5 2 4
[1] "cat"
[1] "dog"
[1] TRUE
[1] 3
[1] "koala" "panda" "rabbit"
Um sicher zu gehen mit welchen Datentyp man es zu tun hat,
könnte man die is. Funktionen benutzen.
Für so ziemlich alle verschiedenen Datentypen gibt es eine is Funktion,
die uns einen Boolean zurück gibt.
Bei Zahlen würde man is.numeric benutzen oder bei Strings is.character.
my_list <- list(c(5, 2, 4),
"cat",
"dog",
TRUE,
3,
c("koala", "panda", "rabbit"))
for (item in my_list) {
if (is.numeric(item)) {
print("is number")
} else if (is.character(item)) {
print("is string")
} else if (is.logical(item)) {
print("is boolean")
} else {
print("i dont know which type it is...")
}
}
[1] "is number"
[1] "is string"
[1] "is string"
[1] "is boolean"
[1] "is number"
[1] "is string"
Oder man könnte auch die typeof Funktion, die uns einen String mit den Namen von dem Datentyp zurückgibt.
my_list <- list(c(5, 2, 4),
"cat",
"dog",
TRUE,
3,
c("koala", "panda", "rabbit"))
for (item in my_list) {
## speicher typeof in einer Variable,
## damit wir es nicht wieder und wieder machen müssen
type <- typeof(item)
if (type == "integer" || type == "double") {
print("is number")
} else if (type == "character") {
print("is string")
} else if (type == "logical") {
print("is boolean")
} else {
print(paste("i dont know which type it is:", type))
}
}
[1] "is number"
[1] "is string"
[1] "is string"
[1] "is boolean"
[1] "is number"
[1] "is string"
Vielleicht ist Dir diese Zeile im Codebeispiel aufgefallen
if (type == "integer" || type == "double").
In vielen Programmiersprachen zieht man einen Unterschied zwischen
Ganzahlen, den sogenannten Integer, und den Fließkommazahlen, den Double.
Beides sind ganz normale Zahlen wie im Kapitel Number schon besprochen, aber der Computer muss technisch einen Unterschied zwischen beider dieser Zahlen nehmen, denn eine Fließkommazahl mit den Nachkommastehlen braucht mehr Platz zum Speichern als eine normale, ganze Zahl.
R tut automatisch jede Zahl, die wir eintippen als double abspeichern,
damit wir nicht nachdenken müssen wenn wir solch eine Zeile Code schreiben:
1 / 0.3
mit Matrizen
Wir können über eine Matrix genau so schleifen wie wir es beim Vektor getan haben.
## Erstelle eine 3x3 Matrix mit den Werten von 1 bis 9
my_matrix <- matrix(1:9, nrow=3, ncol=3)
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
for (i in my_matrix) {
print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
Obwohl eine Matrix zwei-dimensional ist, kann man es auch als ein Vektor repräsentieren und das ist auch was im Hintergrund passiert, weswegen wir syntaktisch auf nichts achten müssen.
Wie man auch an dem Output der Schleife erkennen kann, wenn man über eine Matrix schleift, dann geht man von Spalte zu Spalte bis man durch die ganze Matrix durchgeschleift ist.
Wir können aber auch Zeile für Zeile und
jeweils bei jeder Zeile über alle Spalte gehen.
Dies ist ein bisschen komplizierter, denn hierfür müssen wir zwei for Schleifen
ineinander schreiben:
## Eine 3x3 Matrix
my_matrix <- matrix(1:9, nrow=3, ncol=3)
## Die erste `for` Schleife geht über die Zeilen
for (row in 1:nrow(my_matrix)) {
## Die zweite `for` Schleife geht für die jeweilige Zeile über alle Spalten
for (col in 1:ncol(my_matrix)) {
## Gebe in alle wichtigen Information in der Konsole aus
print(paste('Row', row, 'col', col, 'value', my_matrix[row, col]))
}
}
[1] "Row 1 col 1 value 1"
[1] "Row 1 col 2 value 4"
[1] "Row 1 col 3 value 7"
[1] "Row 2 col 1 value 2"
[1] "Row 2 col 2 value 5"
[1] "Row 2 col 3 value 8"
[1] "Row 3 col 1 value 3"
[1] "Row 3 col 2 value 6"
[1] "Row 3 col 3 value 9"
In der ersten Schleife for (row in 1:nrow(my_matrix)) { ... } gehen wir
über alle Zeilen der Matrix. nrow(my_matrix) gibt die Anzahl der Zeilen zurück,
also in diesem Fall 3.
Der Ausdruck 1:nrow(my_matrix) erzeugt eine Sequenz von 1 bis 3.
Die zweite Schleife for (col in 1:ncol(my_matrix)) { ... } geht über alle Spalten der Matrix.
Ähnlich wie zuvor gibt ncol(my_matrix) die Anzahl der Spalten zurück
und 1:ncol(my_matrix) erzeugt wieder eine Sequenz von 1 bis 3.
print(paste('Row', row, 'col', col, 'value', my_matrix[row, col])) wird für jede Zelle
in der Matrix ausgeführt.
paste ist eine Funktion, die mehrere Zeichenketten zu einer einzigen Zeichenkette verbindet.
In diesem Fall werden die Zeichenketten 'Row', der Wert von row, 'col',
der Wert von col und 'value' und der Wert in der Matrixzelle
my_matrix[row, col] als String zusammengefügt und dann mit print ausgegeben.
Übungsaufgaben
-
Addiere alle ungeraden Zahlen zwischen 1 und 100 miteinander
-
Zähle wie oft ein Buchstabe in einem String vorkommmt
-
Lass den User eine Zahl eingeben und berechne den faktorialen Wert
-
Lass den User ein Text eingeben und zähle wie viele Konsanten und Vokale darin vorkommmen
-
Berechne Medium, Maximum und Minimum von einem Zahlenvektor
-
Lass den User eine Zahl eingeben und addiere alle Zahlen bis null darauf. Sprich wenn der User die Zahl 89 eingibt, dann addiere 88, 87, 86 ... bis 0 auf auf die ursprüngliche Zahl.
-
Tannenbaum
while
Die while Schleife wird verwendet,
um einen Codeblock so lange auszuführen, wie eine bestimmte Bedingung erfüllt ist.
Hier ist die grundlegende Syntax:
while (Bedingung) {
## Code
}
Die Bedingung ist eine logische Aussage (z.B. x < 10),
die entweder TRUE oder FALSE ist.
Solange die Bedingung TRUE ist,
wird der Codeblock in den geschweiften Klammern {} ausgeführt.
x <- 1
while (x <= 5) {
## Gebe x in der Konsole aus
print(x)
## Erhöhe x um 1
x <- x + 1
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
In diesem Beispiel startet x mit dem Wert 1.
Die Bedingung in der while Schleife lautet x <= 5,
was bedeutet "solange x kleiner oder gleich 5 ist".
Innerhalb der Schleife wird zuerst der aktuelle Wert von x mit der print Funktion ausgegeben,
dann wird x um eins erhöht.
Die Schleife wird solange wiederholt, bis x größer als 5 ist.
Wichtig ist, dass wir innerhalb der {} Klammern x um eins erhöhen,
denn sonst würde unsere Bedingung x <= 5 niemals FALSE werden und
unsere while Schleifen würde niemals enden.
Niemals endende Schleifen nennt man auch Endlos-Schleifen.
Endlos-Schleifen
Endlos-Schleifen hören sich erstmal kontraproduktiv an. Warum sollte ich ein Stück Code schreiben, welches niemals endet? Wenn man eine Videospiel programmiert, macht es durchaus Sinn eine Endlos-Schleife zu erstellen, denn man muss 60 oder 100erte Male pro Sekunde das Spielbild updaten und neurendern.
Aber auch in anderen Szenarien kann es durchaus Sinn machen eine Endlos-Schleife
zu benutzen, um dann gegebenfalls mit einem break Statement diese zu abzubrechen,
falls eine Bedingung eingetreten ist, wie zum Beispiel auf eine Antwort
vom User warten, die jederzeit kommen könnte.
Diese Schleife hier läuft solange bis counter den Wert 20.000 erreicht hat:
counter <- 1
while (TRUE) {
print(paste("Iteration", counter))
counter <- counter + 1
if (counter > 20000) break
}
Die Bedingung dieser while Schleife ist TRUE und naja ... diese Aussage ist immer wahr,
aber mit if (counter > 20000) break gehen wir sicher, dass unser PC nicht überhitzt
und unser Programm auch mal beendet wird.
Übungsaufgaben
-
Schreibe eine while-Schleife, die Elemente aus einem Vektor entfernt, bis dieser leer ist.
-
Mit
rnormbekommt man eine zufällige Zahl. Gebe solange die Zufallszahlen in einer Konsole aus, bis eine Zahl größer als 1 kommt. -
Nutze die Schleife von der Aufgabe 2 und füge
nexthinzu, um alle Minuszahlen zu überspringen. -
Lass den User eine Zahl zwischen 1 und 10 erraten. Nachdem 5. Fehlversuch ist das Spiel vorbei.
-
Schreibe ein Multiplikationsspiel mit Zufallsaufgaben und sobald der User das Ergebnis falsch eingibt, ist das Spiel vorbei.
repeat
Anstelle while (TRUE) { ... } können wir auch repeat benutzen.
Eine repeat Schleife ist dabei immer endlos und wir müssen sicher stellen,
dass wir ein break Statement haben.
counter <- 1
repeat {
print(paste("Iteration", counter))
counter <- counter + 1
if (counter > 20000) break
}
Ob wir jetzt while (TRUE) oder repeat benutzen,
macht am Ende des Tages keinen großen Unterschied.
Ich bevorzuge repeat, weil man sich,
im Falle einer Endlosschleife, ein paar Schriftzeichen sparen kann,
aber du kannst es so machen wie du es willst!
Funktionen
Wir haben im Laufe dieses Buches schon viele verschiedene Funktionen benutzt.
Die print Funktion gibt eine Zeile Text in der Konsole aus,
die list Funktion erstellt eine neue Liste,
die matrix Funktion erstellt eine neue Matrix,
die c Funktion, um mehrere Werte zu einem Vektor zu kombinieren, und und und...
Ich glaube du verstehst was ich meine.
In diesem Kapitel werden wir lernen, wie wir unsere eigene Funktionen schreiben können. Durch eigene Funktionen können wir unser Code in wiederverwendbare logische Blöcke einteilen, um somit unseren Code modularer aber auch verständlicher zu machen.
Eigene Funktionen schreiben
Lass uns eine Funktion schreiben, die Hello World in die Konsole schreibt:
hello <- function() {
print("Hello World")
}
Mit dem Keyword function gefolgt von () Klammern erstellen wir eine komplett neue, eigene Funktion.
In den {} Klammern, den Funktionskörper,
müssen wir dann nur noch Code schreiben,
der ausgeführt werden soll,
jedes Mal wenn die Funktion aufgerufen wird.
Wie dir wahrscheinlich schon aufgefallen ist,
wird die Funktion in einer Variable abgespeichert.
Um sie aufzurufen, benutzen wird den Namen dieser Variable gefolgt von () Klammern.
hello()
[1] "Hello World"
Parameter
Wenn wir jetzt eine hello Funktion schreiben wollen,
die für viele verschiedene Personen "Hello <Irgendein Name>" in die Konsole ausgibt, dann könnten wir
für alle verschiedenen Fälle eine komplett neue Funktion schreiben, wie z.B. hello_marc und hello_sven usw.
oder wir könnten unserer bestehende hello Funktion erweitern,
indem wir bei jedem Aufruf auch einen Namen mitgeben.
hello <- function(name) {
print(paste("Hello", name))
}
Innerhalb der () Klammern einer Funktion kommen die sogenannten Funktionsparameter.
Wir könnten beliebig viele Parameter für unsere Funktion definieren.
Innerhalb unseres Funktionskörpers, die {} Klammern,
können wir jetzt den Parameter name wie als wäre es eine Variable verwenden,
aber wir müssen jetzt immer, wenn wir die Funktion hello benutzen wollen,
innerhalb der () Klammern auch einen Namen mitgeben:
hello("Marc")
[1] "Hello Marc"
Wenn wir das nicht machen, dann bekommen wir einen Error,
da unser Code erwartet dass name existiert:
hello()
Error in hello() : argument "name" is missing, with no default
Die Errormessage sagt schon with no default.
Wenn wir einen Defaultwert für den Paramter name definieren,
dann müssten wir nicht unbedingt jedes Mal einen Namen in den Funktionsaufruf mit reinschreiben.
hello <- function(name = "stranger") {
print(paste("Hello", name))
}
Jetzt würde unsere Funktion auch mit nur hello() funktionieren:
hello()
[1] "Hello stranger"
Neue Variablen innerhalb einer Funktion
Nicht nur die Funktionsparameter innerhalb der () Klammern,
sondern auch neue Variablen, die wir innerhalb des Funktionsköprers definieren,
existieren nur dort.
Wenn wir versuchen sollten außerhalb der Funktion auf diese Variablen zuzugreifen,
dann bekommen wir einen Error.
hello <- function(name = "stranger") {
## Erstelle eine neue Variable innerhalb der "hello" Funktion
greeting <- paste("Hello", name)
print(greeting)
}
greeting ## Existiert außerhalb der Funktion nicht
Error: object 'greeting' not found
Named Parameters (Benannte Parameter)
Als wir Matrizen in Kapitel 5.5. gelernt haben, haben wir auch zum aller ersten Mal benannte Parameter kennengelernt.
Schauen wir uns nochmal unsere hello Funktion an:
hello <- function(name = "stranger") {
print(paste("Hello", name))
}
Wenn wir die Funktion aufgerufen haben, dann mussten wir hello("marc") oder ähnliches eintippen.
In der Funktionsdefinition function(name = "stranger") sagen wir, wie der Funktionsparameter
der hello Funktion lautet und so können innerhalb des Funktionskörpers darauf zugreifen.
Aber wir können auch beim Funktionsaufruf den Namen der Parameter verwenden:
hello(name = "Marc")
Bei mehr als einem Parameter macht es durchaus Sinn diese Syntax zu bevorzugen,
da sie zur mehr Klahrheit in unseren Code führt, z.B. bei einer hello Funktion,
die ein Begrüßung und einen Namen erwartet:
hello <- function(greeting = "Hallo", name = "stranger") {
print(paste(greeting, name))
}
hello(greeting="Hi", name="Marc")
[1] "Hi Marc"
Die Reihenfolge der Parameter ist wichtig!
Wenn wir eine Funktion "callen" (coole Programmierersprache für "aufrufen"),
dann ist die Reihenfolge, in welcher wir die Parameter innerhalb der () Klammern schreiben,
wichtig.
Unsere hello Funktion nimmt zwei Parameter; greeting und name:
hello("hi", "marc")
[1] "hi marc"
Wenn wir die Reihenfolge vertauschen, dann werden auch die Werte den falschen Parametern überwiesen:
hello("marc", "hi")
[1] "marc hi"
R kann nicht für uns automatisch erkennen, was ein "name" und was ein "greeting" ist. Aber mit Named Parameter, können wir R genau sagen, welchen Wert wir welchen Parameter zuweisen wollen und dann ist auch die Reihenfolge komplett egal:
hello(name="marc", greeting="hi")
[1] "hi marc"
Aber wir können auch Named Parameters für nur einzelne Werte unserer Wahl nehmen:
hello("hi", name="marc")
[1] "hi marc"
Da wir bei unserer hello Funktion zwei Parameter mit Defaults haben,
können wir auch Named Parameters verwenden,
um nur einen der beiden Werten zu überschreiben:
hello(name="marc")
[1] "hi marc"
Wenn wir das name= vergessen würde, kannst dir ja vorstellen was dann passieren würde?
hello("marc")
[1] "marc stranger"
return
Funktionen können auch auch etwas zurückgeben, aber was bedeutet das überhaupt?
Stell dir eine Funktion als ein Helfer vor. Wenn Du eine Funktion aufrufst, dann schickst Du den Helfer los, um eine Aufgabe zu erledigen. Bis jetzt haben wir den Helfer immer nur los geschickt und gegebenfalls in die Konsole schreiben lassen. Nun kann man aber jetzt den Helfer los schicken und auf ihn warten bis er etwas zurück bringt.
Für das "Zurückbringen" brauchen wir das return() Statement.
do_stuff <- function() {
do_something()
return(some_value)
}
Vorher sahen unserer Funktionen in etwa so aus:
do_stuff <- function() {
do_something()
print_something_to_console()
}
Zum Beispiel hier eine add Funktion, die zwei Werte nimmt, miteinander addiert und
in die Konsole ausgibt:
add <- function(a, b) {
print(paste("a + b =", a + b))
}
add(5, 3)
[1] "a + b = 8"
Aber wenn wir mit dem Ergebnis von add(5, 3) weiter rechnen wollen,
dann funktioniert dies nicht, weil unsere add Funktion,
nicht das Ergebnis zurückgibt, sondern etwas in die Konsole ausgibt.
Mit return(a + b) innerhalb des Funktionskörpers können wir das Ergebnis von a + b,
wo auch immer die Funktion aufgerufen wird, zurückgeben, um damit weiterarbeiten zu können.
Lass zum Beispiel das Ergebnis von add mal 10 nehmen:
add <- function(a, b) {
return(a + b)
}
x <- add(5, 3) * 10
print(x)
[1] 80
Unsere add Funktion tut jetzt das Ergebnis von a + b zurückgeben,
dass bedeutet, dass das add(5, 3) von der Zeile, wo wir x deklarieren,
evaluiert und mit dem Ergebnis ausgetauscht wird.
Sprich R macht aus x <- add(5, 3) * 10 => x <- 8 * 10.
Automatisches "returnen"
R tut automatisch den letzten Wert einer Funktion oder eines {} Blockes "returnen".
Dies beudetet, dass dies hier:
add <- function(x, y) {
return(x + y)
}
dass selbe tut, wie dieses hier:
add <- function(x, y) {
x + y
}
Dies nennt man auch "implicit return" in Programmierfachchinesisch, weil es implizit und nicht explizit ist.
Apropos auch ganz verrückt, wenn wir eine Funktion haben, die sich in eine Zeile schreiben lässt, dann können wir sie auch so schreiben:
add <- function(x, y) x + y
return als Exitpoint
Warum gibt es dann return wenn R automatisch die letzte Zeile einer Funktion "returnt"?
return dient auch als Exitpoint einer Funktion.
Sobald unsere Funktion eine Zeile erreicht hat die "return" in sich hat,
werden alle folgenden Zeilen danach nicht mehr ausgeführt:
can_drive <- function(age) {
if (age < 18) {
return(FALSE)
} else {
return(TRUE)
}
print("This line will never be executed!")
}
can_drive(15)
can_drive(29)
[1] FALSE
[1] TRUE
return richtig zu verwenden, kann Deine Skills als Programmieranfänger verzehnfachen.
Also wenn Du es nicht auf Anhieb alles verstanden hast,
sei nicht demotiviert.
Manche Dinge brauchen einfach länger zum Verstehen.
Les Dir dieses Kapitel nochmal in Ruhe durch oder schau Youtube Videos,
die genau dieses Thema versuchen zu erklären.
Vetrau mir es wird sich für Dich lohnen.
Übungsaufgaben
-
Schreibe eine Funktion, die zwei Zahlen addiert und das Ergebnis zurückgibt.
-
Erstelle eine Funktion, die das Minimum und das Maximum aus einer Liste von Zahlen zurückgibt.
-
Schreibe eine Funktion, die eine Zahlenfolge nimmt und die geraden Zahlen zurückgibt.
-
Erstelle eine Funktion, die einen String und eine Zahl nimmt und den String so oft wiederholt wie die Zahl angibt.
-
Schreibe eine Funktion, die eine Celsius-Temperatur in Fahrenheit umwandelt. Die Formel um Celcius zu Fahrenheit umzuwandeln lautet:
°F = (°C x 1.8) + 32
plot
Herzlichen Glückwunsch, wenn du es bis hier in geschafft hast, dann hast so gut wie alles gelernt was man braucht, um sich Programmierer nennen zu dürfen.
Wir fangen jetzt erst an mit dem richtig coolen Stuff, glaub mir ;)
Mit der plot Funktion kann man ganz easy Grafen erstellen.
Weißte noch die Dinger, die man damals in der Schule immer selber malen musste?
Japp, die können wir ganz easy generieren lassen und
zu unseren Vorstellungen designen.
x und y
Die plot Funktion braucht nur zwei Sachen, um zu funktioneren:
Und zwar Werte für jeweils der x- und y-Achse.
plot(c(1,2,3,4,5),
c(1,4,9,16,25))
oder auch mit named arguments:
plot(x=c(1,2,3,4,5),
y=c(1,4,9,16,25))
Cool, oder? Spiel mit den Werten, um ein besseres Gefühl für das "plotten" zu bekommen.
Im Codeabschnitt von oben haben wir die mathematische Funktion y = x^2 erstellt,
aber wir können dies auch "mathematischer" und mit weniger Code erstellen:
x <- 1:10
plot(x=x,
y=x^2)
Unser Datenset kann beliebig groß sein, aber pass auf! Je nachdem wie langsam oder alt dein Computer ist, kann diese Operation länger dauern und dein Computer in eine Heizung umwandeln, aber vielleicht ist das auch das Feature, welches du programmieren möchtest, dann tobe dich aus!
x <- 1:1000
plot(x=x,
y=x^2)
Plot beschriften
Damit man unser Grafen auch verstehen kann, können wir ihm einen Titel geben.
Der Parameter dafür ist main und mit sub können wir der generierten Grafik auch einen Untertitel geben.
x <- 1:10
plot(x=x,
y=x^2,
main="Maintitle: Quadratische Funktion",
sub="Subtitle: y=x^2")
plot tut automatisch den Ausdruck nach x und y als String nehmen,
um damit die x und y Achse zu beschriften,
aber wir können auch mit den Parametern ylab und xlab selber bestimmen,
wie wir die Achsen beschriftet haben wollen:
x <- 1:10
plot(x=x,
y=x^2,
main="Maintitle: Quadratische Funktion",
sub="Subtitle: y=x^2",
ylab="Y Achse",
xlab="X Achse")
type
Wir brauchen aber keine 1000 Punkte um eine schöne Linie in unseren Grafen zu bekommen.
Die plot Funktion erlaubt es uns mit dem type Parameter
zu bestimmen wie wir die Punkte miteinander verbindet haben wollen.
Mit type="l" können wir der plot Funktion sagen,
dass sie alle Punkte mit einer Linie verbinden soll.
x <- 1:10
plot(x=x,
y=x^2,
main="Maintitle: Quadratische Funktion",
sub="Subtitle: y=x^2",
ylab="Y Achse",
xlab="X Achse",
type="l")
Hier ist eine Liste aller möglichen Werte, die wir als type an die plot Funktion weiter
geben können.
| p | Punkte |
| l | Linien |
| c | Linien mit Lücken |
| b | Punkten mit Linien |
| o | Punkte mit Linie drüber |
| s | Stufen |
| h | Histogram Style |
| n | nichts |
Spiele mit diesen Werten um ein Gefühl zu bekommen, was alles möglich ist!
col
Mit dem col Parameter können wir sagen welche Farbe wir für die Linie und Punkte unseren Grafen geben möchten.
Um zu sehen welche Farben uns in R zur Verfügung stehen, können wir mal die colors Funktion aufrufen.
colors()
[1] "white" "aliceblue" "antiquewhite" "antiquewhite1" "antiquewhite2"
[6] "antiquewhite3" "antiquewhite4" "aquamarine" "aquamarine1" "aquamarine2"
[11] "aquamarine3" "aquamarine4" "azure" "azure1" "azure2"
[16] "azure3" "azure4" "beige" "bisque" "bisque1"
[21] "bisque2" "bisque3" "bisque4" "black" "blanchedalmond"
[26] "blue" "blue1" "blue2" "blue3" "blue4"
[31] "blueviolet" "brown" "brown1" "brown2" "brown3"
[36] "brown4" "burlywood" "burlywood1" "burlywood2" "burlywood3"
[41] "burlywood4" "cadetblue" "cadetblue1" "cadetblue2" "cadetblue3"
[46] "cadetblue4" "chartreuse" "chartreuse1" "chartreuse2" "chartreuse3"
[51] "chartreuse4" "chocolate" "chocolate1" "chocolate2" "chocolate3"
[56] "chocolate4" "coral" "coral1" "coral2" "coral3"
Und noch viele viele mehr...
x <- 1:10
plot(x=x,
y=x^2,
main="Maintitle: Quadratische Funktion",
sub="Subtitle: y=x^2",
ylab="Y Achse",
xlab="X Achse",
type="l",
col="pink")
Aber man kann nicht nur die Farbe der Punkte und Linien selber verändern, sondern wir können alle Fraben in unserem Plot anpassen!
| col | Symbolfarbe |
| col.main | Fontfarbe für den Titel |
| col.sub | Fontfarbe für den Untertitel |
| col.lab | Farbe für die label der X und Y Achse |
| col.axis | Farbe für die Achsen |
| fg | Vordergrundfarbe für die Box des Grafen |
TODO freaky plot pic
Wir können aber auch jede mögliche Farbe verwenden,
selbst wenn diese nicht in R vordefiniert existieren sollte.
Man kann den Computer auch Farben in Form eines Hexadecimal geben.
Zum Beispiel die Farbe weiß wäre als Hexadecimal #FFFFFF oder schwarz wäre #000000.
Wie der Hexadecimal-Farben-Code aufgebaut ist, würde den Rahmen dieses Buches sprängen und ist auch total irrelevant, aber wenn es mal eine Farbe gibt, die du unbedingt verwenden möchtest, dann gibt es hunderte Online-Tools, die dir dabei helfen, den Code für deine Farbe heraus zu spucken.
Punkte eines Plots hübsch machen
Mit pch können wir das Zeichen für einen Punkt ("Point Character") in unserem Plot ändern
und mit cex können wir die Größe der Punkte anpassen.
cex=2 würde jeden Punkt in unserem Plot doppelt so groß machen, cex=3 dreifach und so weiter.
pch kann einen Wert von 0 bis 25 als number haben.
x <- 1:10
plot(x=x,
y=x^2,
main="Maintitle: Quadratische Funktion",
sub="Subtitle: y=x^2",
ylab="Y Achse",
xlab="X Achse",
type="p",
pch=0,
cex=5)
Linien eines Plots hübsch machen
Dasselbe wie bei Punkten können wir auch mit der Linie eines Plottes machen.
Mit lty bestimmen wir die Art einer Linie und mit lwd die Dicke in Pixel.
lty kann den Wert von 0 bis 6 als number haben.
x <- 1:10
plot(x=x,
y=x^2,
main="Maintitle: Quadratische Funktion",
sub="Subtitle: y=x^2",
ylab="Y Achse",
xlab="X Achse",
type="l",
lty=4,
lwd=5)
mehrere Plots in einem Plot
Manchmal wollen wir mehrere Plots in einem Plot haben.
- mehrere Linien in einem Koordinatensystem
- mehrere Datensets in Form von Punkten in einem Plot
- mehrere Koordinatensystem mit deren eigenen Linien, Punkten und Styles
Diese 3. verschiedenen Möglichkeiten werden wir in diesem Kapitel lernen!
mehrere Linien in einem Plot
Das hier ist unser Anfangsplot, wie vom vorherigen Kapitel, die Funktion y=x^2:
x <- 1:10
plot(x,
x^2,
type="l")
Um eine weitere Linie, zum Beispiel die Funktion y=x^3, hinzuzufügen,
müssen wir nur, nachdem wir die plot Funktion benutzt haben, die lines Funktion aufrufen.
Die lines Funktion hat die selben Parameter wie die plot Funktion,
aber nur die, die wichtig sind um eine Linie darzustellen und zu stylen.
x <- 1:10
plot(x,
x^2,
type="l",
lwd=3)
lines(x,
x^3,
col="gray",
lwd=3)
Wir können so viele Linien hinzufügen wie wir wollen:
x <- 1:10
plot(x,
x^2,
type="l",
lwd=3)
lines(x,
x^3,
col="gray",
lwd=3)
lines(x,
x,
col="red",
lwd=3)
Wie dir aber vielleicht aufgefallen ist,
wird unser Koordinatensystem nicht für die neuen Linien angepasst.
Dies liegt daran, dass die lines Funktion die neuen Linien einfach
in das vorhandene Koordinatensystem reinmalt ohne dieses zu verändern.
Im Kapitel ggplot werden wir lernen, wie wir noch viel besser und hübschere Plots erstellen können.
mehrere Punkte in einem Plot
TODO erstelle zwei Fakedatasets mit Leuten die "gesund" oder "ungesund" leben und in welchem Alter sie sterben.
Um noch mehr Punkte zu einem Plot hinzuzufügen, brauchen wir die points Funktion.
Die points Funktion verhält sich im Grunde genau so wie lines, nur wieder hier,
dass man nur die Parameter verwenden kann, die man braucht um Punkte zu stylen.
healthy = sample(60:100, 100, replace=TRUE)
unhealthy = sample(40:90, 100, replace=TRUE)
plot()
points()
mehrere verschiedene Plots aufeinmal
Mehrere Koordinatensystem mit deren eigenen Linien in einem generierten Bild? Mega easy!
Die par Funktion mit dem mfrow Parameter um ein Grid zu erstellen,
dabei wird mit einem Vektor die Zeilen und die Spalten, in dieser Reihenfolge auch, definiert.
par(mfrow=c(2,2))
x <- 1:100
plot(x,
x,
main="y=x",
type="l")
plot(x,
x^2,
main="y=x^2",
type="l")
plot(x,
x^3,
main="y=x^3",
type="l")
plot(x,
x^6,
main="y=x^6",
type="l")
dev.off()
Nach par können wir anfangen unsere Plots zu generieren und
diese werden dann auch in dieser Reihe in das Grid eingesetzt,
welches wir mit mfrow definiert haben.
Dir ist bestimmt das dev.off() am Ende aufgefallen, oder?
dev.off() resetet alles, was die Renderengine der Plotgenerierung beeinflussen könnte.
Dies ist wichtig, sodass wir mögliche Bugs später im Code verhindern können,
denn hunderte Zeilen später wird wohl keiner mehr wissen,
warum sein neu generierter Plot nur noch 1/4 so groß ist wie sonst.
Legende im Plot
Wenn wir uns diesen Plot hier anschauen:
x <- 1:10
plot(x,
x^2,
type="l",
lwd=3)
lines(x,
x^3,
col="gray",
lwd=3)
lines(x,
x,
col="red",
lwd=3)
Dann kann man vielleicht ein Problem erkennen, oder? Man kann nicht wirklich nachvollziehen welche Linie was ist. Wir brauchen eine Legende!
legend()
x <- 1:10
plot(x,
x^2,
type="l",
lwd=3)
lines(x,
x^3,
col="gray",
lwd=3)
lines(x,
x,
col="red",
lwd=3)
Plots speichern
bmp, jpeg, png, tiff, pdf, ps
jpeg
jpeg(file="path/filename",
width=x,
height=y,
units="px",
bg="white",
quality=100)
plot()
dev.off()
png
png(file="path/filename",
width=x,
height=y,
units="px",
bg="white",
quality=100)
plot()
dev.off()
pdf(file="",
width="", # der graphik
height="",
paper="a4",
title="") # "letter", etc.
Übungsaufgaben
-
Erstelle seperate Plots für jeweils den Funktionen
y=2x,y=x^2 + 10undy=x^5 -
Packe alle 3 seperate Plots von der ersten Aufgabe in einem einzigen Plot. Mit verschiedenen Farben für die Funktionen und einer passenden Legende.
-
Die 3 Funktionen der ersten Aufgaben sollen in verschiedenen Koordinatensystem und einem Grid von 3 Zeilen und 1 Spalte angezeigt werden.
-
Speicher den Plot von der letzten Aufgabe als jpeg und pdf ab.
ggplot
Text Mining
Was ist Text Mining?
- Text lesen
- Text reinigen
- Text analysieren
Text lesen
Als aller erstes muessen wir das tm Package installlieren und
dann in unseren Script importieren.
install.packages("tm")
library(tm)
Mit readLines können wir eine Textdatei lesen:
text <- readLines("path/to/file")
Der Funktion readLines müssen wir den Pfad zur unserer Textdatei geben.
Dabei ist der Pfad relativ, dies heißt, dass wir nicht den kompletten Pfad
mit C:\\\\... oder so angeben müssen, sondern nur den restlichen Pfad ab
dem Beginn des Projektordners.
In anderen Worten, wenn unsere Datei in einem Ordner "books" liegt,
dann ist der Pfad books/<Dateiname>.txt.
In R werden Ordner innerhalb eines Pfades mit einem / gekennzeichnet.
readLines gibt uns einen Textvektor wieder,
wo jedes Element einer Zeile in dem gelesenen Textdokument entspricht.
Anstelle einen Pfad angeben zu müssen, können wir auch die Funktion file.choose verwenden.
Diese öffnet jeh nach Platform einen Filechooser Dialog,
wo wir dann die Datei aussuchen können.
text <- readLines(file.choose())
Um zu verstehen wie readLines funktioniert,
sind hier die ersten Zeilen unserer text Variable,
die wir mit Hilfe der head Funktion bekommen.
head(text)
Output
Jetzt haben wir unseren Text gelesen und in der Variable text abgespeichert,
aber arbeiten können wir damit noch nicht.
Bevor wir aber mit dem nächsten Schritt "Reinigung" anfangen können,
müssen wir unserer text Variable von einem Textvektor zu einem Corpus umkonvertieren.
text <- readLines("./path/to/file.txt")
text <- VectorSource(text)
text <- Corpus(text)
Hier konvertieren text erstmal zu einem VectorSource.
VectorSource ist ein spezielles Objekt vom tm Package der,
den Rohtext nimmt und in einer Datenstruktur packt,
die es einfacher macht den gelesenen Text in ein neuen Datentyp,
den Corpus, auch vom tm Package, zu konvertieren.
Dies müssen wir machen, weil es die weitere Arbeit für uns einfacher machen wird.
Aber was ist überhaupt ein Corpus?
Ein Corpus ist ein Objekt, welches mehrere Dokumente auf einmal in sich speichern kann.
In den nächsten Schritten werden wir jetzt den gelesenen Text weiter aufräumen,
um ungewünschte Zeichen wie Punkt, Komma, Zahlen, Links etc. zu entfernen.
Da ein Corpus auch mehrere Texte (Buecher, Artikel etc.) aufeinmal in sich tragen kann,
können wir all diese Prozesse auf ein Corpus Objekt ausführen,
anstelle es für jeden einzelnen Text einzelnd machen zu müssen.
Für die Bereinigung unseres Textes bietet das tm Package mehrere Funktionen an,
die perfekt auf ein Corpus Objekt angewendet werden können.
Text aufräumen
Jetzt können wir anfangen den Textcorpus weiter zu aufzuräumen:
text <- tm_map(text, content_transformer(tolower))
text <- tm_map(text, stripWhitespace)
text <- tm_map(text, removePunctuation)
text <- tm_map(text, removeNumbers)
text <- tm_map(text, removeWords, stopwords("english"))
TODO was macht tm_map und content_transformer.
Zur guter letzt konvertieren jedes Dokument,
welches in unserem Corpus gespeichert worden ist,
zu einem TextDocument Objekt mit der Funktion PlainTextDocument.
Dies muessen wir machen, weil in einem Corpus nicht nur
reine .txt Dateien gespeichert werden koennen,
sondern auch .html oder .pdf Dateien
und diese wollen wir als normale Textdokumente haben.
text <- tm_map(text, PlainTextDocument)
Als letzter Aufräumschritt fehlt nur noch das stemming.
Fuer das stemming benutzen wir das SnowballC package,
welches sich perfekt für englische Texte anbietet.
Als erstes müssen wir dafuer SnowballC installieren und importieren.
install.packages("SnowballC")
library(SnowballC)
tm_map(text, stemDocument)
Visualisierung
als erstes muss zu einer termdocumentmatrix umgewandelt werden
text_matrix <- TermDocumentMatrix(text)
## text_matrix <- DocumentTermMatrix(text)
text_matrix <- as.matrix(text_matrix)
text_matrix <- sort(rowSums(text_matrix), decreasing=T)
text_df <- data.frame(word=names(text_matrix), freq=text_matrix)
Worldcloud
set.seed(1234)
wordcloud(words = text_df$word,
freq = text_df$freq,
min.freq = 1,
max.words=200,
random.order=FALSE,
rot.per=0.35,
color=brewer.pal(1, "Paired"))
ggplot n-bars
DirSource vs. VectorSource
oder DirSource aber dafuer gibt man der DirSource Funktion den Pfad zum Ordner, den wir gelesen haben wollen.
text <- readLines("./src/SUMMARY.md")
text <- DirSource(text)
text
Jetzt muessen wir den text von einem VectorSource zu einem Corpus umkonvertieren, damit wir daraufhin den Text bereinigen koennen.
text <- readLines("./path/to/file")
text <- VectorSource(text)
inspect(text)
Contributors
Author: Marc Mäurer < marc.maeurer@pm.me >