3 Objeto II - Matrizes e data.frames

3.1 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.

3.1.1 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():

# 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.
# exemplo 1 - matriz de 3x3 com zeros
mm <- matrix(data = 0, nrow = 3, ncol = 3, byrow = F, dimnames = NULL)
mm
0 0 0
0 0 0
0 0 0
# 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
1 4 7
2 5 8
3 6 9
# mudando byrow para TRUE preenchemos pelas linhas
mm2 <- matrix(data = dd, nrow = 3, ncol = 3, byrow = TRUE, dimnames = NULL)
mm2
1 2 3
4 5 6
7 8 9
# 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
coluna1 coluna2 coluna3
linha1 1 4 7
linha2 2 5 8
linha3 3 6 9

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.

# 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)
## [1] TRUE
length(v1) == length(v3)
## [1] TRUE
# entao posso criar uma matriz juntando esses vetores em linhas ou colunas
mml <- rbind(v1, v2, v3)
class(mml) # criou um matrix
## [1] "matrix" "array"
mml
v1 1 2 3 4 5 6 7 8 9 10
v2 10 9 8 7 6 5 4 3 2 1
v3 11 12 13 14 15 16 17 18 19 20
# ou
mmc <- cbind(v1, v2, v3)
class(mmc)
## [1] "matrix" "array"
# 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
## [1] TRUE
mml <- rbind(mml, novovetor) # junto a matrix existente com o novo vetor, adicionando uma nova linha
mml
v1 1 2 3 4 5 6 7 8 9 10
v2 10 9 8 7 6 5 4 3 2 1
v3 11 12 13 14 15 16 17 18 19 20
novovetor 31 32 33 34 35 36 37 38 39 40
# 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
## [1] TRUE
mmc <- cbind(mmc, novovetor) # junto a matrix existente com o novo vetor, adicionando uma nova coluna
mmc
v1 v2 v3 novovetor
1 10 11 31
2 9 12 32
3 8 13 33
4 7 14 34
5 6 15 35
6 5 16 36
7 4 17 37
8 3 18 38
9 2 19 39
10 1 20 40
# note que a nova coluna recebeu como nome o nome do objeto que continha o dado

3.1.2 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().

?data.frame # veja o help das funções acima
# 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
# 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
## [1] "data.frame"
dim(dd) # dimensoes, linhas e colunas
## [1] 10  3
ncol(dd) # numero de colunas
## [1] 3
nrow(dd) # numero de linhas
## [1] 10
str(dd) # estrutura do objeto (veja as classes das colunas)
## 'data.frame':    10 obs. of  3 variables:
##  $ v1: int  1 2 3 4 5 6 7 8 9 10
##  $ v2: Factor w/ 10 levels "A","B","C","D",..: 1 2 3 4 5 6 7 8 9 10
##  $ v3: Factor w/ 4 levels "fulano","joaquim",..: 1 3 2 4 1 3 2 4 1 3
# JUNTAMOS SEM FATORES
dd2 <- data.frame(v1, v2, v3, stringsAsFactors = FALSE)
class(dd2) # é um data frame
## [1] "data.frame"
str(dd2) # estrutura do objeto (veja as classes das colunas)
## 'data.frame':    10 obs. of  3 variables:
##  $ v1: int  1 2 3 4 5 6 7 8 9 10
##  $ v2: chr  "A" "B" "C" "D" ...
##  $ v3: chr  "fulano" "jose" "joaquim" "martin" ...
# juntamos com nome de colunas (tag = vetor)
dd2 <- data.frame(RegistorID = v1, CodigoZ = v2, Pessoa = v3, stringsAsFactors = FALSE)
dd2
RegistorID CodigoZ Pessoa
1 A fulano
2 B jose
3 C joaquim
4 D martin
5 E fulano
6 F jose
7 G joaquim
8 H martin
9 I fulano
10 J jose
# 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
## [1] "matrix" "array"
str(dz) # todos os dados são da mesma classe (texto)
##  chr [1:10, 1:3] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "A" "B" "C" "D" ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:3] "RegistorID" "CodigoZ" "Pessoa"
dz <- as.data.frame(dz) # convertemos num data.frame
class(dz) # é um data.frame
## [1] "data.frame"
str(dz) # converte numeros para numerico e texto para fator
## 'data.frame':    10 obs. of  3 variables:
##  $ RegistorID: chr  "1" "2" "3" "4" ...
##  $ CodigoZ   : chr  "A" "B" "C" "D" ...
##  $ Pessoa    : chr  "fulano" "jose" "joaquim" "martin" ...
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
## 'data.frame':    10 obs. of  3 variables:
##  $ RegistorID: chr  "1" "2" "3" "4" ...
##  $ CodigoZ   : chr  "A" "B" "C" "D" ...
##  $ Pessoa    : chr  "fulano" "jose" "joaquim" "martin" ...

