9.4 Manipulação de dados

Com os dados arrumados, a próxima etapa é a manipulação dos dados. O pacote dplyr oferece um conjunto de funções que facilita as operações mais comuns para lidar com dados retangulares de uma forma bem pensada.

Os verbos fundamentais desta gramática de manipulação de dados são:

  • select(), para selecionar variáveis;
  • filter(), para filtrar observações;
  • arrange(), para classificar variáveis;
  • mutate(), para criar e transformar variáveis;
  • group_by(), para agrupar observações;
  • summarise(), para resumir os dados com medidas estatísticas descritivas;

Estes verbos possuem uma sintaxe consistente com uma sentença gramatical:

verbo(sujeito, complemento)

traduzindo de outra forma:

função(dados, z = x + y)

  • o verbo é a função do dplyr;
  • o sujeito (dados) é quem sofre a ação e é sempre o primeiro argumento, nomeado (.data);
  • o complemento são expressões que podem ser usadas como argumentos (o que é representado pela reticência ... no segundo argumento); isso ficará mais claro nos exemplos mais a frente;

Os verbos listados anteriormente possuem versões equivalentes na base do r rblue. Então, por que usar o dplyr ?

  • é muito mais rápido de se aprender, com poucas funções (ou verbos) nomeadas intuitivamente;

  • as funções do dplyr são mais rápidas (parte dos códigos são programados em C++);

  • trabalha bem com dados arrumados e também com sistemas de banco de dados

  • as funções foram projetadas para trabalharem juntas na solução diversos problemas de processamento de dados;

9.4.1 Códigos como fluxogramas

A manipulação de dados requer uma organização apropriada do código. A medida que novas etapas do fluxo de trabalho vão sendo implementadas o código expande-se. As etapas vão sendo implementadas de forma sequencial, combinando funções que geram saídas que servirão de entrada para outras funções na cadeia de processamento.

Essa é justamente a ideia do operador pipe %>%: passar a saída de uma função para outra função como a entrada dessa função por meio de uma seqüência de etapas. O operador %>% está disponível no através do pacote magrittr.

Os pacotes tidyverse integram-se muito bem com o %>%, por isso ele é automaticamente carregado com o tidyverse. Vamos ilustrar as vantagens de uso do %>% com exemplos a seguir.

9.4.1.1 Vantagens do %>%

O exemplo a baixo mostra uma aplicação simples do %>% para extrair a raiz quadrada de um número com a função base::sqrt()e a extração do segundo elemento de um vetor com a função dplyr::nth() (uma função alternativa aos colchetes []).

# chamada tradicional de uma função 
sqrt(4)
#> [1] 2
nth(5:1, 2)
#> [1] 4
# chamada de uma função com %>%
4 %>% sqrt()
#> [1] 2
5:1 %>% nth(2)
#> [1] 4

Ambas formas realizam a mesma tarefa e com mesmo resultado e o benefício do %>% não fica evidente. Entretanto, quando precisamos aplicar várias funções as vantagens ficam mais óbvias.

No código abaixo tente decifrar o objetivo das operações no vetor x.

x <- c(1, 3, -1, 1, 4, 2, 2, -3)
x
#> [1]  1  3 -1  1  4  2  2 -3
nth(sort(cos(unique(x)), decreasing = TRUE), n = 2)
#> [1] 0.5403023

Talvez com o código identado fique mais claro:

nth(            # 4
  sort(         # 3
    cos(        # 2
      unique(x) # 1
    ),
    decreasing = TRUE
  ),n =  2
)

O código acima está aninhando funções e isso leva a uma dificuldade de ler por causa da desordem. Para interpretá-lo precisamos fazer a leitura de dentro para fora:

  1. mantém somente os valores únicos de x
  2. calcula o cosseno do resultado de (1)
  3. coloca em ordem decrescente o resultado de (2)
  4. extrai o 2° elemento do resultado de (3)

Conclusão: o objetivo era obter o segundo maior número resultante do cosseno do vetor numérico x.

A versão usando pipe é:

x %>%
  unique() %>%                # 1
  cos() %>%                   # 2
  sort(decreasing = TRUE) %>% # 3
  nth(n = 2)                      # 4
#> [1] 0.5403023

Dessa forma, o código fica mais simples, legível e explícito. Por isso, daqui para frente, nós utilizaremos extensivamente o operador %>% para ilustrar os verbos do dplyr e suas combinações.

