Diese Sammlung zeigt die wichtigsten Befehle der Programmiersprache R, jeweils mit einem kurzen Beispiel. Zeilen, die mit #> beginnen, zeigen die Ausgabe von R. Alle Beispiele laufen ab R 4.1.

Grundlagen

Kommentare

Alles hinter einem # ignoriert R bis zum Ende der Zeile.

# Ich bin ein Kommentar
x <- 5  # Ein Kommentar kann auch hinter einem Befehl stehen

Ausgabe auf der Konsole

print() gibt ein Objekt so aus, wie R es anzeigt, mit der Positionsnummer [1] davor. cat() gibt den reinen Text aus. sprintf() setzt Werte in eine Textvorlage ein.

print("Hello World")
#> [1] "Hello World"
cat("Hello", "World", "\n")
#> Hello World
cat(sprintf("Pi ist ungefähr %.2f\n", pi))
#> Pi ist ungefähr 3.14
message("Fertig")   # Hinweis ausgeben, RStudio zeigt ihn rot
#> Fertig

Hilfe zu einem Befehl

?which              # Hilfeseite zum Befehl which
help(which)         # dasselbe
??"regression"      # Volltextsuche in allen installierten Hilfeseiten
apropos("which")    # alle Befehle, deren Name "which" enthält
example(which)      # Beispiele aus der Hilfeseite ausführen
args(paste)         # Parameter eines Befehls anzeigen

Befehlsverlauf und R beenden

history()           # zuletzt eingegebene Befehle anzeigen
q()                 # R beenden, in RStudio endet damit die Sitzung

Version und Sitzung

R.version.string    # installierte R Version
sessionInfo()       # Version, Betriebssystem und geladene Pakete

Arbeitsverzeichnis und Dateien

Arbeitsverzeichnis anzeigen und ändern

R liest und schreibt Dateien ohne Pfadangabe im Arbeitsverzeichnis. Auch unter Windows trennt man Ordner mit / oder mit doppeltem \\. Wer in RStudio mit Projekten arbeitet, braucht setwd() meist nicht, weil RStudio das Arbeitsverzeichnis auf den Projektordner setzt.

getwd()                          # aktuelles Arbeitsverzeichnis
setwd("C:/Users/aaron/Projekte") # neues Arbeitsverzeichnis festlegen

Dateien und Ordner

list.files()                         # Dateien im Arbeitsverzeichnis
list.files("daten", pattern = "\\.csv$")  # nur CSV Dateien im Ordner daten
file.exists("daten/kunden.csv")      # TRUE, wenn die Datei existiert
dir.create("ergebnisse")             # neuen Ordner anlegen
file.path("daten", "kunden.csv")     # Pfad zusammensetzen: "daten/kunden.csv"

Objekte speichern und laden

saveRDS() speichert ein einzelnes Objekt. Beim Laden mit readRDS() bestimmt man selbst den Namen. save() speichert mehrere Objekte samt Namen, load() stellt sie unter denselben Namen wieder her.

saveRDS(myTable, file = "myTable.rds")
myTable <- readRDS("myTable.rds")

save(myTable, myList, file = "objekte.RData")
load("objekte.RData")

savehistory("befehle.Rhistory")      # Befehlsverlauf in eine Datei schreiben

CSV Dateien lesen und schreiben

Deutsche CSV Dateien trennen Spalten meist mit Semikolon und schreiben Dezimalzahlen mit Komma. Dafür gibt es read.csv2() und write.csv2(). Für Dateien mit Komma als Trennzeichen und Punkt als Dezimalzeichen sind read.csv() und write.csv() gedacht.

kunden <- read.csv2("C:/daten/kunden.csv", encoding = "UTF-8")
kunden <- read.csv("C:/daten/kunden.csv")
kunden <- read.table("C:/daten/kunden.csv", header = TRUE, sep = ";", dec = ",")

write.csv2(kunden, "kunden_neu.csv", row.names = FALSE)

# Schneller bei großen Dateien: Paket readr
library(readr)
kunden <- read_csv2("C:/daten/kunden.csv")

Pakete

Paket installieren und aktualisieren

install.packages("ggplot2")
install.packages(c("dplyr", "readr", "tidyr"))
update.packages(ask = FALSE)    # alle installierten Pakete aktualisieren
remove.packages("ggplot2")      # Paket entfernen

Eine Übersicht aller Pakete im offiziellen Verzeichnis CRAN steht unter cran.r-project.org/web/packages.

Paket laden

library(ggplot2)              # Fehler, wenn das Paket fehlt
require(ggplot2)              # gibt FALSE zurück, wenn das Paket fehlt
dplyr::filter(iris, Sepal.Length > 7)  # Befehl aufrufen, ohne das Paket zu laden

Installierte und geladene Pakete anzeigen

.libPaths()                   # Ordner, in denen Pakete installiert sind
Sys.getenv("R_LIBS_USER")     # persönlicher Paketordner
installed.packages()[, "Version"]  # alle installierten Pakete mit Version
library()                     # installierte Pakete in einem eigenen Fenster
(.packages())                 # aktuell geladene Pakete
getOption("defaultPackages")  # Pakete, die R beim Start lädt
packageVersion("ggplot2")     # Version eines Pakets

Pakete beim Start automatisch laden

Befehle in der Datei .Rprofile im persönlichen Ordner führt R bei jedem Start aus. Die Datei lässt sich direkt aus R öffnen:

file.edit("~/.Rprofile")

Inhalt der Datei, zum Beispiel:

library(ggplot2)
library(dplyr)

