-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy path03-matrizes_dframes.Rmd
979 lines (736 loc) · 38.9 KB
/
03-matrizes_dframes.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
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
# Objeto II - Matrizes e `data.frames` {#mat-dframe}
## Matriz vs. `data.frame`
Objetos de classe `matrix` ou `data.frame` são objetos bidimensionais (tem linhas e colunas), e constituem a forma como nossos dados estão organizados.
Precisamos entender a diferença entre essas classes e suas propriedades.
Objetos de classe `matrix` contêm linhas e colunas, mas os valores de toda a matriz são da mesma classe (`numeric`, `character`, ou `logical`, por exemplo).
Operações matemáticas com matrizes utilizam matrizes numéricas, portanto, de uma única classe, `matrix`.
Objetos de classe `data.frame` tambêm contém linhas e colunas, mas podem misturar colunas de classes diferentes (`numeric` e `character`, `factor`, e `logical`, por exemplo).
Quando importamos dados ao R, geralmente atribuímos os dados a um objeto de classe `data.frame`.
Podemos converter um objeto de classe `matrix` para `data.frame` e vice-versa, usando as funções `as.data.frame()` ou `as.matrix()`.
Porém, quando convertemos os dados para um objeto de classe `matrix`, todos os dados passam a ser da mesma classe, geralmente havendo perda de dados.
### Criando matrizes
Poder criar uma matriz no R é muito útil para várias finalidades como, por exemplo, simular dados em testes de permutação ou preencher uma tabela com resultados de uma análise.
Matrizes podem ser criadas de diferentes formas (e.g., pode juntar matrizes pelas linhas e colunas, ou pode extrair sub-matrizes de uma matriz).
Para criar matrizes, a função básica se chama `matrix()`:
```{r, echo = TRUE, eval = FALSE}
# veja o help da função
?matrix
# a função se usa assim: matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)
# onde:
# data = NA #um vetor de comprimento igual ao número de células desejadas que é nrow*ncol.
# byrow = FALSE #A forma de preenchimento da planilha pelos dados em data. Se byrow=TRUE, então ele preenche pelas linhas, senão pelas
# colunas
# nrow = número de linhas
# ncol = número de colunas
# dimnames = um objeto do tipo lista (que ainda não vimos), com dois vetores, um com os nomes das linhas, outro com os nomes das colunas.
```
```{r}
# exemplo 1 - matriz de 3x3 com zeros
mm <- matrix(data = 0, nrow = 3, ncol = 3, byrow = F, dimnames = NULL)
mm
# note que data tem comprimento 1, apenas 1 valor. Pela regra da reciclagem ele é repetido até completar o total necessário dado por nrow*ncol
# exemplo2 - matriz de 3x3 com valores
dd <- 1:9 # nove valores
mm <- matrix(data = dd, nrow = 3, ncol = 3, byrow = F, dimnames = NULL)
mm
# mudando byrow para TRUE preenchemos pelas linhas
mm2 <- matrix(data = dd, nrow = 3, ncol = 3, byrow = TRUE, dimnames = NULL)
mm2
# exemplo3 - matriz de 3x3 com valores e nomes de colunas e linhas
# define dimensao
nrow <- 3
ncol <- 3
# define data
dd <- 1:9 # nove valores
# define nome de colunas
cln <- paste("coluna", 1:ncol, sep = "")
# define nome de linhas
lln <- paste("linha", 1:nrow, sep = "")
mm <- matrix(data = dd, nrow = nrow, ncol = ncol, byrow = F, dimnames = list(lln, cln))
mm
```
Para unir ou criar matrizes (e `data.frames`) temos duas funções úteis:
* `rbind()`, que vem do inglês *row bind*, ou seja, cole linhas;
* `cbind()`, que vem do inglês *column bind*, ou seja, cole colunas.
```{r}
# vetores numéricos de mesmo comprimento
v1 <- 1:10
v2 <- 10:1
v3 <- 11:20
# essas duas condições devem ser verdadeiras
length(v1) == length(v2)
length(v1) == length(v3)
# entao posso criar uma matriz juntando esses vetores em linhas ou colunas
mml <- rbind(v1, v2, v3)
class(mml) # criou um matrix
mml
# ou
mmc <- cbind(v1, v2, v3)
class(mmc)
# ou se eu já tenho uma matriz, posso usar essas funções para adicionar novas linhas ou colunas
novovetor <- 31:40
# por linha
ncol(mml) == length(novovetor) # neste caso o número de colunas da matrix precisa ser igual ao número de elementos no vetor
mml <- rbind(mml, novovetor) # junto a matrix existente com o novo vetor, adicionando uma nova linha
mml
# note que a nova linha recebeu como nome o nome do objeto que continha o dado
# por coluna
nrow(mmc) == length(novovetor) # neste caso o número de linhas da matrix precisa ser igual ao número de elementos no vetor
mmc <- cbind(mmc, novovetor) # junto a matrix existente com o novo vetor, adicionando uma nova coluna
mmc
# note que a nova coluna recebeu como nome o nome do objeto que continha o dado
```
### Criando `data.frames`
Objetos de classe `data.frame` são tabelas de dados, apresentam duas dimensões, e permitem misturar dados de classes diferentes, numéricos, texto (character ou factor) e lógicos.
Quando importamos nossos dados ao R, em geral criamos objetos de classe `data.frame`.
Para criar ou converter dados em data.frames, podemos usar as funções `data.frame()` e `as.data.frame()`.
```{r, echo = TRUE, eval = FALSE}
?data.frame # veja o help das funções acima
```
```{r, echo = TRUE, eval = FALSE}
# a funcao que cria o objeto é
data.frame(..., row.names = NULL, check.rows = FALSE, check.names = TRUE, stringsAsFactors = default.stringsAsFactors())
# de todos os argumentos os mais importantes são:
# ... #que pode ser vetores ou tag = vetor (os dados da tabela)
# stringsAsFactors #que especifica se queremos os textos como vetores ou fatores
```
```{r}
# exemplo 1 -
# Primeiro criamos alguns dados
# um vetor numerico
v1 <- 1:10
# um vetor de letras do mesmo comprimento usando a constante LETTERS
v2 <- LETTERS[1:10]
# um vetor de palavras de mesmo comprimento
v3 <- rep(c("fulano", "jose", "joaquim", "martin"), length.out = length(v1))
# Juntamos num data.frame com fatores
dd <- data.frame(v1, v2, v3, stringsAsFactors = T)
class(dd) # é um data frame
dim(dd) # dimensoes, linhas e colunas
ncol(dd) # numero de colunas
nrow(dd) # numero de linhas
str(dd) # estrutura do objeto (veja as classes das colunas)
# JUNTAMOS SEM FATORES
dd2 <- data.frame(v1, v2, v3, stringsAsFactors = FALSE)
class(dd2) # é um data frame
str(dd2) # estrutura do objeto (veja as classes das colunas)
# juntamos com nome de colunas (tag = vetor)
dd2 <- data.frame(RegistorID = v1, CodigoZ = v2, Pessoa = v3, stringsAsFactors = FALSE)
dd2
# agora vamos usar o cbind que vimos acima
dz <- cbind(v1, v2, v3)
# ou entao usando tag=vetor para ter nomes das colunas de acordo
dz <- cbind(RegistorID = v1, CodigoZ = v2, Pessoa = v3)
class(dz) # isso cria uma matriz
str(dz) # todos os dados são da mesma classe (texto)
dz <- as.data.frame(dz) # convertemos num data.frame
class(dz) # é um data.frame
str(dz) # converte numeros para numerico e texto para fator
dz <- as.data.frame(as.matrix(dz), stringsAsFactors = FALSE) # convertemos num data.frame sem fatores
str(dz) # converte numeros para numerico e texto para character
```
### Funções importantes na manipulação de matrizes e `data.frames`
As funções `head()` e `tail()` mostram o cabeçalho e rodapé tanto para matrizes como para `data.frames`, respectivamente.
Vejam o `?` dessas duas funções:
```{r, echo = TRUE, eval = FALSE}
?head
?tail
```
```{r}
# Primeiro criamos alguns dados
# um vetor numerico
v1 <- 1:10
# um vetor de letras do mesmo comprimento usando a constante LETTERS
v2 <- LETTERS[1:10]
# um vetor de palavras de mesmo comprimento
v3 <- rep(c("fulano", "jose", "joaquim", "martin"), length.out = length(v1))
# juntamos com nome de colunas (tag = vetor) e com
dd2 <- data.frame(RegistorID = v1, CodigoZ = v2, Pessoa = v3, stringsAsFactors = TRUE)
# cabeçalho
head(dd2) # primeiras 6 linhas
head(dd2, 3) # três primeiras linhas
# rodapé
tail(dd2) # seis últimas linhas
tail(dd2, 3) # três últimas linhas
```
As funções `dim()`, `nrow()` e `ncol()` informam as dimensões de matrizes e data.frames, número de linhas, e número de colunas, respectivamente.
```{r}
dim(dd2) # vetor com dois valores, número de linhas e número de colunas
nrow(dd2) # número de linhas do data.frame ou matrix
ncol(dd2) # número de colunas do data.frame ou matrix
nrow(as.matrix(dd2))
ncol(as.matrix(dd2))
```
As funções `str()` e `summary()` informam a estrutura dos `data.frames` e o resumo dos dados, respectivamente.
```{r}
str(dd2) # mostra a estrutura do objeto, quais colunas, classes de colunas e total de valores
```
```{r, eval=FALSE, echo=TRUE}
summary(dd2) # mostra para cada coluna a variação encontrada: estatística descritiva de variáveis numéricas, contagem por categoria de fatores, etc. Veremos isso adiante.
```
```{r, eval=TRUE, echo=FALSE}
kable(summary(dd2))
```
As funções `colnames()` e `rownames()` permitem VER e ATRIBUIR valores de nomes de linhas e colunas em data.frames e matrizes.
Em um `data.frame`, os nomes de linhas DEVEM SER ÚNICOS e não podem ter duas linhas com o mesmo nome.
São códigos que identificam registros únicos.
Isso é muito importante para o entendimento dos identificadores dos seus dados.
```{r}
# vamos criar uma matriz com nomes de linhas e colunas
mm <- matrix(1:9, nrow = 3, ncol = 3, dimnames = list(paste("linha", 1:3, sep = ""), paste("coluna", 1:3, sep = "")))
# e converter essa matrix para um data.frame
dd <- as.data.frame(mm)
# vamos também criar outra matriz SEM nomes de linhas e colunas
mm2 <- matrix(1:9, nrow = 3, ncol = 3)
# e converter essa matrix para um data.frame
dd2 <- as.data.frame(mm2)
dd2
```
```{r}
# para os objetos com nomes podemos ver os nomes
rownames(mm)
rownames(dd)
colnames(mm)
colnames(dd)
```
```{r}
# para os objetos sem nomes
rownames(mm2) # nulo, não tem nome
```
```{r}
rownames(dd2) # números em formato de texto
```
```{r}
colnames(mm2) # nulo, não tem nome
```
```{r}
colnames(dd2) # V1 a Vncol(dd) - ele cria nomes das colunas
# note que no caso do data.frame dd2, apesar de não ter nome de linha e coluna, o R criou uma para ele. DATA.FRAMES SEMPRE TEM NOME DE LINHAS E COLUNAS. Note que o nome das linhas apesar de números correspondentes aos índices, são de fato TEXTO
```
```{r}
# essas funções permitem VER mas também permitem ATRIBUIR (modificar) nomes
# modificando quem já tem nome (matriz, mas funciona igual para dd)
colnames(mm) # nomes atuais
colnames(mm) <- c("novonome1", "novonome2", "novonome3")
mm # veja como o nome das colunas mudou
# mudando apenas o nome da coluna2
colnames(mm)[2] <- "colunaDOIS"
colnames(mm) # nomes atuais
# atribuindo quando não tem nome
colnames(mm2) # está vazio ou não existe (NULL)
colnames(mm2) <- paste("banana", 1:ncol(mm2), sep = "-")
mm2 # agora tem nome de coluna
rownames(mm2) # nomes de linhas também está vazio
rownames(mm2) <- paste("chuchu", 1:nrow(mm2), sep = ".")
mm2 # agora tem nomes de linha e coluna
```
Vamos tentar atribuir um mesmo nome de linha `teste1` a duas linhas de nossa matriz `mm2` e ver o que acontece:
```{r}
rownames(mm2)[1:2] <- "teste1" # coloque o nome teste1 para as linhas 1 e 2 FUNCIONA PARA MATRIX
```
Reparem que um mesmo nome de linha pode ser utilizado em mais de uma linha de uma matriz. Será que isso pode ser feito em um `data.frame`? Vejamos:
```{r, echo = TRUE, eval = FALSE}
rownames(dd)[1:2] <- "teste1" # nao funciona, porque ele não aceita nomes repetidos de linhas em DATA.FRAMES
```
## Indexação de matriz e `data.frame`
Entender indexação é fundamental para manipular dados no R.
Em indexação de vetores (seção \@ref(indexacao)), vimos que é possível usar números, códigos/nomes ou valores de verdadeiro ou falso (lógico), como elementos para visualizar, filtrar e mudar dados em vetores unidimensionais.
O mesmo operador, `[]`, pode ser usado para indexação de uma matriz ou um `data.frame`.
A única diferença é que, por matrizes e `data.frames` serem bidimensionais, precisamos indicar a qual dimensão estamos nos referindo.
Portanto o operador de indexação para matrizes e data.frame tem a seguinte estrutura, `[indiceDeLinha , indiceDeColuna]`, em que a vírgula separa os índices de linha e coluna.
### Matrizes
```{r}
# vamos criar uma matriz
mm <- matrix(1:9, nrow = 3, ncol = 3, dimnames = list(paste("linha", 1:3, sep = ""), paste("coluna", 1:3, sep = "")))
# veja a matriz criada
mm
# USANDO INDICE NUMÉRICO
mm[1, 2] # mostra o elemento da linha 1 e coluna 2
mm[1, ncol(mm)] # mostra o elemento da linha 1 e última coluna
mm[nrow(mm), ncol(mm)] # mostra o elemento da última linha e última coluna
mm[, 1] # mostra a coluna 1
# eu posso juntar indices de matrizes e vetores na mesma linha
mm[, 1][2] # mostra o segundo elemento do vetor correspondente a primeira coluna
mm[1, ] # mostra a linha 1
mm[nrow(mm), ] # mostra a ultima linha
mm[, 1:2] # mostra as duas primeiras colunas
mm[1:2, 1:3] # mostra as duas primeiras linhas e duas primeiras colunas
mm[3:nrow(mm), ] # mostra da linha tres a ultima linha
mm[c(3, 1), c(3, 2)] # mostra as linhas 3 e 1 e colunas 3 e 2 (nessa ordem)
# USANDO INDICES DE NOMES
mm["linha1", ] # mostra a linha 1 - note que poderia ser outro nome, poderia ter chamado no inicio do script a linha 1 de "banana"
mm[, "coluna1"] # mostra a coluna 1
mm[c("linha3", "linha1"), c("coluna3", "coluna1")] # mostra a linhas 3 e 1 e colunas 3 e 1. NOTE QUE POSSO ASSIM INVERTER AS COLUNAS E LINHAS
# SE EU POSSO VER EU POSSO MUDAR
mm
mm[1, 3] # elemento da linha 1 coluna 3
mm[1, 3] <- 33 # mudei o elemento
mm[2, 2:3]
mm[2, 2:3] <- mm[2, 2:3] * 10 # mudei os valores das colunas 2 e 3 para a linha 2, multiplicando o original por 10
mm[2, 2:3]
mm
```
### `data.frame`
O operador `[indiceDeLinha , indiceDeColuna]` também funciona para `data.frames`.
Outro operador útil na manipulação de `data.frames` é o `$`.
Ele permite a visualização e atribuição de valores a qualquer coluna.
```{r}
# vamos criar uma matriz com nomes de linhas e colunas
mm <- matrix(1:9, nrow = 3, ncol = 3, dimnames = list(paste("linha", 1:3, sep = ""), paste("coluna", 1:3, sep = "")))
# veja a matriz criada
mm
# convertemos para um data.frame
dd <- as.data.frame(mm)
dd$coluna1 # pego a coluna 1 (note que o nome da coluna vai sem "aspas")
```
Veja que o uso do operador `$` não funciona em matrizes:
```{r, echo = TRUE, eval = FALSE}
mm$coluna1 # veja como não funciona para o objeto matrix
```
```{r}
dd$coluna1[2] # vejo o segundo elemento da coluna1
# isso é o mesmo que
dd[2, "coluna1"]
# se eu vejo eu posso mudar
dd[2, "coluna1"] <- 10
dd$coluna1[3] <- 20
dd$coluna3 # pego a coluna tres
# também posso adicionar uma nova coluna
dd$novacoluna <- LETTERS[1:nrow(dd)]
dd # agora tenho uma nova coluna
```
```{r, echo = TRUE, eval = FALSE}
# ou poderia usar outra forma
dd[, "nova2"] <- LETTERS # nao vai funcionar por estou atribuindo um vetor muito mais longo do que tenho linhas
```
```{r}
length(LETTERS) > nrow(dd) # essa expressão é verdadeira
dd[, "nova2"] <- LETTERS[1:nrow(dd)] # isso tem o mesmo comprimento e funciona
dd
# posso adicionar uma coluna vazia
dd$outracoluna <- NA
dd
# e ainda outra (lógica)
dd$maisuma <- TRUE
dd
```
Adicionar colunas em uma matriz é um pouco diferente do que se faz com um `data.frame`:
```{r, echo = TRUE, eval = FALSE}
# primeiro nao posso usar $ porque matrix não entende isso
class(mm) # é uma matrix
mm$colun3 # isso nao funciona
```
```{r}
mm[, "coluna3"] # isso funciona
# adicionando uma coluna
```
```{r, echo = TRUE, eval = FALSE}
mm[, 4] # isso nao existe
mm[, 4] <- log(mm[, "coluna3"]) # isso não funciona
```
```{r}
# poderia usar a função cbind que vimos anteriormente
mm <- cbind(mm, LOGCOLUNA3 = log(mm[, "coluna3"])) # assim eu posso
```
## Filtrando e ordenando matrizes e `data.frames`
### Filtragem de dados
Já vimos como fazer perguntas sobre vetores (Seção \@ref(perg-vetores)) e obter vetores lógicos ou valores de índices que nos permitem extrair ou filtrar de vetores os dados que satisfazem às condições das perguntas feitas.
Aqui vamos estender isso para objetos de classe `matrix` e `data.frame`, porque é através de vetores lógicos ou de matrizes lógicas que podemos filtrar dados de objetos bidimensionais.
```{r, echo = TRUE, eval = FALSE}
?iris # veja o help do R sobre Edgar Anderson's Iris Data que explica esses dados que vem com o R
```
```{r}
class(iris)
str(iris) # estrutura, veja as colunas
# vamos filtrar os dados de uma das especies
unique(iris$Species) # vemos os valores únicos
# ou, tendo em vista que é um fator
levels(iris$Species)
sp1 <- levels(iris$Species)[1]
# quais linhas correspondem a essa especie
vl <- iris$Species == sp1
sum(vl) # numero de linhas que satisfazem a pergunta
nrow(iris) # numero total de linhas no data.frame
# filtrando os dados eu simplesmente uso o vetor lógico como índice de linha. O novo objeto criado terá apenas as linhas em que vl é verdadeiro
iris.sp1 <- iris[vl, ]
nrow(iris.sp1) == sum(vl) # entao esta condição é verdadeira
# filtrar segundo duas colunas
vl <- iris$Species == sp1 # seja da especie em sp1
sum(vl) # quantas sao?
vl2 <- iris$Sepal.Length <= 5 # tenha sepala menor ou igual a 5
sum(vl2) # quantas sao?
# combinando as duas perguntas
vll <- vl & vl2
sum(vll) # quantas sao?
# filtrando
ff <- iris[vll, ]
class(ff) # novo data.frame resultando do filtro realizado
nrow(ff) == sum(vll) # isso deve ser verdadeiro
```
#### Dados com valores ausentes
As funções`is.na()` e `na.omit()` vistas anteriormente (Seção \@ref(filtro-dados-ausentes)) permitem eliminar linhas e colunas que tenham valores ausentes.
A presença de valores às vezes impede certas análises de serem executadas.
```{r}
# vamos fazer uma cópia do objeto iris e modificar ele acrescentando alguns NAs
dd <- iris
# tem algum NA originalmente?
sum(is.na(dd)) # não tem
# qual a dimensão?
dim(dd)
# pega 10 valores aletórios entre 1:150 (linhas)
v1 <- sample(1:nrow(dd), size = 10, replace = F)
# nessas linhas acrescenta NAs na coluna 2
dd[v1, 2] <- NA
# pega outros 10 valores aletórios entre 1:150 (linhas)
v1 <- sample(1:nrow(dd), size = 10, replace = F)
# nessas linhas acrescenta NAs na coluna 3
dd[v1, 3] <- NA
# pronto agora temos um data.frame com NAs
sum(is.na(dd)) # tem 20 NAs na tabela
# quais linhas tem NA
vl <- is.na(dd[, 2]) | is.na(dd[, 3]) # ou é NA em 2 ou em 3 que foi onde mudei
dd[vl, ]
# use na.omit() para eliminar todas as linhas que NA em alguma coluna
sum(is.na(dd)) # tem 20 valores
dd2 <- na.omit(dd)
sum(is.na(dd2)) # nao tem mais nenhum
```
### Ordenação de dados
Para ordenar matrizes e `data.frames`, é preciso entender a diferença entre duas funções:
* `sort()` ordena um vetor e retorna os valores ordenados;
* `order()` ordena um vetor e retorna os **índices dos valores ordenados**. É isso que deve ser utilizado para ordenar matrizes e `data.frames`.
```{r, echo = TRUE, eval = FALSE}
?sort
?order
```
```{r}
# ordenação
str(iris)
```
```{r}
# funcao SORT()
o1 <- sort(iris$Sepal.Length) # pega os valores ordenados da coluna comprimento de sépala
o1 # sao valores de sepalas do menor para o maior
# em ordem decrescente
o2 <- sort(iris$Sepal.Length, decreasing = T)
o2 # sao valores de sepalas do maior para o menor
```
```{r}
# FUNCAO order()
# qual o indice dos valores ordenados em ordem crescente?
o3 <- order(iris$Sepal.Length)
o3 # esses valores correspondem aos INDICES dos valores ordenados
# entao para ver os valores ordenados
iris$Sepal.Length[o3]
# então isso deve ser totalmente verdadeiro:
iris$Sepal.Length[o3] == sort(iris$Sepal.Length) # as comparações para a par são identicas
# então esta expressão também é verdadeira:
sum(iris$Sepal.Length[o3] == sort(iris$Sepal.Length)) == nrow(iris)
```
```{r}
# portanto a função sort ordena os valores e funcao order mostra apenas os indices dos valores ordenados. Assim, posso usar a funcao order() para ordenar data.frames, matrizes e vetores
idx <- order(iris$Sepal.Length) # indice das linhas ordenadas segundo o comprimento das sepalas
# compara com o original:
sum(iris$Sepal.Length[idx] == iris$Sepal.Length) == nrow(iris) # é FALSO porque em iris as linhas não estão originalmente ordenadas segundo o comprimento.
```
```{r}
# vamos mudar isso
novo.iris <- iris[idx, ] # pego o vetor de indices dos valores ordenados e uso na indexacao para ordenar o objeto original segunda a coluna escolhida
idx2 <- order(novo.iris$Sepal.Length) # indice das linhas ordenadas segundo o comprimento das sepalas
```
```{r}
# note que agora essa expressão é verdadeira, porque o original já está ordenado segundo essa coluna
sum(novo.iris$Sepal.Length[idx2] == novo.iris$Sepal.Length) == nrow(novo.iris) # é VERDADEIRO
```
```{r}
# AGORA ORDENANDO POR MULTIPLAS COLUNAS
idx <- order(iris$Species, iris$Sepal.Length, decreasing = TRUE) # por especie e por sepala em ordem decrescente
# ordena segundo essas duas colunas
novo.iris <- iris[idx, ]
novo.iris[, c("Species", "Sepal.Length")]
# para cada especie esta ordenado por sepala:
novo.iris[novo.iris$Species == "versicolor", ]$Sepal.Length
novo.iris[novo.iris$Species == "virginica", ]$Sepal.Length
```
## Importando e exportando dados no R
```{block, type = "rmdwarning"}
ATENÇÃO! Se você utiliza Windows, e no seu gerenciador de arquivos os arquivos aparecem sem **extensão** (`.csv`, `.txt`, `.doc` etc.), mude nas suas preferências para não **ocultar extensões de arquivos conhecidos**. Dessa forma você consegue ver os arquivos pelo tipo (extensão).
```
Existem diversas funções para importar dados para objetos do R, incluindo funções para ler arquivos do Excel (`.xls`, ou `.xlsx`), arquivos XML, arquivos `*.DBF` etc.
O R também tem pacotes que interagem diretamente com bancos de dados (mysql, postgres, sql etc.).
Não cobriremos a importação desses tipos aqui, mas você pode pesquisar sozinho no rede.
É frequente encontrarmos problemas de acentuação e na transferibilidade entre sistemas operacionais diferentes (Mac, Linux, Windows).
A palavra chave aqui é [codificação de caracteres](https://en.wikipedia.org/wiki/Character_encoding) (em inglês, "character encoding").
### Arquivos de texto simples para estocar dados
Muitos dados que obtemos online e os próprios scripts do R são do formato mais simples que existe, que são arquivos de texto, geralmente arquivos salvos com extensões `.csv` ou `.txt`.
Arquivos desse tipo podem ser abertos em qualquer editor de texto, em qualquer sistema operacional e em qualquer versão.
Isso garante arquivamento, longevidade e transferibilidade.
Portanto, é a melhor forma de salvar seus dados e compartilhá-los.
Qualquer arquivo desse tipo pode ser lido pelos os editores de script do R ou RStudio.
Pode também exportar planilhas do Excel ou LibreOffice (e afins) para esse formato.
Vamos nos concentrar neste curso em lidar com arquivos deste tipo.
É importante atentar em arquivos de texto contendo dados tabulados para:
* O **separador** das colunas pode ser `;`, tabulação (no R = ''\t''), `,`, ou qualquer símbolo que indique a separação das colunas (ou seja não está nas células);
* Casas decimais podem ser separadas por `.` ou `,`.
```{block, type = "rmdidea"}
DICA: Procure saber como seus dados estão antes de tentar importá-los, de forma a indicar corretamente o delimitador e o separador das casas decimais adequados. Você evitará assim muita dor de cabeça!
```
* Datas - colunas com datas constituem um objeto de classe `date` no R, que a converte em número que pode ser usado em operações matemáticas. Dependendo de como seus dados estão formatados no original, é comum a inversão de mês com dia entre, por exemplo, o sistema inglês (MM-DD-YYYY) e o sistema português (DD-MM-YYYY). Tenha controle disso!
Por isso, recomenda-se que:
* Defina um padrão que você sempre usará para formatar seus dados ANTES de importá-los ao R. Dessa forma você irá memorizar rapidamente como importar os dados do jeito que você sempre prepara;
* Padronize a codificação dos caracteres (UTF8 é padrão Mac e Linux; Latin1 é padrão Windows) em arquivos `.txt`;
* Padronize o separador de casa decimal (ponto ou vírgula?);
* Pradonize a quebra de linha, i.e., o que indica no texto o início de uma nova linha (novamente, isso é diferente entre Mac, Linux e Windows);
* Padronize se colunas de texto vão entre aspas;
* Padronize como você dá nome às colunas; nome de colunas e de linhas não devem ser muito longos, e deve-se **evitar acentos ou espaços em branco em nomes de colunas**. Isso é muito importante!
* Se você usa planilhas, recomendamos usar uma versão de software livre da família LibreOffice/OpenOffice pois eles permitem um maior controle da exportação dos dados, o que inclui controlar o tipo de codificação de caracter dos dados de saída e também separadores das colunas, tanto para ler como para salvar arquivos de planilhas.
### Importando dados {#import-dados}
#### Pacote base do R
A principal função para importar dados no R é `read.table()`.
Ela funciona para importar arquivos em formato de texto simples (`.csv`, `.txt`).
Vamos utilizar um conjunto de [dados contendo as coordenadas geográficas dos municípios brasileiros](https://github.com/LABOTAM/IntroR/blob/main/dados/municipiosbrasil.csv) para praticar a importação dos dados.
Baixe-o para a sua pasta de trabalho.
Em seguida, abra o arquivo com um editor de texto simples (Bloco de Notas, Notepad++, TextWrangler, gedit, etc.) e veja como ele está formatado.
Verifique:
* Qual é o separador de colunas?;
* Qual é a codificação dos caracteres? (consegue ver e editar isso no seu editor?);
* Qual é a quebra de linha? (consegue ver e editar isso no seu editor?
* Aspas duplas ou simples definem colunas? (este arquivo não tem nenhuma aspas!)
Vamos agora abrir este arquivo no LibreOffice (ou similar, como o Excel).
Busque os comandos de importação para poder importar o arquivo de texto^[Isso é mais fácil de ser feito no LibreOffice/OpenOffice].
Veja os controles na importação quanto aos elementos acima:
* Salve o arquivo como `*.ods`;
* Salve novamente como `*.csv` - veja como você tem controle na exportação quanto aos elementos acima.
```{r, echo = TRUE, eval = FALSE}
# se você colocou o arquivo na sua pasta de trabalho, ele deve estar visível por
dir(pattern = "csv")
```
```{r, echo = TRUE, eval = FALSE}
# então posso ler sem precisar especificar o caminho até o arquivo
# veja o help da função antes de começar
?read.table
# os seguintes argumentos são mais importantes:
# sep = " " #o codigo que separa as colunas, o padrão é espaço
# quote = "\"'" #o que define células de texto - o padrão é interpretar tanto aspa simples como dupla presentes
# dec = "." #ponto é a casa decimal padrão
# header = FALSE #a primeira linha não tem o nome de colunas
# as.is = FALSE #o padrão é converter texto em fatores, se usar T não fará isso
# na.strings #se definir, pode informar aqui que símbolos em células inteiras que sejam interpretados como valores ausentes (NA)
# encoding #codificação da acentuação. o padrão é 'unknown' (desconhecido), na qual ele reconhece segundo o sistema operacional. As opções mais usadas são 'latin1' ou 'utf8' e alterne com isso se você tem problemas com acentos.
```
```{r, echo = TRUE, eval = FALSE}
# o arquivo original tem os seguintes formatos:
# colunas separadas por tabulação (no R isso é definido pela expressão regular "\t")
# decimal com ponto
# não tem aspas definindo as colunas de texto.
# a primeira linha é o nome das colunas.
# Então, para ler posso usar:
dd <- read.table(file = "municipiosbrasil.csv", sep = "\t", header = T)
class(dd) # data.frame
dim(dd) # dimensão do objeto
head(dd) # cabeçalho do data.frame
# veja o que aconteceria se eu achasse que no meu arquivo as colunas são separadas por vírgula
dd2 <- read.table(file = "municipiosbrasil.csv", sep = ",", header = T)
head(dd2)
dim(dd2) # apenas 1 coluna, porque o separador informado não é o mesmo dos dados
# e se o encoding do meu arquivo estiver errado?
dd3 <- read.table(file = "municipiosbrasil.csv", sep = "\t", header = T, encoding = "latin1")
dd3[5562, ] # veja o que aconteceu com os acentos nessa linha
dd[5562, ] # no original o encoding não é "latin1"
# veja a estrutura do objeto correto
str(dd)
# Poxa, todas as colunas são fatores, mesmo as colunas Latitude e Longitude que são numéricas.
# Deve ter algum valor nessas colunas que não são numéricos.
# Quais são?
vl <- is.na(as.numeric(as.vector(dd$Latitude))) # quais são NA quando eu converto para numérico? Pois esses devem ser valores de texto e não numéricos. Note que converti o fator para vetor antes de converter para numérico. A função is.na pergunta o que é NA, pois os textos que não podem ser convertidos para número serão valores ausentes (NA)
sum(vl)
dd[vl, ] # essas linhas tem a palavra "NULL" para Latitude e Longitude no arquivo original (volte lá para confirmar), e o R não reconheceu isso como ausente NA. Como vetores devem ser da mesma classe, os números dessas colunas foram codificados como texto e as colunas convertidas a fatores de texto que é o padrão da função read.table()
```
Podemos usar o argumento `na.strings` para corrigir isso durante a importação:
```{r, echo = TRUE, eval = FALSE}
dd4 <- read.table(file = "municipiosbrasil.csv", sep = "\t", header = T, dec = ".", na.strings = c("NULL", "NA", ""))
# qualquer CELULA INTEIRA que contenha NULL ou NA ou esteja vazia SERÁ INTERPRETADA COMO VALOR AUSENTE e codificada como NA no R.
str(dd4)
# note que agora as colunas Latitude e Longitude foram interpretadas como número
# mas o que acontece se informamos mal a casa decimal?
dd5 <- read.table(file = "municipiosbrasil.csv", sep = "\t", header = T, dec = ",", na.strings = c("NULL", "NA", ""))
str(dd5)
# como tem ponto como definição de casa decimal no arquivo de dados, as colunas numéricas foram novamente interpretadas como texto.
# Texto como vetores ou fatores?
dd4 <- read.table(file = "municipiosbrasil.csv", sep = "\t", header = T, dec = ".", na.strings = c("NULL", "NA", ""))
str(dd4) # todas as colunas de texto neste objeto foram interpretadas como fatores
# o argumento as.is permite corrigir isso. "as is" significa "como está" nos dados originais, então valores de texto são lidos como vetores de caracteres não codificados em fatores.
dd6 <- read.table(
file = "municipiosbrasil.csv",
sep = "\t",
header = T,
dec = ".",
na.strings = c("NULL", "NA", ""),
as.is = TRUE
)
str(dd6) # diferentemente do objeto anterior, não há mais fatores
```
```{r, echo = TRUE, eval = FALSE}
# Lembram que colocamos o endereco do arquivo mais acima?
# podemos usar um endereco da internet para baixar um arquivo
dd7 <-
read.table(
"https://github.com/LABOTAM/IntroR/blob/main/dados/municipiosbrasil.csv",
sep = "\t",
header = TRUE
)
dd7
head(dd7)
# a função read.table tem vários outros argumentos. Veja o help e entenda isso bem.
```
#### Pacote readr
A principal função para ler arquivos do pacote `readr` [@R-readr] se chama `read_delim()`.
Funciona de maneira parecida com o `read.table()` com algumas pequenas diferenças: não converte colunas de texto que possam ser categorizadas em fatores (`read.table()` faz isso por padrão), retorna um `tibble` no lugar de um data.frame (`tibbles` são `data.frames` diferentes na maneira como aparecem no console; além de mostrar apenas uma porção dos dados, para cada coluna há a indicação do tipo de variável presente), assume por padrão que o dado importado possui cabeçalho.
Existem outras diferenças que podem ser melhor entendidas na página do pacote (https://github.com/tidyverse/readr).
Os argumentos possuem nomes diferentes do que os utilizados em `read.table()` e, como este, importa arquivos em formato de texto simples (*.csv* , *.txt*).
```{r, echo = TRUE, eval = FALSE}
# pacote readr
# usando como exemplo o mesmo arquivo municipiosbrasil.csv
library("readr")
rr1 <- read_delim("municipiosbrasil.csv", delim = "\t")
rr1
dd7
dim(rr1)
dim(dd7)
```
Como no pacote `base`, também podemos ler arquivos diretamente da rede:
```{r, echo = TRUE, eval = FALSE}
rr2 <- read_delim("https://github.com/LABOTAM/IntroR/blob/main/dados/municipiosbrasil.csv", delim = "\t")
rr2
```
#### Pacote data.table
A principal função para ler arquivos do pacote `data.table` [@R-datatable] se chama `fread()`.
Este pacote é muito conhecido devido à velocidade de suas ações, funcionando perfeitamente para dados grandes.
Esta função possui uma particularidade: o usuário não precisa indicar o separador; automaticamente ele descobre o separador e lê o arquivo.
Em casos especiais, é necessário a indicação do separador com o argumento `sep`, igual ao `read.table()`.
Ao ler um arquivo, a função retorna também um `data.frame`, porém com certas particularidades quanto à impressão do resultado na tela do console, como acontece com a função `read_delim()` do pacote `readr`.
Mais informações, leiam atentamente o site do pacote: <https://github.com/Rdatatable/data.table>.
O pacote como um todo é uma excelente ferramenta na manipulação de dados.
Como os pacotes citados acima, esta função é capaz de importar arquivos em formato de texto simples (`.csv` , `.txt`).
```{r, echo = TRUE, eval = FALSE}
# pacote data.table
# usando como exemplo o mesmo arquivo municipiosbrasil.csv
library("data.table")
dt1 <- fread("municipiosbrasil.csv")
dt1
```
Também podemos ler arquivos diretamente da rede, providenciando um endereço que contenha um arquivo de texto simples:
```{r, echo = TRUE, eval = FALSE}
dt2 <- fread("https://github.com/LABOTAM/IntroR/blob/main/dados/municipiosbrasil.csv")
dt2
```
#### Importando do Excel diretamente
Utilizamos o pacote `readxl`[@R-readxl] para ler dados de arquivos Excel, isto é, arquivos `.xlsx` ou `.xls`.
A principal função para importar dados deste pacote se chama `read_excel()`.
Os principais erros aqui podem ser por células unidas, cabeçalhos no topo da planilha, e acentos.
Veja o `?` das funções usadas para conhecer parâmetros opcionais para resolver esses possíveis problemas.
```{r, echo = TRUE, eval = FALSE}
# instale o pacote
library("readxl")
# se o arquivo for xls
# Salve o arquivo municipiosbrasil.csv como xlsx ou xls
meuxlsx <- "municipiosbrasil.xlsx"
dd <- read_excel(path = meuxlsx, sheet = 1)
dd
dd <- as.data.frame(dd)
dd
# se o arquivo for xls
meuxls <- "municipiosbrasil.xls"
dd <- read_excel(path = meuxls, sheet = 1)
dd
dd <- as.data.frame(dd)
dd
```
### Exportando dados
#### Pacote base do R
A principal função do pacote `base` do R para exportar dados se chama `write.table()`.
Ela funciona para exportar arquivos em formato de texto simples (`.csv`, `.txt`) e usa basicamente os mesmos argumentos da função `read.table()`.
```{r, echo = TRUE, eval = FALSE}
?write.table # veja o help - recomendo usar essa função genérica e evitar de usar atalhos tipo write.csv, que no fundo usam esta mesma função
```
Por se tratar de uma função do pacote `base`, não é necessário recorrer a função `library()` para chamar nenhum pacote, pois a função encontra-se disponível a qualquer momento para ser utilizada no R:
```{r, echo = TRUE, eval = FALSE}
# vamos usar o mesmo arquivo
dir(pattern = "csv")
# ler o arquivo para o R para ter algo a exportar
dd <- read.table(file = "municipiosbrasil.csv", sep = "\t", header = T, dec = ".", na.strings = c("NULL", "NA", ""), as.is = TRUE)
str(dd) # diferentemente do objeto anterior, não há mais fatores
# filtrando apenas para municipios do Amazonas:
vl <- dd$Province %in% "Amazonas"
sum(vl) # quantos são?
# ou, desse jeito que é identico:
vl <- dd$Province == "Amazonas"
sum(vl)
dd.am <- dd[vl, ]
nrow(dd.am) == sum(vl) # deve ser verdadeiro, certo?
# salvando esses dados num novo arquivo com diferentes formatações:
# separado por tabulação e textos sem aspas e células NA sem nada
write.table(dd.am, file = "muni-am1.csv", sep = "\t", na = "", quote = FALSE)
# separado por tabulação e textos com aspas e células NA sem nada
write.table(dd.am, file = "muni-am2.csv", sep = "\t", na = "", quote = TRUE)
# separado por vírgula e textos com aspas e células NA com a palavra valor.ausente
write.table(dd.am, file = "muni-am3.csv", sep = ",", na = "valor.ausente", quote = TRUE)
# separado por vírgula e textos com aspas e células NA vazios e não adicona nomes das linhas como primeira coluna (row.names=FALSE). Pode deslocar a primeira linha na sua planilha SE você NAO USAR este argumento)
write.table(dd.am, file = "muni-am4.csv", sep = ",", na = "", quote = TRUE, row.names = FALSE)
# separado por tabulação e textos sem aspas e células NA vazias, sem nomes das linhas, e quebra de linha no formato do windows (eol = "\r\n")
write.table(dd.am, file = "muni-am5.csv", sep = ",", na = "", quote = TRUE, row.names = FALSE, eol = "\r\n")
# ABRA OS ARQUIVOS GERADOS NO SEU EDITOR DE TEXTO E COMPARE AS FORMATAÇÕES GERADAS
```
#### Pacote `readr`
A principal função do pacote `readr`para exportar dados se chama `write_delim()`.
Ela exporta `data.frames` em formato de texto simples (`.csv`, `.txt`), utilizando basicamente os mesmos argumentos da função `read_delim()`, pertencente ao mesmo pacote.
```{r, echo = TRUE, eval = FALSE}
# exportando dados com pacote readr
# utilizando mesmo objeto criado com pacote base
write_delim(dd.am, "muni-am6.csv", delim = "\t")
write_delim(dd.am, "muni-am7.csv", delim = ";")
```
#### Pacote `data.table`
A principal função do pacote `data.table` para exportar `data.frames` em formato de texto simples (`.csv`, `.txt`) se chama `fwrite()` e usa basicamente os mesmos argumentos da função `read_delim()`, pertencente ao pacote `readr`.
```{r, echo = TRUE, eval = FALSE}
# exportando dados com pacote data.table
# utilizando mesmo objeto criado com pacote base
fwrite(dd.am, "muni-am8.csv", sep = "\t")
```
### Outras funções úteis
A função `scan()` lê um arquivo de texto em qualquer formato para um vetor ou lista no R.
Trata-se de uma função genérica que é bom memorizar.
Vamos usar o mesmo arquivo `municipiosbrasil.csv` para demonstrar sua utilidade:
```{r, echo = TRUE, eval = FALSE}
# esta função é muito util para ler linha por linha um arquivo de texto que você quer explorar.
dd <- scan(file = "municipiosbrasil.csv", what = "complex", sep = "\n")
```
```{r, include = FALSE, message = FALSE}
load("dados/dd.rda")
load("dados/dd2.rda")
```
```{r, echo = TRUE, eval = TRUE}
class(dd)
length(dd) # cada linha é um elemento do vetor
dd[1]
```
```{r, echo = TRUE, eval = FALSE}
# usando tabulação
dd2 <- scan(file = "municipiosbrasil.csv", what = "complex", sep = "\t")
```
```{r, echo = TRUE, eval = TRUE}
class(dd2)
length(dd2) # cada célula é um elemento deste vetor
dd2[1:5]
# nao faz muito sentido com esses dados que tem formato de tabela, mas essa função pode ser usada com qualquer arquivo de tipo texto, por exemplo:
# de um artigo no qual você quer buscar palavras e tabular palavras chaves?
# num log de uma análise feita por outro software (nao no R) do qual você quer extrair resultados a partir da lógica complicada do texto de resultado)
# etc.
```
## Para saber mais: {#sabermais-indexacao-matrizes-dframes}
* Vídeoaula de [Indexação de matrizes e `data.frames`](https://youtu.be/CJILnDzVviQ).
* Vídeoaula de [filtragem e ordenação de matrizes](http://www.botanicaamazonica.wiki.br/labotam/lib/exe/fetch.php?media=bot89:precurso:3matrizes:video01_bot89-2020-04-08_11.41.08.mp4).
## Exercícios
* Resolva o exercício [103.01 Distância entre cidades](http://notar.ib.usp.br/exercicio/23).
* Resolva o exercício [103.02 Criação de um data frame](http://notar.ib.usp.br/exercicio/2).
* Resolva o exercício [103.03 Criando uma Matriz](http://notar.ib.usp.br/exercicio/13).