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
| Struktur | Inhalt | Erzeugen mit |
|---|---|---|
| Vektor | Werte gleichen Typs in einer Reihe | c() |
| Liste | Werte beliebigen Typs, auch weitere Listen | list() |
| Matrix | Werte gleichen Typs in Zeilen und Spalten | matrix() |
| Array | Werte gleichen Typs in beliebig vielen Dimensionen | array() |
| Faktor | Kategorien mit festen Ausprägungen | factor() |
| Data Frame | Tabelle, jede Spalte hat ihren eigenen Typ | data.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
| Operator | Bedeutung | Beispiel | Ergebnis |
|---|---|---|---|
+ - * / | Grundrechenarten | 7 / 2 | 3.5 |
^ | Potenz | 2 ^ 3 | 8 |
%% | Rest einer Division (Modulo) | 7 %% 2 | 1 |
%/% | ganzzahlige Division | 7 %/% 2 | 3 |
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
| Operator | Bedeutung |
|---|---|
<- 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:
| Datenbank | Paket |
|---|---|
| SQLite | RSQLite |
| MySQL und MariaDB | RMariaDB (löst das ältere RMySQL ab) |
| PostgreSQL | RPostgres |
| alle Datenbanken mit ODBC Treiber | odbc (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.