3.1.3 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:

?head
?tail
# 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
RegistorID CodigoZ Pessoa
1 A fulano
2 B jose
3 C joaquim
4 D martin
5 E fulano
6 F jose
head(dd2, 3) # três primeiras linhas
RegistorID CodigoZ Pessoa
1 A fulano
2 B jose
3 C joaquim
# rodapé
tail(dd2) # seis últimas linhas
RegistorID CodigoZ Pessoa
5 5 E fulano
6 6 F jose
7 7 G joaquim
8 8 H martin
9 9 I fulano
10 10 J jose
tail(dd2, 3) # três últimas linhas
RegistorID CodigoZ Pessoa
8 8 H martin
9 9 I fulano
10 10 J jose

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.

dim(dd2) # vetor com dois valores, número de linhas e número de colunas
## [1] 10  3
nrow(dd2) # número de linhas do data.frame ou matrix
## [1] 10
ncol(dd2) # número de colunas do data.frame ou matrix
## [1] 3
nrow(as.matrix(dd2))
## [1] 10
ncol(as.matrix(dd2))
## [1] 3

As funções str() e summary() informam a estrutura dos data.frames e o resumo dos dados, respectivamente.

str(dd2) # mostra a estrutura do objeto, quais colunas, classes de colunas e total de valores
## 'data.frame':    10 obs. of  3 variables:
##  $ RegistorID: int  1 2 3 4 5 6 7 8 9 10
##  $ CodigoZ   : Factor w/ 10 levels "A","B","C","D",..: 1 2 3 4 5 6 7 8 9 10
##  $ Pessoa    : Factor w/ 4 levels "fulano","joaquim",..: 1 3 2 4 1 3 2 4 1 3
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.
RegistorID CodigoZ Pessoa
Min. : 1.00 A :1 fulano :3
1st Qu.: 3.25 B :1 joaquim:2
Median : 5.50 C :1 jose :3
Mean : 5.50 D :1 martin :2
3rd Qu.: 7.75 E :1 NA
Max. :10.00 F :1 NA
NA (Other):4 NA

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.

# 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)

# para os objetos com nomes podemos ver os nomes
rownames(mm)
## [1] "linha1" "linha2" "linha3"
rownames(dd)
## [1] "linha1" "linha2" "linha3"
colnames(mm)
## [1] "coluna1" "coluna2" "coluna3"
colnames(dd)
## [1] "coluna1" "coluna2" "coluna3"
# para os objetos sem nomes
rownames(mm2) # nulo, não tem nome
## NULL
rownames(dd2) # números em formato de texto
## [1] "1" "2" "3"
colnames(mm2) # nulo, não tem nome
## NULL
colnames(dd2) # V1 a Vncol(dd) - ele cria nomes das colunas
## [1] "V1" "V2" "V3"
# 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


