Skip to Tutorial Content

Tools in DataScience 6: Grundlagen Programmierung

Hinweis:

  • An jenen Stellen, die mit einem Graduation-Cap gekennzeichnet sind , erarbeiten Sie sich die Inhalte selbständig
  • An Stellen mit einer Wandtafel warten Sie bitte auf Input seitens der Dozierenden


Worum geht es in dieser Sitzung:

Da R in erster Linie eine Programmiersprache ist, gehen wir hier auf die grundlegenden Möglichkeiten zur Vereinfachung von Abfragen und der Datenbearbeitung ein. Diese können Sie in Verbindung mit anderen Aufgaben in R einfliessen lassen. Dies kann die Arbeit enorm erleichtern.

Die meisten Programmierfunktionen sind in BaseR hinterlegt. Neben den hier besprochenen gibt es zusätzliche Funktionen, wie split (teilt Vektor, Matrix,Dataframe auf Basis von Faktor(en) in Gruppen), cut (unterteilt kontinuierliche in kategorielle Variablen), do.call (ruft Funktion mit Liste von Argumenten auf) und reduce (wendet eine Funktion wiederholt auf Elemente eines Vektors oder einer Liste an), sowie das data.table - package (erweiterte Bearbeitung von Dataframes). Sollten Sie ProgrammmierIn in R werden wollen, sind diese zusätzlichen Funktionen ebenfalls für Sie interessant.


Grundlagen der Programmierung in R

Für die grundlegenden Programmierfunktionen in R brauchen Sie kein zusätzliches package. Die Befehle sind in BaseR hinterlegt.

Wir besprechen im Folgenden 3 Schlüsselkonzepte: Bedingungen, for-loops und Funktionen.

Weitere Informationen zu diesen Funktionen und das Cheat sheet finden Sie unter:

https://raw.githubusercontent.com/rstudio/cheatsheets/main/base-r.pdf


Grundlagen Programmierung I


aggregierte Beispieldaten:
Der Datensatz ag1 enthält 13 Spalten und 1348 Zeilen, wobei die Zeile jeweils einen Tag darstellt. Wie sie sehen, sind die Daten nach dem Datum aufsteigend geordnet.

    ag1 <- df_work %>% 
      mutate(date = as.Date(date)) %>% 
      select(date,price,quantity,is_outdoor,is_weekend) %>% 
      aggregate(. ~ date, ., FUN=mean, na.rm=FALSE) %>%
      mutate(we=recode(is_weekend, '0'='weekday', '1'='weekend'),
             week=as.integer(is_weekend),
             locat=as.integer(is_outdoor),
             is_outdoor=as.integer(is_outdoor),
             is_weekend=as.integer(is_weekend),
             loc = recode(is_outdoor,'0'='indoor','1'='outdoor'),
             revenue = round(price*quantity, 2),
             calendar = as.character(date))

Datenauszug:



1. Bedingungen mit if und else()

if und else() - hat folgende Form:

(1) wenn [Bedingung = wahr],

  • die Bedingung ist ein boolescher Operator, wie bspw. “und”, “oder”, “nicht”


(2) dann führe aus [Anweisung],

(3) sonst führe aus [andere Anweisung]

Erklärung:

Ist die Bedingung in (1) wahr, dann wird (2) ausgeführt.

Ist die Bedingung in (1) falsch, dann wird (3) ausgeführt.

Man unterscheidet zwischen der if und else Bedingung und der ifelse-Funktion. Letztere ist “vektorisiert”, kann also mit vielen Werten statt mit nur einem Wert abgleichen.

 

a) if() und else() Bedingung:

Beispiel:

  • Wir wollen den natürlichen Logarithmus zur Basis e (Eulersche Zahl = 2.718….) berechnen.

  • Der Definitionsbereich ist “R+” (ohne Null), d.h. für unser x in der unten stehenden Bedingung dürfen nur positive, reelle Zahlen eingesetzt werden. Der Wertebereich ist ganz R (genauer: im Definitionsbereich 0 < x < 1 ist das Ergebnis von “log(x)” negativ; im Bereich x > 1 positiv). Falls die eingebene Zahl kleinergleich 0 ist, wollen wir die Fehlermeldung “Funktion nur für R+ definiert” angezeigt bekommen, sonst den Logarithmus dieser Zahl.

     x <- irgendeine Zahl
    
     if(x>0){
       print(log(x))
     }else{
         print("Funktion nur für R+ definiert")
       }
  • Speichern Sie im Vektor x eine Zahl und führen Sie die Syntax mit “Run Code” aus.

    x<- 
    
    if(x>0){
      print(log(x))
      }else{
        print("Funktion nur für R+ definiert")
        }
  • Nun wenden wir die Funktion zur Veranschaulichung auf unsere Daten an. Sie sind, beginnend mit dem 01.01.2012, nach dem Datum aufsteigend geordnet. Jeder Tag entspricht einer Zeile im dataframe “ag1”. Wir wollen nun herausfinden, ob ein beliebiger Eintrag im Data Frame - also ein bestimmter Tag - ein “Wochenendtag” ist oder nicht. Die Spalte “week” enthält die gesuchte Information. Wir können durch die Auswahl der entsprechenden Zeile, bspw. für den 08.Tag (08.01.2012) mittels der Syntax: “ag1$week[8]”, zu diesem Ergebnis kommen. Eine Funktion zur Abfrage für beliebige Daten bzw. Zeilen könnte wie folgt aussehen.

     x<-8
    
     if(ag1$week[x]==1){
       print(ag1$date[x])
     }else{
     print("no")
     }
    ## [1] "2012-01-08"


  • Da die Position im Data Frame von dessen Ordnung abhängt, ist die Suche über die direkte Datumseingabe eine elegantere Lösung. “Week = 1” steht für einen Wochenendtag.

     x<-as.Date("2012-01-09")
    
       if(ag1$week[ag1$date==x]==1){
         print("yes")
       }else{
         print("no")}
    ## [1] "no"


  • Probieren Sie nun selbst, indem Sie für x ein Datum im selben Format wie im Beispiel einsetzen (x<-as.Date(“2012-01-09”)) - also Jahr-Monat-Tag.

     x<-as.Date("")
    
      if(ag1$week[ag1$date==x]==1){
        print("weekend")
      }else{
        print("no weekend")
        }



Mehrere Objekte

  • In diesem Beispiel werden 2 Objekte, nämlich week und locat, angesprochen. “Locat” steht für “Location” und entspricht bei “1 = outdoor”, sonst “indoor”. Wenn beides am ausgewählten Tag den Wert 1 aufweist, wird “true” angezeigt, ansonsten erscheint “not true”.

      x<-as.Date("2012-01-09")
    
         if((ag1$week[ag1$date==x]==1) & (ag1$locat[ag1$date==x]==1)){
           print("true")
         }else{
           print("not true")
           }
    ## [1] "not true"
  • Wie sah es am 15.01.2012 aus? War es ein Wochenendtag und wurde draussen verkauft?

    x<-as.Date("")
    
    if((ag1$week[ag1$date==x]==1) & (ag1$locat[ag1$date==x]==1)){
      print("true")
    }else{
      print("not true")
      }


Mehrere Bedingungen

  • Wir können auch bspw. mehrere if- oder und mehrere else -Bedingungen setzen. Wie man sieht, wird es recht schnell komplex.

     x<-as.Date("2012-02-12")
    
     if ((ag1$week[ag1$date==x]==1) & (ag1$locat[ag1$date==x]==1)) {
       print("both are correct")
     } else if ((ag1$week[ag1$date==x]==0) & (ag1$locat[ag1$date==x]==1)) {
       print("only location is correct")
     } else if ((ag1$week[ag1$date==x]==1) & (ag1$locat[ag1$date==x]==0)) {
       print("only weekend is correct")
     } else print("not correct")  
    ## [1] "only weekend is correct"
  • Probieren Sie selbst. Wie war es am 25.08.2012?

    x<-as.Date("")
    
    
    if ((ag1$week[ag1$date==x]==1) & (ag1$locat[ag1$date==x]==1)) {
      print("both are correct")
    } else if ((ag1$week[ag1$date==x]==0) & (ag1$locat[ag1$date==x]==1)) {
      print("only location is correct")
    } else if ((ag1$week[ag1$date==x]==1) & (ag1$locat[ag1$date==x]==0)) {
      print("only weekend is correct")
    } else 
      print("not correct")

Zusammenfassung:

  • Die if und else Bedingung ist nicht vektorisiert, kann aber - wie wir noch sehen werden - in Verbindung mit bspw. for-loops benutzt werden um Werte in Variablen zu vergleichen und abzuändern.





b) ifelse() - Funktion:

  • Mit der ifelse() Bedingung kann man Vektoren bearbeiten. Ansonsten funktioniert sie wie die “if und else Bedingung”, ist aber weniger kompliziert in der Eingabe.

