5 Data Modification
Quanto valgono gli assi nel nostro dataframe deck
? Andiamo a vedere la tredicesima carta del mazzo:
13, ] deck[
## figura seme valore
## 13 asso picche 1
A quanto pare, nel nostro mazzo gli assi valgono 1 ma in molti giochi, come ad esempio il poker, gli assi valgono più dei re, quindi 14.
Proveremo in questo capitolo a modificare il nostro dataframe in modo da rendere il nostro mazzo adeguato a diversi giochi. Andremo a modificare il contenuto del dataset.
Prima di tutto facciamo una copia del nostro mazzo (non vogliamo perdere i dati contenuti nel mazzo iniziale):
<- deck deck2
5.1 Modificare i valori
Per modificare i valori contenuti in un oggetto possiamo usare l’operatore di assegnazione <-
, già visto in precedenza. Tramite l’assegnazione andiamo a sovrascrivere gli elementi dell’oggetto che vogliamo modificare. R andrà ad aggiornare tali elementi.
Vediamolo con un esempio:
<- c(0, 0, 0, 0, 0, 0)
vec vec
## [1] 0 0 0 0 0 0
Ecco come selezioniamo l’elemento da modificare (il primo elemento di vec):
1] vec[
## [1] 0
E così lo andiamo a sovrascrivere:
1] <- 1000
vec[ vec
## [1] 1000 0 0 0 0 0
Possiamo sostituire più elementi nello stesso modo, l’importante è che il numero di elementi che si vogliono modificare sia uguale al numero dei nuovi elementi:
c(1, 3, 5)] <- c(1, 1, 1)
vec[ vec
## [1] 1 0 1 0 1 0
4:6] <- vec[4:6] + 1
vec[ vec
## [1] 1 0 1 1 2 1
Si può espandere un vettore aggiungendo un elemento che non esiste:
7] <- 0
vec[ vec
## [1] 1 0 1 1 2 1 0
Per aggiungere una riga o una colonna possiamo usare rispettivamente le funzioni rbind()
(row bind) e cbind()
(column bind). Per quest’ultima azione è possibile definire direttamente una nuova colonna assegnandogli un vettore. Ad esempio:
<- deck
deck2 <- rbind(deck2, c("re", "picche", 13)) #aggiungiamo una riga
deck2 tail(deck2)
## figura seme valore
## 48 cinque cuori 5
## 49 quattro cuori 4
## 50 tre cuori 3
## 51 due cuori 2
## 52 asso cuori 1
## 53 re picche 13
<- cbind(deck2, nuovaColonna = rep(1, 53)) #aggiungiamo una colonna di 1
deck2 #or
$nuovaColonna <- rep(1, 53)
deck2str(deck2)
## 'data.frame': 53 obs. of 4 variables:
## $ figura : chr "re" "regina" "jack" "dieci" ...
## $ seme : chr "picche" "picche" "picche" "picche" ...
## $ valore : chr "13" "12" "11" "10" ...
## $ nuovaColonna: num 1 1 1 1 1 1 1 1 1 1 ...
Ogni data frame dovrebbe avere una colonna che contiene gli identificatori delle righe che contiene, aggiungiamo quindi al nostro mazzo una colonna id
che contenga gli interi da 1 a 52.
$id <- 1:52 deck
Possiamo eliminare delle colonne da un dataframe (o elementi di una lista) assegnandogli il valore NULL
:
$nuovaColonna <- NULL
deck2head(deck2)
## figura seme valore
## 1 re picche 13
## 2 regina picche 12
## 3 jack picche 11
## 4 dieci picche 10
## 5 nove picche 9
## 6 otto picche 8
Andiamo quindi a sostituire il valore degli assi con il 14. Se il dataframe non è stato riordinato gli assi saranno nelle righe 13, 26, 39 e 52:
c(13, 26, 39, 52), 3] #righe 13, 26, 39 e 52, colonna 3 (valore) deck2[
## [1] "1" "1" "1" "1"
$value[c(13, 26, 39, 52)] #idem deck2
## NULL
Ora dobbiamo assegnare il nuovo valore alla colonna:
$value[c(13, 26, 39, 52)] <- c(14, 14, 14, 14) deck2
## Error in `$<-.data.frame`(`*tmp*`, value, value = c(NA, NA, NA, NA, NA, : replacement has 52 rows, data has 53
# or
$value[c(13, 26, 39, 52)] <- 14 deck2
## Error in `$<-.data.frame`(`*tmp*`, value, value = c(NA, NA, NA, NA, NA, : replacement has 52 rows, data has 53
# or
$value[c(13, 26, 39, 52)] <- rep(14,4) deck2
## Error in `$<-.data.frame`(`*tmp*`, value, value = c(NA, NA, NA, NA, NA, : replacement has 52 rows, data has 53
# vediamo se sono stati sostituiti i valori
$value[c(13, 26, 39, 52)] deck2
## NULL
Un’alternativa allo scrivere il vettore c(14, 14, 14, 14)
sarebbe quella di usare la funzione rep(14, 4)
.
rep(x, times)
La funzione rep(x, times)
ritorna un vettore di times
elementi uguali a x
La stessa tecnica funziona con i vettori, le matrici, gli array e le liste. Basta selezionare i valori da cambiare e indicare con cosa sostituirli.
In questo caso è stato facile trovare gli elementi da sostituire perchè il mazzo è ordinato per valore in ordine decrescente, ma come avremmo fatto a trovare gli assi se il mazzo non fosse stato ordinato?
5.1.1 Subsetting logico
R permette di indicizzare un oggetto attraverso dei vettori logici.
Riassumendo, si possono selezionare degli elementi attraverso un vettore logico, cioè contenente elementi TRUE o FALSE.
Il vettore logico dovrà avere la stessa lunghezza dell’oggetto da indicizzare. R ritornerà gli elementi che corrispondono al TRUE nel vettore logico:
vec
## [1] 1 0 1 1 2 1 0
c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)] vec[
## [1] 2
5.2 Condizioni logiche
Una condizione logica è una domanda che puà avere come risposta solo vero o falso. Un esempio è “uno è più grande di due?”, 1 < 2
. R fornisce 7 diversi operatori logici che possono essere usati per i confronti:
Operator | Syntax | Tests |
---|---|---|
> |
a > b |
a è maggiore di b? |
>= |
a >= b |
a è maggiore uguale di b? |
< |
a < b |
a è minore di b? |
<= |
a <= b |
a è minore uguale di b? |
== |
a == b |
a è uguale a b? |
!= |
a != b |
a è diverso da b? |
%in% |
a %in% c(a, b, c) |
a è nel vettore c(a,b,c)? |
Ogni operatore ritorna un TRUE
o un FALSE
. Se si farenno dei confronti tra vettori R userà la tecnica element-wise e confronterà elemento per elemento restituendo un vettore logico:
1 > 2
## [1] FALSE
1 > c(0, 1, 2)
## [1] TRUE FALSE FALSE
c(1, 2, 3) == c(3, 2, 1)
## [1] FALSE TRUE FALSE
%in%
è l’unico operatore che non segue la logica element-wise ma controlla se un certo elemento si trova o meno all’interno di un vettore.
1 %in% c(3, 4, 5)
## [1] FALSE
c(1, 2) %in% c(3, 4, 5)
## [1] FALSE FALSE
c(1, 2, 3) %in% c(3, 4, 5)
## [1] FALSE FALSE TRUE
c(1, 2, 3, 4) %in% c(3, 4, 5)
## [1] FALSE FALSE TRUE TRUE
Ora possiamo usare l’operatore di uguaglianza ==
per cercare gli assi nel mazzo e sostituirne il valore. Partiamo vedendo quali figure sono uguali ad "asso"
:
$figura == "asso" deck2
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [9] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## [17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
## [33] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
## [41] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [49] FALSE FALSE FALSE TRUE FALSE
Con la funzione sum
possiamo contare quanti assi abbiamo. In questo caso i TRUE verranno trasformati in 1 e i FALSE in 0 prima di eseguire la somma (coercion):
sum(deck2$figura == "asso")
## [1] 4
Siamo pronti per modificare il valore degli assi:
<- deck
deck3 $valore[deck3$figura == "asso"] <- 14
deck3c(13, 26, 39, 52), 3] deck3[
## [1] 14 14 14 14
Esercizio 11
Creare un nuovo mazzo uguale a deck
e assegnare il valore 1 a tutte le carte di cuori.
<- deck
deck4 $valore[deck4$seme == "cuori"] <- 1
deck4$valore[deck4$seme == "cuori"] deck4
## [1] 1 1 1 1 1 1 1 1 1 1 1 1 1
Ma se volessimo modificare il valore di tutte le regine di picche?. Quello che ci serve sono gli operatori booleani. Gli operatori booleani convertono più test logici in un unico test.
5.3 Operatori booleani
Gli operatori booleani sono gli equivalenti delle nostre congiunzioni, come “e” e “o”. Servono per unire i risultati di condizioni logiche in una condizione il cui risultato sarà TRUE
o FALSE
. R possiede 6 operatori booleani:
Operator | Syntax | Tests |
---|---|---|
& |
cond1 & cond2 |
Sono entrambe vere? |
| |
cond1 | cond2 |
'E vera almeno una delle due? |
xor |
xor(cond1, cond2) |
'E vera SOLO una delle due? |
! |
!cond1 |
Is cond1 'E falsa? (e.g., ! serve per invertire il risultato di una condizione) |
any |
any(cond1, cond2, cond3, ...) |
'E vera almeno una? |
all |
all(cond1, cond2, cond3, ...) |
Sono tutte vere? |
Per usare un operatore booleano bisogna posizionarlo tra due condizioni logiche. R eseguirà ogni condizione e raggrupperà i le condizioni attraverso l’operatore booleano producendo un solo risultato.

(#fig:boolean )Operatori booleani
Usati su vettori logici anche gli operatori booleani si applicheranno element-wise.
<- c(1, 2, 3)
a <- c(1, 2, 3)
b <- c(1, 2, 4)
c == b a
## [1] TRUE TRUE TRUE
== c b
## [1] TRUE TRUE FALSE
== b & b == c a
## [1] TRUE TRUE FALSE
Esercizio 12
Tradurre le seguenti frasi in condizioni logiche scritte in R:
- w è positivo?
- x è maggiore di 10 e minore di 20?
- y è uguale alla parola “Febbraio”?
- Gli elementi di z sono giorni della settimana?
Usare i seguenti oggetti per risolvere l’esercizio:
<- c(-1, 0, 1)
w <- c(5, 15)
x <- "Febbraio"
y <- c("Lunedi", "Martedi", "Venerdi") #meglio non usare gli accenti z
> 0 w
## [1] FALSE FALSE TRUE
10 < x & x < 20
## [1] FALSE TRUE
== "Febbraio" y
## [1] TRUE
all(z %in% c("Lunedi", "Martedi", "Mercoledi", "Giovedi", "Venerdi",
"Sabato", "Domenica"))
## [1] TRUE
Si può utilizzare un operatore booleano per individuare l’asso di picche?
Certo che sì! Dobbiamo analizzare ogni carta e assicurarci che la sua figura sia un asso e il suo seme sia uguale a “picche”. Possiamo farlo in R nel seguente modo:
$figura == "asso" & deck4$seme == "picche" deck4
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [9] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## [17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [33] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [41] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [49] FALSE FALSE FALSE FALSE
Salviamo questo vettore logico in un oggetto:
<- deck4$figura == "asso" & deck4$seme == "picche" assoDiPicche
Ora possiamo usare questo vettore per fare un subsetting logico delle righe del data frame deck4
:
deck4[assoDiPicche, ]
## figura seme valore id
## 13 asso picche 1 13
$valore[assoDiPicche] # per scoprirne il valore deck4
## [1] 1
Ora che abbiamo trovato l’elemento “asso di picche” possiamo modificarne il valore adeguandolo a quello del poker:
$valore[assoDiPicche] <- 13
deck4 deck4[assoDiPicche, ]
## figura seme valore id
## 13 asso picche 13 13
Il nostro mazzo è pronto per essere giocato a poker!
Esercizio 13
Selezionare:
- le prime 20 carte del mazzo;
- le carte con valore pari;
- le carte con figura “regina” o “re”;
- le figure delle carte vestite (con valore 1 o superiore a 10);
- le figure delle carte non vestite (con valore tra 2 e 10);
1:20, ] #a. deck[
## figura seme valore id
## 1 re picche 13 1
## 2 regina picche 12 2
## 3 jack picche 11 3
## 4 dieci picche 10 4
## 5 nove picche 9 5
## 6 otto picche 8 6
## 7 sette picche 7 7
## 8 sei picche 6 8
## 9 cinque picche 5 9
## 10 quattro picche 4 10
## 11 tre picche 3 11
## 12 due picche 2 12
## 13 asso picche 1 13
## 14 re fiori 13 14
## 15 regina fiori 12 15
## 16 jack fiori 11 16
## 17 dieci fiori 10 17
## 18 nove fiori 9 18
## 19 otto fiori 8 19
## 20 sette fiori 7 20
$valore %% 2 == 0,] #b. deck[deck
## figura seme valore id
## 2 regina picche 12 2
## 4 dieci picche 10 4
## 6 otto picche 8 6
## 8 sei picche 6 8
## 10 quattro picche 4 10
## 12 due picche 2 12
## 15 regina fiori 12 15
## 17 dieci fiori 10 17
## 19 otto fiori 8 19
## 21 sei fiori 6 21
## 23 quattro fiori 4 23
## 25 due fiori 2 25
## 28 regina quadri 12 28
## 30 dieci quadri 10 30
## 32 otto quadri 8 32
## 34 sei quadri 6 34
## 36 quattro quadri 4 36
## 38 due quadri 2 38
## 41 regina cuori 12 41
## 43 dieci cuori 10 43
## 45 otto cuori 8 45
## 47 sei cuori 6 47
## 49 quattro cuori 4 49
## 51 due cuori 2 51
$figura == "regina" | deck$figura == "re", ] #c. deck[deck
## figura seme valore id
## 1 re picche 13 1
## 2 regina picche 12 2
## 14 re fiori 13 14
## 15 regina fiori 12 15
## 27 re quadri 13 27
## 28 regina quadri 12 28
## 40 re cuori 13 40
## 41 regina cuori 12 41
$figura %in% c("regina", "re"),] #c. deck[deck
## figura seme valore id
## 1 re picche 13 1
## 2 regina picche 12 2
## 14 re fiori 13 14
## 15 regina fiori 12 15
## 27 re quadri 13 27
## 28 regina quadri 12 28
## 40 re cuori 13 40
## 41 regina cuori 12 41
$figura[(deck$valore == 1 | deck$valore > 11)] #d deck
## [1] "re" "regina" "asso" "re" "regina"
## [6] "asso" "re" "regina" "asso" "re"
## [11] "regina" "asso"
$figura[(deck$valore > 2 & deck$valore <= 10)] #e deck
## [1] "dieci" "nove" "otto" "sette" "sei"
## [6] "cinque" "quattro" "tre" "dieci" "nove"
## [11] "otto" "sette" "sei" "cinque" "quattro"
## [16] "tre" "dieci" "nove" "otto" "sette"
## [21] "sei" "cinque" "quattro" "tre" "dieci"
## [26] "nove" "otto" "sette" "sei" "cinque"
## [31] "quattro" "tre"
$figura[!(deck$valore == 1 | deck$valore > 11)] #e deck
## [1] "jack" "dieci" "nove" "otto" "sette"
## [6] "sei" "cinque" "quattro" "tre" "due"
## [11] "jack" "dieci" "nove" "otto" "sette"
## [16] "sei" "cinque" "quattro" "tre" "due"
## [21] "jack" "dieci" "nove" "otto" "sette"
## [26] "sei" "cinque" "quattro" "tre" "due"
## [31] "jack" "dieci" "nove" "otto" "sette"
## [36] "sei" "cinque" "quattro" "tre" "due"
Consideriamo il gioco del blackjack. Ogni carta vestita (jack, regina, re), tranne l’asso, vale 10 punti, le altre carte valgono uguale al numero che c’è scritto sulla carta (“tre”=3) mentre l’asso vale 1 o 11 a seconda se la somma dei valori sulle carte nella mano ecceda o meno 21 (es: Mano: asso, quattro, cinque, Somma: 20. Mano: re, asso, cinque, Somma: 16. Mano: sei, quattro, asso, Somma: 21.)
Esercizio 14
Creare un mazzo “BJdeck”. Modificare il valore delle carte vestite (escluso l’asso) e portarlo a 10.
<- deck
BJdeck <- BJdeck$figura %in% c("jack","regina","re")
vestite #or
<- BJdeck$valore > 10
vestite #or
<- !(BJdeck$valore <= 10)
vestite $valore[vestite] <- 10 BJdeck
Ci manca solo da modificare il valore degli assi. Siccome gli assi assumono un valore di comodo (1 o 11), diverso a seconda se la somma delle altre carte è maggiore di 21, assegnargli il valore giusto non è un compito facile e non possiamo ancora farlo date le funzioni che abbiamo appreso finora.
Ma non demordiamo, alla fine del capitolo 7 saremo in grado di fare anche questo!