-
Notifications
You must be signed in to change notification settings - Fork 2
/
11-matrices.Rmd
763 lines (545 loc) · 34.8 KB
/
11-matrices.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
# Matrici {#matrix}
```{r settings, echo = FALSE}
knitr::opts_chunk$set(
echo = FALSE,
collapse=TRUE,
fig.align="center"
)
library(kableExtra)
library(magrittr)
```
Le matrici sono una struttura di dati **bidimensionale**, dove gli elementi sono disposti secondo righe e colonne. Possiamo quindi immaginare una matrice generica di *m* righe e *n* colonne in modo simile a quanto rappresentato in Figura \@ref(fig:matrix).
```{r, matrix, fig.cap="Rappresentazione della struttura di una matrice di *m* colonne e *n* righe", out.width="75%"}
knitr::include_graphics("images/matrix.png")
```
Due caratteristiche importanti di una matrice sono:
- la **dimensione** - il numero di **righe** e di **colonne** da cui è formata la matrice
- la **tipologia** - la tipologia di dati che sono contenuti nella matrice. Infatti, in modo analogo a quanto visto con i vettori, una matrice deve esssere formata da **elementi tutti dello stesso tipo**. Pertanto esistono diverse tipologie di matrici a seconda del tipo di dati da cui è formata, in particolare abbiamo matrici numeriche, di valori logici e di caratteri (vedi Capitolo \@ref(matrix-types)).
E' fondamentale inoltre sottolineare come ogni **elemento** di una matrice sia caratterizzato da:
- un **valore** - ovvero il valore dell'elemento che può essere di qualsiasi tipo ad esempio un numero o una serie di caratteri.
- un **indice di posizione** - ovvero una **coppia di valori (*i*, *j*)** interi positivi che indicando rispettivamente **l'indice di riga** e **l'indice di colonna** e che permettono di identificare univocamente l'elemento all'interno della matrice.
Ad esempio, data una matrice $X$ di dimensione $3\times4$ (i.e., 3 righe e 4 colonne) così definita:
$$
X =
\begin{bmatrix}
3 & 12 & 7 & 20\\
16 & 5 & 9 & 13\\
10 & 1 & 14 & 19
\end{bmatrix},
$$
abbiamo che $x_{2, 3} = 9$ mentre $x_{3, 2} = 1$. Questo ci serve solo per ribadire il corretto uso degli indici, dove per un generico elemento $x_{i, j}$, il valore *i* è l'indice di riga mentre il valore *j* è l'indice di colonna. **Prima si indicano le righe poi le colonne**.
Vediamo ora come creare delle matrici in R e come compiere le comuni operazini di selezione. Successivamente vedremo diverse manipolazioni e operazioni con le matrici. Infine estenderemo brevemente il concetto di matrici a dimensioni maggiori di due atttraverso l'uso degli **array**.
## Creazione
Il comando usato per creare una matrice in R è `matrix()` e contiene diversi argomenti:
```{r, eval=FALSE, echo=TRUE}
nome_matrice <- matrix(data, nrow = , ncol = , byrow = FALSE)
```
* `data` - un **vettore di valori** utilizzati per popolare la matrice
* `nrow` e `ncol` - sono rispettivamente il numero di righe e il numero di colonne della matrice
* `byrow` - indica se la matrice deve essere popolata per riga oppure per colonna. Il valore di default è `FALSE` quindi i valori della matrice vengono aggiunti colonna dopo colonna. Indicare `TRUE` per aggiungere gli elementi riga dopo riga
Creiamo come esempio una matrice di 3 righe e 4 colonne con i valori che vanno da 1 a 12.
```{r, echo = T}
# Dati per popolare la matrice
my_values <- 1:12
my_values
# Matrice popolata per colonne
mat_bycol <- matrix(my_values, nrow = 3, ncol = 4)
mat_bycol
```
La funzione `matrix()` ha di default l'argomento `byrow = FALSE`, quindi di base R popola le matrici colonna dopo colonna. Per popolare le matrici riga dopo riga invece, è necessario richiederlo esplicitamente specificando `byrow = TRUE`.
```{r, echo = T}
# Matrice popolata per righe
mat_byrow <- matrix(my_values, nrow = 3, ncol = 4, byrow = TRUE)
mat_byrow
```
E' importante notare come mentre sia possibile specificare qualsiasi combinazione di righe e colonne, il numero di valori forniti per popolare la matrice deve essere compatibile con la dimensione della matrice. In altre parole, **non posso fornire più o meno dati di quelli che la matrice può contenere**.
Pertanto, la launghezza del vettore passato all'argomento `data` deve essere compatibile con gli argomenti `nrow` e `ncol`. E' possibile tuttavia, fornire un unico valore se si desidera ottenre una matrice in cui tutti i valori siano identici. Creiamo ad esempio una matrice vuota con soli valori `NA` con 3 righe e 3 colonne.
```{r, echo = TRUE}
mat_NA <- matrix(NA, nrow = 3, ncol = 3)
mat_NA
```
:::{.tip title="Ciclare Valori" data-latex="[Ciclare Valori]"}
In realtà è possibile fornire più o meno dati di quelli che la matrice può contenere. Nel caso vengano forniti più valori, R semplicemente utilizza i primi valori disponibili ignorando quelli successivi.
```{r, echo = TRUE}
matrix(1:20, nrow = 2, ncol = 2)
```
Nel caso vengano forniti meno valori, invece, R riutilizza gli stessi valori nello stesso ordinere per completare la matrice avvertendoci del problema.
```{r, echo = TRUE}
matrix(1:4, nrow = 3, ncol = 4)
```
Tuttavia, è meglio evitare questa pratica di *ciclare* i valori poichè i risultati potrebbero essere poco chiari ed è facile commettere errori.
:::
### Tipologie di Matrici {#matrix-types}
Abbiamo viso che, in modo analogo ai vettori, anche per le matrici è necessario che tutti i dati siano della stessa tipologia. Avremo pertanto matrici che includono solo valori `character`, `double`, `integer` oppure `logical` e le operazioni che si potranno eseguire (uso di operatori matematiche o operatori logici-relazionali) dipenderanno dalla tipologia di dati. Tuttavia, a differenza dei vettori, la tipologia di oggetto rimarrà sempre `matrix` indipendentemente dai dati contenuti. Le matrici sono sempre matrici, è la tipologia di dati che varia.
#### Character {-}
E' possibile definire una matrice di soli caratteri, tuttavia sono usate raramente visto che chiaramente tutte le operazioni matematiche non sono possibili.
```{r, echo = TRUE}
mat_char <- matrix(letters[1:12], nrow = 3, ncol = 4, byrow = TRUE)
mat_char
class(mat_char)
typeof(mat_char)
```
:::{.trick title="Letters" data-latex="[Letters]"}
In R esistono due speciali oggetti `letters` e `LETTERS` che includono rispettivamente le lettere minuscole e maiuscole dell'alfabeto inglese.
```{r, echo = TRUE}
letters[1:5]
LETTERS[6:10]
```
:::
#### Numeric {-}
Le matrici di valori numerici, sia `double` che `integer`, sono senza dubbio le più comuni ed utilizzate. Vengono spesso sfruttate per eseguire calcoli algebrici computazionalemnte molto efficienti.
```{r, echo = TRUE}
# doubles
mat_num <- matrix(5, nrow = 3, ncol = 4)
class(mat_num)
typeof(mat_num)
# integers
mat_int <- matrix(5L, nrow = 3, ncol = 4)
class(mat_int)
typeof(mat_int)
```
#### Logical {-}
Infine le matrici possono essere formate anche da valori logici `TRUE` e `FALSE`. Vedremo un loro importante utilizzo per quanto riguarda la selezione degli elementi di una matrice nel Capitolo \@ref(matrix-selection-advanced).
```{r, echo = TRUE}
mat_logic <- matrix(c(TRUE, FALSE), nrow = 3, ncol = 4)
mat_logic
class(mat_logic)
typeof(mat_logic)
```
Ricordiamo che è comunque possibile eseguire operazioni matematiche con i valori logici poichè verranno automaticamente rasformatti nei rispettivi valori numerici 1 e 0.
### Esercizi {-}
Esegui i seguenti esercizi ([soluzioni](https://github.com/psicostat/Introduction2R/blob/master/exercises/chapter-09-matrices.R)):
1. Crea la matrice `A` così definita:
$$
\begin{matrix}
2 & 34 & 12 & 7\\
46 & 93 & 27 & 99\\
23 & 38 & 7 & 04
\end{matrix}
$$
2. Crea la matrice `B` contenente tutti i primi 12 numeri dispari disposti su 4 righe e 3 colonne.
3. Crea la matrice `C` contenente i primi 12 multipli di 9 disposti su 3 righe e 4 colonne.
4. Crea la matrice `D` formata da 3 colonne in cui le lettere `"A"`,`"B"` e `"C"` vengano ripetute 4 volte ciascuna rispettivamente nella prima, seconda e terza colonna.
5. Crea la matrice `E` formata da 3 righe in cui le lettere `"A"`,`"B"` e `"C"` vengano ripetute 4 volte ciascuna rispettivamente nella prima, seconda e terza riga.
## Selezione Elementi {#sel-matrix}
L'aspetto sicuramente più importante (e divertente) riguardo le matrici è accedere ai vari elementi. Ricordiamo che una matrice non è altro che una griglia di righe e colonne dove vengono disposti i vari valori. Indipendentemente da cosa la matrice contenga, è possibile utilizzare gli indici di riga e di colonna per identificare univocamente un dato elemento nella matrice. Pertanto ad ogni elemento è associata una coppia di valori (*i*, *j*) dove *i* è l'indice di riga e *j* è l'indice di colonna.
Per visualizzare questo concetto, riportiamo nel seguente esempio gli indici per ogni elemnto di una matrice $3\times4$:
```{r}
mat <- matrix(NA, 3, 4)
for(i in 1:nrow(mat)){
for(j in 1:ncol(mat)){
mat[i,j] <- paste0(i,",",j)
}
}
mat
```
In R è possibile selezionare un elemento di una matrice utilizzando il suo indice di riga e di colonna. In modo analogo ai vettori è necessario quindi indicare all'inerno delle **parentesi quadre** `[]` poste dopo il nome del vettore, **l'indice di riga** e **l'indice di colonna** **separati da virgola**.
```{r, echo = TRUE, eval = FALSE}
nome_matrice[<indice-riga>, <indice-colonna>]
```
L'**ordine** `[<indice-riga>, <indice-colonna>]` è prestabilito e deve essere rispettato affinché la selezione avvenga correttamente. Vediamo un semplice esempio di come sia possibile accedere ad un qualsiasi elemento:
```{r, echo = TRUE, error=TRUE}
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
my_matrix
# Selezioniamo l'elemento alla riga 2 e colonna 3
my_matrix[2,3]
# Selezioniamo il valore 6
my_matrix[3,2]
```
:::{.warning title="Subscript out of Bounds" data-latex="[Subscript out of Bounds]"}
Notiamo come indicando degli indici al di fuori della dimensione della matrice otteniamo un messaggio di errore.
```{r, echo = TRUE, error = TRUE}
my_matrix[20,30]
```
:::
Oltre alla selezione di un singolo elemento è possibile eseguire altri tipi di selezione:
#### Selezionare Riga o Colonna {-}
E' possibile selezionare **tutti** gli elementi di una riga o di una colonna utilizzando la seguente sintassi:
```{r, echo = TRUE, eval = FALSE}
# Selzione intera riga
nome_matrice[<indice-riga>, ]
# Selzione intera colonna
nome_matrice[ , <indice-colonna>]
```
Nota come sia comunque necessario l'utilizzo della **virgola** lasciando vuoto il posto prima o dopo la virgola per indicare ad R di selezionare rispettivamente tutte le righe o tutte le colonne.
```{r, echo = TRUE}
# Selezioniamo la 2 riga e tutte le colonne
my_matrix[2, ]
# Selezioniamo tutte le righe e la 3 colonna
my_matrix[ ,3]
```
Qualora fosse necessario selezionare più righe o più colonne è sufficiente indicare tutti gli indici di interesse. Ricorda che questi devono essere specificati in un unico vettore. All'interno delle parentesi quadre, R si aspetta una sola virgola che separa gli indici di riga da quelli di colonna. E' quindi necessario combinare gli indici che vogliamo selezionare in un unico vettore sia nel caso delle righe che delle colonne. Per selezionare righe o colonne in successione, ad esempio le prime 3 colonne, posso utilizzare la scrittura compatta `1:3` che è equivalente a `c(1,2,3)`.
```{r, echo = TRUE}
# Selezioniamo la 1 e 3 riga
my_matrix[c(1,3), ]
# Selezioniamo dalla 2° alla 4° colonna
my_matrix[ , 2:4]
```
#### Selezionare Regione Matrice {-}
Combinando indici di righe e di colonne è anche possibile selezionare specifiche regioni di una matrice o selezionare alcuni suoi valori per creare una nuova matrice.
```{r, echo = TRUE}
# Selezioniamo un blocco
my_matrix[1:2, 3:4]
# Selezioniamo valori sparsi
my_matrix[c(1,3), c(2,4)]
```
:::{.tip title="Selezionare non è Modificare" data-latex="[Selezionare non è Modificare]"}
Ricordiamo che, come per i vettori, l'operazione di selezione non modifichi l'oggetto iniziale. Pertanto è necessario salvare il risultato della selezione se si desidera mantenere le modifiche.
:::
:::{.design title="Matrici e Vettori" data-latex="[Matrici e Vettori]"}
I più attenti avranno notato che che i comandi di selezione non restituiscono sempre lo stesso oggetto, a volte otteniamo come risultato un vettore e delle altre una matrice.
E' importante chiarire che una **un vettore non è un matrice** e tanto più vale l'opposto. In R questi sono due tipologie di oggetti diversi e sarà importante tenere a mente questa distinzione.
```{r, echo=TRUE, eval = FALSE}
# Un vettore non è una matrice
my_vector <- 1:5
is.vector(my_vector) # TRUE
is.matrix(my_vector) # FALSE
# Una matrice non è un vettore
my_matrix <- matrix(1, nrow = 3, ncol = 3)
is.vector(my_matrix) # FALSE
is.matrix(my_matrix) # TRUE
```
Il risultato che otteniamo da una selezione potrebbe essere un vettore oppure una matrice a seconda del tipo di selezione. Vediamo in particolare come selezionando un'unica colonna (o riga) otteniamo un vettore mentre selezionando più colonne (o righe) otteniamo una matrice.
```{r, echo=TRUE, eval = FALSE}
# Seleziono una colonna
is.vector(my_matrix[, 1]) # TRUE
is.matrix(my_matrix[, 1]) # FALSE
# Seleziono più colonne
is.vector(my_matrix[, c(1,2)]) # FALSE
is.matrix(my_matrix[, c(1,2)]) # TRUE
```
Questa distinzione influirà sul successivo utilizzo dell'oggetto ottenuto dalla selezione.
#### Vettore Riga e Vettore Colonna {-}
Una particolare fonte di incomprensioni e successivi errori riguarda proprio l'utilizzo di un vettore ottenuto dalla selezione di una singola riga (o una singola colonna) di una matrice come fosse un *vettore riga* (o un *vettore colonna*).
In algebra lineare, i *vettori riga* ed i *vettori colonna* non sono altro che delle matrici rispettivamente di dimensione $1\times n$ e $m \times 1$. La dimensione ($righe \times colonne$) di una matrice, e quindi anche di un vettore, rivestono un ruolo importante nelle operazioni con le matrici ed in particolare nel prodotto matriciale.
In R i vettori hanno una sola dimensione ovvero la *lunghezza* e quindi nel loro utilizzo con operazioni tra matrici vengono convertiti automaticamente in vettori riga o vettori colonna a seconda delle necessità. Tuttavia, questa trasformazione potrebbe non sempre rispettare le attuali intenzioni ed è quindi meglio utlizzare sempre le matrici e non i vettori.
```{r, echo = TRUE}
# Vettore
my_vector <- 1:4
dim(my_vector) # dimensione righe, colonne
length(my_vector)
# Matrice 1x4 (vettore riga)
my_row_vector <- matrix(1:4, nrow = 1, ncol = 4)
dim(my_row_vector)
my_row_vector
# Matrice 4x1 (vettore colonna)
my_col_vector <- matrix(1:4, nrow = 4, ncol = 1)
dim(my_col_vector)
my_col_vector
```
#### Srotolare una Matrice {-}
Abbiamo visto che possiamo facilmente popolare una matrice con un vettore. Allo stesso modo possiamo vettorizzare una matrice (in altri termini "srotolare" la matrice) per ritornare al vettore originale. Con il comando `c(matrice)` oppure forzando la tipologia di oggetto a vettore con `vector(matrice)` o `as.vector(matrice)`.
```{r, echo = T}
# Da matrice a vettore
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
c(my_matrix)
as.vector(my_matrix)
```
:::
### Utilizzi Avanzati Selezione {#matrix-selection-advanced}
Vediamo ora alcuni utilizzi avanzati della selezione di elementi di una matrice. In particolare impareremo a:
- utilizzare gli operatori relazionali e logici per selezionare gli elementi di una martrice
- modificare l'ordine di righe e colonne
- sostituire degli elementi
- eliminare delle righe o colonne
Nota che queste operazioni siano analoghe a quelle viste per i vettori e quindi seguiranno le stesse regole e principi.
#### Operatori Relazionali e Logici {-}
Un'utile funzione è quella di selezionare tra gli elementi di una matrice quelli che rispetano una certa condizione. Possiamo ad esempio valutare *"quali elementi della matrice sono maggiori di x?"*. Per fare questo dobbiamo specificare all'interno delle parentesi quadre la proposizione di interesse utilizzando gli operatori relazionali e logici (vedi Capitolo \@ref(operators-rel-log)).
Quando una matrice è valutata all'interno di una proposizione, R valuta la veridicità di tale proposizione rispetto ad ogni suo elemento. Come risultato otteniamo una matrice di valori logici con le rispettive risposte per ogni elemento (`TRUE` o `FALSE`).
```{r, echo = TRUE}
my_matrix <- matrix(1:23, nrow = 3, ncol = 4)
my_matrix
# Elemeni maggiori di 4 e minori di 10
test <- my_matrix >= 4 & my_matrix <=10
test
```
Questa matrice può essere utilizzata all'interno delle parentesi quadre per selezionare gli elementi della matrice originale che soddisfano la proposizione. Gli elementi associati al valore `TRUE` sono selezionati mentre quelli associati al valore `FALSE` sono scartati.
```{r, echo = TRUE}
# Seleziono gli elementi
my_matrix[test]
```
Nota come in questo caso non sia necessaria alcuna virgola all'interno delle parentesi quadre e come il risultato ottenuto sia un vettore.
#### Modificare Ordine Righe e Colonne {-}
Gli indici di riga e di colonna possono essere utilizzati per riordinare le righe e le colonne di una matrice a seconda delle necessità.
```{r, echo = TRUE}
my_matrix <- matrix(1:6, nrow = 3, ncol = 4)
my_matrix
# Altero l'ordinen delle righe
my_matrix[c(3,2,1), ]
# Altero l'ordine delle colonnne
my_matrix[ ,c(1,3,2, 4)]
```
#### Modificare gli Elementi {-}
Un importante utilizzo degli indici riguarda la modifica di un elemento di una matrice. Per sostituire un vecchio valore con un nuovo valore, seleziono il vecchio valore della matrice e utilizzo la funzione `<-` (o `=`) per assegnare il nuovo valore.
```{r, echo = TRUE}
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
my_matrix
# Modifico il l'elemento con il valore 5
my_matrix[2,2] <- 555
my_matrix
```
E' possibile anche sostituire tutti i valori di un'intera riga o colonnna opportunamente selezionata. In questo caso sarà necessario fornire un corretto numero di nuovi valori da utilizzare.
```{r, echo = TRUE}
# Modifico la 2 colonna
my_matrix[ ,2] <- c(444, 555, 666)
my_matrix
# Modifico la 3 riga
my_matrix[3, ] <- c(111, 666, 999, 122)
my_matrix
```
Nota come a differenza dei vettori non sia possibile aggiungere una nuova riga o colonna attraverso questa operazione ma sarà necesssario utilizzare una diversa procedura (vedi Capitolo \@ref(matrix-bind)).
```{r, echo = TRUE, error = TRUE}
# Aggiungo una nuova colona [errore selezione indici]
my_matrix[, 5] <- c(27, 27, 27)
```
#### Eliminare Righe o Colonne {-}
Per **eliminare** delle righe (o delle colonne) da una matrice, è necessario indicare all'interno delle parentesi quadre gli indici di riga (o di colonna) che si intende eliminare, preceduti dall'operatore `-` (*meno*). Nel caso di più righe (o colone) è possibile indicare il meno solo prima del comando `c()` analogamente con quanto fatto con i vettori.
```{r, echo = TRUE}
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
# Elimino la 2° riga
my_matrix[-2, ]
# Elimino la 2° riga e la 2° e 3° colonna
my_matrix[-2, -c(2,3)]
```
Nota come l'operazione di eliminazione sia comunque un'operazione di selezione. Pertanto è necessario salvare il risultato ottenuto se si desidera mantenere le modifiche.
### Esercizi {-}
Utilizzando le matrici create nei precedenti esercizi esegui le seguenti consegne ([soluzioni](https://github.com/psicostat/Introduction2R/blob/master/exercises/chapter-09-matrices.R)):
1. Utilizzando gli indici di riga e di colonna selziona il numero 27 della matrice `A`
2. Selziona gli elementi compresi tra la seconda e quarta riga, seconda e terza colonna della matrice `B`
3. Seleziona solo gli elementi pari della matrice `A` (Nota: utilizza l'operazione resto `%%`)
4. Elimina dalla matrice `C` la terza riga e la terza colonna
5. Seleziona tutti gli elementi della seconda e terza riga della matrice `B`
6. Seleziona tutti gli elementi diversi da “B” appartenenti alla matrice `D`
## Funzioni ed Operazioni
Vediamo ora alcune funzioni frequentemente usate e le comuni operazioni eseguite con le matrici (vedi Tabella \@ref(tab:table-matrix-operators)).
```{r table-matrix-operators}
names_function <- c("Unire due matrici creando nuove colonne (le matrici devono avere lo stesso numero di righe)",
"Unire due matrici creando nuove righe (le matrici devono avere lo stesso numero di colonne)",
"Numero di righe della matrice",
"Numero di colonne della matrice",
"Dimensione della matrice (righe e colonne)",
"Nomi delle colonne della matrice",
"Nomi delle righe della matrice",
"Nomi delle righe e delle colonne",
"Trasposta della matrice",
"Vettore con gli elementi della diagonale della matrice",
"Determinante della matrice (la matrice deve essere quadrata)",
"Inversa della matrice",
"Somma elemento per elemento di due matrici",
"Differenza elemento per elemento tra due matrici",
"Prodotto elemento per elemento tra due matrici",
"Rapporto elemento per elemento tra due matrici",
"Prodotto matriciale")
formula <- sprintf("matrice1 %s matrice2", c("+", "-", "*", "/"))
if (knitr::is_html_output()) {
matrix_operators <- data.frame(formula = c(
sprintf("nuova_matrice <- %s(matrice1, matrice2)", c("cbind", "rbind")),
sprintf("%s(nome_matrice)", c("nrow", "ncol", "dim", "colnames", "rownames", "dimnames",
"t", "diag", "det", "solve")),
formula, "matrice1 %*% matrice2"),
name = names_function)
escape = TRUE
}
if (knitr::is_latex_output()) {
matrix_operators <- data.frame(formula = sprintf("\\texttt{%s}", c(
sprintf("nuova\\_matrice <- %s(matrice1, matrice2)", c("cbind", "rbind")),
sprintf("%s(nome\\_matrice)", c("nrow", "ncol", "dim", "colnames", "rownames", "dimnames",
"t", "diag", "det", "solve")),
formula, "matrice1 \\%*\\% matrice2")),
name = names_function)
escape = FALSE
}
kableExtra::kable(matrix_operators, col.names = c("Funzione", "Descrizione"),
escape = escape, caption = "Funzioni e operazioni con matrici") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = F,
latex_options = c("hold_position"))
```
Descriviamo ora nel dettaglio alcuni particolari utilizzi.
### Attributi di una Matrice {#mat-prop}
Abbiamo visto nel Capitolo \@ref(attributes) che gli oggetti in R possiedono quelli che sono definiti *attributi* ovvero delle utili informazioni riguardanti l'oggetto stesso, una sorta di *metadata*. Vediamo ora alcuni attributi particolarmente rilevanti nel caso delle matrici ovvero la dimensione (`dim`) e i nomi delle righe e colonne (`names`).
#### Dimensione {-}
Ricordiamo che la matrice è un oggetto **bidimensionale** formato da righe e colonne. Queste formano pertanto le dimensioni di una matrice. Per ottenere il numero di righe e di colonne di una matrice, possiamo usare rispettivamente i comandi `nrow()` e `ncol()`.
```{r, echo = TRUE}
my_matrix <- matrix(1:12, ncol = 3, nrow = 4)
# Numero di righe
nrow(my_matrix)
# Numero di colonne
ncol(my_matrix)
```
In alternativa per conoscere le dimensioni di una matrice è possibile utilizzare la funzione `dim()`. Questa ci restituirà un vettore con due valori dove il primo rappresenta il numero di righe e il secondo il numero di colonne.
```{r, echo = TRUE}
dim(my_matrix)
```
#### Nomi Righe e Colonne {-}
Come avrete notato, di base le dimensioni di una matrice (ovvero le righe e le colonne) vengono identificate attraverso i loro indici numerici. In R tuttavia, è anche possibile assegnare dei nomi alle righe e alle colonne di una matrice.
Con i comandi `rownames()` e `colnames()` possiamo accedere rispettivamente ai nomi delle righe e delle colonne.
```{r, echo = T}
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
# Nome di righe
rownames(mat)
# Nome di colonne
colnames(mat)
```
Non essendo impostati, ottieniamo inizialmente come output il valore `NULL`. Per impostare i nomi di righe e/o colonne, sarà quindi necessario assegnare a `rownames(nome_matrice)` e `colnames(nome_matrice)` un vettore di caratteri della stessa lunghezza della dimensione che stiamo rinominando. Se impostiamo un unico carattere, tutte le righe/colonne avranno lo stesso valore. Questo ci fa capire che, se vogliamo impostare dei nomi, R richiede che questo venga fatto per tutte le righe/colonne.
```{r, echo=TRUE}
# Assegnamo i nomi alle righe
rownames(my_matrix) <- LETTERS[1:3]
my_matrix
# Assegno i nomi alle colonne
colnames(my_matrix) <- LETTERS[4:7]
my_matrix
```
In alternativa posso utilizzare il `dimnames()` per accedere contemporaneamente sia ai nomi di riga che a quelli di colonna. Come output ottengo una lista (vedi Capitolo \@ref(list)) dove vengono prima indicati i nomi di riga e poi quelli di colonna
```{r, echo = TRUE}
dimnames(my_matrix)
```
:::{.design title="Selezione per Nomi" data-latex="[Selezione per Nomi]"}
Quando nel Capitolo \@ref(sel-matrix) abbiamo visto i diversi modi di selezionare gli elementi di una matrice, abbiamo sempre usato gli indici numerici di riga e di colonna. Tuttavia, quando i nomi delle delle dimensioni sono disponibili, è possibile indicizzare una matrice in base ai nomi delle righe e/o colone.
Possiamo quindi selezionare la prima colonna sia con il suo indice numerico `nome_matrice[ , 1]` ma anche con il nome assegnato `nome_matrice[ ,"nome_colonna"]`. Queste sono operazioni poco utili con le matrici ma che saranno fondamentali nel caso dei **dataframe** (vedi Capitolo \@ref(dataframe)).
```{r, echo=TRUE}
# Seleziono la colona "F"
my_matrix[ , "F"]
# Selezioniamo la riga "A" e "C"
my_matrix[c("A", "C"), ]
```
:::
### Unire Matrici {#matrix-bind}
Abbiamo visto nel Capitolo \@ref(vector-functions) come si possono unire diversi vettori tramite la funnzione `c()`. Anche per le matrici è possibile combinare matrici diverse, rispettando però alcune regole:
* `rbind()` - Posso unire matrici **per riga** ovvero aggiungo una o più righe ad una matrice, in questo caso le matrici devono avere lo stesso numero di colonne
* `cbind()` - Posso unire matrici **per colonna** ovvero aggiungo una o più colonne ad una matrice, in questo caso le matrici devono avere lo stesso numero di righe
* Le matrici che unisco devono essere della **stessa tipologia** (numeri o caratteri)
Quindi, data una matrice `my_matrix` di dimensione $m \times n$ se volgiamo aggiungere le righe di una seconda matrice `row_matrix` possiamo utlizzare il comando `rbind(my_matrix, row_matrix)` a patto che abbiano lo stesso numero di colonnne ($n$). Se vogliamo invece aggiungere le colonne di una nuova matrice `col_matrix` possiamo utlizzare il comando `cbind(my_matrix, col_matrix)` a patto che abbiano lo stesso numero di righe ($m$). E' utile pensare all'unione come ad un collage tra matrici. In Figura\@ref(fig:mat-comb) è presente uno schema utile per capire visivamente questo concetto, dove le matrici colorate in verde possono essere correttamente unite mentre le matrici in rosso non presentano la corretta dimensionalità.
```{r mat-comb, fig.cap="Schema per la combinazione di matrici", out.width="50%"}
knitr::include_graphics("images/matrix_comb.png")
```
Vediamo un esempio in R:
```{r, error=TRUE, echo = T}
# Matrice di partenza
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
my_matrix
# Matrice con stesso numero di colonne
row_matrix <- matrix(77, nrow = 2, ncol = 4)
row_matrix
# Unione per riga
rbind(my_matrix, row_matrix)
# Matrice con stesso numero di righe
col_matrix <- matrix(99, nrow = 3, ncol = 2)
col_matrix
# Unione per colonna
cbind(my_matrix, col_matrix)
```
Un ultimo aspetto utile è l'estensione dei comandi `cbind()` ed `rbind()`. Fino ad ora li abbiamo utilizzati con due soli elementi: matrice di partenza e matrice da aggiungere. Tuttavia, è possibile indicare più matrici che si vogliono unire, separandole con una virgola. Se vogliamo combinare $n$ matrici possiamo usare il comando `cbind(mat1, mat2, mat3, ...)` o `rbind(mat1, mat2, mat3, ...)`. In questo caso il risultato finale sarà l'unione delle matrici nell'ordine utilizzato nel definire gli argomenti quindi prima la `mat1`, poi la `mat2` e così via.
:::{.warning title='"Matrices Must Match"' data-latex='["Matrices Must Match"]'}
Abbiamo visto che possiamo unire matrici per riga/colonna solo se il numero di colonne/righe delle due matrici sono equivalenti. Otteniamo un errore, invece, quando cerchiamo di combinare matrici di dimensioni diverse *"number of columns/rows of matrices must match "*.
```{r, echo = TRUE, error = TRUE}
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
# Matrice con errato numero di colonne
wrong_matrix <- matrix(77, nrow = 2, ncol = 7)
rbind(my_matrix, wrong_matrix)
```
:::
### Operatori Matematici
Gli operatori matematici (e.g., `+`, `-`, `*`, `/`, etc.) svolgono le operazioni tra una matrice ed un singolo valore, tra una matrice ed un vettore oppure tra due matrici.
#### Operazione tra Matrice e Valore Singolo {-}
Nel caso di un singolo valore, la stessa operazione viene semplicemente eseguita su tutti gli elementi della matrice. Possiamo ad esempio aggiungere a tutti gli elementi di una matrice il volore 100.
```{r, echo = TRUE}
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
my_matrix
# Aggiungo 100
my_matrix + 100
```
#### Operazione tra Matrice e Vettore {-}
Nel caso di un vettore, l'operazione viene eseguita **elemento per elemento** ciclando i valori del vettore qualora la sua lunghezza non sia suficiente. Possiamo ad esempio aggiungere a tutti gli elementi di una matrice il vettore di valori `c(100, 200, 300, 400)`.
```{r, echo = TRUE}
# Aggiungo un vettore di valori
my_matrix + c(100, 200, 300, 400)
```
Com'è facilmente intuibile, questa operazione è poco connsigliata poichè è facile causa di errori ed incomprensioni.
#### Operazione tra Matrici {-}
Nel caso di operazioni tra matrici, l'operazione viene eseguita **elemento per elemento** ed è quindi importante che le matrici abbiano la **stessa dimensione**. Possiamo ad esempio aggiungere a tutti gli elementi di una matrice nelle cui righe abbiamo i valori 100, 200, 300 e 400.
```{r, echo = TRUE}
sum_matrix <- matrix(rep(c(100, 200, 300, 400), each = 3), nrow = 3, ncol = 4)
sum_matrix
# sommo due matrici
my_matrix + sum_matrix
```
:::{.tip title="Matrix Multiplication" data-latex="[Matrix Multiplication]"}
Nota come in R l'operatore `*` indichi il semmplice prodotto elemento per elemento mentre per ottenere il prodotto matriciale è necessario utilizzare l'operatore `%*%`.
Il prodotto matriciale segue delle specifiche regole e specifiche proprietà. In particolare, il numero di colonne della prima matrice deve essere uguale al numero di righe della seconda matrice. Per un approfondimento vedi <https://it.wikipedia.org/wiki/Moltiplicazione_di_matrici>.
```{r}
```
#### Algebra Lineare {-}
Anche altri aspetti che riguardano le operazioni con le matrici non vengono qui discusse ma si rimanda il lettore interessato alle seguenti pagine:
- Per il significato di determinante di una matrice considera <https://it.wikipedia.org/wiki/Determinante>
- Per il significato di matrice inversa considera <https://it.wikipedia.org/wiki/Matrice_invertibile>
#### Diagonale {-}
Vediamo ora alcuni utili funzioni che riguardano la diagonale di una matrice. La diagonale di una matrice è formata dagli elementi i cui indici di riga e di colonna sono uguali, ovvero l'insieme di elementi associati allo stesso indice di riga e colonna ($x_{i,i}$).
Il comando `diag(nome_matrice)` permette di estrarre la diagonale di una matrice e trattarla come un semplice vettore:
```{r, echo = T}
# Matrice quadrata
square_matrix <- matrix(1:16, nrow = 4, ncol = 4)
square_matrix
diag(square_matrix)
# Matrice non quadrata
my_matrix <- matrix(1:12, nrow = 3, ncol = 4)
my_matrix
diag(my_matrix)
```
La funzione `diag()` può anche essere usata per sostituire in modo semplice gli elementi sulla diagonale di una matrice oppure per creare una matrice diagonale in cui gli altri valori siano tutti zero, ad esempio la matrice identità.
```{r, echo = TRUE}
# Sostituisco gli elementi della diagonale
diag(my_matrix) <- 999
my_matrix
# Creo una matrice diagonale
diag(4, nrow = 3, ncol = 4)
# Creo una matrice identità 4X4
diag(4)
```
:::
### Esercizi {-}
Utilizzando le matrici create nei precedenti esercizi, esegui le seguentti consegne ([soluzioni](https://github.com/psicostat/Introduction2R/blob/master/exercises/chapter-09-matrices.R)):
1. Crea la matrice `G` unendo alla matrice `A` le prime due colonne della matrice `C`
2. Crea la matrice `H` unendo alla matrice `C` le prime due righe della matrice trasposta di `B`
3. Ridefinisci la matrice `A` eliminando la seconda colonna. Ridefinisci la matrice `B` eliminando la prima riga. Verifica che le matrici così ottenute abbiano la stessa dimensione.
4. Commenta i differenti risultati che otteniamo nelle operazioni `A*B`, `B*A`, `A%*%B` e `B%*%A`.
5. Assegna i seguenti nomi alle colonne e alle righe della matrice `C`: `"col_1", "col_2", "col_3", "col_4", "row_1", "row_2", "row_3"`.
## Array
Abbiamo visto come le matrici siano un oggetto *bidimensionale*, tuttavia è possibile anche creare oggetti che abbiano 3, 4 o un qualsiasi numero (*n*) di dimensioni. Tali oggetti sono definiti **array** e possono essere creati con il comando `array()` indicando il vettore di valori utilizzati per popolare l'oggetto e la grandezza di ciascuna delle sue dimensioni.
```{r, echo = TRUE, eval = FALSE}
array(data = , dim = )
```
Ad esempio per creare un cubo di lato 3 contenete i valori interi dall'1 al 27 possiamo eseguire il seguente comando.
```{r, echo = TRUE}
my_cube <- array(1:27, dim = c(3,3,3))
my_cube
```
Tutte le principali funzioni ed operazioni di selezione che abbiamo visto per le matrici ed i vettori posono essere eseguite in modo analogo anche con gli array. Il funzinonamento generale della selezione di elementi tramite parentesi quadre risulterà ora certamente pù chiaro. Per ogni dimensione vengono indicarti gli indici di posizioni desiderati. L'ordine all'interno delle parentesi quadre determina la specifica deimensione a cui ci si riferisce e le virgole sono utilizzate per separare gli indici delle diverse dimensioni.
```{r, echo = TRUE, eval = FALSE}
my_hypercube[<dim-1>, <dim-2>, <dim-3>, ..., <dim-n>]
```
Vediamo ora alcuni semplici eempi di selezione.
```{r}
# Seleziono un singolo elemento
my_cube[1,1,1]
# Seleziono un singolo vettore
my_cube[ ,1,1]
# Seleziono un singolo piano
my_cube[ , ,1]
# Seleziono una porzione
my_cube[ 1:2, 1:2, 1:2]
```
:::{.tip title="Array Mother of All Matrix" data-latex="[Array Mother of All Matrix]"}
E' facilmente intuibile come le matrici non siano altro che un caso speciale di array con 2 dimensioni. Infatti i più attenti avranno notato che il valore `"array"` compariva insieme a `"matrix"` nel valutare la tipologia di oggetto.
```{r, echo = TRUE}
my_matrix <- matrix(1:12, nrow = 2, ncol = 2)
is.array(my_matrix)
class(my_matrix)
```
Tuttavia nota come un semplice vettore non sia un array. Ricordiamo infatti che un vettore non possiede una dimensione (`dim`) ma semplicemente una lunghezza (`length`).
```{r, echo = TRUE}
my_vector <- 1:12
is.array(my_vector)
dim(my_vector)
```
:::