Beispiel:

  • Wir wollen den natürlichen Logarithmus zur Basis e (Eulersche Zahl = 2.718….) berechnen. Der Definitionsbereich ist “R+” (ohne Null), d.h. für unser x in der unten stehenden Bedingung dürfen nur positive, reelle Zahlen eingesetzt werden. Der Wertebereich ist ganz R (genauer: im Definitionsbereich 0 < x < 1 ist das Ergebnis von “log(x)” negativ; im Bereich x > 1 positiv). Falls die eingebene Zahl kleinergleich 0 ist, wollen wir die Fehlermeldung “Funktion nur für R+ definiert” angezeigt bekommen, sonst den Logarithmus dieser Zahl.

     x <- irgendeine Zahl oder Zahlenbereich
    
     ifelse(x > 0, log(x), "Funktion nur für R+ definiert")
  • Anwendungsbeispiel für eine Zahl grösser oder gleich Null.

    x <- 1
    
    ifelse(x > 0, log(x), "Funktion nur für R+ definiert")


  • Anwendung auf Vektoren: Wir geben für den Vektor “x” mehrere Zahlen ein und führen Sie mit “Run Code” aus. Das Format sollte wie folgt lauten: ersteZahl:letzteZahl oder c(ersteZahl:letzteZahl) oder c(Zahl1,Zahl2,…)

    x <- 1:10
    
    result <- ifelse(x > 0, log(x), NA)
    result


  • Nun wenden wir die Funktion zur Veranschaulichung auf unsere Daten an.

  • Wir zählen die Wochenendtage (wenn ag1$week = 1, dann ist es ein Wochenendtag) für die ersten 10 Datumseinträge: x <- seq(as.Date(“2012-01-01”), as.Date(“2012-01-10”), by=“days”). Danach summieren wir die Werte auf und zeigen sie an.

     x <- seq(as.Date("2012-01-01"), as.Date("2012-01-10"), by="days")
    
     result <- sum(ifelse(ag1$week[ag1$date==x]==1,1,0),
           na.rm = TRUE)
    
     result
    ## [1] 3
  • Probieren Sie selbst. Für den besseren Überblick definieren wir nun Start- und Enddatum separat.

    Fügen Sie für “x1” als Startdatum den 03.03.2012 und für “x2” als Enddatum den 02.04.2012 ein. Wie viele Wochenendtage gibt es im besagten Zeitraum?

    x1<-as.Date("2012-03-03")
    x2<-as.Date("2012-04-02")
    
    result <- sum(ifelse(ag1$week[ag1$date>=x1 & ag1$date<x2]==1,1,0),
              na.rm = TRUE)
    
    result



  • Man kann die ifelse-Funktion auch verwenden, um Fehlwerte in Vektoren zu ersetzen.

      v1 <- c(1,2,3,NA,NA)
      v1
    ## [1]  1  2  3 NA NA


  • Nun ersetzen wir die Fehlwerte und prüfen danach, ob es funktioniert hat.

     v2 <- ifelse(is.na(v1),0,v1)
     v2
    ## [1] 1 2 3 0 0


  • Bspw. könnte man so auch in einem Data Frame aus einer früheren Übung - df3- die Fehlwerte einer Variable “New Year” ändern.

     df3
     is.na(df3)
    ##      sell_id calendar_date New Year Luner New Year  NULL
    ## [1,]   FALSE         FALSE    FALSE           TRUE  TRUE
    ## [2,]   FALSE         FALSE     TRUE          FALSE  TRUE
    ## [3,]   FALSE         FALSE     TRUE           TRUE FALSE
     df3$`New Year`<-ifelse(is.na(df3$`New Year`),0,df3$`New Year`)
     df3$`New Year`
    ## [1] 24.8  0.0  0.0


Übung I

Laden Sie nun das aktuelle R-Skript zur weiteren lokalen Bearbeitung der folgenden Übungsaufgaben herunter und speichern Sie es lokal in Ihrem Projektordner.

Lösen Sie die folgenden Übungsaufgaben und vervollständigen Sie das Skript Schrittweise nach jeder Übungsaufgabe.

Bei einigen der direkten Multiple Choice Aufgaben ist es sicher hilfreich, wenn Sie die Syntax zur Lösungsfindung jeweils lokal bei sich in R ausprobieren.


1

Beantworten Sie zu folgender Syntax die Quizfrage.

    x<- -5
    if (x != 0) {(1/x)
    }else{print("Fehler")
    }
Quiz


2

Schreiben Sie eine “if() und else() Funktion”, die den Data Frame “ag” einbezieht. Die Bedingung soll Ihnen “hot day” ausgeben, wenn die Temperatur (“temp”) höher als die Mediantemperatur von “temp” ist und sonst “normal day”. Prüfen sie Ihre Syntax, indem Sie für die zu prüfende Temperatur “dailytemp” den Wert 24 einsetzen.

#dailytemp <-
#if()
#  {print()
#  }else{
#    print()
#    }
dailytemp<-24
if(dailytemp>=median(ag$temp))
  {print("hot day")
  }else{
    print("normal day")
    }


3

Schreiben Sie eine “if() und else() Funktion”, die den Data Frame “ag1” einbezieht. Bei Eingabe eines Datums soll sie, Wenn der Umsatz an diesem Tag >=600US$ beträgt, einen Kommentar und den Tagesumsatz wie folgt anzeigen: “really good: Höhe Umsatz als Zahl”. Ansonsten soll die Bedingung “could have been better” ausgegeben werden. Nutzen Sie für beide Ergebnisanzeigen diesmal die Funktion cat(“Kommentar”, Anzeige Umsatz, “US$”) bzw. cat(“Kommentar”). Lassen Sie sich abschliessend das Ergebnis für den 14.08.2014 anzeigen.

x<-as.Date("2014-08-14")

if(ag1$revenue[ag1$date==x]>=600)
  {cat("really good:", ag1$revenue[ag1$date==x], "US$")
  }else{
    print("could have been better")
    }