# 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
## [1] "coluna1" "coluna2" "coluna3"
colnames(mm) <- c("novonome1", "novonome2", "novonome3")
mm # veja como o nome das colunas mudou
novonome1 novonome2 novonome3
linha1 1 4 7
linha2 2 5 8
linha3 3 6 9
# mudando apenas o nome da coluna2
colnames(mm)[2] <- "colunaDOIS"
colnames(mm) # nomes atuais
## [1] "novonome1"  "colunaDOIS" "novonome3"
# atribuindo quando não tem nome
colnames(mm2) # está vazio ou não existe (NULL)
## NULL
colnames(mm2) <- paste("banana", 1:ncol(mm2), sep = "-")
mm2 # agora tem nome de coluna
banana-1 banana-2 banana-3
1 4 7
2 5 8
3 6 9
rownames(mm2) # nomes de linhas também está vazio
## NULL
rownames(mm2) <- paste("chuchu", 1:nrow(mm2), sep = ".")
mm2 # agora tem nomes de linha e coluna
banana-1 banana-2 banana-3
chuchu.1 1 4 7
chuchu.2 2 5 8
chuchu.3 3 6 9

Vamos tentar atribuir um mesmo nome de linha teste1 a duas linhas de nossa matriz mm2 e ver o que acontece:

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:

rownames(dd)[1:2] <- "teste1" # nao funciona, porque ele não aceita nomes repetidos de linhas em DATA.FRAMES

3.2 Indexação de matriz e data.frame

Entender indexação é fundamental para manipular dados no R. Em indexação de vetores (seção 2.5), 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.

3.2.1 Matrizes

# 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
coluna1 coluna2 coluna3
linha1 1 4 7
linha2 2 5 8
linha3 3 6 9
# USANDO INDICE NUMÉRICO
mm[1, 2] # mostra o elemento da linha 1 e coluna 2
## [1] 4
mm[1, ncol(mm)] # mostra o elemento da linha 1 e última coluna
## [1] 7
mm[nrow(mm), ncol(mm)] # mostra o elemento da última linha e última coluna
## [1] 9
mm[, 1] # mostra a coluna 1
## linha1 linha2 linha3 
##      1      2      3
# eu posso juntar indices de matrizes e vetores na mesma linha
mm[, 1][2] # mostra o segundo elemento do vetor correspondente a primeira coluna
## linha2 
##      2
mm[1, ] # mostra a linha 1
## coluna1 coluna2 coluna3 
##       1       4       7
mm[nrow(mm), ] # mostra a ultima linha
## coluna1 coluna2 coluna3 
##       3       6       9
mm[, 1:2] # mostra as duas primeiras colunas
coluna1 coluna2
linha1 1 4
linha2 2 5
linha3 3 6
mm[1:2, 1:3] # mostra as duas primeiras linhas e duas primeiras colunas
coluna1 coluna2 coluna3
linha1 1 4 7
linha2 2 5 8
mm[3:nrow(mm), ] # mostra da linha tres a ultima linha
## coluna1 coluna2 coluna3 
##       3       6       9
mm[c(3, 1), c(3, 2)] # mostra as linhas 3 e 1 e colunas 3 e 2 (nessa ordem)
coluna3 coluna2
linha3 9 6
linha1 7 4
# 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"
## coluna1 coluna2 coluna3 
##       1       4       7
mm[, "coluna1"] # mostra a coluna 1
## linha1 linha2 linha3 
##      1      2      3
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
coluna3 coluna1
linha3 9 3
linha1 7 1
# SE EU POSSO VER EU POSSO MUDAR
mm
coluna1 coluna2 coluna3
linha1 1 4 7
linha2 2 5 8
linha3 3 6 9
mm[1, 3] # elemento da linha 1 coluna 3
## [1] 7
mm[1, 3] <- 33 # mudei o elemento
mm[2, 2:3]
## coluna2 coluna3 
##       5       8
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]
## coluna2 coluna3 
##      50      80
mm
coluna1 coluna2 coluna3
linha1 1 4 33
linha2 2 50 80
linha3 3 6 9

3.2.2 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.

# 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
coluna1 coluna2 coluna3
linha1 1 4 7
linha2 2 5 8
linha3 3 6 9
# 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")
## [1] 1 2 3

Veja que o uso do operador $ não funciona em matrizes:

mm$coluna1 # veja como não funciona para o objeto matrix
dd$coluna1[2] # vejo o segundo elemento da coluna1
## [1] 2
# isso é o mesmo que
dd[2, "coluna1"]
## [1] 2
# se eu vejo eu posso mudar
dd[2, "coluna1"] <- 10
dd$coluna1[3] <- 20
dd$coluna3 # pego a coluna tres
## [1] 7 8 9
# também posso adicionar uma nova coluna
dd$novacoluna <- LETTERS[1:nrow(dd)]
dd # agora tenho uma nova coluna
coluna1 coluna2 coluna3 novacoluna
linha1 1 4 7 A
linha2 10 5 8 B
linha3 20 6 9 C
# ou poderia usar outra forma
dd[, "nova2"] <- LETTERS # nao vai funcionar por estou atribuindo um vetor muito mais longo do que tenho linhas
length(LETTERS) > nrow(dd) # essa expressão é verdadeira
## [1] TRUE
dd[, "nova2"] <- LETTERS[1:nrow(dd)] # isso tem o mesmo comprimento e funciona
dd
coluna1 coluna2 coluna3 novacoluna nova2
linha1 1 4 7 A A
linha2 10 5 8 B B
linha3 20 6 9 C C
# posso adicionar uma coluna vazia
dd$outracoluna <- NA
dd
coluna1 coluna2 coluna3 novacoluna nova2 outracoluna
linha1 1 4 7 A A NA
linha2 10 5 8 B B NA
linha3 20 6 9 C C NA
# e ainda outra (lógica)
dd$maisuma <- TRUE
dd
coluna1 coluna2 coluna3 novacoluna nova2 outracoluna maisuma
linha1 1 4 7 A A NA TRUE
linha2 10 5 8 B B NA TRUE
linha3 20 6 9 C C NA TRUE

Adicionar colunas em uma matriz é um pouco diferente do que se faz com um data.frame:

# primeiro nao posso usar $ porque matrix não entende isso
class(mm) # é uma matrix
mm$colun3 # isso nao funciona
mm[, "coluna3"] # isso funciona
## linha1 linha2 linha3 
##      7      8      9
# adicionando uma coluna
mm[, 4] # isso nao existe
mm[, 4] <- log(mm[, "coluna3"]) # isso não funciona
# poderia usar a função cbind que vimos anteriormente
mm <- cbind(mm, LOGCOLUNA3 = log(mm[, "coluna3"])) # assim eu posso

3.3 Filtrando e ordenando matrizes e data.frames

3.3.1 Filtragem de dados

Já vimos como fazer perguntas sobre vetores (Seção 2.6.1) 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.

?iris # veja o help do R sobre Edgar Anderson's Iris Data que explica esses dados que vem com o R
class(iris)
## [1] "data.frame"
str(iris) # estrutura, veja as colunas
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
# vamos filtrar os dados de uma das especies
unique(iris$Species) # vemos os valores únicos
## [1] setosa     versicolor virginica 
## Levels: setosa versicolor virginica
# ou, tendo em vista que é um fator
levels(iris$Species)
## [1] "setosa"     "versicolor" "virginica"
sp1 <- levels(iris$Species)[1]
# quais linhas correspondem a essa especie
vl <- iris$Species == sp1
sum(vl) # numero de linhas que satisfazem a pergunta
## [1] 50
nrow(iris) # numero total de linhas no data.frame
## [1] 150
# 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
## [1] TRUE
# filtrar segundo duas colunas
vl <- iris$Species == sp1 # seja da especie em sp1
sum(vl) # quantas sao?
## [1] 50
vl2 <- iris$Sepal.Length <= 5 # tenha sepala menor ou igual a 5
sum(vl2) # quantas sao?
## [1] 32
# combinando as duas perguntas
vll <- vl & vl2
sum(vll) # quantas sao?
## [1] 28
# filtrando
ff <- iris[vll, ]
class(ff) # novo data.frame resultando do filtro realizado
## [1] "data.frame"
nrow(ff) == sum(vll) # isso deve ser verdadeiro
## [1] TRUE

