Capítulo 7 Manipulação e formatação de dados

Após importar uma basde de dados para o R, os principais pacotes para manipulação e formatação de data frames são o dplyr e o tidyr. As funções destes pacotes oferecem uma etapa inicial na análise, modelagem e comunicação de dados. Lembramos aqui que uma base de dados é organizada em um formato de tabela em que as linhas são as observações e as colunas são as variáveis que descrevem estas observações. Estas variáveis podem ser quantitativas (contínuas ou discretas) ou qualitativas (ordenadas ou não).

7.1 Transformação de dados: os pacotes dplyr e tidyr

Aqui veremos funções principais para manipular as observações nas linhas (Manipulate Cases), manipular descritores nas colunas (Manipulate Variables) e combinar tabelas (Combine Tables). Na Cheatsheets do dplyr você verá também a variedade de funções relacionadas ao cáculo sobre vetores (Vector Functions), funções resumo (Summary Functions, e Summarise Cases) e manipulação de nomes das linhas.

7.1.1 Ordenando as linhas: funções arrange() e desc()

Estas funções permitem que você ordene a base de dados seguindo os valores de alguma de suas colunas. Vamos utilizar como exemplo a base de dados iris comumente utilizadas em uma variedade de tutorias sobre ciência de dados. Carrege e verifique a base de dados composta por 150 linhas e 5 colunas.

data("iris")
head(iris, 10)

Vamos ordenar a tabela pela coluna Sepal.Length.

iris %>% 
  arrange(Sepal.Length)
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1           4.3         3.0          1.1         0.1  setosa
## 2           4.4         2.9          1.4         0.2  setosa
## 3           4.4         3.0          1.3         0.2  setosa
## 4           4.4         3.2          1.3         0.2  setosa
## 5           4.5         2.3          1.3         0.3  setosa
## 6           4.6         3.1          1.5         0.2  setosa
## 7           4.6         3.4          1.4         0.3  setosa
## 8           4.6         3.6          1.0         0.2  setosa
## 9           4.6         3.2          1.4         0.2  setosa
## 10          4.7         3.2          1.3         0.2  setosa

As linhas aparecem agora de acordo com os valores (em ordem crescente) de Sepal.Length, iniciando em 4.3.

Em seguida faça o mesmo em ordem decresente.

iris %>% 
  arrange(desc(Sepal.Length))

Podemos combinar duas colunas, ordenando a tabela em função da coluna Species (em ordem alfabética decrescente) e em função de Sepal.Length (em ordem crescente).

iris %>% 
  arrange(desc(Species), Sepal.Length)

Nos exemplos acimas, somente visualizamos a tabela em diferentes ordens. Se quisermos criar um novo objeto com a tabela em alguma destas sequências fazemos:

iris_ordenado <- iris %>% 
  arrange(Sepal.Length)

iris_ordenado
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          4.3         3.0          1.1         0.1  setosa
## 2          4.4         2.9          1.4         0.2  setosa
## 3          4.4         3.0          1.3         0.2  setosa
## 4          4.4         3.2          1.3         0.2  setosa
## 5          4.5         2.3          1.3         0.3  setosa
## 6          4.6         3.1          1.5         0.2  setosa

7.1.2 Filtrando linhas: função filter()

Esta função permite extrair somente as linhas de uma tabela que satisfaçam uma condição lógica. Vamos extrair as linhas referentes à espécie virginica.

iris %>% 
  filter(Species == "virginica")

Ou às espécies diferentes de virginica.

iris %>% 
  filter(Species != "virginica")

Agora, vamos filtrar as linhas em que o comprimento das pétalas seja menor que 1.3.

iris %>% 
  filter(Petal.Length < 1.3)

E para o comprimento das pétalas seja menor que 1.3 E o comprimento das sépalas maior ou igual a 5.

iris %>% 
  filter(Petal.Length < 1.3 & Sepal.Length >= 5)

7.1.3 Selecionando colunas: função select()