4

Erweitern Sie nun die obige Syntax (für den ag1). Die Anzeige “really good…” soll nur dann erscheinen, wenn zusätzlich ein Stückzahl von mind. 50 verkauft wurde. In der Ausgabe soll vor der Umsatzanzeige noch erscheinen, um was für eine Art von Wochentag (Wochentag oder Wochenendtag) es sich jeweils handelt, also etwa: “really good: weekday with 600 US$”.

x<-as.Date("2014-08-14")

if(ag1$revenue[ag1$date==x]>=600 & ag1$quantity[ag1$date==x]>=50)
  {cat("really good:", ag1$we[ag1$date==x], "with", ag1$revenue[ag1$date==x], "US$")
  }else{
    print("could have been better")
    }


5

Ändern Sie die Syntax (für den ag1), sodass Sie angezeigt bekommen welche der beiden Bedingungen am jeweiligen Tag jeweils nicht erfüllt war. Fügen Sie zwei weitere else if Bedingungen hinzu, die Ihnen anzeigen: “high revenue, low quantity” oder das Gegenteil. Was ergibt sich für den 12.08.2014?

x<-as.Date("2014-08-12")

if(ag1$revenue[ag1$date==x]>=600 & ag1$quantity[ag1$date==x]>=50)
  {cat("really good:", ag1$we[ag1$date==x], "with", ag1$revenue[ag1$date==x], "US$")} else
    if(ag1$revenue[ag1$date==x]>=600 & ag1$quantity[ag1$date==x]<50) {
    print("high revenue, low quantity")}else
      if(ag1$revenue[ag1$date==x]<600 & ag1$quantity[ag1$date==x]>=50) {
      print("low revenue, high quantity")
      } else
        print("both too low")


6

Summieren Sie nun die Tage, an denen Umsatz- und Mengenbedingung zutreffen, speichern Sie den Wert im Vektor “r” und geben Sie den Wert aus.

x <- ag1$date

r <- sum(ifelse(ag1$revenue[ag1$date==x]>=600
                     & ag1$quantity[ag1$date==x]>=50,
                     1,0),na.rm = TRUE)
r






Laden Sie nun das aktualisierte R-Skript herunter und speichern Sie es lokal in Ihrem Projektordner.


Grundlagen Programmierung II


Wir besprechen nun Funktionen mittels function() und Schleifen mit for-loops. Abschliessend gehen wir auf apply - Funktionen ein.

1. Die Funktion function()

Funktionen sind Objekte, denen Sie einen Namen zuweisen und bestimmte Operationen bzw. Aufgaben übergeben.

Variablen innerhalb der Funktion werden NICHT im WorkSpace gespeichert, sind also nicht global zu jeder Zeit verfügbar, sondern nur bei Funktionsaufruf innerhalb der Funktion.

Mit der Funktion function() können Sie eigene Funktionen schreiben.


Die allgemeine Form zur Erstellung einer Funktion ist wie folgt:

Funktionsname <- function(Argument1, Argument2,...){

Durchführen der notwendigen Operationen

berechnen der(des) finalen Werte(s) (bspw. Mittelwert, Summe,...)

}

Die allgemeine Form zum Aufrufen einer Funktion ist wie folgt:

Funktionsname(Argument1, ...)

Es gibt verschiedene Möglichkeiten, wie Sie (oder Dritte) auch in anderen Skripten von Ihnen erstellte Funktionen wieder verwenden können:

  1. Sie speichern Ihre Funktion als Skript (nur die Funktion, keine weiteren Anweisungen). Sie können sie bei Bedarf mit source("SKRIPTNAME.R") laden. Die Funktion ist dann global (also allgemein) für Sie in der “environment” (Fenster oben rechts) als Objekt des Typs “function” verfügbar und kann mit Ihrem Funktionsnamen auf von Ihnen bestimmte Argumente angewendet werden.

  2. In Form eines von Ihnen erstellten packages (wie bspw. tidyr). Diese Vorgehensweise können Sie unter folgendem Link recherchieren: https://r-pkgs.org/).


