--- title: "R Einführung" output: html_document --- In diesem Markdown-Dokument lernen wir die Grundlegenden Funktionen der Statistiksoftware R kennen. Hierbei werden wir die folgenden Kapitel behandeln: 1. Datentypen 2. Packages 3. Funktionen 4. Schleifen und Bedingungen 5. Zufallszahlen 6. Plotten Am besten öffnest du ebenfalls ein R Skript und wandelst die aufgeführten Beispiele nach Lust und Laune ab. Da es laut [Wikipedia](https://de.wikipedia.org/wiki/Hallo-Welt-Programm#Geschichte) Tradition ist, starten wir auch diese Einführung mit einem "Hello World!" Programm. ```{r} print("Hello World!") ``` # 1. Datentypen In R gibt es die üblichen Datentypen: - Integer - Double - Complex - Character (String) - Logical (Boolean) - Raw Wobei jedoch Raw sehr selten benutzt wird und deshlab hier nicht wirklich behandelt wird. ## Grundlegendes Mit `typeOf()` oder `classOf()` kann der Datentyp eines Objektes ausgegeben werden. Per default sind alle "Zahlen" als double gespeichert, können jedoch mit einem "L" zu einem Integer umgewandelt werden. Strings werden mit `" "` oder `' '` erstellt. ```{r} typeof(42) typeof(42L) class(42L) typeof(42.0) typeof("Zweiundvierzig") typeof('Zweiundvierzig') typeof(TRUE) typeof(4+2i) charToRaw("Zwei und Vierzig") typeof(charToRaw("Zwei und Vierzig")) ``` Mit `#` wird ein Kommentar markiert, d.h. der Interpreter ignoeriert was nach # kommt. ```{r} # wichtige Inforamtion zum Verstehen des Codes ``` Selbstverständlich können Werte auch in Variablen gespeichert werden. Dies geschieht mit `=` oder `<-`, wobei letzteres geläufiger ist. Durch `x<-3` wird eine Variable x mit dem Wert 3 erstellt. Durch einfassen in Klammern, `(x<-3)` wird die Variable direkt ausgegeben. Dies ist äquivalent zu `x` in einem Codeblock. Mit `print(x)` werden ebenfalls Konsolenausgaben erzeugt. ```{r} x<-3 # keine Ausgabe (x<-3) # Ausgabe x # Ausgabe print(x) # Ausgabe ``` Für numerische Datentypen funktioneren die Grundrechenarten wie zu erwarten. Der Modulo-Operator wird in R mit `%%` aufgerufen und mit `%/%` wird ganzzahlig geteilt. ```{r} 3+3 16-4 7*9.1 4/2 5%%2 3%/%2 3%/%4 ``` Logicals nehmen die Werte `True` und `FALSE`an und können mit `T` bzw. `F` abgekürzt werden. Wie üblich werden sie mit `x & y`, `x | y`, ` ! x` sowie `xor(x,y)` verarbeitet. Mit `isTRUE()` bzw. `isFALSE` lässt sich der Zustand des Boolens abfragen. Hier ist jedoch die Besonderheit zu beachten, dass bei der Verwendung von Vektoren (nächster Abschnitt) im Falle von ` & ` und ` | ` wird der Operator elementweise interpretiert wird. ```{r} x<-TRUE y<-FALSE x&y x|y !x xor(x,y) isTRUE(x) isFALSE(x) c(T,T,T) & c(F,T,T) c(T,T,T) & c(T,T,T) ``` Vergleichsoperatoren vergleichen zwei Objekte und liefern einen Boolen zurück. Für Skalare Zahlen sind in R die folgenden Operatoren definiert. ```{r} 1==1 #gleich 1!=1 #ungleich 1<2 #kleiner als 1>2 #größer als 1<=2 #kleiner oder gleich 1>=2 #größer oder gleich ``` ## Vektoren und Matrizen ### Vektoren Wie gerade schon angedeuted ist es selbstverständlich auch möglich Werte in Arrays zu speichern. Arrays sind ein, zwei oder n- dimensionale Objekte in denen sich mehrere Werte speichern lassen. Aufgrund des mathematischen Schwerpunkts benutzen wir hier in dieser Einfühung die Worte Array und Vektor, bzw. 2D Array und Matrix synonym. Mit der Funktion `c( )` können wir einen Vektor erstellen ```{r} x<-c(1,2,3) c(14,23,4)->z #"Pfeilschreibweise" funktioniert auch in die andere Richtung c(x,0,0,0,0,z) ``` Die oben beschriebenen Grundrechenarten können auch auch Vektoren angewendet werden und werden dabei elementweise verstanden. ```{r} x+3 x+x x+c(1,2) #c(1,2) ist kürzer als x, deshalb wird mit dem ersten Element wieder begonnen ``` Die grundlegenden Funktionen wie `log(),exp(),min(),sin(),..` sind in der R Basisversion bereits implementiert und lassen sich einfach auf Vektoren anwenden. Besonders nützlich ist die Funktion `length()`, welche die Länge des Vektors ausgibt. Inforamtionen zu Funktionnen können mit `help(sqrt), ?sqrt, ??sqrt` oder allgemein `help.start()` ausgegeben werden. ```{r} sin(x) log(x) max(x) length(x) help(sqrt) ``` Vektoren mit Zahlenfolgen können so erzeugt werden ```{r} 1:10 #ganzzahlig von 1 bis 10 seq(from=0,to=5.7,by=0.3) #von 0 bis 5,7 in Schritten der Größe 0,3 seq(0,0.5,0.1) #analog ohne "from","to" und "by rep(0,10) # 0 wird 10 mal wiederholt rep(c(1,2),3) rep(c(1,2,3),each=3) rep(c(1,2,3), times=c(2,1,3)) ``` Es gibt verschiedene Arten auf die Einträge von Vektoren zuzugreifen, z.B. über den Index, bzw. über Vektoren mit Indizes (R indiziert ab 1, d.h. auf das erste Element eines Vektors wird mit 1 zugegriffen) ```{r} x<-1:10 x[1] x[c(1,3)] ``` Mit negativen Indizes lassen sich Werte aus Vektoren entfernen ```{r} x<-1:10 x[-1] x[-c(1,3,5)] ``` Auch mithilfe von logischen Vektoren lässt sich auf Elemente eines Vektors zugreifen. Dies vereinfacht den Zugriff auf Einträge mit gewissen Eigenschaften, z.B. alle durch zwei teilbaren Einträge. Hierbei wird nur auf Elemente zugegriffen bei denen der Indexvektor TRUE ist. ```{r} x<-1:10 b<-rep(c(T,T,F,F,T),2) x[b] durchZweiTeilbar<-c(x%%2==0) x[durchZweiTeilbar]=42 x ``` ### Matrizen Wie bereits erwähnt sind Matrizen zweidimensionale Arrays. ```{r} (M<-matrix(1:9,nrow=3)) matrix(10:18,nrow=3,byrow=TRUE) ``` Und der Zugriff erfolgt entweder elementweise, oder spalten/zeilenweise ```{r} M[1,2] M[1,] M[,1] M[,c(1,3)] M[c(1,2),c(1,3)] ``` Auch für Matrizen gibt es unzählige Rechenoperationen. Hier eine kleine Auswahl ```{r} t(M) # Transponieren M%*%M #Matrixmultiplikation A<-matrix(c(1,2,1,2,5,-1,4,8,5),nrow=3) b<-c(1,1,1) solve(A) #Invertieren solve(A,b) #Löse: Ax=b eigen(A) #Eigenwertzerlegung ``` # 2. Packages Bis jetzt haben wir nur mit der Basisfunktion von R gearbeitet, jedoch gibt es unzählige Packages, die Funktionen und Datensätze enthalten. Diese müssen einmalig mit dem Befehl `install.packages()` installiert werden und mit `library()` im Skript geladen werden. Im Laufe dieser Einführung werden wir noch einige nützlichen Packages kennenlernen. ```{r} install.packages("data.table") library(data.table) ``` # 3. Funktionen Durch Funktionen lassen sich gewisse Operationen leichter an verscheidenen Stellen ausführen und der Code wird dadurch überichtlicher. Definiert werden Funktionen in einer Funktionsumgebeung und können dann an einer beliebeigen Stelle im Skript aufgerufen werden. Innerhalb einer Funktion können Variablen definiert werden, diese überschreiben keine gleichnamigen Variablen außerhalb der Funktion, außer dies wird mit einem ` <<- ` erzwungen. Falls es lokal, innerhalb einer Funktion eine Variable nicht gibt, wird auf die globale Variable mit dem gleichen Namen zugegriffen. Mit ` return() ` wird ein Wert zurück gegeben. Ohne das Stichwort return wird die letzte Zeile inerhalb der Funktion zurückgegeben. ```{r} addieren <- function(a,b,c){ a=a+b+c #ohne globales a zu überschreiben return (a) # mit return } addieren2 <- function(a,b,c){ a+b+c #ohne return } ueberschreiben <- function(b){ a<<-3 # überschreiben return ("Überschrieben") } globalerZugriff <- function (x){ return( x+a ) # ohne, dass a übergeben oder in der Funktion definiert wurde } a<-1 addieren(a,2,3) addieren2(1,2,3) a ueberschreiben(a) a globalerZugriff (4) ``` Bei der Übergabe können default Parameter festgelegt werden, die benutzt werden, sofern nichts anderes angegeben wird. ```{r} sinus <- function (x,ampl=1,shift=0){ return (ampl*(sin(x-shift))) } sinus(3) sinus(3,1,0) ``` Außerdem kann die Reihenfolge der Inputparamter vertauscht werden, wenn diese mit ` = ` übergeben werden ```{r} sinus2 <- function (x,ampl,shift) { return (ampl*(sin(x-shift))) } sinus2(3,2,1) sinus2(3,shift=1,ampl=2) ``` Operatoren wie z.B. ` + ` sind auch Funktionen. Durch eine Verschachtelung des Funktionsnamens in ` "% %" ` lässt sich eine Funktion konstruieren, welcher eine Variable vor und eine nach dem Funktionsnamen übergeben wird. ```{r} `%addmult2%` <- function(x,y) { x+2*y } 1:4 %addmult2% 3:6 ``` # 4. Schleifen und Bedingungen ## 4.1 If - Bedingungen Eine If Bedigung hat eine Boolean als Input und führt je nach Zustand eine Operation aus Die einfachste If- Bedingung hat die Gestalt ` if (Bedingung){ ` ` Anweisung ` `}` Dies lässt sich zu einem "Entweder, oder" erweitern ` if (Bedingung){` ` Anweisung ` `}else{` ` Anweisung alternativ` `}` Mit ` else if ` lassen sich mehrere Fälle abfragen, wobei immer der erste Fall der zutrifft eintritt ` if (Bedingung){` ` Anweisung ` `}else if (Bedinung 2){` ` Anweisung alternativ` `}else{` ` Anweisung alternativ 2` `}` ```{r} x<-0 if (x<10){ print("kleine Zahl") } if (x>10){ print("große Zahl") }else{ print("kleine Zahl") } if (x>0){ print("positiv") }else if (x<0){ print("negativ") }else{ print("Null") } ``` Diese Art von If-Bedinung ist jedoch nur skalarwertig. Wollen wir Vektoren abfragen, können wir dies folgendermaßen machen ```{r} x<- -2:10 ifelse(x>0,"positiv","Null oder negativ") ``` ## 4.2 For Schleife In einer for-Schleife läuft ein Zähler über jedes Element eines Vektors. In der Regel sind dies Integer von 1 bis n, da man so auf andere Vektoren und Matrizen zugreifen kann, doch ist es auch möglich über z.B. Doubles oder Logicals zu iterieren. ```{r} frucht<-c('Apfel','Banane','Kiwi','Birne','Mango') for (zähler in 1:length(frucht)){ print(frucht[zähler]) } for (count in seq(from=0.1,to=1,by=0.1)) { print(count) } for (bool in c(T,T,T)){ print("Iteration") } ``` ## 4.3 While-Schleife Prinzipiell kann jede for-Schleife durch eine while-Schleife ersetzt werden und umgekehrt. Jedoch gibt es durchaus Anwendungen bei dennen das eine sinnvoller ist als das andere. Eine while-Schleife wiederholt so lange einen Prozess, bis eine Bedingung erfüllt ist. ```{r} n<-0 while(n<5){ print(n) n<-n+1 } c<-1 bool<-T while(bool){ print("Yap") if (c==5){ bool<-F } c<-c+1 } ``` ## 4.4 Repeat-Schleife Sehr ähnlich wie eine while-Schleife, ist eine repeat-Schleife. Diese wird mit dem Stichwort ` break ` beendet. ```{r} n<-0 repeat{ n<-n+1 if(n==7) break if(n %% 2 == 0) print(n) } ``` ## 4.5 Exkurs: Vektorisieren Schleifen sind oft sehr ineffektiv und langsam, deshalb werden Operationen vektorisiert um die Performance des Codes zu erhöhen. Z.B. wendet die Funktion ` apply(M,dim,fun) ` die function ` fun ` zeilen- oder spaltenweise (dim=1,2) auf ` M ` an. ```{r} (M<-matrix(1:9,nrow=3)) apply(M,1,mean) apply(M,2,sum) ``` Dieses Prinzip kann auch mit ` lapply(x,fun) ` auf Listen übertragen werden. ```{r} liste=list(Spaß=c("Fahrrad","Mathe","Party"),kleineZahlen=1:9,großeZahlen=12:18) lapply(liste,length) ``` Kompliziertere Funktionen lassen sich entweder vorab definieren, oder mit sog. anonymen Funktionen auf Listen/ Matrizen/ ... anwenden. Anonyme Funktionen wenden die Funktion auf jedes Element ` x ` der List/ Matrix/ ... an. Und werden entweder mit ` function(x) ... ` oder ` \ (x) ... ` initialisiert. Mit der Funktion ` sapply(I,fun) ` kann auf auf jedes Element von I die Funktion angewandt werden. Der Unterscheid zwischen den Funktionen ist, dass ` apply(I,dim,fun) ` die Funktion spalten- oder zeilenweise anwendet, ` lapply(I,fun) ` elementweise anwendet, aber Listen zurückgibt und ` sapply(I,fun) ` ebenfalls elementweise anwendet, aber Vetoren/ Matrizen/ Listen zurückgibt. ```{r} SehrNuetzlicheFunktion<-function(x){ if (any(x>5)){ return(x) }else{ return("klein") } } print("apply()") apply(M,2,SehrNuetzlicheFunktion) # Vordefinierte Funktion print("sapply()") sapply(M,SehrNuetzlicheFunktion) print("lapply()") lapply(M,SehrNuetzlicheFunktion) print("Anonyme Funktionen") lapply(liste,function(x) length(x)<4) # Anonyme Funktion apply(M,2,\(x){if (any(x>5)){x}else{"klein"}}) # Das selbe wie oben, nur anonym ``` ## 5 Zufallszahlen Um Plots wie Histogramme oder Boxplots zu erklären, führen wir noch kurz die Generierung von Zufallszahlen ein. Einer der wichtigsten Befehle ist hierbei ` runif(n) `, welcher einen Vektor mit n Zufallszahlen auf [0,1] erzeugt. Flexibler ist der Befehl ` sample(x,n,replace,prob) `, welcher n Elemente aus x zieht. Mit dem Boolean replace kann Zurücklegen aktiviert, bzw. deaktiviert werden. Mit dem Vektor prob kann die Wahrscheinlichkeit, dass ein Element gezogen wird gewichtet werden. Alle am PC generierten Zufallszahlen sind nicht echt zufällig, siehe [Wikipedia](https://de.wikipedia.org/wiki/Pseudozufall). Sie werden mit einem Algorithmus berechnet und können nicht vom echten Zufall unterscheiden werden, jedoch können sie reproduziert werden, indem der selbe Startwert, der sog. Seed übergeben wird. Wird kein Seed übergeben wird bei R die Systemzeit als Seed benutzt. ```{r} # Bei jeder Ausführung verscheidene zufällige Ergebnisse (uniform<-runif(1)) (uniform2d<-runif(3)) (integer<-sample(1:10,10,replace=T)) # Ganzzahlig zwischen 1:100, mit zurücklegen , ACHTUNG: defaul=F (zufall<-sample(c("apfel","birne","melone"),1)) # Funktioniert auch für beliebige Mengen (normalverteilt<-rnorm(n=3,mean=1,sd=2)) ``` ```{r} # Bei jeder Ausführung die selben Ergebnisse set.seed(43) sample(1:10,1) ``` Es können auch Zufallszahlen nach bestimmten Verteilungen erzeugt werden Diskret: - ` rpois(), rbinom(), ... ` Stetig: - ` rbeta(), rexp(), rnorm(), runif(),.. ` Hierbei wird immer erst die Anzahl der Zufallsvariablen übergeben und dann optional die jeweiligen Parameter der Verteilung. ```{r} rpois(10,1000) rexp(5,3) ``` # 6. Plotten ## 6.1 Lineplots, Scatterplots In R gibt es nur einen Befehl zum plotten, nämlich plot(x,y,...) . Es werden sämtliche (optionale) Parameter direkt mit übergeben: - type: (als String): 'n' - none, 'l' - line, 's' - stepfunction, 'p' - points (default) - xlim, ylim (als Vektor): Achsenabschnitte z.B. c(0,4.2) - main: (als String): Titel - xlab, ylab (als String): Achsenbeschriftungen - col: (als String) siehe. colours() oder (als Zahl) z.B: 2 = rot - pch (Zahl oder String): Markerymbol - cex (Zahl): Markergröße (character expansion) - lty (Zahl): Linetype - 1 = durchgezogen, 2 = gestrichelt... - lwd (Zahl): Lininedicke ```{r} x<-seq(from=0,to=2*pi,by=0.1) y<-sin(x) plot(x,y,main='Sinus',col=1,pch='+',cex=2) plot(x,y,type='s',col=3,lwd=3) ``` Mit lines() oder points() lassen sich mehrere Muster in das selbe Bild einfügen. ```{r} plot(x,y,main="Mehr Information",type='l',lwd=3,ylim=c(-0.8,1.5)) points(x,y+0.4,col=5,pch='^') lines(x,y-0.4,col=8) legend(4.5,1.2,c('Linie','Hüte','graue Linie'),col=c(1,5,8),lty=c(1,2,1)) ``` Funktionen können auch direkte mit dem Befehl curve geplottet werden ```{r} curve(sin,from=13.2,to=26) ``` Höhenlinien lassen sich mit contour(x,y,z) erstellen ```{r} xs=seq(from=-5,to=5,by=0.1) ys=seq(from=-5,to=5,by=0.1) zs=sin(sqrt(outer(xs^2,ys^2,'+'))) #outer erstellt eine Matrix mit den passenden Funktionswerten zu sqrt(x^2+y^2) contour(xs,ys,zs,main='Höhenlinien') filled.contour(xs,ys,zs,main='heatmap') ``` ## 6.2 "Statistische" plots Ohne viel zu erklären, hier ein paar Möglichkeiten Datensätze zu visualisieren. ```{r} h<-c(rnorm(n=1000,mean=1,sd=1.5),rnorm(n=750,mean=7,sd=1)) hist(h,breaks=20,ylab='Häufigkeit',xlab='Kontostand in [€]',main='Histogramm',col='blue') boxplot(rexp(1000),main='Boxplot',col='darkgreen') i<-c(3,5,9,1) # Zu Gruppe 1-4 gehören jeweils 3,5,9,1 Datenpunkte label<-c('Apfel', 'Banane', 'Birne', 'Erdbeere') # Labels initialisieren barplot(i,names.arg=label,col='red',main="Balkendiagramm") ``` Abschließend hier noch eine Möglichkeit einen erzeugten Plot zu speichern. Analog lassen sich Plots auch als andere Dateiformate speichern. ```{r} jpeg(file="Tortendiagramm.jpeg") #Speichert als .jpeg ins aktuelle Arbeitsverzeichnis pie(i,labels=label,main='Mhhhhh....Torte') # Bild erstellen pdf(file="Tortendiagramm.pdf") # als .pdf pie(i,labels=label,main='Mhhhhh....Torte') # Bild erstellen ```