3.3.1.1 Dados com valores ausentes

As funçõesis.na() e na.omit() vistas anteriormente (Seção 2.6.3) permitem eliminar linhas e colunas que tenham valores ausentes. A presença de valores às vezes impede certas análises de serem executadas.

# 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
## [1] 0
# qual a dimensão?
dim(dd)
## [1] 150   5
# 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
## [1] 20
# 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, ]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
18 5.1 3.5 NA 0.3 setosa
19 5.7 3.8 NA 0.3 setosa
22 5.1 NA 1.5 0.4 setosa
28 5.2 NA 1.5 0.2 setosa
34 5.5 4.2 NA 0.2 setosa
42 4.5 NA 1.3 0.3 setosa
50 5.0 3.3 NA 0.2 setosa
55 6.5 NA 4.6 1.5 versicolor
60 5.2 2.7 NA 1.4 versicolor
71 5.9 3.2 NA 1.8 versicolor
73 6.3 2.5 NA 1.5 versicolor
78 6.7 3.0 NA 1.7 versicolor
90 5.5 NA 4.0 1.3 versicolor
103 7.1 3.0 NA 2.1 virginica
105 6.5 NA 5.8 2.2 virginica
121 6.9 NA 5.7 2.3 virginica
127 6.2 NA 4.8 1.8 virginica
129 6.4 2.8 NA 2.1 virginica
147 6.3 NA 5.0 1.9 virginica
150 5.9 NA 5.1 1.8 virginica
# use na.omit() para eliminar todas as linhas que NA em alguma coluna
sum(is.na(dd)) # tem 20 valores
## [1] 20
dd2 <- na.omit(dd)
sum(is.na(dd2)) # nao tem mais nenhum
## [1] 0

3.3.2 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.