A função select permite extrair ou reorganizar um subconjunto colunas de um data frame. Rode os exemplos a seguir:

iris %>% 
  select(Petal.Length)
iris %>% 
  select(Petal.Length, Species)
iris %>% 
  select(Petal.Length:Species)
iris %>% 
  select(Species:Petal.Length)
iris %>% 
  select(!c(Petal.Length, Species))
iris %>% 
  select(starts_with("Sepal"))

Finalmente, combine as funções filter() e select() para extrair um subconjunto do data frame:

iris %>% 
  select(starts_with("Sepal")) %>% 
  filter(Sepal.Length <= 4.5)

7.1.4 Agrupando tabelas: funções do grupo join

Se você tem alguma experiência em linguagem SQL para banco de dados, irá compreender facilmente o uso do grupo de funções join. Veremos aqui as funções left_join(), right_join(), inner_join() e anti_join().

Considere as duas tabelas abaixo que podem ser acessadas em Regiao.csv e Habitat.csv

## # A tibble: 10 x 4
##    Riacho Bacia      Município      Área
##    <chr>  <chr>      <chr>         <dbl>
##  1 R1     Boicucanga São Sebastião  30.3
##  2 R4     Boicucanga São Sebastião  30.3
##  3 R8     Boicucanga São Sebastião  30.3
##  4 R2     Cubatão    Cubatão       189  
##  5 R5     Cubatão    Cubatão       189  
##  6 R10    Cubatão    Cubatão       189  
##  7 R13    Cubatão    Cubatão       189  
##  8 R6     Quilombo   Santos         86  
##  9 R9     Quilombo   Santos         86  
## 10 R7     Quilombo   Santos         86
## # A tibble: 8 x 4
##   Riacho Altitude Largura Profundidade
##   <chr>     <dbl>   <dbl>        <dbl>
## 1 R1           74     7.8         20.2
## 2 R4           14    10.9         17.7
## 3 R8          245     8.3         19.5
## 4 R11         241     2.2         20.3
## 5 R2           29     1.6         11.8
## 6 R6           86    15.2         35.3
## 7 R9           77     4.1         18.9
## 8 R7           63    14.2         42.1
regiao <- read_csv("C:/seu_caminho/IntroR/Regiao.csv")
regiao
habitat <- read_csv("C:/seu_caminho/IntroR/Habitat.csv")
habitat

A tabela regiao, contém informações sobre alguns o bacia hidrográfica, á área da bacia e o múnicípio de alguns riachos da região litorânea do estado de São Paulo. A tabela trecho, contém informações sobre a largura e profundidades destes riachos. Alguns riachos são coumuns às duas tabelas, enquanto outros ocorrem somente em uma ou em outra tabela. Sabendo que a coluna Riacho (R1, R2, R3,….R13) se referem ao mesmo ponto de amostragem, podemos utilizá-la para combinar as informações das duas tabelas.

7.1.4.1 Função left_join()

Retorna todas as linhas da tabela à esquerda as as colunas das duas tabelas. Linhas sem correspondência na tabela da direita terão valores de NA. Se houver várias correspondências, todas as combinações serão retornadas.

regiao %>% left_join(y = habitat)
## # A tibble: 10 x 7
##    Riacho Bacia      Município      Área Altitude Largura Profundidade
##    <chr>  <chr>      <chr>         <dbl>    <dbl>   <dbl>        <dbl>
##  1 R1     Boicucanga São Sebastião  30.3       74     7.8         20.2
##  2 R4     Boicucanga São Sebastião  30.3       14    10.9         17.7
##  3 R8     Boicucanga São Sebastião  30.3      245     8.3         19.5
##  4 R2     Cubatão    Cubatão       189         29     1.6         11.8
##  5 R5     Cubatão    Cubatão       189         NA    NA           NA  
##  6 R10    Cubatão    Cubatão       189         NA    NA           NA  
##  7 R13    Cubatão    Cubatão       189         NA    NA           NA  
##  8 R6     Quilombo   Santos         86         86    15.2         35.3
##  9 R9     Quilombo   Santos         86         77     4.1         18.9
## 10 R7     Quilombo   Santos         86         63    14.2         42.1

