5 Data Modification

Quanto valgono gli assi nel nostro dataframe deck? Andiamo a vedere la tredicesima carta del mazzo:

deck[13, ]
##    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):

deck2 <- deck

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:

vec <- c(0, 0, 0, 0, 0, 0)
vec
## [1] 0 0 0 0 0 0

Ecco come selezioniamo l’elemento da modificare (il primo elemento di vec):

vec[1]
## [1] 0

E così lo andiamo a sovrascrivere:

vec[1] <- 1000
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:

vec[c(1, 3, 5)] <- c(1, 1, 1)
vec
## [1] 1 0 1 0 1 0
vec[4:6] <- vec[4:6] + 1
vec
## [1] 1 0 1 1 2 1

Si può espandere un vettore aggiungendo un elemento che non esiste:

vec[7] <- 0
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:

deck2 <- deck 
deck2 <- rbind(deck2, c("re", "picche", 13)) #aggiungiamo una riga
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
deck2 <- cbind(deck2, nuovaColonna = rep(1, 53)) #aggiungiamo una colonna di 1
#or
deck2$nuovaColonna <- rep(1, 53)
str(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.

deck$id <- 1:52 

Possiamo eliminare delle colonne da un dataframe (o elementi di una lista) assegnandogli il valore NULL:

deck2$nuovaColonna <- NULL
head(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:

deck2[c(13, 26, 39, 52), 3]      #righe 13, 26, 39 e 52, colonna 3 (valore)
## [1] "1" "1" "1" "1"
deck2$value[c(13, 26, 39, 52)]   #idem
## NULL

Ora dobbiamo assegnare il nuovo valore alla colonna:

deck2$value[c(13, 26, 39, 52)] <- c(14, 14, 14, 14)
## Error in `$<-.data.frame`(`*tmp*`, value, value = c(NA, NA, NA, NA, NA, : replacement has 52 rows, data has 53
# or
deck2$value[c(13, 26, 39, 52)] <- 14
## Error in `$<-.data.frame`(`*tmp*`, value, value = c(NA, NA, NA, NA, NA, : replacement has 52 rows, data has 53
# or
deck2$value[c(13, 26, 39, 52)] <- rep(14,4)
## 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
deck2$value[c(13, 26, 39, 52)]    
## 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
vec[c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)]
## [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":

deck2$figura == "asso"
##  [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:

deck3 <- deck
deck3$valore[deck3$figura == "asso"] <- 14
deck3[c(13, 26, 39, 52), 3]
## [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.

deck4 <- deck
deck4$valore[deck4$seme == "cuori"] <- 1
deck4$valore[deck4$seme == "cuori"]
##  [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.

Operatori booleani

(#fig:boolean )Operatori booleani

Usati su vettori logici anche gli operatori booleani si applicheranno element-wise.

a <- c(1, 2, 3)
b <- c(1, 2, 3)
c <- c(1, 2, 4)
a == b
## [1] TRUE TRUE TRUE
b == c
## [1]  TRUE  TRUE FALSE
a == b & b == c
## [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:

w <- c(-1, 0, 1)
x <- c(5, 15)
y <- "Febbraio"
z <- c("Lunedi", "Martedi", "Venerdi") #meglio non usare gli accenti
w > 0
## [1] FALSE FALSE  TRUE
10 < x & x < 20
## [1] FALSE  TRUE
y == "Febbraio"
## [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:

deck4$figura == "asso" & deck4$seme == "picche"
##  [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:

assoDiPicche <- deck4$figura == "asso" & deck4$seme == "picche"

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
deck4$valore[assoDiPicche] # per scoprirne il valore
## [1] 1

Ora che abbiamo trovato l’elemento “asso di picche” possiamo modificarne il valore adeguandolo a quello del poker:

deck4$valore[assoDiPicche] <- 13
deck4[assoDiPicche, ]
##    figura   seme valore id
## 13   asso picche     13 13

Il nostro mazzo è pronto per essere giocato a poker!

Esercizio 13

Selezionare:

  1. le prime 20 carte del mazzo;
  2. le carte con valore pari;
  3. le carte con figura “regina” o “re”;
  4. le figure delle carte vestite (con valore 1 o superiore a 10);
  5. le figure delle carte non vestite (con valore tra 2 e 10);
deck[1:20, ]                                          #a.
##     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
deck[deck$valore %% 2 == 0,]                         #b.
##     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
deck[deck$figura == "regina" | deck$figura == "re", ] #c.
##    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
deck[deck$figura %in% c("regina", "re"),]             #c.
##    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
deck$figura[(deck$valore == 1 | deck$valore > 11)]   #d
##  [1] "re"     "regina" "asso"   "re"     "regina"
##  [6] "asso"   "re"     "regina" "asso"   "re"    
## [11] "regina" "asso"
deck$figura[(deck$valore > 2 & deck$valore <= 10)]   #e
##  [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"
deck$figura[!(deck$valore == 1 | deck$valore > 11)]  #e
##  [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.

BJdeck <- deck
vestite <- BJdeck$figura %in% c("jack","regina","re")
#or
vestite <- BJdeck$valore > 10
#or 
vestite <- !(BJdeck$valore <= 10)
BJdeck$valore[vestite] <- 10

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!