Beispiel

  • Die folgende Funktion soll die Summe über die Werte im Vektor “x” bilden (Summe über die ersten N-natürlichen Zahlen - (Gauss)).

  • Gegebenenfalls definieren Sie noch die Variablen oder Vektoren für die Funktion. Dann setzen Sie sie in die Funktion ein und lassen Sich das Ergebnis ausgeben. Sie können die Funktion im Beispiel mit s(Name des Vektors) aufrufen.

  • Für die Zahlen 1 bis 3 berechnet sich die Summe mit der Funktion s wie folgt:

     n<-3
    
     s <- function(n){
     n*(n+1)/2
     }
    
     s(n)
    ## [1] 6


  • Sie können Funktionsargumente auch direkt in die Funktion einsetzen. Geben Sie für “Argument1” die Zahl “10” ein. Welches Ergebnis erhalten Sie? Testen Sie anschliessend mit dem Vektor 1:10.

    s <- function(n){
    n*(n+1)/2
    }
    
    s("Argument1")
  • Benennen Sie den Vektor “n”, der den Eingabewert für die Funktion enthält, nun in “k” um und geben Sie die Zahl 5 ein. Ergänzen Sie dann die “Eingabe” mit “k” beim Aufrufen der Funktion. Welches Ergebnis erhalten Sie?

    n<-
    
    s <- function(n){
    n*(n+1)/2
    }
    
    s("Eingabe")
  • Abschliessend kann man “nur” die Funktion in einem Skript, bspw. mit dem Namen “Gauss.R” speichern:

    s <- function(n){
    n*(n+1)/2
    } 
  • Danach kann man sie in einem anderen Skript beliebig einbinden. Dazu muss man sie dort mit “source(”Verzeichnisname/Gauss.R“)” laden. Danach kann die Funktion, wie im obigen Beispiel, mit “s(”hier stehen die Funktionsargumente“)” erneut aufgerufen wieder.


Mehrere Argumente

  • Funktionen können auch mehrere Argumente haben. Wir fügen das Argument “y” hinzu und übergeben es an die Funktion “s”.**

     x <- 1:2
     y <- 4:6
    
     s <- function(a, b) {
       summe <- a + b
       cat("Die Summe von", a, "und", b, "ist", summe, "\n")
       }
    
    
     s(x,y)
    ## Die Summe von 1 2 und 4 5 6 ist 5 7 7


  • Eine Anwendung auf unsere Beispieldaten könnte bspw. so aussehen: Wir möchten uns den Anteil der Tage mit einer bestimmten Ausprägung (hier in “x”: der Umsatzhöhe von max. 300 Dollar) für eine beliebige Variable (hier dem Umsatz - Spalte: ag1$revenue) ausgeben lassen. Probieren Sie selbst.

    x<-
    y<-
    
    m_spec <- function(x,y){
      ant<-round((length(y[which(y <= x)])/length(y))*100,2)
      cat("Der Anteil <=", x,"betraegt", ant, "Prozent", "\n")
      }
    
    m_spec()


Auswahloption und Verbindung mit Bedingungen

  • Eine Funktion kann bspw. auch so konzipiert werden, dass jeweils eine von zwei hinterlegten Berechnungen anhand einer Voreinstellung (TRUE, FALSE) (Gauss “Summe” der ersten N natürlichen Zahlen oder deren Fakultät) aktiviert werden können.

     x<-4
    
     gf <- function(n, sum_n = TRUE){
     ifelse(sum_n, n*(n+1)/2, factorial(n))
     }
    
     gf(x,T)
    ## [1] 10


  • Probieren Sie nun selbst. Die Funktion “avg” gibt Ihnen den Mittelwert oder den Median aus. Geben Sie in die Funktion avg(y, ) noch T (TRUE - optional - da Grundeinstellung) oder F (FALSE) ein und führen Sie den Befehl mit “Run Code” aus. Wie verhält es sich für die Variable “revenue”?

    y<-ag1$revenue
    
    avg <- function(x, mi = TRUE) {
      if (mi) {
        result <- mean(x)
        cat("Der Durchschnitt betraegt:", result, "\n")
        } else {
          result <- median(x)
          cat("Der Median betraegt:", result, "\n")
        }
      }
    
    avg(y,)






2. Schleifen bzw. Loops

Schleifen bzw. Loops verwenden Sie dann, wenn Sie repetitive Aufgaben bzw. Operationen automatisiert erledigen wollen.

Es gibt verschiedene Formen von loops. Wir beschäftigen uns hier mit for und while.


a1) for loops


Die allgemeine Form einer for-Schleife ist:

(1) für i (die Bezeichnung i ist willkürlich) in einem bestimmten Bereich,

(2) führe bestimmte Aufgaben bzw. Operationen aus.

Anmerkung: i kann eine selbstgewählte Zahl oder bestimmte Werte eines Vektors oder einer Variablen sein und die Operation in b) wird für jede Ausprägung in i ausgeführt. i ist also ein Zähler, der durchlaufen wird.


Beispiel

  • Wir wollen für die Zahlen von 1 bis 5 jeweils die Zahl “i” mit ihrem Nachfolger addieren und die jeweilige Summe angezeigt bekommen.

     for(i in 1:5){
     print(i+(i+1))
     }
    ## [1] 3
    ## [1] 5
    ## [1] 7
    ## [1] 9
    ## [1] 11


  • Wir wollen für die Zahlen von 1 bis 5 jeweils die Zahl “i” mit ihrem Nachfolger addieren und alle jeweiligen Summen in einem Vektor “s” speichern. Abschliessend geben wir “s” aus. Die Werte von “i” sind im Vektor “k” gespeichert und “k” definiert auch die Länge des neuen Vektors “s”.

     k <- 5
    
     s <- vector(length = k)
     for(i in 1:k){
     s[i]<-(i+(i+1))
     }
    
     s
    ## [1]  3  5  7  9 11


  • Eine Anwendung auf unsere Beispieldaten könnte bspw. so aussehen: Wir schreiben einen for-loop, der uns den mittleren Umsatz pro Monat im Jahr 2012 im Vektor s speichert. Die 12 Monate sind im Vektor k hinterlegt.

     k <- 12
    
     s <- vector(length = k)
     for(i in 1:k){
     s[i]<-mean(ag1$revenue[ag1$month==i & ag1$year==2012])
     }
    
     s
    ##  [1] 631.7252 516.9039 559.7797 535.3540 566.7858 553.4653 572.2413 582.4829
    ##  [9] 539.3667 536.5081 570.0697 608.7877