Veja que todas as linhas da tabela regiao estão presentes. Foram adicionadas a elas as informações de Altitude, Largura e Profundidade, somente para os riachos que também estavam presentes na tabela habitat.

7.1.4.2 Função right_join()

Retorna todas as linhas da tabela à direita as as colunas das duas tabelas. Linhas sem correspondência na tabela da esquerda terão valores de NA. Se houver várias correspondências, todas as combinações serão retornadas.

regiao %>% right_join(y = habitat)
## # A tibble: 8 x 7
##   Riacho Bacia      Município      Área Altitude Largura Profundidade
##   <chr>  <chr>      <chr>         <dbl>    <dbl>   <dbl>        <dbl>
## 1 R1     Boicucanga São Sebastião  30.3       74     7.8         20.2
## 2 R4     Boicucanga São Sebastião  30.3       14    10.9         17.7
## 3 R8     Boicucanga São Sebastião  30.3      245     8.3         19.5
## 4 R2     Cubatão    Cubatão       189         29     1.6         11.8
## 5 R6     Quilombo   Santos         86         86    15.2         35.3
## 6 R9     Quilombo   Santos         86         77     4.1         18.9
## 7 R7     Quilombo   Santos         86         63    14.2         42.1
## 8 R11    <NA>       <NA>           NA        241     2.2         20.3

Note que a coluna de R11 contém NA nas colunas Bacia e Município, pois este riacho não está presente na tabela regiao.

7.1.4.3 Função inner_join()

Resgata apenas as linhas coincidantes a ambas as tabelas.

regiao %>% inner_join(y = habitat)

7.1.4.4 Função anti_join()

Retorna todas as linhas da tabela à esquerda em que existem correspondências com a de direita. Retorna todas as colunas de ambas. Se houver várias correspondências, todas as combinações serão retornadas. Testes os comandos abaixo.

regiao %>% anti_join(y = habitat)
habitat %>% anti_join(y = regiao)

7.1.4.5 Função full_join()

Retorna todas as linhas e colunas das duas tabelas. Nas células em que não houver valores correspondentes, retorna NA na posição faltante.

regiao %>% full_join(y = habitat)
habitat %>% semi_join(y = regiao)

7.1.5 Criando e modificando colunas: função mutate()

Voltemos à base de dados Doubs river.

library(ade4)
data(doubs)
ambiente <- doubs$env
head(ambiente)
##   dfs alt   slo flo pH har pho nit amm oxy bdo
## 1   3 934 6.176  84 79  45   1  20   0 122  27
## 2  22 932 3.434 100 80  40   2  20  10 103  19
## 3 102 914 3.638 180 83  52   5  22   5 105  35
## 4 185 854 3.497 253 80  72  10  21   0 110  13
## 5 215 849 3.178 264 81  84  38  52  20  80  62
## 6 324 846 3.497 286 79  60  20  15   0 102  53

Veja que a coluna pH está dada em ph \(\times\) 10. Vamos retornar à escala unitária.

ambiente %>% 
  mutate(pH = pH/10)