Ein Skript, das sich auf automatisch geladene Pakete verlässt, läuft auf einem anderen Rechner nicht. Deshalb gehört library() trotzdem an den Anfang jedes Skripts.

Die Datei Rprofile.site im Ordner etc der R Installation gilt für alle Benutzer und braucht zum Ändern Administratorrechte. Jede neue R Version bringt eine eigene Rprofile.site mit, die Änderungen müssen also nach jedem Update neu eingetragen werden. Die persönliche .Rprofile ist deshalb die bessere Wahl.

Variablen

Wert zuweisen

a <- 1          # übliche Schreibweise
b = 2           # funktioniert ebenfalls
3 -> c          # Zuweisung nach rechts, selten verwendet
assign("d", 4)  # Name als Text übergeben
`<-`(e, 5)      # Zuweisung als Funktionsaufruf
c(a, b, c, d, e)
#> [1] 1 2 3 4 5

Variablen anzeigen und löschen

var1 <- 10; var2 <- 20; name <- "R"; .versteckt <- 30
ls()                        # alle Variablen
#> [1] "name" "var1" "var2"
ls(pattern = "var")         # Variablen, deren Name "var" enthält
#> [1] "var1" "var2"
ls(all.names = TRUE)        # auch Variablen, die mit einem Punkt beginnen
#> [1] ".versteckt" "name"       "var1"       "var2"
rm(var1)                    # eine Variable löschen
exists("var1")
#> [1] FALSE
rm(list = ls())             # alle Variablen löschen

Werte vergleichen

Bei Dezimalzahlen rechnet der Computer mit Rundungsfehlern. Deshalb vergleicht man sie mit all.equal() statt mit ==.

a <- 1; b <- 2
a == b
#> [1] FALSE
0.1 + 0.2 == 0.3
#> [1] FALSE
isTRUE(all.equal(0.1 + 0.2, 0.3))
#> [1] TRUE
identical(c(1, 2), c(1, 2))
#> [1] TRUE

Datentypen

Grundtypen

class(TRUE)          # logical: TRUE oder FALSE
#> [1] "logical"
class(42L)           # integer: ganze Zahl, erkennbar am L
#> [1] "integer"
class(42)            # numeric: Zahl mit Nachkommastellen
#> [1] "numeric"
class(1 + 2i)        # complex: komplexe Zahl
#> [1] "complex"
class("Hallo")       # character: Text
#> [1] "character"
class(as.raw(255))   # raw: einzelne Bytes, Ausgabe hexadezimal
#> [1] "raw"
as.raw(255)
#> [1] ff
typeof(42)           # interner Speichertyp
#> [1] "double"

Datenstrukturen

StrukturInhaltErzeugen mit
VektorWerte gleichen Typs in einer Reihec()
ListeWerte beliebigen Typs, auch weitere Listenlist()
MatrixWerte gleichen Typs in Zeilen und Spaltenmatrix()
ArrayWerte gleichen Typs in beliebig vielen Dimensionenarray()
FaktorKategorien mit festen Ausprägungenfactor()
Data FrameTabelle, jede Spalte hat ihren eigenen Typdata.frame()

Typ prüfen und umwandeln

is.numeric(42)
#> [1] TRUE
is.character("42")
#> [1] TRUE
as.numeric("42") + 1
#> [1] 43
as.integer(3.9)       # schneidet die Nachkommastellen ab
#> [1] 3
as.character(42)
#> [1] "42"
as.logical(c("TRUE", "nein", "T"))
#> [1] TRUE   NA TRUE
as.numeric("abc")     # nicht umwandelbar: NA mit Warnung
#> Warnmeldung: NAs durch Umwandlung erzeugt
#> [1] NA

Fehlende Werte

NA steht für einen fehlenden Wert, NULL für ein leeres Objekt, NaN für ein ungültiges Rechenergebnis und Inf für unendlich.

x <- c(4, NA, 7)
is.na(x)
#> [1] FALSE  TRUE FALSE
sum(x)                 # NA, sobald ein Wert fehlt
#> [1] NA
sum(x, na.rm = TRUE)   # fehlende Werte auslassen
#> [1] 11
0 / 0
#> [1] NaN
1 / 0
#> [1] Inf
length(NULL)
#> [1] 0

Operatoren

Rechenoperatoren

OperatorBedeutungBeispielErgebnis
+ - * /Grundrechenarten7 / 23.5
^Potenz2 ^ 38
%%Rest einer Division (Modulo)7 %% 21
%/%ganzzahlige Division7 %/% 23

Rechenoperatoren wirken auf jedes Element eines Vektors:

a <- c(4, 8, 12)
b <- c(12, 8, 4)
a %% b      # Rest der Division a / b
#> [1] 4 0 0
a %/% b     # wie oft b ganz in a passt
#> [1] 0 1 3
c(2, 3, 4) ^ c(3, 2, 2)   # 2*2*2, 3*3, 4*4
#> [1]  8  9 16

Vergleichsoperatoren

== gleich, != ungleich, < kleiner, > größer, <= kleiner oder gleich, >= größer oder gleich.

a <- c(4, 8, 12)
b <- c(12, 8, 4)
a > b
#> [1] FALSE FALSE  TRUE
a == b
#> [1] FALSE  TRUE FALSE

Logische Operatoren

Die Operatoren & (und), | (oder) und ! (nicht) prüfen Vektoren Element für Element. && und || prüfen genau einen Wert und gehören in Bedingungen wie if. Die Zahl 0 gilt als FALSE, jede andere Zahl als TRUE.