a2) verschachtelte for-loops

  • For-loops können auch ineinander verschachtelt vorkommen. Dazu erweitern wir das vorige Beispiel um einen Zähler für das Jahr. Zudem fügen wir einen Index hinzu, der alle Wertpaarkombinationen (Jahr und Monat) durchläuft. So können wir für jede Position im Vektor den entsprechenden Wert hinterlegen.

  • Probieren Sie selbst. Ändern Sie die Anzahl der Monate (k) oder das Jahr (y). Welche Werte ergeben sich für das Jahr 2014?

    k <-               # hier die Anzahl der Monate eingeben
    y <-              # hier das Jahr eingeben, mehrere Jahre als Sequenz
    
    
    s <- vector(length=length(y)*length(k))
    
    index = 1
    
    for(j in 1:length(y)){
    for(i in 1:length(k)){
      s[index]<-mean(ag1$revenue[ag1$month == k[i] & ag1$year == y[j]])
      index <- index + 1
    }
      }
    
    s


  • Man kann auch über verschiedene Variablen bzw. Spalten in einem Data Frame gehen um diesselbe Operation auszuführen. Wir wollen bspw. für jede geeignete Variable den Mittelwert bzw Anteil berechnen, ansonsten NA. Der Datensatz ag2 enthält alle Variablen aus ag1 ausser der Datumsvariable.

     ag2<-ag1%>%select(-date)


  • Wir führen die Syntax aus:

     med <- vector(mode = "numeric",length = ncol(ag2))
    
     for(i in 1:ncol(ag2)){
     med[i] <- mean(ag2[,i], na.rm = TRUE)
     }
     med
    ##  [1]   12.8669455   44.3360534    0.8620178    0.2856083           NA
    ##  [6]    0.2856083    0.8620178           NA  567.9635089 2013.3753709
    ## [11]    6.1839763   15.6416914






b) while loops


Die while loopskönnen ähnlich wie for loops eingesetzt werden. While loops durchlaufen die Aufgabe solange, bis der zu testende Wert (bspw.) i falsch ist.


Die allgemeine Form einer while-Schleife ist:

(1) Solange i (die Bezeichnung i ist willkürlich) wahr ist,

(2) führe bestimmte Aufgaben bzw. Operationen aus.


Dies braucht man bspw. häufig bei Simulationen oder der Abschätzung bestimmter Werte.

Da viele while-loops auch als for loops geschrieben werden können, gehen wir an dieser Stelle nur kurz darauf ein.

Generell sollte man while loops eher dann nutzen, wenn man die Anzahl der Iterationen nicht vorab kennt.


Beispiel

  • Wir greifen auf das vorgängig mit einem for-loop bearbeitete Anwendungsbeispiel von oben zurück und bearbeiten es mit einem while-loop: Wir wollen den mittleren Umsatz pro Monat im Jahr 2012 berechnen.

  • Wir speichern das Ergebnis wieder im Vektor “s”.

    s <- vector(length = length(unique(ag1$month)))
    i<-1
    while (i<=length(unique(ag1$month))) {
    s[i] <- mean(ag1$revenue[ag1$month==i & ag1$year==2012])
    i = i + 1
    }
    s
    ##  [1] 631.7252 516.9039 559.7797 535.3540 566.7858 553.4653 572.2413 582.4829
    ##  [9] 539.3667 536.5081 570.0697 608.7877


  • Ändern Sie das Beispiel, sodass statt dem Gewinn die Menge (quantity) ausgegeben wird. Wie hoch sind die durchschnittlichen Mengen pro jeweiligem Monat im Jahr 2013?

    s <- vector(length = length(unique(ag1$month)))
    
    i<-1
    
    while (i<=length(unique(ag1$month))) {
      s[i] <- round(mean(ag1$revenue[ag1$month==i & ag1$year==2012]),1)
      i = i + 1
    }
    
    s






3. weitere nützliche Funktionen - apply(), sapply(), tapply()

  • Die apply-Funktionen wenden eine Funktion auf einen Dataframe, eine Liste oder einen Vektor an und geben das Ergebnis in Form einer Liste oder eines Vektors aus.
  • Neben den folgenden 3 Funktionen gibt es noch weitere apply-Funktionen.