##     dfs alt   slo  flo  pH har pho nit amm oxy bdo
## 1     3 934 6.176   84 7.9  45   1  20   0 122  27
## 2    22 932 3.434  100 8.0  40   2  20  10 103  19
## 3   102 914 3.638  180 8.3  52   5  22   5 105  35
## 4   185 854 3.497  253 8.0  72  10  21   0 110  13
## 5   215 849 3.178  264 8.1  84  38  52  20  80  62
## 6   324 846 3.497  286 7.9  60  20  15   0 102  53
## 7   268 841 4.205  400 8.1  88   7  15   0 111  22
## 8   491 792 3.258  130 8.1  94  20  41  12  70  81
## 9   705 752 2.565  480 8.0  90  30  82  12  72  52
## 10  990 617 4.605 1000 7.7  82   6  75   1 100  43
## 11 1234 483 3.738 1990 8.1  96  30 160   0 115  27
## 12 1324 477 2.833 2000 7.9  86   4  50   0 122  30
## 13 1436 450 3.091 2110 8.1  98   6  52   0 124  24
## 14 1522 434 2.565 2120 8.3  98  27 123   0 123  38
## 15 1645 415 1.792 2300 8.6  86  40 100   0 117  21
## 16 1859 375 3.045 1610 8.0  88  20 200   5 103  27
## 17 1985 348 1.792 2430 8.0  92  20 250  20 102  46
## 18 2110 332 2.197 2500 8.0  90  50 220  20 103  28
## 19 2246 310 1.792 2590 8.1  84  60 220  15 106  33
## 20 2477 286 2.197 2680 8.0  86  30 300  30 103  28
## 21 2812 262 2.398 2720 7.9  85  20 220  10  90  41
## 22 2940 254 2.708 2790 8.1  88  20 162   7  91  48
## 23 3043 246 2.565 2880 8.1  97 260 350 115  63 164
## 24 3147 241 1.386 2976 8.0  99 140 250  60  52 123
## 25 3278 231 1.792 3870 7.9 100 422 620 180  41 167
## 26 3579 214 1.792 3910 7.9  94 143 300  30  62  89
## 27 3732 206 2.565 3960 8.1  90  58 300  26  72  63
## 28 3947 195 1.386 4320 8.3 100  74 400  30  81  45
## 29 4220 183 1.946 6770 7.8 110  45 162  10  90  42
## 30 4530 172 1.099 6900 8.2 109  65 160  10  82  44

Vamos agora criar uma variavel categórica pH_cat com os níveis Elevado se maior ou igual a 8 e Neutro caso contrátio.

ambiente %>% 
  mutate(pH = pH/10) %>% 
  mutate(pH_cat = ifelse(pH < 8, yes = "Neutro", no = "Elevado"))

A nova variável está no final da tabela. Vamos colocá-la logo após a coluna pH

ambiente %>% 
  mutate(pH = pH/10) %>% 
  mutate(pH_cat = ifelse(pH < 8, yes = "Neutro", no = "Elevado"), .after = pH)

7.1.6 Unindo colunas: função unite()