a <- c(3, 0, TRUE, FALSE, 0)
b <- c(4, 0, FALSE, FALSE, 0.1)
a & b
#> [1]  TRUE FALSE FALSE FALSE FALSE
a | b
#> [1]  TRUE FALSE  TRUE FALSE  TRUE
!c(TRUE, FALSE)
#> [1] FALSE  TRUE
xor(TRUE, FALSE)    # genau einer von beiden ist TRUE
#> [1] TRUE
any(a > 2)          # mindestens ein Wert erfüllt die Bedingung
#> [1] TRUE
all(a > 2)          # alle Werte erfüllen die Bedingung
#> [1] FALSE

Zuweisungsoperatoren

OperatorBedeutung
<- und =Wert der Variablen links zuweisen
->Wert der Variablen rechts zuweisen
<<- und ->>Wert einer Variablen außerhalb der aktuellen Funktion zuweisen
zaehler <- 0
erhoehen <- function() zaehler <<- zaehler + 1
erhoehen(); erhoehen()
zaehler
#> [1] 2

Weitere Operatoren

1:5                   # Zahlenfolge
#> [1] 1 2 3 4 5
3 %in% c(1, 2, 3)     # ist der Wert im Vektor enthalten?
#> [1] TRUE
matrix(1:4, 2) %*% matrix(1:4, 2)   # Matrixmultiplikation
#>      [,1] [,2]
#> [1,]    7   15
#> [2,]   10   22
c(1, 4, 9) |> sqrt() |> sum()       # Pipe: Ergebnis an den nächsten Befehl weitergeben
#> [1] 6

Vektoren

Vektor erzeugen

c(1, 2, c(3, 4), 5)          # verschachtelte Vektoren werden flach
#> [1] 1 2 3 4 5
1:10
#>  [1]  1  2  3  4  5  6  7  8  9 10
seq(0, 100, by = 25)
#> [1]   0  25  50  75 100
seq(1, 2, length.out = 5)
#> [1] 1.00 1.25 1.50 1.75 2.00
rep(0, 5)
#> [1] 0 0 0 0 0
rep(c("a", "b"), times = 2)
#> [1] "a" "b" "a" "b"
rep(c("a", "b"), each = 2)
#> [1] "a" "a" "b" "b"
c("hallo", 0, TRUE)          # ein Text macht alle Werte zu Text
#> [1] "hallo" "0"     "TRUE"

Auf Elemente zugreifen

Die Zählung beginnt bei 1.

days <- c("mo", "di", "mi", "do", "fr", "sa", "so")
days[3]                 # drittes Element
#> [1] "mi"
days[1:3]               # erstes bis drittes Element
#> [1] "mo" "di" "mi"
days[c(1, 7)]           # erstes und siebtes Element
#> [1] "mo" "so"
days[-c(2, 3)]          # alles außer dem zweiten und dritten Element
#> [1] "mo" "do" "fr" "sa" "so"
days[c(TRUE, FALSE)]    # jedes zweite Element
#> [1] "mo" "mi" "fr" "so"
days[length(days)]      # letztes Element
#> [1] "so"
head(days, 2)
#> [1] "mo" "di"
tail(days, 2)
#> [1] "sa" "so"

Elemente benennen und auswählen

preise <- c(brot = 3.5, milch = 1.2, kaese = 4.9)
preise["milch"]
#> milch 
#>   1.2
preise[preise > 2]      # nur Werte, die die Bedingung erfüllen
#>  brot kaese 
#>   3.5   4.9
which(preise > 2)       # Positionen dieser Werte
#>  brot kaese 
#>     1     3
names(preise)
#> [1] "brot"  "milch" "kaese"

Mit Vektoren rechnen

Bei ungleicher Länge wiederholt R den kürzeren Vektor. Das Beispiel rechnet 1*1, 2*2, 3*3, 1*4, 2*5, 3*6.

a <- c(1, 2, 3)
b <- c(1, 2, 3, 4, 5, 6)
a * b
#> [1]  1  4  9  4 10 18
a + 10
#> [1] 11 12 13

Sortieren und auswerten

x <- c(4, 1, 9, 4, 10, 18)
length(x)
#> [1] 6
sort(x)
#> [1]  1  4  4  9 10 18
sort(x, decreasing = TRUE)
#> [1] 18 10  9  4  4  1
order(x)            # Positionen in sortierter Reihenfolge
#> [1] 2 1 4 3 5 6
rev(x)              # Reihenfolge umdrehen
#> [1] 18 10  4  9  1  4
unique(x)
#> [1]  4  1  9 10 18
duplicated(x)
#> [1] FALSE FALSE FALSE  TRUE FALSE FALSE
table(x)            # Häufigkeit jedes Werts
#> x
#>  1  4  9 10 18 
#>  1  2  1  1  1
cumsum(x)           # laufende Summe
#> [1]  4  5 14 18 28 46

Vektoren verbinden

x <- c("a", "b")
y <- c("A", "B")
c(x, y)             # zu einem längeren Vektor
#> [1] "a" "b" "A" "B"
paste(x, y)         # Element für Element zu Text
#> [1] "a A" "b B"
rbind(x, y)         # als Zeilen einer Matrix
#>   [,1] [,2]
#> x "a"  "b" 
#> y "A"  "B"
cbind(x, y)         # als Spalten einer Matrix
#>      x   y  
#> [1,] "a" "A"
#> [2,] "b" "B"

Zeichenketten

Länge und Teile

a <- "abcdefg"
nchar(a)
#> [1] 7
substr(a, 3, 5)
#> [1] "cde"
toupper("Hello")
#> [1] "HELLO"
tolower("Hello")
#> [1] "hello"
trimws("   Leerzeichen   ")
#> [1] "Leerzeichen"