No exemplo anterior nós introduzimos a função dplyr::nth(). Ela é equivalente ao operador colchetes [ da base do R. Se a <- 5:1 então as instruções abaixo produzem resultados equivalentes:

a[2]; nth(a, 2)

#> [1] 4 #> [1] 4

9.4.1.2 O operador . como argumento

Uma representação mais explícita do código usado na cadeia de funções acima, seria com a inclusão do operador . e os nomes dos argumentos das funções:

x %>%
  unique(x = .) %>%                  # 1
  sort(x = ., decreasing = TRUE) %>% # 2
  cos(x = .) %>%                     # 3
  nth(x = ., n = 2)                  # 4
#> [1] -0.9899925

O tempo a mais digitando é compensado posteriormente quando o você mesmo futuramente tiver que reler o código. Essa forma enfatiza com o . que o resultado à esquerda é usado como entrada para função à direita do %>%.

Mas nem todas funções do foram construídas com os dados de entrada no primeiro argumento. Essa é a deixa para outra funcionalidade do . que é redirecionar os dados de entrada para a posição adequada naquelas funções. Uma função que se encaixa neste caso é a base::grep() que detecta uma expressão regular num conjunto de caracteres (strings).

adverbs <- c("ontem", "hoje", "amanhã")
grep(
  pattern = "h",
  x = adverbs,
  value = TRUE
)
#> [1] "hoje"   "amanhã"

O código acima seve para retornar os elementos do vetor dias que contenham a letra h. No entanto os dados de entrada da base::grep() são esperados no 2° argumento (x). Para redirecioná-los para essa posição dentro de uma cadeia de funções com %>%, colocamos o operador . no 2° argumento da função:

adverbs %>%
  grep(
  pattern = "h",
  x = .,
  value = TRUE
)
#> [1] "hoje"   "amanhã"

9.4.2 Seleção de variáveis

Para selecionar somente variáveis de interesse em uma tabela de dados podemos usar a função dplyr::select(.data, ...). Nos dados clima_rs_tbl se desejamos selecionar apenas as colunas estacao e tmax aplicamos a dplyr::select() com o 2° argumento listando as colunas que desejamos selecionar:

select(clima_rs_tbl, estacao, tmax)
#> # A tibble: 23 x 2
#>    estacao              tmax
#>  * <chr>               <dbl>
#>  1 Alegrete             25.4
#>  2 Bagé                 24.1
#>  3 Bento Gonçalves      23  
#>  4 Bom Jesus            20.3
#>  5 Cachoeira do Sul     25.1
#>  6 Caxias do Sul        21.8
#>  7 Cruz Alta            24.5
#>  8 Encruzilhada do Sul  22.5
#>  9 Guaporé              24.7
#> 10 Iraí                 27.1
#> # ... with 13 more rows

O resultado é um subconjunto dos dados originais contendo apenas as colunas nomeadas nos argumentos seguintes aos dados de entrada.

A função dplyr::select() possui funções auxiliares para seleção de variáveis:

clima_rs_tbl %>%
  # as variáveis entre uf e tmax
  select(., uf:tmax) %>%
  head(., n = 3)
#> # A tibble: 3 x 3
#>   uf     prec  tmax
#>   <chr> <dbl> <dbl>
#> 1 RS    1492.  25.4
#> 2 RS    1300.  24.1
#> 3 RS    1684.  23

clima_rs_tbl %>%
  # todas variáveis menos as entre codigo:uf
  select(., -(codigo:uf)) %>%
  head(., n = 3)
#> # A tibble: 3 x 2
#>    prec  tmax
#>   <dbl> <dbl>
#> 1 1492.  25.4
#> 2 1300.  24.1
#> 3 1684.  23

clima_rs_tbl %>%
  # ordem inversa das variáveis
  select(., tmax:codigo) %>%
  head(., n = 3)
#> # A tibble: 3 x 5
#>    tmax  prec uf    estacao         codigo
#>   <dbl> <dbl> <chr> <chr>           <chr> 
#> 1  25.4 1492. RS    Alegrete        83931 
#> 2  24.1 1300. RS    Bagé            83980 
#> 3  23   1684. RS    Bento Gonçalves 83941

clima_rs_tbl %>%
  # nomes que contenham a letra "a"
  select(., contains("a")) %>%
  head(n = 3)
#> # A tibble: 3 x 2
#>   estacao          tmax
#>   <chr>           <dbl>
#> 1 Alegrete         25.4
#> 2 Bagé             24.1
#> 3 Bento Gonçalves  23

clima_rs_tbl %>%
  # variáveis que iniciam com "c"
  select(., starts_with("c")) %>%
  head(., n = 3)
#> # A tibble: 3 x 1
#>   codigo
#>   <chr> 
#> 1 83931 
#> 2 83980 
#> 3 83941

clima_rs_tbl %>%
  # usando um vetor de caracteres
  select(., one_of(c("estacao", "uf"))) %>%
  head(., n = 3)
#> # A tibble: 3 x 2
#>   estacao         uf   
#>   <chr>           <chr>
#> 1 Alegrete        RS   
#> 2 Bagé            RS   
#> 3 Bento Gonçalves RS

clima_rs_tbl %>%
  # combinações
  select(., -uf, ends_with("o")) %>%
  head(., n = 3)
#> # A tibble: 3 x 4
#>   codigo estacao          prec  tmax
#>   <chr>  <chr>           <dbl> <dbl>
#> 1 83931  Alegrete        1492.  25.4
#> 2 83980  Bagé            1300.  24.1
#> 3 83941  Bento Gonçalves 1684.  23

clima_rs_tbl %>%
  # variáveis que inciam com letras minúsculas e com 4 caracteres
  select(., matches("^[a-z]{4}$")) %>%
  head(., n = 3)
#> # A tibble: 3 x 2
#>    prec  tmax
#>   <dbl> <dbl>
#> 1 1492.  25.4
#> 2 1300.  24.1
#> 3 1684.  23

O último exemplo usa uma expressão regular (regex). Regex é uma linguagem para descrever e manipular caracteres de texto. Há livros sobre este assunto e diversos tutorias sobre regex no R. Para saber mais sobre isso veja o capítulo sobre strings do livro de H. Wickham and Grolemund (2017). Conhecendo o básico, você poupará tempo automatizando a formatação de caracteres de texto.

Veja mais funções úteis para seleção de variáveis em ?dplyr::select.

9.4.3 Seleção de observações

A filtragem de observações geralmente envolve uma expressão que retorna valores lógicos ou as posições das linhas selecionadas (como a função which()).

A função dplyr::filter() permite filtrar observações de um data frame correspondentes a alguns critérios lógicos. Estes critérios podem ser passados um de cada vez ou com um operador lógico (e: &, ou: |). Veja abaixo alguns exemplos de filtragem de observações:

  • linhas correspondentes ao codigo da estação 83936.
clima_rs_tbl %>%
  filter(codigo == 83936)
#> # A tibble: 1 x 5
#>   codigo estacao     uf     prec  tmax
#>   <chr>  <chr>       <chr> <dbl> <dbl>
#> 1 83936  Santa Maria RS    1617.  24.9
  • linhas da variável estacao que contenham o vetor caractere litoraneas.
litoraneas <- c("Torres", 
                "Guaporé")
clima_rs_tbl %>%
  filter(estacao %in% litoraneas)
#> # A tibble: 2 x 5
#>   codigo estacao uf     prec  tmax
#>   <chr>  <chr>   <chr> <dbl> <dbl>
#> 1 83915  Guaporé RS    1759.  24.7
#> 2 83948  Torres  RS    1363.  22.3
  • observações com tmax acima de 10% da média
filter(clima_rs_tbl,  tmax > 1.1*mean(tmax))
#> # A tibble: 1 x 5
#>   codigo estacao uf     prec  tmax
#>   <chr>  <chr>   <chr> <dbl> <dbl>
#> 1 83881  Iraí    RS    1807.  27.1
  • observações com tmax e prec acima de suas médias
clima_rs_tbl %>%
filter(
  tmax > mean(tmax),  
  prec > mean(prec)
)
#> # A tibble: 7 x 5
#>   codigo estacao              uf     prec  tmax
#>   <chr>  <chr>                <chr> <dbl> <dbl>
#> 1 83912  Cruz Alta            RS    1631.  24.5
#> 2 83915  Guaporé              RS    1759.  24.7
#> 3 83881  Iraí                 RS    1807.  27.1
#> 4 83880  Palmeira das Missões RS    1748.  24  
#> 5 83936  Santa Maria          RS    1617.  24.9
#> 6 83907  São Luiz Gonzaga     RS    1771.  26.1
#> 7 83927  Uruguaiana           RS    1647.  25.8
# equivalente a
#clima_rs %>% 
#  filter(tmax > mean(tmax) & prec > mean(prec))
  • observações cuja variável estacao tem a palavra "Sul"
# estações com "Sul" no nome
clima_rs_tbl %>% 
  filter(str_detect(estacao, "Sul"))
#> # A tibble: 3 x 5
#>   codigo estacao             uf     prec  tmax
#>   <chr>  <chr>               <chr> <dbl> <dbl>
#> 1 83963  Cachoeira do Sul    RS    1477.  25.1
#> 2 83942  Caxias do Sul       RS    1823   21.8
#> 3 83964  Encruzilhada do Sul RS    1511.  22.5

O exemplo acima é mais uma operação com caracteres onde foi usada a função stringr::str_detect() para detectar os elementos da variável do tipo caractere que contenham o termo “Sul”. O pacote stringr (Hadley Wickham 2018) fornece funções para casar padrões de caracteres de texto e os nomes das funções são fáceis de lembrar. Todos começam com str_ (de string) seguido do verbo, p.ex.:

str_replace_all(

string = c(“abc”, “lca”),

pattern = “a”,

replacement = “A”

)

#> [1] “Abc” “lcA”

A seleção de observações também pode ser baseada em índices passados para função dplyr::slice() que retorna o subconjunto de observações correspondentes. Abaixo vejamos alguns exemplos de filtragem de linhas baseada em índices ou posições:

#linhas 2 e 4 
clima_rs_tbl %>%
  slice(., c(2,4))
#> # A tibble: 2 x 5
#>   codigo estacao   uf     prec  tmax
#>   <chr>  <chr>     <chr> <dbl> <dbl>
#> 1 83980  Bagé      RS    1300.  24.1
#> 2 83919  Bom Jesus RS    1807.  20.3
#última linha
clima_rs_tbl %>%
  slice(., n())
#> # A tibble: 1 x 5
#>   codigo estacao    uf     prec  tmax
#>   <chr>  <chr>      <chr> <dbl> <dbl>
#> 1 83927  Uruguaiana RS    1647.  25.8
# exlui da última à 3a linha
clima_rs_tbl %>%
  slice(., -(n():3))
#> # A tibble: 2 x 5
#>   codigo estacao  uf     prec  tmax
#>   <chr>  <chr>    <chr> <dbl> <dbl>
#> 1 83931  Alegrete RS    1492.  25.4
#> 2 83980  Bagé     RS    1300.  24.1
# linhas com tmax > 26
clima_rs_tbl %>%
  slice(., which(tmax > 26))
#> # A tibble: 3 x 5
#>   codigo estacao          uf     prec  tmax
#>   <chr>  <chr>            <chr> <dbl> <dbl>
#> 1 83881  Iraí             RS    1807.  27.1
#> 2 83929  Itaqui           RS    1369.  26.2
#> 3 83907  São Luiz Gonzaga RS    1771.  26.1
# linhas com tmax mais próxima a média de tmax
clima_rs_tbl %>%
  slice(., which.min(abs(tmax - mean(tmax))))
#> # A tibble: 1 x 5
#>   codigo estacao              uf     prec  tmax
#>   <chr>  <chr>                <chr> <dbl> <dbl>
#> 1 83880  Palmeira das Missões RS    1748.    24

9.4.4 Reordenando dados

As vezes é útil reordenar os dados segundo a ordem (crescente ou decrescente) dos valores de uma variável. Por exemplo, os dados clima_rs_tbl podem ser arranjados em ordem decrescente da precipitação anual, conforme abaixo.

clima_rs_tbl %>% 
  arrange(., desc(prec)) %>%
  head(., n = 3)
#> # A tibble: 3 x 5
#>   codigo estacao       uf     prec  tmax
#>   <chr>  <chr>         <chr> <dbl> <dbl>
#> 1 83942  Caxias do Sul RS    1823   21.8
#> 2 83919  Bom Jesus     RS    1807.  20.3
#> 3 83881  Iraí          RS    1807.  27.1

A função dplyr::arrange() por padrão ordena os dados em ordem crescente. A função dplyr::desc() ordena os valores da variável em ordem descendente.

Os dados ordenados pela tmax, ficam da seguinte forma:

clima_rs_tbl %>% 
  arrange(., tmax) %>%
  head(., n = 3)
#> # A tibble: 3 x 5
#>   codigo estacao       uf     prec  tmax
#>   <chr>  <chr>         <chr> <dbl> <dbl>
#> 1 83919  Bom Jesus     RS    1807.  20.3
#> 2 83995  Rio Grande    RS    1234.  21.7
#> 3 83942  Caxias do Sul RS    1823   21.8

9.4.5 Criando e renomeando variáveis

Uma nova variável pode ser adicionada aos dados através da função dplyr::mutate(). A tmax expressa em Kelvin pode ser adicionada aos dados clima_rs_tbl, com:

clima_rs_tbl %>%
  # tmax em Kelvin
  mutate(., tmaxK = tmax + 273.15) %>%
  # só as colunas de interesse
  select(., contains("tmax")) %>%
  # 3 primeiras linhas
  head(., n = 3)
#> # A tibble: 3 x 2
#>    tmax tmaxK
#>   <dbl> <dbl>
#> 1  25.4  299.
#> 2  24.1  297.
#> 3  23    296.

Podemos renomear variáveis com a função dplyr::rename().

clima_rs_tbl %>%
rename(., 
       "id" = codigo,
       "site" = estacao,
       "temp_max" = tmax,
       "precip" = prec
       ) %>%
  head(., n = 3)
#> # A tibble: 3 x 5
#>   id    site            uf    precip temp_max
#>   <chr> <chr>           <chr>  <dbl>    <dbl>
#> 1 83931 Alegrete        RS     1492.     25.4
#> 2 83980 Bagé            RS     1300.     24.1
#> 3 83941 Bento Gonçalves RS     1684.     23

Podemos sobrescrever variáveis e recodificar seus valores, conforme o exemplo abaixo. A variável site será corrigida, de forma os valores iguais a “A803” sejam substituídos por “A003”.

prec_anual_corr <- prec_anual %>%
mutate(
  site = recode(site, A803 = "A003")
) 
tail(prec_anual_corr, n = 4)
#>   site  ano prec
#> 5 A002 2002 1630
#> 6 A003 2004 1300
#> 7 A003 2005 1950
#> 8 A003 2006 1100

Podemos preencher os valores faltantes de uma variável por um valor prescrito, por exemplo baseado na média de outras observações, ou nos valores prévios, ou posteriores. Variáveis podem ser derivadas das variáveis sendo criadas dentro da dplyr::mutate().

# preenchendo prec faltante pela média
prec_anual_comp %>%
  mutate(., 
         prec = replace_na(prec,
                           mean(prec, na.rm = TRUE)
                           ),
         ndias = ifelse(ano %% 4 == 0, 
                      366, 
                      365),
         # intensidade de ndias, criada na linha acima
         intensidade = prec / ndias
         )
#> # A tibble: 24 x 5
#>    site    ano  prec ndias intensidade
#>    <fct> <dbl> <dbl> <dbl>       <dbl>
#>  1 A001   2000  1800   366        4.92
#>  2 A001   2001  1400   365        3.84
#>  3 A001   2002  1550   365        4.25
#>  4 A001   2004  1550   366        4.23
#>  5 A001   2005  1550   365        4.25
#>  6 A001   2006  1550   365        4.25
#>  7 A002   2000  1750   366        4.78
#>  8 A002   2001  1470   365        4.03
#>  9 A002   2002  1630   365        4.47
#> 10 A002   2004  1550   366        4.23
#> # ... with 14 more rows

prec_anual_comp %>%
  # preenche com  a observação prévia
  fill(prec, .direction = "down")
#> # A tibble: 24 x 3
#>    site    ano  prec
#>    <fct> <dbl> <dbl>
#>  1 A001   2000  1800
#>  2 A001   2001  1400
#>  3 A001   2002  1400
#>  4 A001   2004  1400
#>  5 A001   2005  1400
#>  6 A001   2006  1400
#>  7 A002   2000  1750
#>  8 A002   2001  1470
#>  9 A002   2002  1630
#> 10 A002   2004  1630
#> # ... with 14 more rows

prec_anual_comp %>%
  # preenche com  a observação posterior
  fill(prec, .direction = "up")
#> # A tibble: 24 x 3
#>    site    ano  prec
#>    <fct> <dbl> <dbl>
#>  1 A001   2000  1800
#>  2 A001   2001  1400
#>  3 A001   2002  1750
#>  4 A001   2004  1750
#>  5 A001   2005  1750
#>  6 A001   2006  1750
#>  7 A002   2000  1750
#>  8 A002   2001  1470
#>  9 A002   2002  1630
#> 10 A002   2004  1300
#> # ... with 14 more rows

9.4.6 Agregando observações

A função dplyr::summarise() (ou dplyr::sumarize()) agrega valores de uma variável e os fornece para uma função que retorna um único resultado. O resultado será armazenado em um data frame.

Por exemplo, qual a prec média anual do RS?

clima_rs_tbl %>%
  summarise(
    .,
    prec_med = mean(prec)
  )
#> # A tibble: 1 x 1
#>   prec_med
#>      <dbl>
#> 1    1554.

Se você só quer o valor (ou o vetor), ao invés de um data frame, pode usar a função dplyr::pull():

clima_rs_tbl %>%
  summarise(
    .,
    prec_med = mean(prec)
  ) %>%
  pull()
#> [1] 1554.183

Podemos aplicar uma ou mais funções a mais de uma variável usando dplyr::summarise_at():

clima_rs_tbl %>%
  summarise_at(
    .,
    .vars = vars(prec, tmax),
    .funs = funs(min, median, max),
    na.rm = TRUE
  )
#> # A tibble: 1 x 6
#>   prec_min tmax_min prec_median tmax_median prec_max tmax_max
#>      <dbl>    <dbl>       <dbl>       <dbl>    <dbl>    <dbl>
#> 1    1229.     20.3       1617.        24.1     1823     27.1

Observações repetidas devem ser removidas dos dados antes de qualquer cálculo. Suponha os dados abaixo:

prec_anual_comp_rep <-
  prec_anual_comp %>%
  mutate(
    site = recode(site, A803 = "A003"),
    ano = NULL
  ) %>%
  # preenche com  a observação posterior
  fill(., prec, .direction = "up")
prec_anual_comp_rep
#> # A tibble: 24 x 2
#>    site   prec
#>    <fct> <dbl>
#>  1 A001   1800
#>  2 A001   1400
#>  3 A001   1750
#>  4 A001   1750
#>  5 A001   1750
#>  6 A001   1750
#>  7 A002   1750
#>  8 A002   1470
#>  9 A002   1630
#> 10 A002   1300
#> # ... with 14 more rows

Para desconsiderar linhas duplicadas nos dados usamos a função dplyr::distinct():

# remove observações repetidas
prec_anual_comp_rep %>%
  distinct(site, prec)
#> # A tibble: 10 x 2
#>    site   prec
#>    <fct> <dbl>
#>  1 A001   1800
#>  2 A001   1400
#>  3 A001   1750
#>  4 A002   1750
#>  5 A002   1470
#>  6 A002   1630
#>  7 A002   1300
#>  8 A003   1300
#>  9 A003   1950
#> 10 A003   1100

A função dplyr::count() é útil para obter a frequência de ocorrência de uma variável ou da combinação de variáveis.

prec_anual_comp_rep %>%
  count(site)
#> # A tibble: 3 x 2
#>   site      n
#>   <fct> <int>
#> 1 A001      6
#> 2 A002      6
#> 3 A003     12
prec_anual_comp_rep %>%
  count(site, prec)
#> # A tibble: 10 x 3
#>    site   prec     n
#>    <fct> <dbl> <int>
#>  1 A001   1400     1
#>  2 A001   1750     4
#>  3 A001   1800     1
#>  4 A002   1300     3
#>  5 A002   1470     1
#>  6 A002   1630     1
#>  7 A002   1750     1
#>  8 A003   1100     1
#>  9 A003   1300     4
#> 10 A003   1950     7

9.4.7 Agrupando observações

Frequentemente temos que agrupar observações em categorias ou grupos para realizar uma análise estatística. A função dplyr::group_by() é uma função silenciosa que separa (invisivelmente) as observações em categorias ou grupos. A única mudança ao aplicar a dplyr::group_by() à um data frame é a indicação da variável agrupada e o seu número de grupos na saída do console. No exemplo a seguir vamos agrupar os dados prec_anual_tbl por site e teremos 4 grupos para esta variável.

prec_anual_tbl %>%
  group_by(site)
#> # A tibble: 8 x 4
#> # Groups:   site [4]
#>   site    ano  prec intensidade
#>   <chr> <dbl> <dbl>       <dbl>
#> 1 A001   2000  1800        4.93
#> 2 A001   2001  1400        3.83
#> 3 A002   2000  1750        4.79
#> 4 A002   2001  1470        4.02
#> 5 A002   2002  1630        4.46
#> 6 A003   2004  1300        3.56
#> 7 A803   2005  1950        5.34
#> 8 A803   2006  1100        3.01

A grande funcionalidade da dplyr::group_by() surge quando combinada com a função dplyr::summarise(), o que nos permite obter resumos estatísticos para cada grupo da variável.

Por exemplo a chuva anual média por site é obtida com o seguinte código:

prec_anual_tbl %>%
  group_by(., site) %>%
  summarise(., prec_med = mean(prec))
#> # A tibble: 4 x 2
#>   site  prec_med
#>   <chr>    <dbl>
#> 1 A001     1600 
#> 2 A002     1617.
#> 3 A003     1300 
#> 4 A803     1525

A prec média para cada ano e o número de anos utilizados em seu cálculo é obtida por:

prec_anual_tbl %>%
  group_by(., ano) %>%
  summarise(
    .,
    prec_med = mean(prec),
    nobs = n()
  )
#> # A tibble: 6 x 3
#>     ano prec_med  nobs
#>   <dbl>    <dbl> <int>
#> 1  2000     1775     2
#> 2  2001     1435     2
#> 3  2002     1630     1
#> 4  2004     1300     1
#> 5  2005     1950     1
#> 6  2006     1100     1

A função n() conta quantas observações temos em um subconjunto dos dados.

Os grupos podem ser compostos de mais de uma variável. Para o exemplo com os dados prec_anual_long;

prec_anual_long
#> # A tibble: 16 x 4
#>    site    ano variavel     medida
#>    <chr> <dbl> <chr>         <dbl>
#>  1 A001   2000 prec        1800   
#>  2 A001   2001 prec        1400   
#>  3 A002   2000 prec        1750   
#>  4 A002   2001 prec        1470   
#>  5 A002   2002 prec        1630   
#>  6 A003   2004 prec        1300   
#>  7 A803   2005 prec        1950   
#>  8 A803   2006 prec        1100   
#>  9 A001   2000 intensidade    4.93
#> 10 A001   2001 intensidade    3.83
#> 11 A002   2000 intensidade    4.79
#> 12 A002   2001 intensidade    4.02
#> 13 A002   2002 intensidade    4.46
#> 14 A003   2004 intensidade    3.56
#> 15 A803   2005 intensidade    5.34
#> 16 A803   2006 intensidade    3.01

podemos obter a média por variavel e site, fazendo:

estats_por_site_var <- prec_anual_long %>%
  group_by(site, variavel) %>%
  summarise(
    media = mean(medida, na.rm = TRUE)
  ) %>%
  arrange(variavel, site)
estats_por_site_var
#> # A tibble: 8 x 3
#> # Groups:   site [4]
#>   site  variavel      media
#>   <chr> <chr>         <dbl>
#> 1 A001  intensidade    4.38
#> 2 A002  intensidade    4.43
#> 3 A003  intensidade    3.56
#> 4 A803  intensidade    4.18
#> 5 A001  prec        1600   
#> 6 A002  prec        1617.  
#> 7 A003  prec        1300   
#> 8 A803  prec        1525

Com o conjunto de verbos exemplificados você agora é capaz de realizar as tarefas mais comuns de manipulação de dados tabulares de forma clara e confiável.

Há mais funções úteis disponíveis no pacote dplyr e você é encorajado a descubrí-las.

9.4.8 Combinação de dados

O processamento de dados frequentemente envolve a manipulação de diversas tabelas de dados. Ás vezes precisamos juntar dados de diferentes fontes, formar uma tabela única de dados com o período em comum à elas, ou combiná-las para compará-las.

A combinação de 2 data frames, com observações similares, que tem variáveis diferentes e algumas em comum é uma tarefa muito comum na manipulação de conjuntos dados. Este tipo de operação é chamada de junção (do termo em inglês join) de tabelas . O pacote dplyr possui uma gama de funções do tipo join para combinar data frames, genericamente representadas por <tipo>_join(), onde <tipo> pode ser substituído por dplyr::full_join(), dplyr::inner_join(), dplyr::left_join(), dplyr::right_join().

Essas funções combinam informação em dois data frames baseada na unificação de valores entre as variáveis que ambos compartilham.

Vamos considerar os dados clima_rs_tbl e metadados_rs. Para melhor compreensão do exemplo vamos remover algumas variáveis.

# normais climatológicas das estaçõess
clima_rs_tbl <- clima_rs_tbl %>%
  select(-(estacao:uf))
head(clima_rs_tbl)
#> # A tibble: 6 x 3
#>   codigo  prec  tmax
#>   <chr>  <dbl> <dbl>
#> 1 83931  1492.  25.4
#> 2 83980  1300.  24.1
#> 3 83941  1684.  23  
#> 4 83919  1807.  20.3
#> 5 83963  1477.  25.1
#> 6 83942  1823   21.8
# metadados das estações convertidos para tibble
metadados_rs <- metadados_rs %>% 
  as_tibble()
head(metadados_rs)
#> # A tibble: 6 x 4
#>   codigo   lon   lat    alt
#>   <chr>  <dbl> <dbl>  <dbl>
#> 1 83931  -55.5 -29.7  121. 
#> 2 83980  -54.1 -31.3  242. 
#> 3 83941  -51.5 -29.2  640  
#> 4 83919  -50.4 -28.7 1048. 
#> 5 83959  -53.5 -30.5  450  
#> 6 83963  -52.9 -30.0   72.7

A variável comum às duas tabelas é:

var_comum <- names(clima_rs_tbl) %in% names(metadados_rs)
names(clima_rs_tbl)[var_comum]
#> [1] "codigo"

Vamos comparar os valores da variável codigo em cada tabela de dados para verificar se todos valores contidos em uma tabela também estão presentes na outra e vice-versa.

Para saber se algum valor da variável codigo da tabela clima_rs_tbl não está contido na tabela metadados_rs podemos usar o seguinte código:

# algum codigo não está presente na tabela metadados_rs
clima_rs_tbl %>%
  filter(., ! codigo %in% metadados_rs$codigo ) %>%
  select(., codigo)
#> # A tibble: 0 x 1
#> # ... with 1 variable: codigo <chr>

Não há nenhum caso.

Analogamente, vamos verificar se algum valor da variável codigo dos metadados_rs não está contido em clima_rs_tbl.

# algum codigo não está presente na tabela metadados_rs
metadados_rs %>%
  filter(., ! codigo %in% clima_rs_tbl$codigo )%>%
  select(., codigo)
#> # A tibble: 7 x 1
#>   codigo
#>   <chr> 
#> 1 83959 
#> 2 83885 
#> 3 83985 
#> 4 83983 
#> 5 83953 
#> 6 83909 
#> 7 83918

Obtemos que 7 valores da variável codigo dos metadados_rs que não estão presentes na tabela clima_rs_tbl. Portanto, não há valores de tmax e prec para essas observações.

Suponha agora que desejássemos visualizar a variação espacial da precipitação (prec) ou da temperatura máxima do ar (tmax) climatológica. Precisaríamos além dessas variáveis, as coordenadas geográficas das estações meteorológicas para plotar sua localização espacial. As coordenadas lon e lat da metadados_rs podem ser combinadas com clima_rs_tbl em uma nova tabela (clima_rs_comb), usando a função dplyr::full_join():

clima_rs_comb <- full_join(
  x = clima_rs_tbl, 
  y = metadados_rs,
  by = "codigo")
clima_rs_comb
codigo prec tmax lon lat alt
83931 1492.2 25.4 -55.51667 -29.68333 120.91
83980 1299.9 24.1 -54.10000 -31.33333 242.31
83941 1683.7 23.0 -51.51667 -29.15000 640.00
83919 1807.3 20.3 -50.43333 -28.66667 1047.50
83963 1477.1 25.1 -52.88333 -30.03333 72.71
83942 1823.0 21.8 -51.20000 -29.16667 759.60
83912 1630.7 24.5 -53.60000 -28.63333 472.50
83964 1510.8 22.5 -52.51667 -30.53333 427.75
83915 1758.7 24.7 -51.90000 -28.91667 471.51
83881 1806.7 27.1 -53.23333 -27.18333 247.10
83929 1369.4 26.2 -56.53333 -29.11667 76.00
83916 1691.1 23.0 -51.50000 -28.21667 840.00
83880 1747.8 24.0 -53.43333 -27.88333 634.00
83914 1803.1 23.6 -52.40000 -28.21667 684.05
83967 1320.2 24.8 -51.16667 -30.05000 46.97
83995 1233.6 21.7 -52.10000 -32.03333 2.46
83936 1616.8 24.9 -53.70000 -29.70000 95.00
83997 1228.9 21.8 -53.35000 -33.51667 24.01
83957 1313.9 25.0 -54.31667 -30.33333 124.00
83907 1770.9 26.1 -55.01667 -28.40000 245.11
83966 1349.8 23.8 -51.58333 -30.83333 5.00
83948 1363.2 22.3 -49.71667 -29.35000 4.66
83927 1647.4 25.8 -57.08333 -29.75000 62.31
83959 NA NA -53.48333 -30.51667 450.00
83885 NA NA -51.90000 -27.45000 414.17
83985 NA NA -52.41667 -31.78333 13.00
83983 NA NA -53.10000 -31.43333 345.00
83953 NA NA -55.60000 -30.83333 328.00
83909 NA NA -54.25000 -28.30000 284.50
83918 NA NA -50.70000 -28.55000 954.60

Da inspeção das últimas linhas de clima_rs_comb verificamos que o resultado é uma tabela que contém todos valores da variável codigo das duas tabelas. Os valores das variáveis prec e tmax, para as observações da variável codigo sem valores ( na metadados_rs) são preenchidos com NA.

Se a combinação de interesse for nas observações em comum entre as tabelas, usaríamos:

clima_rs_intersec <- inner_join(
  x = metadados_rs, 
  y = clima_rs_tbl,
  by = "codigo"
)
clima_rs_intersec
codigo lon lat alt prec tmax
83931 -55.51667 -29.68333 120.91 1492.2 25.4
83980 -54.10000 -31.33333 242.31 1299.9 24.1
83941 -51.51667 -29.15000 640.00 1683.7 23.0
83919 -50.43333 -28.66667 1047.50 1807.3 20.3
83963 -52.88333 -30.03333 72.71 1477.1 25.1
83942 -51.20000 -29.16667 759.60 1823.0 21.8
83912 -53.60000 -28.63333 472.50 1630.7 24.5
83964 -52.51667 -30.53333 427.75 1510.8 22.5
83915 -51.90000 -28.91667 471.51 1758.7 24.7
83881 -53.23333 -27.18333 247.10 1806.7 27.1
83929 -56.53333 -29.11667 76.00 1369.4 26.2
83916 -51.50000 -28.21667 840.00 1691.1 23.0
83880 -53.43333 -27.88333 634.00 1747.8 24.0
83914 -52.40000 -28.21667 684.05 1803.1 23.6
83967 -51.16667 -30.05000 46.97 1320.2 24.8
83995 -52.10000 -32.03333 2.46 1233.6 21.7
83936 -53.70000 -29.70000 95.00 1616.8 24.9
83997 -53.35000 -33.51667 24.01 1228.9 21.8
83957 -54.31667 -30.33333 124.00 1313.9 25.0
83907 -55.01667 -28.40000 245.11 1770.9 26.1
83966 -51.58333 -30.83333 5.00 1349.8 23.8
83948 -49.71667 -29.35000 4.66 1363.2 22.3
83927 -57.08333 -29.75000 62.31 1647.4 25.8

Para obter uma tabela com as observações diferentes entre as duas tabelas, usamos:

clima_rs_disj <- anti_join(
  x = metadados_rs, 
  y = clima_rs_tbl,
  by = "codigo"
)
clima_rs_disj
codigo lon lat alt
83959 -53.48333 -30.51667 450.00
83885 -51.90000 -27.45000 414.17
83985 -52.41667 -31.78333 13.00
83983 -53.10000 -31.43333 345.00
83953 -55.60000 -30.83333 328.00
83909 -54.25000 -28.30000 284.50
83918 -50.70000 -28.55000 954.60

O exemplo abaixo demonstram os resultados das funções dplyr::left_join() e dplyr::right_join() para um versão reduzida dos dados clima_rs_tbl.

clima_rs_tbl_mini <- clima_rs_tbl %>%
  slice(., 1:3) 
clima_rs_tbl_mini
#> # A tibble: 3 x 3
#>   codigo  prec  tmax
#>   <chr>  <dbl> <dbl>
#> 1 83931  1492.  25.4
#> 2 83980  1300.  24.1
#> 3 83941  1684.  23
# combina os dados baseado nas observações dos dados à esquerda (x)
left_join(
  x = clima_rs_tbl_mini, 
  y =metadados_rs, 
  by = "codigo"
)
#> # A tibble: 3 x 6
#>   codigo  prec  tmax   lon   lat   alt
#>   <chr>  <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 83931  1492.  25.4 -55.5 -29.7  121.
#> 2 83980  1300.  24.1 -54.1 -31.3  242.
#> 3 83941  1684.  23   -51.5 -29.2  640
# combina os dados baseado nas observações dos dados à direita (y)
right_join(
  x = clima_rs_tbl_mini, 
  y = metadados_rs, 
  by = "codigo"
)
#> # A tibble: 30 x 6
#>    codigo  prec  tmax   lon   lat    alt
#>    <chr>  <dbl> <dbl> <dbl> <dbl>  <dbl>
#>  1 83931  1492.  25.4 -55.5 -29.7  121. 
#>  2 83980  1300.  24.1 -54.1 -31.3  242. 
#>  3 83941  1684.  23   -51.5 -29.2  640  
#>  4 83919    NA   NA   -50.4 -28.7 1048. 
#>  5 83959    NA   NA   -53.5 -30.5  450  
#>  6 83963    NA   NA   -52.9 -30.0   72.7
#>  7 83942    NA   NA   -51.2 -29.2  760. 
#>  8 83912    NA   NA   -53.6 -28.6  472. 
#>  9 83964    NA   NA   -52.5 -30.5  428. 
#> 10 83915    NA   NA   -51.9 -28.9  472. 
#> # ... with 20 more rows

References

Wickham, H., and G. Grolemund. 2017. R for Data Science. O’Reilly Media. http://r4ds.had.co.nz/.

Wickham, Hadley. 2018. Stringr: Simple, Consistent Wrappers for Common String Operations. https://CRAN.R-project.org/package=stringr.