Abbiamo visto nel concetto Vectors che un vettore può essere usato in un'espressione condizionale, che dà un vettore logico.
A sua volta, questo può essere usato in funzioni come all() e any().
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
La tecnica è molto più potente di così.
Gli elementi selezionati di un vettore si possono estrarre con un indice o con un vettore di indici:
v <- 5:10
v
#> [1] 5 6 7 8 9 10
v[3]
#> [1] 7
v[c(2, 4)]
#> [1] 6 8
In alternativa, usa un vettore logico per filtrare il vettore originale, ottenendo un sottoinsieme di elementi che corrispondono a un valore TRUE:
v <- 1:3
bools <- c(FALSE, TRUE, TRUE)
v[bools]
#> [1] 2 3
Da qui a generare il vettore booleano con un'espressione condizionale il passo è breve:
bools <- v >= 2
bools
#> [1] FALSE TRUE TRUE
v[bools]
#> [1] 2 3
# simpler form
v[v >= 2]
#> [1] 2 3
Queste espressioni condizionali possono essere arbitrariamente complesse, coinvolgendo più vettori o funzioni:
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
Abbiamo chiamato questo concetto «Filtraggio di vettori», sperando che risulti comprensibile al maggior numero possibile di programmatori.
All'interno della comunità R, il termine «Subsetting» è probabilmente più comune.
Per semplicità, negli esempi precedenti tutti i vettori confrontati avevano la stessa lunghezza. Cosa succede se le lunghezze non coincidono?
La risposta è qualcosa di potente, se la usi di proposito, ma che confonde se non te l'aspetti.
Un esempio:
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
Che cosa è successo qui? Abbiamo indicizzato un vettore di lunghezza 6 con un booleano di lunghezza 2, ma come risultato abbiamo ottenuto un vettore di lunghezza 3.
Se un vettore è in qualche modo «troppo corto» per l'espressione in cui viene usato, R lo ripeterà finché non raggiunge la lunghezza desiderata: è quello che si chiama «riciclo».
Questo ha trasformato il nostro vettore c(TRUE, FALSE) in c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE), e v[cond] ha poi restituito i valori TRUE di v: in questo caso, i numeri dispari.
E allora cosa succede nell'esempio successivo?
v <- 1:3
0.5 * v
#> [1] 0.5 1.0 1.5
2 + v
#> [1] 3 4 5
A prima vista sembra una moltiplicazione scalare e una (un po' strana) addizione scalare, ma in R non esistono gli scalari.
Invece, i numeri vengono riciclati per ottenere un vettore della stessa lunghezza di v, poi l'operazione aritmetica viene applicata elemento per elemento usando c(0.5, 0.5, 0.5) o c(2, 2, 2).
Il riciclo è molto, molto utile, ma assicurati che sia davvero quello che vuoi.
Una piccola discrepanza nelle lunghezze dei vettori (ad esempio 99 contro 100) può causare bug che confondono.
Alcuni autori consigliano di usare il riciclo solo quando uno dei vettori ha lunghezza 1.
La funzione which() accetta un argomento booleano e restituisce un vettore di indici che danno TRUE.
v
#> [1] 2 7 9
v[v > 5] # returns values
#> [1] 7 9
which(v > 5) # returns indices
#> [1] 2 3
Le sezioni precedenti riguardavano la ricerca di elementi che soddisfano un certo criterio.
Se ti serve solo sapere se il vettore contiene un elemento specifico, si usa l'operatore %in%.
3 %in% 1:5
#> [1] TRUE
7 %in% 1:5
#> [1] FALSE
Dal punto di vista funzionale è equivalente al codice seguente, ma molto più comodo:
length(v[v == 3]) > 0
#> [1] TRUE
Esiste anche un operatore %notin%, che dà il not logico di %in%.