Einfache und doppelte Anführungszeichen sind gleichwertig. R zeigt Text immer mit doppelten Anführungszeichen an.

'hello'
#> [1] "hello"

Text verbinden

a <- "Hello"
b <- "How"
c <- "are you?"
paste(a, b, c)
#> [1] "Hello How are you?"
paste(a, b, c, sep = "@")
#> [1] "Hello@How@are you?"
paste0(a, b, c)                       # ohne Trennzeichen
#> [1] "HelloHoware you?"
paste(c("a", "b", "c"), collapse = "-")  # Vektor zu einem Text
#> [1] "a-b-c"
sprintf("%s hat %d Punkte", "Linda", 42L)
#> [1] "Linda hat 42 Punkte"

Text formatieren

format(18, width = 5)
#> [1] "   18"
format("Hello", width = 9, justify = "centre")   # "left", "right", "centre"
#> [1] "  Hello  "
formatC(7, width = 3, flag = "0")                # führende Nullen
#> [1] "007"

Suchen, ersetzen und teilen

texte <- c("Apfel", "Birne", "Ananas")
grepl("^A", texte)              # beginnt der Text mit A?
#> [1]  TRUE FALSE  TRUE
startsWith(texte, "A")          # dasselbe ohne regulären Ausdruck
#> [1]  TRUE FALSE  TRUE
sub("a", "4", "Ananas")         # ersten Treffer ersetzen
#> [1] "An4nas"
gsub("a", "4", "Ananas")        # alle Treffer ersetzen
#> [1] "An4n4s"
strsplit("a,b,c", ",")[[1]]     # Text an einem Zeichen teilen
#> [1] "a" "b" "c"
text <- "Bestellung 4711 vom Montag"
regmatches(text, regexpr("[0-9]+", text))   # erste Zahl im Text
#> [1] "4711"

Faktoren

Ein Faktor speichert Kategorien. Die möglichen Ausprägungen heißen Levels.

v <- c("thomas", "thomas", "thomas", "linda")
class(v)
#> [1] "character"
levels(v)
#> NULL

v <- factor(v)
class(v)
#> [1] "factor"
typeof(v)        # intern speichert R die Levels als ganze Zahlen
#> [1] "integer"
levels(v)
#> [1] "linda"  "thomas"
nlevels(v)
#> [1] 2
table(v)
#> v
#>  linda thomas 
#>      1      3

Reihenfolge der Levels

Ohne Angabe sortiert R die Levels alphabetisch. Mit levels legt man die Reihenfolge selbst fest, zum Beispiel für die Achsen eines Diagramms.

a <- factor(c("Thomas", "Linda", "Felix", "Sam", "Linda"))
levels(a)
#> [1] "Felix"  "Linda"  "Sam"    "Thomas"
a <- factor(a, levels = c("Linda", "Sam", "Thomas", "Felix"))
levels(a)
#> [1] "Linda"  "Sam"    "Thomas" "Felix"

Geordnete Faktoren

groesse <- factor(c("M", "S", "L", "M"), levels = c("S", "M", "L"), ordered = TRUE)
groesse
#> [1] M S L M
#> Levels: S < M < L
groesse > "S"
#> [1]  TRUE FALSE  TRUE  TRUE

Faktor erzeugen und bereinigen

gl(n, k) erzeugt n Levels, die jeweils k mal hintereinander vorkommen.

gl(2, 4, labels = c("Linda", "Sam"))
#> [1] Linda Linda Linda Linda Sam   Sam   Sam   Sam  
#> Levels: Linda Sam
f <- factor(c("a", "b", "c"))[1:2]
droplevels(f)        # nicht mehr verwendete Levels entfernen
#> [1] a b
#> Levels: a b
as.character(f)      # zurück in Text
#> [1] "a" "b"

Seit R 4.0 wandelt data.frame() Text nicht mehr automatisch in Faktoren um. Wer das alte Verhalten braucht, setzt stringsAsFactors = TRUE.

df <- data.frame(name = c("Thomas", "Linda"))
is.factor(df$name)
#> [1] FALSE
df <- data.frame(name = c("Thomas", "Linda"), stringsAsFactors = TRUE)
is.factor(df$name)
#> [1] TRUE

Listen

Eine Liste kann Werte unterschiedlicher Typen enthalten, auch Vektoren und weitere Listen.

meineListe <- list(1, "Text", c(3, 4))
meineListe
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] "Text"
#> 
#> [[3]]
#> [1] 3 4

Elemente benennen und auslesen

Einfache eckige Klammern [ ] liefern wieder eine Liste. Doppelte eckige Klammern [[ ]] und $ liefern das Element selbst.

person <- list(name = "Linda", alter = 34, hobbys = c("Tanzen", "Lesen"))
names(person)
#> [1] "name"   "alter"  "hobbys"
person$name
#> [1] "Linda"
person[["hobbys"]]
#> [1] "Tanzen" "Lesen"
person["alter"]
#> $alter
#> [1] 34
str(person)
#> List of 3
#>  $ name  : chr "Linda"
#>  $ alter : num 34
#>  $ hobbys: chr [1:2] "Tanzen" "Lesen"

Elemente ändern, hinzufügen und entfernen

person$alter <- 35          # Element überschreiben
person$stadt <- "Köln"      # Element hinzufügen
person$hobbys <- NULL       # Element entfernen
names(person)
#> [1] "name"  "alter" "stadt"

Listen verbinden und umwandeln

