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)
dd2
V1 V2 V3
1 4 7
2 5 8
3 6 9
# 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