Im Konzept Vectors haben wir gesehen, dass ein Vektor in einem bedingten Ausdruck verwendet werden kann und dabei einen logischen Vektor liefert. Diesen kannst du wiederum in Funktionen wie all() und any() verwenden.
v <- c(4, 7, 10)
v >= 6
#> [1] FALSE TRUE TRUE
all(v > 6)
#> [1] FALSE # not all elements match this condition
any(v > 6)
#> [1] TRUE # at least one element matches
Die Technik ist weit mächtiger, als dieses Beispiel vermuten lässt.
Einzelne Elemente eines Vektors kannst du mit einer Indexnummer oder einem Vektor von Indizes herausziehen:
v <- 5:10
v
#> [1] 5 6 7 8 9 10
v[3]
#> [1] 7
v[c(2, 4)]
#> [1] 6 8
Alternativ filterst du den ursprünglichen Vektor mit einem logischen Vektor und erhältst so eine Teilmenge der Einträge, deren zugehöriger Wert TRUE ist:
v <- 1:3
bools <- c(FALSE, TRUE, TRUE)
v[bools]
#> [1] 2 3
Von dort ist es nur ein kleiner Schritt: Du erzeugst den booleschen Vektor einfach mit einem bedingten Ausdruck:
bools <- v >= 2
bools
#> [1] FALSE TRUE TRUE
v[bools]
#> [1] 2 3
# simpler form
v[v >= 2]
#> [1] 2 3
Solche bedingten Ausdrücke können beliebig komplex sein und mehrere Vektoren oder Funktionen einbeziehen:
v <- 1:10
w <- 10:1
# pairwise comparison
v > w
#> [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
# shorter form
v[v > w]
#> [1] 6 7 8 9 10
Wir haben dieses Konzept „Vektor-Filterung“ genannt, in der Hoffnung, dass es für möglichst viele Programmiererinnen und Programmierer Sinn ergibt.
In der R-Community ist der Begriff „Subsetting“ wahrscheinlich gebräuchlicher.
Der Einfachheit halber haben die Beispiele oben Vektoren gleicher Länge verglichen. Was passiert, wenn die Längen nicht übereinstimmen?
Die Antwort ist etwas Mächtiges, wenn du es bewusst einsetzt, aber verwirrend, wenn es unbeabsichtigt passiert.
Ein Beispiel:
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
Was ist da passiert? Wir haben einen Vektor der Länge 6 mit einem booleschen Vektor der Länge 2 indiziert, aber als Ergebnis einen Vektor der Länge 3 erhalten.
Wenn ein Vektor für den Ausdruck, in dem er verwendet wird, irgendwie „zu kurz“ ist, wiederholt R ihn so lange, bis die gewünschte Länge erreicht ist: Das nennt man „Recycling“.
Dadurch wurde unser Vektor c(TRUE, FALSE) zu c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE), und v[cond] lieferte anschließend die TRUE-Werte von v: in diesem Fall die ungeraden Zahlen.
Was passiert also im nächsten Beispiel?
v <- 1:3
0.5 * v
#> [1] 0.5 1.0 1.5
2 + v
#> [1] 3 4 5
Auf den ersten Blick sieht es wie eine Skalarmultiplikation und eine (etwas seltsame) Skalaraddition aus, aber in R gibt es keine Skalare.
Stattdessen werden die Zahlen recycelt, um einen Vektor passend zur Länge von v zu erhalten, und anschließend wird die Rechenoperation elementweise mit c(0.5, 0.5, 0.5) oder c(2, 2, 2) angewendet.
Recycling ist sehr, sehr nützlich, aber stell sicher, dass es auch wirklich das ist, was du beabsichtigst.
Eine kleine Abweichung bei den Vektorlängen (z. B. 99 statt 100) kann verwirrende Bugs verursachen.
Manche Autoren empfehlen, Recycling nur dann zu verwenden, wenn einer der Vektoren die Länge 1 hat.
Die Funktion which() nimmt ein boolesches Argument und gibt einen Vektor von Indizes zurück, für die TRUE gilt.
v
#> [1] 2 7 9
v[v > 5] # returns values
#> [1] 7 9
which(v > 5) # returns indices
#> [1] 2 3
In den vorherigen Abschnitten ging es darum, Einträge zu finden, die ein bestimmtes Kriterium erfüllen.
Wenn du nur wissen möchtest, ob der Vektor ein bestimmtes Element enthält, verwendest du den Operator %in%.
3 %in% 1:5
#> [1] TRUE
7 %in% 1:5
#> [1] FALSE
Das ist funktional äquivalent zum folgenden Code, aber viel bequemer:
length(v[v == 3]) > 0
#> [1] TRUE
Es gibt außerdem einen %notin%-Operator, der das logische not von %in% liefert.