Todas as funções vistas acima neste capítulo são do pacote dplyr. A função unite() é do pacotetidyr``` e permite unir duas colunas. Para isto, vamos retornar à tabela Iris e selecionar algumas linhas para exemplificar a união de colunas.

iris2 <- iris %>% filter(Sepal.Length > 5.5 & Sepal.Length < 6.1)
iris2
##    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
## 1           5.8         4.0          1.2         0.2     setosa
## 2           5.7         4.4          1.5         0.4     setosa
## 3           5.7         3.8          1.7         0.3     setosa
## 4           5.7         2.8          4.5         1.3 versicolor
## 5           5.9         3.0          4.2         1.5 versicolor
## 6           6.0         2.2          4.0         1.0 versicolor
## 7           5.6         2.9          3.6         1.3 versicolor
## 8           5.6         3.0          4.5         1.5 versicolor
## 9           5.8         2.7          4.1         1.0 versicolor
## 10          5.6         2.5          3.9         1.1 versicolor
## 11          5.9         3.2          4.8         1.8 versicolor
## 12          6.0         2.9          4.5         1.5 versicolor
## 13          5.7         2.6          3.5         1.0 versicolor
## 14          5.8         2.7          3.9         1.2 versicolor
## 15          6.0         2.7          5.1         1.6 versicolor
## 16          6.0         3.4          4.5         1.6 versicolor
## 17          5.6         3.0          4.1         1.3 versicolor
## 18          5.8         2.6          4.0         1.2 versicolor
## 19          5.6         2.7          4.2         1.3 versicolor
## 20          5.7         3.0          4.2         1.2 versicolor
## 21          5.7         2.9          4.2         1.3 versicolor
## 22          5.7         2.8          4.1         1.3 versicolor
## 23          5.8         2.7          5.1         1.9  virginica
## 24          5.7         2.5          5.0         2.0  virginica
## 25          5.8         2.8          5.1         2.4  virginica
## 26          6.0         2.2          5.0         1.5  virginica
## 27          5.6         2.8          4.9         2.0  virginica
## 28          6.0         3.0          4.8         1.8  virginica
## 29          5.8         2.7          5.1         1.9  virginica
## 30          5.9         3.0          5.1         1.8  virginica

Vamos criar uma nova coluna Genus e uní-la às coluna Species.

iris2 %>% 
  mutate(Genus = rep("Iris", times = nrow(iris2)), .before = Species)
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Genus    Species
## 1           5.8         4.0          1.2         0.2  Iris     setosa
## 2           5.7         4.4          1.5         0.4  Iris     setosa
## 3           5.7         3.8          1.7         0.3  Iris     setosa
## 4           5.7         2.8          4.5         1.3  Iris versicolor
## 5           5.9         3.0          4.2         1.5  Iris versicolor
## 6           6.0         2.2          4.0         1.0  Iris versicolor
## 7           5.6         2.9          3.6         1.3  Iris versicolor
## 8           5.6         3.0          4.5         1.5  Iris versicolor
## 9           5.8         2.7          4.1         1.0  Iris versicolor
## 10          5.6         2.5          3.9         1.1  Iris versicolor
## 11          5.9         3.2          4.8         1.8  Iris versicolor
## 12          6.0         2.9          4.5         1.5  Iris versicolor
## 13          5.7         2.6          3.5         1.0  Iris versicolor
## 14          5.8         2.7          3.9         1.2  Iris versicolor
## 15          6.0         2.7          5.1         1.6  Iris versicolor
## 16          6.0         3.4          4.5         1.6  Iris versicolor
## 17          5.6         3.0          4.1         1.3  Iris versicolor
## 18          5.8         2.6          4.0         1.2  Iris versicolor
## 19          5.6         2.7          4.2         1.3  Iris versicolor
## 20          5.7         3.0          4.2         1.2  Iris versicolor
## 21          5.7         2.9          4.2         1.3  Iris versicolor
## 22          5.7         2.8          4.1         1.3  Iris versicolor
## 23          5.8         2.7          5.1         1.9  Iris  virginica
## 24          5.7         2.5          5.0         2.0  Iris  virginica
## 25          5.8         2.8          5.1         2.4  Iris  virginica
## 26          6.0         2.2          5.0         1.5  Iris  virginica
## 27          5.6         2.8          4.9         2.0  Iris  virginica
## 28          6.0         3.0          4.8         1.8  Iris  virginica
## 29          5.8         2.7          5.1         1.9  Iris  virginica
## 30          5.9         3.0          5.1         1.8  Iris  virginica
iris3 <- iris2 %>% 
  mutate(Genus = rep("Iris", times = nrow(iris2)), .before = Species) %>% 
  unite(col = scientic_name, Genus, Species, sep = " ")
iris3
##    Sepal.Length Sepal.Width Petal.Length Petal.Width   scientic_name
## 1           5.8         4.0          1.2         0.2     Iris setosa
## 2           5.7         4.4          1.5         0.4     Iris setosa
## 3           5.7         3.8          1.7         0.3     Iris setosa
## 4           5.7         2.8          4.5         1.3 Iris versicolor
## 5           5.9         3.0          4.2         1.5 Iris versicolor
## 6           6.0         2.2          4.0         1.0 Iris versicolor
## 7           5.6         2.9          3.6         1.3 Iris versicolor
## 8           5.6         3.0          4.5         1.5 Iris versicolor
## 9           5.8         2.7          4.1         1.0 Iris versicolor
## 10          5.6         2.5          3.9         1.1 Iris versicolor
## 11          5.9         3.2          4.8         1.8 Iris versicolor
## 12          6.0         2.9          4.5         1.5 Iris versicolor
## 13          5.7         2.6          3.5         1.0 Iris versicolor
## 14          5.8         2.7          3.9         1.2 Iris versicolor
## 15          6.0         2.7          5.1         1.6 Iris versicolor
## 16          6.0         3.4          4.5         1.6 Iris versicolor
## 17          5.6         3.0          4.1         1.3 Iris versicolor
## 18          5.8         2.6          4.0         1.2 Iris versicolor
## 19          5.6         2.7          4.2         1.3 Iris versicolor
## 20          5.7         3.0          4.2         1.2 Iris versicolor
## 21          5.7         2.9          4.2         1.3 Iris versicolor
## 22          5.7         2.8          4.1         1.3 Iris versicolor
## 23          5.8         2.7          5.1         1.9  Iris virginica
## 24          5.7         2.5          5.0         2.0  Iris virginica
## 25          5.8         2.8          5.1         2.4  Iris virginica
## 26          6.0         2.2          5.0         1.5  Iris virginica
## 27          5.6         2.8          4.9         2.0  Iris virginica
## 28          6.0         3.0          4.8         1.8  Iris virginica
## 29          5.8         2.7          5.1         1.9  Iris virginica
## 30          5.9         3.0          5.1         1.8  Iris virginica

Observação: a função unite() *exclui as colunas que foram unificadas da tabela.

7.1.7 Reformatando tabelas: funções spread() e gather()

Importe a tabela HubbardBrook.csv com dados anuais de vazão e precipitação em dois riachos de áreas desmatadas e referência. Os dados foram retirados de tiee.esa.org e podem ser obtidos em HubbardBrook.csv.

## # A tibble: 62 x 4
##     Year Treatment   Flow Precipitation
##    <dbl> <chr>      <dbl>         <dbl>
##  1  1958 Deforested  645.         1168.
##  2  1959 Deforested 1012.         1483.
##  3  1960 Deforested  825.         1321.
##  4  1961 Deforested  470.          980.
##  5  1962 Deforested  777.         1232.
##  6  1963 Deforested  774.         1139.
##  7  1964 Deforested  712.         1175.
##  8  1965 Deforested  599.         1115.
##  9  1966 Deforested 1189.         1222.
## 10  1967 Deforested 1132.         1315.
## # ... with 52 more rows
stream <- read_csv("C:/seu_caminho/IntroR/HubbardBrook.csv")
stream

A função spread() reorganiza dados do formato longo para o formato largo. Vamos vazer isto abaixo somente para a variável Flow e excluindo Precipitation.

stream_largo <- stream %>% 
  select(-Precipitation) %>% 
  spread(key = Treatment, value = Flow)
stream_largo
## # A tibble: 31 x 3
##     Year Deforested Reference
##    <dbl>      <dbl>     <dbl>
##  1  1958       645.      567.
##  2  1959      1012.      918.
##  3  1960       825.      752.
##  4  1961       470.      436.
##  5  1962       777.      699.
##  6  1963       774.      663.
##  7  1964       712.      630.
##  8  1965       599.      547.
##  9  1966      1189.      727.
## 10  1967      1132.      781.
## # ... with 21 more rows

Enquanto a função gather() faz o caminho reverso.

stream_longo <- stream_largo %>% 
  gather(key = Desmatamento, value = Flow, -Year)
stream_longo
## # A tibble: 62 x 3
##     Year Desmatamento  Flow
##    <dbl> <chr>        <dbl>
##  1  1958 Deforested    645.
##  2  1959 Deforested   1012.
##  3  1960 Deforested    825.
##  4  1961 Deforested    470.
##  5  1962 Deforested    777.
##  6  1963 Deforested    774.
##  7  1964 Deforested    712.
##  8  1965 Deforested    599.
##  9  1966 Deforested   1189.
## 10  1967 Deforested   1132.
## # ... with 52 more rows