a <- list(1, 2, 3)
b <- list(4, 5, 6)
meineNeueListe <- c(a, b)
unlist(meineNeueListe)      # Liste in einen Vektor umwandeln
#> [1] 1 2 3 4 5 6

Matrizen

Eine Matrix hat immer zwei Dimensionen und Werte eines einzigen Typs. Ohne byrow = TRUE füllt R die Matrix spaltenweise.

matrix(1:6, nrow = 2, ncol = 3)
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
matrix(1:6, nrow = 2, ncol = 3, byrow = TRUE)
#>      [,1] [,2] [,3]
#> [1,]    1    2    3
#> [2,]    4    5    6

Zeilen und Spalten benennen

myMatrix <- matrix(1:12, nrow = 3, ncol = 4, byrow = TRUE,
                   dimnames = list(c("row1", "row2", "row3"),
                                   c("col1", "col2", "col3", "col4")))
myMatrix
#>      col1 col2 col3 col4
#> row1    1    2    3    4
#> row2    5    6    7    8
#> row3    9   10   11   12
dim(myMatrix)
#> [1] 3 4

Auf Zeilen, Spalten und Werte zugreifen

myMatrix[1, ]           # erste Zeile
#> col1 col2 col3 col4 
#>    1    2    3    4
myMatrix[, 1]           # erste Spalte
#> row1 row2 row3 
#>    1    5    9
myMatrix[1, 3]          # Wert in Zeile 1, Spalte 3
#> [1] 3
myMatrix["row2", "col4"]
#> [1] 8

Mit Matrizen rechnen

+, -, * und / rechnen Element für Element. %*% ist die Matrixmultiplikation.

m1 <- matrix(1:6, nrow = 2, byrow = TRUE)
m2 <- matrix(1:6, nrow = 2, byrow = TRUE)
m1 + m2
#>      [,1] [,2] [,3]
#> [1,]    2    4    6
#> [2,]    8   10   12
t(m1)                   # Zeilen und Spalten tauschen
#>      [,1] [,2]
#> [1,]    1    4
#> [2,]    2    5
#> [3,]    3    6
m1 %*% t(m2)
#>      [,1] [,2]
#> [1,]   14   32
#> [2,]   32   77
rowSums(m1)
#> [1]  6 15
colMeans(m1)
#> [1] 2.5 3.5 4.5

Zeilen und Spalten anhängen

m <- matrix(1:4, nrow = 2)
rbind(m, c(9, 9))       # Zeile anhängen
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4
#> [3,]    9    9
cbind(m, c(9, 9))       # Spalte anhängen
#>      [,1] [,2] [,3]
#> [1,]    1    3    9
#> [2,]    2    4    9

Arrays

Ein Array kann beliebig viele Dimensionen haben. Das folgende Array hat 3 Zeilen, 4 Spalten und 2 Ebenen.

array("a", dim = c(3, 4, 2))
#> , , 1
#> 
#>      [,1] [,2] [,3] [,4]
#> [1,] "a"  "a"  "a"  "a" 
#> [2,] "a"  "a"  "a"  "a" 
#> [3,] "a"  "a"  "a"  "a" 
#> 
#> , , 2
#> 
#>      [,1] [,2] [,3] [,4]
#> [1,] "a"  "a"  "a"  "a" 
#> [2,] "a"  "a"  "a"  "a" 
#> [3,] "a"  "a"  "a"  "a"

Array mit Namen

Reichen die Werte nicht für alle Felder, wiederholt R sie. Die Zahlen 1 bis 12 füllen hier beide Ebenen.

myArray <- array(1:12, dim = c(3, 4, 2),
                 dimnames = list(c("row1", "row2", "row3"),
                                 c("col1", "col2", "col3", "col4")))
myArray[1, 3, 2]        # Zeile 1, Spalte 3, Ebene 2
#> [1] 7
myArray[, , 2]          # ganze zweite Ebene
#>      col1 col2 col3 col4
#> row1    1    4    7   10
#> row2    2    5    8   11
#> row3    3    6    9   12

Ebene überschreiben und auswerten

apply() wendet eine Funktion auf Zeilen (1), Spalten (2) oder beides (c(1, 2)) an. Das Beispiel bildet für jede Zeile die Summe über alle Spalten und beide Ebenen.

myArray[, , 2] <- 13:24
apply(myArray, 1, sum)
#> row1 row2 row3 
#>   92  100  108

Data Frames

Ein Data Frame ist eine Tabelle. Alle Werte einer Spalte haben denselben Typ, alle Spalten sind gleich lang.

myDataFrame <- data.frame(
  name       = c("Thomas", "Johan", "Linda"),
  alter      = c(34, 21, 49),
  geschlecht = c("M", "M", "W")
)
myDataFrame
#>     name alter geschlecht
#> 1 Thomas    34          M
#> 2  Johan    21          M
#> 3  Linda    49          W

Aufbau anzeigen

str(myDataFrame)
#> 'data.frame':	3 obs. of  3 variables:
#>  $ name      : chr  "Thomas" "Johan" "Linda"
#>  $ alter     : num  34 21 49
#>  $ geschlecht: chr  "M" "M" "W"
summary(myDataFrame)
#>      name               alter        geschlecht       
#>  Length:3           Min.   :21.00   Length:3          
#>  Class :character   1st Qu.:27.50   Class :character  
#>  Mode  :character   Median :34.00   Mode  :character  
#>                     Mean   :34.67                     
#>                     3rd Qu.:41.50                     
#>                     Max.   :49.00
dim(myDataFrame)          # Zeilen und Spalten
#> [1] 3 3
nrow(myDataFrame)
#> [1] 3
colnames(myDataFrame)
#> [1] "name"       "alter"      "geschlecht"
head(myDataFrame, 2)      # die ersten Zeilen
#>     name alter geschlecht
#> 1 Thomas    34          M
#> 2  Johan    21          M