a) apply(x, margin, function)


Die apply Funktion wendet eine Funktion auf die Zeilen oder Spalten einer Matrix oder eines Dataframes an.

  • Das Argument x definiert den Input (array oder Matrix). Das Argument margin nimmt den Wert 1 an, wenn wir die Funktion auf Zeilen, 2 wenn wir die Funktion auf Spalten anwenden wollen. function entspricht der Funktion, die wir auf unsere Inputdaten anwenden wollen.


Beispiel

  • Wir wollen die Summe jeder Zeile für die Spalten 4 und 5 (die Verkaufsmenge verschiedener Tage) des Beispieldatensatzes wide1 angezeigt bekommen.

     test1 <- df_work %>% 
              select( sell_id, calendar_date, price, quantity, average_temperature) %>% 
              filter(sell_id == c(1070, 2051)) %>% 
              slice(1:4)
    
              names(test1)[names(test1) == 'average_temperature'] <- 'temp'
    
     wide1 <- test1 %>% 
               pivot_wider(names_from = calendar_date, 
                           values_from = c("price", "quantity", "temp"), names_sep = ":")
    
    
    
     wide1
     apply(wide1[,4:5], 1, sum)
    ## [1] 116  44


  • Wir wollen den Mittelwert für die Spalten 4 und 5 des Beispieldatensatzes wide1 angezeigt bekommen.

     wide1
     apply(wide1[,4:5], 2, mean)
    ## quantity:2012-01-01 quantity:2012-01-02 
    ##                  34                  46






b) sapply(x, fun)


Die sapply Funktion wendet eine Funktion auf alle Elemente eines Inputs, also einer Liste, eines Vektors oder Dataframes, an und gibt einen array oder eine Matrix derselben Länge zurück.

Das “s” steht für “simplified”, da sie eine vereinfachte Funktion einer anderen Funktion (lapply) ist.

  • Das Argument x definiert den Input. fun entspricht der Funktion, die wir auf unsere Inputdaten anwenden wollen.


Beispiel

  • Wir wollen das Minimum jeder Spalte des Beispieldatensatzes wide1 angezeigt bekommen.

     wide1
     sapply(wide1, min)
    ##             sell_id    price:2012-01-01    price:2012-01-02 quantity:2012-01-01 
    ##             1070.00               12.73               12.73               22.00 
    ## quantity:2012-01-02     temp:2012-01-01     temp:2012-01-02 
    ##               22.00               24.80               24.80






c) tapply(x, index, fun)


Die tapply Funktion berechnet Funktionen oder statistische Lage- und Streuungsmasse für Faktorvariablen bzw. Ausprägungen eines Vektors.

  • Das Argument x definiert den Input. index definiert den Faktor bzw. Vektor. fun entspricht der Funktion, die wir auf unsere Inputdaten anwenden wollen.


Beispiel

  • Wir wollen für jede Ausprägung des Vektors “location” (indoor, outdoor) des Beispieldatensatzes ag1 den mittleren Umsatz (revenue) angezeigt bekommen.

     ag1[1:10,]
     tapply(ag1$revenue, ag1$loc, mean)
    ##   indoor  outdoor 
    ## 649.3432 554.9372


Übung II

Nutzen Sie Ihr lokales R-Skript “06_Programming_1.R” und vervollständigen Sie es Schrittweise nach jeder Übungsaufgabe.






1

Schreiben Sie eine Funktion mit dem Argument “n”, die für jedes “n” die Summe 1^2 + 2^2 + …+ n^2 ausgibt. Nennen Sie die Funktion s. Welcher Wert ergibt sich für n = 32?

n<-32
s <- function(n){
    x<-1:n
    sum(x^2)
}
s(n)


2

Schreiben Sie eine Funktion “r”, die eine if und else Bedingung (nicht die ifelse Funktion) integriert. Die Funktion soll die Werte der Variablen auf einen Wertebereich zwischen 0 und 1 reskalieren, indem Sie die Variablenwerte durch deren Maximum teilt. Bei Variablen mit nicht numerischen Variablentypen soll die Variable unverändert bleiben. Wenden Sie die Funktion auf die Variable “revenue” an und lassen Sie sich den auf 2 Nachkommastellen gerundeten Mittelwert ausgeben.

# r <- function(x) {
# if(){
#   ()
#   }else{
#     print()
#    }
# }

# round(mean(r()),2)
r <- function(x) {
if(class(x) == "numeric"){
      (x/max(x))
      }else{
        x
      }
  }

head(r(ag1$revenue))

round(mean(r(ag1$revenue)),2)


3

Erstellen Sie eine Funktion mit Auswahloption, die Mittelwert (“mean()”) oder Standardabweichung(“sd()”) der ausgewählten Variable ausgibt. Die Auswahloption soll standardmässig auf “TRUE” gestellt sein. Die Funktion soll eine zusätzliche if und else Bedingung enthalten. Wenn class(x) == “numeric” unwahr ist, soll ein “NA” ausgegeben werden, sonst Varianz bwz. Standardabweichung. Welchen Wert erhalten Sie für die Variable “weekend” im df “ag1”, wenn Sie bei check “FALSE” wählen?