?sort
?order
# ordenação
str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
# 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
##   [1] 4.3 4.4 4.4 4.4 4.5 4.6 4.6 4.6 4.6 4.7 4.7 4.8 4.8 4.8 4.8 4.8 4.9 4.9
##  [19] 4.9 4.9 4.9 4.9 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.1 5.1 5.1 5.1
##  [37] 5.1 5.1 5.1 5.1 5.1 5.2 5.2 5.2 5.2 5.3 5.4 5.4 5.4 5.4 5.4 5.4 5.5 5.5
##  [55] 5.5 5.5 5.5 5.5 5.5 5.6 5.6 5.6 5.6 5.6 5.6 5.7 5.7 5.7 5.7 5.7 5.7 5.7
##  [73] 5.7 5.8 5.8 5.8 5.8 5.8 5.8 5.8 5.9 5.9 5.9 6.0 6.0 6.0 6.0 6.0 6.0 6.1
##  [91] 6.1 6.1 6.1 6.1 6.1 6.2 6.2 6.2 6.2 6.3 6.3 6.3 6.3 6.3 6.3 6.3 6.3 6.3
## [109] 6.4 6.4 6.4 6.4 6.4 6.4 6.4 6.5 6.5 6.5 6.5 6.5 6.6 6.6 6.7 6.7 6.7 6.7
## [127] 6.7 6.7 6.7 6.7 6.8 6.8 6.8 6.9 6.9 6.9 6.9 7.0 7.1 7.2 7.2 7.2 7.3 7.4
## [145] 7.6 7.7 7.7 7.7 7.7 7.9
# em ordem decrescente
o2 <- sort(iris$Sepal.Length, decreasing = T)
o2 # sao valores de sepalas do maior para o menor
##   [1] 7.9 7.7 7.7 7.7 7.7 7.6 7.4 7.3 7.2 7.2 7.2 7.1 7.0 6.9 6.9 6.9 6.9 6.8
##  [19] 6.8 6.8 6.7 6.7 6.7 6.7 6.7 6.7 6.7 6.7 6.6 6.6 6.5 6.5 6.5 6.5 6.5 6.4
##  [37] 6.4 6.4 6.4 6.4 6.4 6.4 6.3 6.3 6.3 6.3 6.3 6.3 6.3 6.3 6.3 6.2 6.2 6.2
##  [55] 6.2 6.1 6.1 6.1 6.1 6.1 6.1 6.0 6.0 6.0 6.0 6.0 6.0 5.9 5.9 5.9 5.8 5.8
##  [73] 5.8 5.8 5.8 5.8 5.8 5.7 5.7 5.7 5.7 5.7 5.7 5.7 5.7 5.6 5.6 5.6 5.6 5.6
##  [91] 5.6 5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.4 5.4 5.4 5.4 5.4 5.4 5.3 5.2 5.2 5.2
## [109] 5.2 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0
## [127] 5.0 5.0 4.9 4.9 4.9 4.9 4.9 4.9 4.8 4.8 4.8 4.8 4.8 4.7 4.7 4.6 4.6 4.6
## [145] 4.6 4.5 4.4 4.4 4.4 4.3
# 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
##   [1]  14   9  39  43  42   4   7  23  48   3  30  12  13  25  31  46   2  10
##  [19]  35  38  58 107   5   8  26  27  36  41  44  50  61  94   1  18  20  22
##  [37]  24  40  45  47  99  28  29  33  60  49   6  11  17  21  32  85  34  37
##  [55]  54  81  82  90  91  65  67  70  89  95 122  16  19  56  80  96  97 100
##  [73] 114  15  68  83  93 102 115 143  62  71 150  63  79  84  86 120 139  64
##  [91]  72  74  92 128 135  69  98 127 149  57  73  88 101 104 124 134 137 147
## [109]  52  75 112 116 129 133 138  55 105 111 117 148  59  76  66  78  87 109
## [127] 125 141 145 146  77 113 144  53 121 140 142  51 103 110 126 130 108 131
## [145] 106 118 119 123 136 132
# entao para ver os valores ordenados
iris$Sepal.Length[o3]
##   [1] 4.3 4.4 4.4 4.4 4.5 4.6 4.6 4.6 4.6 4.7 4.7 4.8 4.8 4.8 4.8 4.8 4.9 4.9
##  [19] 4.9 4.9 4.9 4.9 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.1 5.1 5.1 5.1
##  [37] 5.1 5.1 5.1 5.1 5.1 5.2 5.2 5.2 5.2 5.3 5.4 5.4 5.4 5.4 5.4 5.4 5.5 5.5
##  [55] 5.5 5.5 5.5 5.5 5.5 5.6 5.6 5.6 5.6 5.6 5.6 5.7 5.7 5.7 5.7 5.7 5.7 5.7
##  [73] 5.7 5.8 5.8 5.8 5.8 5.8 5.8 5.8 5.9 5.9 5.9 6.0 6.0 6.0 6.0 6.0 6.0 6.1
##  [91] 6.1 6.1 6.1 6.1 6.1 6.2 6.2 6.2 6.2 6.3 6.3 6.3 6.3 6.3 6.3 6.3 6.3 6.3
## [109] 6.4 6.4 6.4 6.4 6.4 6.4 6.4 6.5 6.5 6.5 6.5 6.5 6.6 6.6 6.7 6.7 6.7 6.7
## [127] 6.7 6.7 6.7 6.7 6.8 6.8 6.8 6.9 6.9 6.9 6.9 7.0 7.1 7.2 7.2 7.2 7.3 7.4
## [145] 7.6 7.7 7.7 7.7 7.7 7.9
# então isso deve ser totalmente verdadeiro:
iris$Sepal.Length[o3] == sort(iris$Sepal.Length) # as comparações para a par são identicas
##   [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [16] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [31] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [46] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [61] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [76] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [91] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [106] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [121] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [136] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
# então esta expressão também é verdadeira:
sum(iris$Sepal.Length[o3] == sort(iris$Sepal.Length)) == nrow(iris)
## [1] TRUE
# 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.
## [1] FALSE
# 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
# 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) # é FALSO
## [1] TRUE
# 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")]
Species Sepal.Length
132 virginica 7.9
118 virginica 7.7
119 virginica 7.7
123 virginica 7.7
136 virginica 7.7
106 virginica 7.6
131 virginica 7.4
108 virginica 7.3
110 virginica 7.2
126 virginica 7.2
130 virginica 7.2
103 virginica 7.1
121 virginica 6.9
140 virginica 6.9
142 virginica 6.9
113 virginica 6.8
144 virginica 6.8
109 virginica 6.7
125 virginica 6.7
141 virginica 6.7
145 virginica 6.7
146 virginica 6.7
105 virginica 6.5
111 virginica 6.5
117 virginica 6.5
148 virginica 6.5
112 virginica 6.4
116 virginica 6.4
129 virginica 6.4
133 virginica 6.4
138 virginica 6.4
101 virginica 6.3
104 virginica 6.3
124 virginica 6.3
134 virginica 6.3
137 virginica 6.3
147 virginica 6.3
127 virginica 6.2
149 virginica 6.2
128 virginica 6.1
135 virginica 6.1
120 virginica 6.0
139 virginica 6.0
150 virginica 5.9
102 virginica 5.8
115 virginica 5.8
143 virginica 5.8
114 virginica 5.7
122 virginica 5.6
107 virginica 4.9
51 versicolor 7.0
53 versicolor 6.9
77 versicolor 6.8
66 versicolor 6.7
78 versicolor 6.7
87 versicolor 6.7
59 versicolor 6.6
76 versicolor 6.6
55 versicolor 6.5
52 versicolor 6.4
75 versicolor 6.4
57 versicolor 6.3
73 versicolor 6.3
88 versicolor 6.3
69 versicolor 6.2
98 versicolor 6.2
64 versicolor 6.1
72 versicolor 6.1
74 versicolor 6.1
92 versicolor 6.1
63 versicolor 6.0
79 versicolor 6.0
84 versicolor 6.0
86 versicolor 6.0
62 versicolor 5.9
71 versicolor 5.9
68 versicolor 5.8
83 versicolor 5.8
93 versicolor 5.8
56 versicolor 5.7
80 versicolor 5.7
96 versicolor 5.7
97 versicolor 5.7
100 versicolor 5.7
65 versicolor 5.6
67 versicolor 5.6
70 versicolor 5.6
89 versicolor 5.6
95 versicolor 5.6
54 versicolor 5.5
81 versicolor 5.5
82 versicolor 5.5
90 versicolor 5.5
91 versicolor 5.5
85 versicolor 5.4
60 versicolor 5.2
99 versicolor 5.1
61 versicolor 5.0
94 versicolor 5.0
58 versicolor 4.9
15 setosa 5.8
16 setosa 5.7
19 setosa 5.7
34 setosa 5.5
37 setosa 5.5
6 setosa 5.4
11 setosa 5.4
17 setosa 5.4
21 setosa 5.4
32 setosa 5.4
49 setosa 5.3
28 setosa 5.2
29 setosa 5.2
33 setosa 5.2
1 setosa 5.1
18 setosa 5.1
20 setosa 5.1
22 setosa 5.1
24 setosa 5.1
40 setosa 5.1
45 setosa 5.1
47 setosa 5.1
5 setosa 5.0
8 setosa 5.0
26 setosa 5.0
27 setosa 5.0
36 setosa 5.0
41 setosa 5.0
44 setosa 5.0
50 setosa 5.0
2 setosa 4.9
10 setosa 4.9
35 setosa 4.9
38 setosa 4.9
12 setosa 4.8
13 setosa 4.8
25 setosa 4.8
31 setosa 4.8
46 setosa 4.8
3 setosa 4.7
30 setosa 4.7
4 setosa 4.6
7 setosa 4.6
23 setosa 4.6
48 setosa 4.6
42 setosa 4.5
9 setosa 4.4
39 setosa 4.4
43 setosa 4.4
14 setosa 4.3
# para cada especie esta ordenado por sepala:
novo.iris[novo.iris$Species == "versicolor", ]$Sepal.Length
##  [1] 7.0 6.9 6.8 6.7 6.7 6.7 6.6 6.6 6.5 6.4 6.4 6.3 6.3 6.3 6.2 6.2 6.1 6.1 6.1
## [20] 6.1 6.0 6.0 6.0 6.0 5.9 5.9 5.8 5.8 5.8 5.7 5.7 5.7 5.7 5.7 5.6 5.6 5.6 5.6
## [39] 5.6 5.5 5.5 5.5 5.5 5.5 5.4 5.2 5.1 5.0 5.0 4.9
novo.iris[novo.iris$Species == "virginica", ]$Sepal.Length
##  [1] 7.9 7.7 7.7 7.7 7.7 7.6 7.4 7.3 7.2 7.2 7.2 7.1 6.9 6.9 6.9 6.8 6.8 6.7 6.7
## [20] 6.7 6.7 6.7 6.5 6.5 6.5 6.5 6.4 6.4 6.4 6.4 6.4 6.3 6.3 6.3 6.3 6.3 6.3 6.2
## [39] 6.2 6.1 6.1 6.0 6.0 5.9 5.8 5.8 5.8 5.7 5.6 4.9