In RStudio öffnet View(myDataFrame) die Tabelle in einem eigenen Fenster. edit() und fix() öffnen einen einfachen Editor, in dem man Werte direkt ändern kann.

View(myDataFrame)
myDataFrame <- edit(myDataFrame)
fix(myDataFrame)          # speichert die Änderung direkt in myDataFrame

Spalten auslesen

myDataFrame$name                 # Spalte als Vektor
#> [1] "Thomas" "Johan"  "Linda"
myDataFrame[["alter"]]           # dasselbe mit Namen als Text
#> [1] 34 21 49
myDataFrame[, c("name", "alter")]  # mehrere Spalten als Data Frame
#>     name alter
#> 1 Thomas    34
#> 2  Johan    21
#> 3  Linda    49
myDataFrame[c(1, 3)]             # Spalte 1 und 3
#>     name geschlecht
#> 1 Thomas          M
#> 2  Johan          M
#> 3  Linda          W

Zeilen auslesen und filtern

myDataFrame[1, ]                          # erste Zeile
#>     name alter geschlecht
#> 1 Thomas    34          M
myDataFrame[myDataFrame$alter > 30, ]     # Zeilen mit Bedingung
#>     name alter geschlecht
#> 1 Thomas    34          M
#> 3  Linda    49          W
subset(myDataFrame, alter > 30, select = c(name, alter))
#>     name alter
#> 1 Thomas    34
#> 3  Linda    49

Spalten und Zeilen hinzufügen

myDataFrame$stadt <- c("Köln", "Bonn", "Essen")   # neue Spalte

neueZeilen <- data.frame(
  name       = c("Igor", "Andreas"),
  alter      = c(19, 25),
  geschlecht = c("M", "M"),
  stadt      = c("Bonn", "Köln")
)
myDataFrame <- rbind(myDataFrame, neueZeilen)     # neue Zeilen
myDataFrame
#>      name alter geschlecht stadt
#> 1  Thomas    34          M  Köln
#> 2   Johan    21          M  Bonn
#> 3   Linda    49          W Essen
#> 4    Igor    19          M  Bonn
#> 5 Andreas    25          M  Köln

Sortieren

myDataFrame[order(myDataFrame$alter), ]
#>      name alter geschlecht stadt
#> 4    Igor    19          M  Bonn
#> 2   Johan    21          M  Bonn
#> 5 Andreas    25          M  Köln
#> 1  Thomas    34          M  Köln
#> 3   Linda    49          W Essen
myDataFrame[order(-myDataFrame$alter), ]   # absteigend
#>      name alter geschlecht stadt
#> 3   Linda    49          W Essen
#> 1  Thomas    34          M  Köln
#> 5 Andreas    25          M  Köln
#> 2   Johan    21          M  Bonn
#> 4    Igor    19          M  Bonn

Gruppieren und zusammenfassen

aggregate(alter ~ geschlecht, data = myDataFrame, FUN = mean)
#>   geschlecht alter
#> 1          M 24.75
#> 2          W 49.00
table(myDataFrame$stadt)
#> 
#>  Bonn Essen  Köln 
#>     2     1     2

Tabellen verknüpfen

staedte <- data.frame(stadt = c("Köln", "Bonn", "Essen"),
                      einwohner = c(1084831, 336465, 586608))
merge(myDataFrame, staedte, by = "stadt")
#>   stadt    name alter geschlecht einwohner
#> 1  Bonn   Johan    21          M    336465
#> 2  Bonn    Igor    19          M    336465
#> 3 Essen   Linda    49          W    586608
#> 4  Köln  Thomas    34          M   1084831
#> 5  Köln Andreas    25          M   1084831

Bedingungen

if und else

x <- 7
if (x > 10) {
  "groß"
} else if (x > 5) {
  "mittel"
} else {
  "klein"
}
#> [1] "mittel"

ifelse für ganze Vektoren

if prüft genau einen Wert. ifelse() prüft jeden Wert eines Vektors.

zahlen <- c(3, 8, 12)
ifelse(zahlen > 5, "groß", "klein")
#> [1] "klein" "groß"  "groß"

switch

tag <- "sa"
switch(tag,
       sa = ,
       so = "Wochenende",
       "Werktag")
#> [1] "Wochenende"

Mehrere Befehle in einer Zeile trennt man mit Semikolon:

"hello"; 1 + 1; 1 * 3
#> [1] "hello"
#> [1] 2
#> [1] 3

Schleifen

for

for (i in seq(from = 0, to = 20, by = 5)) {
  print(i)
}
#> [1] 0
#> [1] 5
#> [1] 10
#> [1] 15
#> [1] 20

seq_along() liefert die Positionen eines Vektors und funktioniert auch bei leeren Vektoren:

namen <- c("Linda", "Sam")
for (i in seq_along(namen)) {
  cat(i, namen[i], "\n")
}
#> 1 Linda 
#> 2 Sam

while und repeat

i <- 5
while (i <= 15) {
  print(i)
  i <- i + 5
}
#> [1] 5
#> [1] 10
#> [1] 15

i <- 5
repeat {
  if (i > 15) break   # Schleife verlassen
  print(i)
  i <- i + 5
}
#> [1] 5
#> [1] 10
#> [1] 15

next