# z <- function(){
#  if()
#  {
#    ifelse()
#  }else{
#  }
#  }

z(x, Check = FALSE)
x<-ag1$weekend

z <- function(x, check = TRUE){
  if(class(x)=="numeric")
    {
    ifelse(check, mean(x), sd(x))
    }else{
      NA
    }
  }

z(x, check = FALSE)





4

Wir wollen nun, ähnlich zur ersten Übungsaufgabe, einen for-loop schreiben, der uns für jedes “n” die Summe 1^2 + 2^2 + …+ n^2 ausgibt. Die Ergebnisse des loops sollen für jedes n<=n in einem numerischen Vektor “v1” der Länge 32 gespeichert werden. Im Unterschied zum Output zur ersten Übungsaufgabe, erhalten Sie diesmal einen Vektor der alle Summen für n<=n enthält. welcher Wert ergibt sich an der 22. Position im Vektor “v1”?

#v1 <- vector("numeric", 32)
#for() {}
v1 <- vector("numeric", 32)
n <- 32

for(i in 1:n){
    x <- 1:i
    v1[i] <- sum(x^2)
}

v1[22]


5

Bei dieser Übung beziehen wir uns auf Ihre bereits erstellten Ergebnisse aus der 3. Übungsaufgabe. Sie haben dort eine Funktion “z” geschrieben, die, je nach Auswahl, Mittelwert oder Standarabweichung einer Variable anzeigt. Wenden Sie diese Funktion mithilfe eines for-loops auf alle Variablen im Data Frame “ag2” an. Stellen Sie mit “check = FALSE” auf die Standardabweichung. Runden Sie Ihre Ergebnisse auf 2 Nachkommastellen und speichern Sie sie in einem Vektor “v1”. Welcher Wert ergibt sich für die Variable “quantity” (Position 2) im df "ag2.

# Erstellen sie, wenn nötig, die Funktion z erneut
# z <- function(x, check = FALSE){ ....

#v1<-vector(length = ncol(ag2))
#for(){}
z <- function(x, check = FALSE){
  if(class(x)=="numeric")
    {
    ifelse(check, mean(x), sd(x))
    }else{
      NA
    }
  }

v1<-vector(length = ncol(ag2))

for(i in 1:ncol(ag2)){
  v1[i] <- round(z(ag2[,i], check = FALSE),2)
  }

v1[2]


6

Es können auch mehrere Schleifen ineinander verschachtelt werden. Ergänzen Sie die folgende Syntax an den mit “input” markierten Stellen. Orientieren Sie sich dabei an den Beispielen aus der Vorlesung bei a2). Erstellen Sie eine Syntax, die den nach der Variable “loc” (location: indoor, outdoor) gruppierten Mittelwert pro Spalte des df “ag2” berechnet und diesen in einem Vektor “v1” hinterlegt, welcher vorab erstellt werden soll. Verwenden sie dabei einen Index. Hinterlegen Sie zudem den df “ag2” und die Spalte “loc” in den Objekten “d” und “g” und verwenden Sie diese in den Schleifen. Welchen auf 2 Nachkommastellen gerundeten Mittelwert erhalten Sie für den ersten Eintrag in v1, d.h. dem Preis bei Indoorverkäufen?

d <- ag2
g <- ag2$loc

v1 <- numeric("input" * length(unique(g)))

index <- "input"

for (i in "input") {
  for (j in unique("input")) {
    v1[index] <- mean("input"[, i]["input" == "input"], na.rm = TRUE)
    index <- "input"
  }
}

round(v1[1],2)
d <- ag2
g <- ag2$loc

v1 <- numeric(ncol(d) * length(unique(g)))

index <- 1

for (i in 1:ncol(d)) {
  for (j in unique(g)) {
    v1[index] <- mean(d[, i][g == j], na.rm = TRUE)
    index <- index + 1
  }
}

round(v1[1],2)


7

Lassen Sie sich mithilfe der tapply - Funktion das Maximum des mittleren Umsatzes für Wochenenden und Wochentage (Variable: we) im Data Frame “ag2” anzeigen. Wie lautet der auf 2 Nachkommastellen gerundete Maximalwert?

a<-max(tapply(ag1$revenue, as.factor(ag1$we), mean))
round(a,2)

Laden Sie nun das aktualisierte R-Skript herunter und speichern Sie es lokal in Ihrem Projektordner.

Was haben wir gelernt:

Wir wissen, welche grundlegenden Programmieroptionen es in R gibt.

Wir können einfache if und else- Bedingungen bzw. ifelse- Funktionen anwenden, eigene Funktionen schreiben, die verschiedenen apply Funktionen anwenden und for- und while loops verwenden, um repetetive Aufgaben zu vereinfachen.

Data Science für ManagerInnen