Skip to content

Files

Latest commit

0961df7 · Oct 23, 2015

History

History
450 lines (327 loc) · 8.87 KB

Konstrukte_VP.md

File metadata and controls

450 lines (327 loc) · 8.87 KB

Konstrukte und vektorwertiges Programmieren

Bedingte Anweisungen

Für bedingte Anweisungen gibt es in R die beiden Konstrukte

> if (bedingung){ausdruck1} else {ausdruck2}
> ifelse(bedingung, vektor1, vektor2)

Mit if ... else können sehr komplexe Ausdrücke gewählt werden. Die bedingung darf hier allerdings nicht vektorwertig sein. Die if ... else Konstruktion kann natürlich auch geschachtelt werden.

ifelse erlaubt eine vektorwertige Bedingung. Je nach logischem Wert wird das entsprechende Element von vektor1 oder vektor2 gewählt.

> a <- 1
> if(exists("a", mode = "numeric")) {
+   a <- a + 1
+ } else {
+   a <- 1
+ }
> a
## [1] 2
> rm(a)
> if(exists("a", mode = "numeric")) {
+   a <- a + 1
+ } else {
+   a <- 1
+ }
> a
## [1] 1

ifelse kann, wie schon gesagt, vektorwertig arbeiten. So kann für jeden Eintrag eine Bedingung überprüft werden und abhängig vom Ausgang der Überprüfung ein Ergebnis berechnet werden.

> x <- sample(-5:5, size = 7, replace = TRUE)
> ifelse(x > 0, x^2, 0)
## [1]  0  0  4  0  0  1 16

Man beachte, dass beide Vektoren ausgewertet werden, wie man hier sieht

> ifelse(x < 0, x^2, sqrt(x))
## Warning in sqrt(x): NaNs wurden erzeugt
## [1]  1.000000  1.000000  1.414214  4.000000 25.000000  1.000000  2.000000

Schleifen

Schleifen erlauben den wiederholten Aufruf (in größerer Anzahl) einer Reihe von Befehlen. Diese können dabei iterativ voneinander abhängen (durch die verwendeten Inputgrößen).

Schleife bzw. Kontrollwert Beschreibung
repeat{ausdruck} wiederhole ausdruck
while(bedingung){ausdruck} wiederhole ausdruck, solange bedingung erfüllt
for(i in M){ausdruck} wiederhole ausdruck i M
next springe in den nächsten Schritt
break beende die Schleife

Die einfachste Form einer Schleife ist aber replicate().

> hist(replicate(1000, mean(runif(100))), main = "Bsp ZGWS", freq = FALSE, ylim = c(0,14))
> curve(dnorm(x, mean = 0.5, sd = 1/sqrt(12*100)), 
+       from = 0.4, to = 0.6,  col = "red",add = TRUE)

Konstrukte: Schleifen

Hier ein paar Beispiele zu den drei Schleifen-Typen. Diese dienen nur zur Demonstration, da deren Output wenig sinnvoll ist.

repeat()
> i <- 0 
> repeat{
+   i <- i + 1
+   print(i)
+   if(i < 2) next
+   i <- 2*i
+   print(i)
+   if(i > 6) break
+ }
## [1] 1
## [1] 2
## [1] 4
## [1] 5
## [1] 10
while()
> while(i > 1){ 
+   i <- i-1
+   print(i)
+ }
## [1] 9
## [1] 8
## [1] 7
## [1] 6
## [1] 5
## [1] 4
## [1] 3
## [1] 2
## [1] 1
for()
> x <- 1:10
> z <- NULL
> for(i in seq(along = x)) {
+     if (x[i] < 5) {
+         z <- c(z, x[i] - 1) 
+     } else {
+         stop("Wert muss kleiner als 5 sein")
+     }
+ }
## Error in eval(expr, envir, enclos): Wert muss kleiner als 5 sein
> z
## [1] 0 1 2 3

Für eine for Schleife entlang eines Vektors x sollte man nicht for(i in 1:length(x)) verwenden, da

> x <- NULL
> for(i in 1:length(x)) print(x[i])
## NULL
## NULL
> for(i in seq(along = x)) print(x[i])

Bemerkung: Schleifen sind, wenn möglich, zu vermeiden. R ist ein interpretierte Sprache, d.h. jeder Befehl wird zur Laufzeit interpretiert. Somit wird jeder Befehl in einer Schleife bei jedem Durchlauf neu interpretiert.

Vektorwertig Programmieren und Schleifen

Es sollte so oft wie möglich vektorwertig programmiert werden.

Einschränkung: Fälle, in denen durch vektorwertiges und/oder rekursives Programmieren ein zu hoher Speicherbedarf entsteht. Hier können Schleifen wieder sinnvoll sein.

Benutzt man Schleifen, sollt man auf folgende Punkte achten:

  • Objekte zu Beginn initialisieren