for (i in 1:6) {
  if (i %% 2 == 0) next   # gerade Zahlen überspringen
  print(i)
}
#> [1] 1
#> [1] 3
#> [1] 5

Viele Schleifen lassen sich in R kürzer ohne Schleife schreiben, weil Befehle wie seq(), sum() oder + auf ganze Vektoren wirken:

seq(0, 20, by = 5)
#> [1]  0  5 10 15 20

Funktionen

Funktion aufrufen

Parameter lassen sich nach Position oder nach Namen übergeben.

log2(64)
#> [1] 6
log(x = 64, base = 2)
#> [1] 6
log(64, 2)
#> [1] 6

Eigene Funktion schreiben

Eine Funktion gibt das Ergebnis der letzten Zeile zurück. Mit return() kann man sie vorher verlassen.

quadrat <- function(x) x^2
quadrat(4)
#> [1] 16

begruessen <- function(vorname, gruss = "Hallo") {
  if (vorname == "") return("Kein Name angegeben")
  paste(gruss, vorname)
}
begruessen("Linda")                 # Standardwert für gruss
#> [1] "Hallo Linda"
begruessen("Linda", gruss = "Hi")   # Standardwert überschreiben
#> [1] "Hi Linda"
begruessen("")
#> [1] "Kein Name angegeben"

Kurzschreibweise

Seit R 4.1 kann man \(x) statt function(x) schreiben.

verdoppeln <- \(x) x * 2
verdoppeln(21)
#> [1] 42

Beliebig viele Parameter

summe <- function(...) sum(...)
summe(1, 2, 3, 4)
#> [1] 10

Funktion auf viele Werte anwenden

sapply() gibt einen Vektor zurück, lapply() eine Liste. vapply() prüft zusätzlich den Typ des Ergebnisses. Map() geht mehrere Vektoren gleichzeitig durch.

sapply(1:5, \(x) x^2)
#> [1]  1  4  9 16 25
lapply(1:2, \(x) x * 10)
#> [[1]]
#> [1] 10
#> 
#> [[2]]
#> [1] 20
vapply(c("a", "bb", "ccc"), nchar, integer(1))
#>   a  bb ccc 
#>   1   2   3
Map(\(x, y) x + y, 1:3, 4:6) |> unlist()
#> [1] 5 7 9

Fehlerbehandlung

ergebnis <- tryCatch(
  log(-1),
  warning = function(w) "Warnung abgefangen",
  error   = function(e) "Fehler abgefangen"
)
ergebnis
#> [1] "Warnung abgefangen"

pruefen <- function(x) {
  if (!is.numeric(x)) stop("x muss eine Zahl sein")
  x * 2
}
tryCatch(pruefen("a"), error = function(e) conditionMessage(e))
#> [1] "x muss eine Zahl sein"

stopifnot(is.numeric(5), 5 > 0)   # bricht ab, wenn eine Bedingung nicht stimmt

Mathematik und Statistik

Runden und Grundfunktionen

round(sqrt(99))
#> [1] 10
round(sqrt(99), digits = 2)
#> [1] 9.95
signif(123456, 2)        # auf 2 gültige Stellen
#> [1] 120000
ceiling(2.1)             # aufrunden
#> [1] 3
floor(2.9)               # abrunden
#> [1] 2
abs(-3)
#> [1] 3
exp(1)
#> [1] 2.718282
10 %% 3
#> [1] 1

Angezeigte Stellen

R zeigt Zahlen standardmäßig mit 7 gültigen Stellen an. options(digits = ) erlaubt Werte von 1 bis 22. Die Einstellung ändert nur die Anzeige, nicht die Rechengenauigkeit.

pi
#> [1] 3.141593
options(digits = 15)
pi
#> [1] 3.14159265358979
options(digits = 7)

Lage und Streuung

x <- c(1, 2, 4, 99)
mean(x)                  # Durchschnitt
#> [1] 26.5
median(x)                # Zentralwert
#> [1] 3
min(x); max(x)
#> [1] 1
#> [1] 99
range(x)
#> [1]  1 99
diff(range(x))           # Spannweite
#> [1] 98
sum(x)
#> [1] 106
var(x)                   # Varianz
#> [1] 2337.667
sd(x)                    # Standardabweichung
#> [1] 48.34942
quantile(x)
#>    0%   25%   50%   75%  100% 
#>  1.00  1.75  3.00 27.75 99.00
summary(x)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>    1.00    1.75    3.00   26.50   27.75   99.00

Zusammenhang zweier Werte

cor(mtcars$mpg, mtcars$wt)              # Korrelation
#> [1] -0.8676594
modell <- lm(mpg ~ wt, data = mtcars)   # lineare Regression
coef(modell)
#> (Intercept)          wt 
#>   37.285126   -5.344472

Zufallszahlen

Mit set.seed() liefert R bei jedem Durchlauf dieselben Zufallszahlen. So lassen sich Ergebnisse nachvollziehen.

set.seed(42)
sample(1:1000, 5)                # 5 Zahlen ohne Wiederholung
#> [1] 561 997 321 153  74
sample(c("Kopf", "Zahl"), 3, replace = TRUE)
#> [1] "Zahl" "Zahl" "Zahl"
round(runif(3), 2)               # gleichverteilt zwischen 0 und 1
#> [1] 0.66 0.71 0.46
round(rnorm(3, mean = 100, sd = 15), 1)   # normalverteilt
#> [1] 108.7  90.1 123.3

Zahlen formatieren