3.4 Importando e exportando dados no R

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 (em inglês, “character encoding”).

3.4.1 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 = ’‘’), ,, 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 ,.

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, anto para ler como para salvar arquivos de planilhas.

3.4.2 Importando dados

3.4.2.1 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 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 texto2. 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.

# se você colocou o arquivo na sua pasta de trabalho, ele deve estar visível por
dir(pattern = "csv")
# 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.
# 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:

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
# 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/LABOTAM.github.io/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.

3.4.2.2 Pacote readr

A principal função para ler arquivos do pacote readr (Wickham e Hester 2020) 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).

# 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:

rr2 <- read_delim("https://github.com/LABOTAM/LABOTAM.github.io/blob/main/dados/municipiosbrasil.csv", delim = "\t")
rr2

3.4.2.3 Pacote data.table

A principal função para ler arquivos do pacote data.table (Dowle e Srinivasan 2020) 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).

# 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:

dt2 <- fread("https://github.com/LABOTAM/LABOTAM.github.io/blob/main/dados/municipiosbrasil.csv")
dt2

3.4.2.4 Importando do Excel diretamente

Utilizamos o pacote readxl(Wickham e Bryan 2019) 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.

# 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

3.4.3 Exportando dados

3.4.3.1 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().

?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:

# 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