> x <- matrix(rnorm(1000000), 100000, 10 )
> mittelwerte_x <- NULL
> system.time(for(i in seq(along = x[, 1])) 
+   mittelwerte_x <- c(mittelwerte_x, mean(x[i, ])))
##    user  system elapsed 
##   17.50    0.10   17.63
> mittelwerte_x <- numeric(length(x[, 1]))
> system.time(for(i in seq(along = x[, 1])) 
+   mittelwerte_x[i] <- mean(x[i, ]))
##    user  system elapsed 
##    0.64    0.00    0.64
  • Keine unnötigen Fehlerüberprüfungen in Schleifen durchführen
  • Keine Berechnung mehrfach ausführen (sollte man nie)
> n <- 10000
> x <- rnorm(n)
> a <- numeric(n)
> system.time(for(i in 1:n) a[i] <- 2 * mean(x) * pi * sin(3 * i))
##    user  system elapsed 
##    0.33    0.00    0.33
> system.time({for(i in 1:n) a[i] <- sin(3 * i)
+              a <- 2 * mean(x) * pi * a})
##    user  system elapsed 
##    0.01    0.00    0.01

Neben Laufzeitvorteilen vektorwertiger Operationen

> system.time(for(i in 1:n) a[i] <- 2 * mean(x) * pi * sin(3 * i))
##    user  system elapsed 
##    0.32    0.00    0.31
> system.time(a <- 2 * mean(x) * pi * sin(3 * (1:n)))
##    user  system elapsed 
##       0       0       0

sind diese oftmals leichter verständlich.
In der folgenden Tabelle sind einige Funktionen aufgelistet, die zum vektorwertigen Programmieren verwendet werden können.

Funktion Beschreibung
%*% Vektor- oder Matrixmultiplikation
%o%, outer() äußeres Produkt
%x%, kronecker() Kronecker-Produkt
colSums(), rowSums() schnelle Spalten-, Zeilensummen
colMeans(), rowMeans() schnelle Spalten-, Zeilenmittel
apply() spalten- und zeilenweise Funktionen auf Matrizen und Arrays anwenden
lapply() elementweise Funktionen auf Listen anwenden
sapply() wie lapply() mit "vereinfachter" Ausgabe
mapply() multivariates sapply()
tapply() Funktionen auf "gruppierbare" Vektoren anwenden

Die Funktion apply() lässt sich z.B. folgendermaßen nutzen zur Berechnung von Mittelwerten der Spalten (oder Zeilen) einer Matrix bzw. zur Mittelung über die jeweiligen Einträge von mehreren Matrizen.

> str(apply)
## function (X, MARGIN, FUN, ...)
> x <- matrix(rnorm(200), nrow = 20, ncol = 5)
> apply(x, MARGIN = 2, FUN = mean)
## [1]  0.12791559 -0.08395936 -0.14192528 -0.22353197  0.50435694
> a <- array(rnorm(2 * 2 * 10), dim = c(2, 2, 5))
> apply(a, MARGIN = c(1, 2), FUN = mean)
##            [,1]       [,2]
## [1,] -0.1284584 -0.4842267
## [2,]  0.2311143  0.1106028

Die Funktionen rowSums(), colSums(), rowMeans() und colMeans() sind Abkürzungen der apply() Funktion mit FUN gleich sum oder mean. Diese Abkürzungen können deutlich schneller sein.

> x <- matrix(rnorm(1000000), 100000, 10)
> system.time(mittelwerte_x <- apply(x, 1, mean))
##    user  system elapsed 
##    0.76    0.00    0.77
> system.time(mittelwerte_x <- rowMeans(x))
##    user  system elapsed 
##    0.02    0.00    0.01

Arbeitet man mit Listen, wie z.B.

> head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
> iris_list <- as.list(iris[, 1:4])

so gibt es spezielle Funktionen aus der "apply Familie", die speziell (aber nicht nur) zur Anwendung auf Listen vorgesehen sind, wie z.B. lapply() oder sapply().

> str(lapply)
## function (X, FUN, ...)
> lapply(iris_list, FUN = mean)
## $Sepal.Length
## [1] 5.843333
## 
## $Sepal.Width
## [1] 3.057333
## 
## $Petal.Length
## [1] 3.758
## 
## $Petal.Width
## [1] 1.199333
> sapply(iris_list, FUN = mean)
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##     5.843333     3.057333     3.758000     1.199333

Will man eine Funktion auf mehrere Listen anwenden, so bietet sich mapply() an.

> str(mapply) # FUN ist das erste Argument
## function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
> mapply(FUN = sum, 1:10, 10:1, 5)
##  [1] 16 16 16 16 16 16 16 16 16 16

Möchte man hingegen eine Funktion auf die einzelnen Elemente einer Gruppe in einer vorgegebenen Gruppierung anwenden, so verwendet man tapply().

> str(tapply) # INDEX Liste von Faktoren 
## function (X, INDEX, FUN = NULL, ..., simplify = TRUE)
> tapply(iris$Petal.Length, INDEX = iris$Species, 
+        FUN = mean)
##     setosa versicolor  virginica 
##      1.462      4.260      5.552

Berechnet wurden also die empirischen Mittelwerte von Petal.Length in den drei Gruppen setosa, versicolor und virginica.