format(20, nsmall = 2)                         # mindestens 2 Nachkommastellen
#> [1] "20.00"
format(20.123456789, digits = 4)               # 4 gültige Stellen
#> [1] "20.12"
format(20, scientific = TRUE)                  # wissenschaftliche Schreibweise
#> [1] "2e+01"
format(1234567.891, big.mark = ".", decimal.mark = ",", nsmall = 2)
#> [1] "1.234.567,89"
format(100)                                    # Zahl als Text
#> [1] "100"

Datum und Uhrzeit

Sys.Date()       # heutiges Datum
Sys.time()       # aktuelle Uhrzeit
d <- as.Date("18.10.2016", format = "%d.%m.%Y")
d
#> [1] "2016-10-18"
format(d, "%d.%m.%Y")
#> [1] "18.10.2016"
format(d, "%Y")                         # nur das Jahr
#> [1] "2016"
d + 30                                  # 30 Tage später
#> [1] "2016-11-17"
as.numeric(as.Date("2026-10-29") - d)   # Abstand in Tagen
#> [1] 3663
seq(d, by = "month", length.out = 3)
#> [1] "2016-10-18" "2016-11-18" "2016-12-18"

Diagramme

Die Grundausstattung von R zeichnet Diagramme ohne weitere Pakete:

plot(mtcars$wt, mtcars$mpg, xlab = "Gewicht", ylab = "Verbrauch")
hist(mtcars$mpg, main = "Verteilung", col = "steelblue")
barplot(table(mtcars$cyl))
boxplot(mpg ~ cyl, data = mtcars)

Das Paket ggplot2 baut Diagramme aus Ebenen auf:

library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg, colour = factor(cyl))) +
  geom_point() +
  labs(x = "Gewicht", y = "Verbrauch", colour = "Zylinder")
ggsave("diagramm.png", width = 6, height = 4)

Weitere Pakete für Diagramme sind lattice und plotly.

Daten bearbeiten mit dplyr

Das Paket dplyr bietet einfache Befehle zum Filtern, Auswählen, Berechnen und Zusammenfassen. Die Pipe |> reicht das Ergebnis an den nächsten Befehl weiter.

library(dplyr)

iris |>
  filter(Sepal.Length > 5) |>                  # Zeilen filtern
  mutate(Laenge_zu_Breite = Sepal.Length / Sepal.Width) |>   # Spalte berechnen
  group_by(Species) |>                         # gruppieren
  summarise(Anzahl = n(),
            Mittelwert = mean(Laenge_zu_Breite)) |> # je Gruppe zusammenfassen
  arrange(desc(Mittelwert))                    # absteigend sortieren
#> # A tibble: 3 × 3
#>   Species    Anzahl Mittelwert
#>   <fct>       <int>      <dbl>
#> 1 virginica      49       2.24
#> 2 versicolor     47       2.16
#> 3 setosa         22       1.44
iris |>
  select(Species, Petal.Length) |>    # Spalten auswählen
  slice_head(n = 3)                   # die ersten Zeilen
#>   Species Petal.Length
#> 1  setosa          1.4
#> 2  setosa          1.4
#> 3  setosa          1.3

Datenbanken

Das Paket DBI ist die gemeinsame Schnittstelle zu Datenbanken in R. Für jede Datenbank gibt es einen eigenen Treiber:

DatenbankPaket
SQLiteRSQLite
MySQL und MariaDBRMariaDB (löst das ältere RMySQL ab)
PostgreSQLRPostgres
alle Datenbanken mit ODBC Treiberodbc (das ältere RODBC funktioniert weiterhin, nutzt aber nicht die DBI Schnittstelle)
install.packages(c("DBI", "RSQLite"))

Das folgende Beispiel legt eine Datenbank im Arbeitsspeicher an, schreibt eine Tabelle hinein und fragt sie mit SQL ab:

library(DBI)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbWriteTable(con, "autos", mtcars[1:5, c("mpg", "cyl", "hp")])
dbListTables(con)
#> [1] "autos"
dbGetQuery(con, "SELECT cyl, AVG(hp) AS ps FROM autos GROUP BY cyl")
#>   cyl  ps
#> 1   4  93
#> 2   6 110
#> 3   8 175
dbDisconnect(con)

Verbindung zu einem MariaDB oder MySQL Server:

con <- dbConnect(RMariaDB::MariaDB(),
                 host = "localhost", dbname = "shop",
                 user = "aaron", password = Sys.getenv("DB_PASSWORT"))

Das Passwort gehört nicht in den Code. Sys.getenv() liest es aus einer Umgebungsvariablen, die man zum Beispiel in der Datei .Renviron festlegt.

Entropie und Information Gain

Die Entropie misst, wie gemischt die Werte einer Zielspalte sind. Der Information Gain gibt an, um wie viel ein Merkmal diese Mischung verringert. Beide Werte helfen bei Entscheidungsbäumen, das Merkmal mit der größten Aussagekraft zu finden.

entropie <- function(y) {
  p <- table(y) / length(y)
  p <- p[p > 0]
  -sum(p * log2(p))
}

information_gain <- function(y, x) {
  bedingt <- sum(sapply(split(y, x), \(teil) length(teil) / length(y) * entropie(teil)))
  entropie(y) - bedingt
}

entropie(iris$Species)
#> [1] 1.584963
information_gain(iris$Species, cut(iris$Petal.Length, 3))
#> [1] 1.324664
information_gain(iris$Species, cut(iris$Sepal.Width, 3))
#> [1] 0.2658956

Die Blütenblattlänge (Petal.Length) trennt die Arten deutlich besser als die Kelchblattbreite (Sepal.Width). Das Paket FSelector bietet die Funktion information.gain() für alle Spalten auf einmal, braucht aber eine Java Installation.