3.4.3.2 Pacote readr

A principal função do pacote readrpara 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.

# 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 = ";")

3.4.3.3 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.

# exportando dados com pacote data.table
# utilizando mesmo objeto criado com pacote base
fwrite(dd.am, "muni-am8.csv", sep = "\t")

3.4.4 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:

# 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")
class(dd)
## [1] "character"
length(dd) # cada linha é um elemento do vetor
## [1] 5568
dd[1]
## [1] "Country\tProvince\tRegiao\tMunicipio\tLatitude\tLongitude"
# usando tabulação
dd2 <- scan(file = "municipiosbrasil.csv", what = "complex", sep = "\t")
class(dd2)
## [1] "character"
length(dd2) # cada célula é um elemento deste vetor
## [1] 33408
dd2[1:5]
## [1] "Country"   "Province"  "Regiao"    "Municipio" "Latitude"
# 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.

3.5 Para saber mais:

3.6 Exercícios

Referências

Dowle, Matt, e Arun Srinivasan. 2020. data.table: Extension of ‘data.frame‘. https://CRAN.R-project.org/package=data.table.

Wickham, Hadley, e Jennifer Bryan. 2019. readxl: Read Excel Files. https://CRAN.R-project.org/package=readxl.

Wickham, Hadley, e Jim Hester. 2020. readr: Read Rectangular Text Data. https://CRAN.R-project.org/package=readr.


  1. Isso é mais fácil de ser feito no LibreOffice/OpenOffice↩︎