Módulo 2 O pacote tidyr

Script da aula do Módulo II abaixo. Download 02-tidying-data.R

2.1 Conceito tidy

Dentro do tidyverse, um dataset tidy é uma base fácil de se trabalhar, isto é, fácil de se fazer manipulação de dados, criar visualizações e de se ajustar modelos.

Na prática, uma base tidy é aquela que se encaixa bem no framework do tidyverse, pois os pacotes como o dplyr e o ggplot2 foram desenvolvidos para funcionar bem com bases tidy(Dados organizados).

Dados organizados são uma forma padrão de mapear o significado de um conjunto de dados em sua estrutura. Um conjunto de dados é confuso ou organizado, dependendo de como as linhas, colunas e tabelas são combinadas com observações, variáveis e tipos. Em dados organizados:

  • Cada coluna é uma variável.

  • Cada linha é uma observação.

  • Cada célula é um único valor.

Tidy concept

Esta é a 3ª forma normal de Codd, mas com as restrições enquadradas em linguagem estatística e o foco colocado em um único conjunto de dados, em vez dos muitos conjuntos de dados conectados comuns em bancos de dados relacionais. Dados bagunçados são qualquer outro arranjo dos dados.

Os dados organizados tornam mais fácil para um analista ou um computador extrair as variáveis necessárias porque fornecem uma maneira padrão de estruturar um conjunto de dados. Compare as diferentes versões dos dados da sala de aula: na versão confusa, você precisa usar estratégias diferentes para extrair variáveis diferentes. Isso retarda a análise e convida a erros. Se você considerar quantas operações de análise de dados envolvem todos os valores em uma variável (cada função de agregação), você pode ver como é importante extrair esses valores de uma maneira simples e padrão. Dados organizados são particularmente adequados para linguagens de programação vetorizadas como R, porque o layout garante que os valores de diferentes variáveis da mesma observação estejam sempre emparelhados.

Embora a ordem das variáveis e observações não afete a análise, uma boa ordem torna mais fácil verificar os valores brutos. Uma maneira de organizar as variáveis é por meio de seu papel na análise: os valores são fixados pelo projeto da coleta de dados ou são medidos durante o curso do experimento? Variáveis fixas descrevem o projeto experimental e são conhecidas com antecedência. Os cientistas da computação costumam chamar dimensões de variáveis fixas, e os estatísticos geralmente as denotam com índices em variáveis aleatórias. As variáveis medidas são o que realmente medimos no estudo. As variáveis fixas devem vir primeiro, seguidas pelas variáveis medidas, cada uma ordenada de forma que as variáveis relacionadas sejam contíguas. As linhas podem então ser ordenadas pela primeira variável, quebrando os laços com a segunda variável e as subsequentes (fixas).

Isto nos proporciona uma maneira consistente de se referir às variáveis e observações.

O pacote {tidyr} possui funções que nos ajudam a transformar uma base confusa em uma base organizada (bem formatada). Ou então, nos ajudam a retirar do formato tidy quando isso nos convém.

Vamos ver aqui algumas de suas principais funções:

  • pivot_wider() e pivot_longer(): para pivotar a base.

  • separate() e unite(): para separar variáveis concatenadas em uma única coluna ou uni-las.

  • nest() e unnest(): para criar list columns.

Como motivação para utilizar esssas funções.

2.2 Pivotagem

O princípio de pivotagem no tidyverse se refere a mudança da estrutura da base, normalmente para alcançar o formato tidy.

Os princípios de dados organizados parecem tão óbvios que você pode se perguntar se algum dia encontrará um conjunto de dados que não seja organizado. Infelizmente, porém, a maioria dos dados que você encontrará estarão desordenados. Há duas razões principais:

  • A maioria das pessoas não está familiarizada com os princípios de dados organizados e é difícil derivá-los sozinho, a menos que você passe muito tempo trabalhando com dados.

  • Os dados são frequentemente organizados para facilitar algum uso além da análise. Por exemplo, os dados são frequentemente organizados para tornar a entrada o mais fácil possível.

Isso significa que, para a maioria das análises reais, você precisará fazer algumas arrumações. O primeiro passo é sempre descobrir quais são as variáveis e observações. Às vezes, isso é fácil, outras vezes, você precisará consultar as pessoas que detém o conhecimento do negócio relacionado aos dados que se está trabalhando. A segunda etapa é resolver um dos dois problemas comuns:

  • Uma variável pode ser espalhada por várias colunas.

  • Uma observação pode estar espalhada por várias linhas.

Geralmente, a base de dados sofrerá apenas de um desses problemas. Só vai sofrer de ambos se você for realmente azarado! Para corrigir esses problemas, você precisará das duas funções mais importantes no tidyr: pivot_longer() e pivot_wider().

O ato de pivotar resulta em transformar uma base de dados long em wide e vice-versa.

Esses formatos são sempre relativos às colunas que estão sendo pivotadas, sendo que uma base tidy pode estar tanto no formato long quanto wide.

Antigamente, utilizávamos as funções gather() e spread() para fazer as operações de pivotagem.

2.2.1 Long data

Agora, no lugar de gather(), utilizamos a função pivot_longer().

Um problema comum é que alguns dos nomes das colunas não são nomes de variáveis, mas sim valores de uma variável. Tome a tabela4b: os nomes das colunas 1999 e 2000 representam os valores da variável ano, os valores nas colunas 1999 e 2000 representam os valores da variável de população e cada linha representa mais de uma observação.

table4b
## # A tibble: 3 x 3
##   country         `1999`     `2000`
## * <chr>            <int>      <int>
## 1 Afghanistan   19987071   20595360
## 2 Brazil       172006362  174504898
## 3 China       1272915272 1280428583

Para conseguir representar esse dataset dentro do conceito tidy, precisamos transformar as colunas “problemáticas” em um novo par de variáveis. Para descrever esse processo, precisamos de três parâmetros:

  • O conjunto de colunas cujos nomes são valores, não variáveis. Neste exemplo, essas são as colunas 1999 e 2000.

  • O nome da variável para a qual mover os nomes das colunas. Aqui é ano.

  • O nome da variável para a qual mover os valores da coluna. Aqui estão os totais populacionais.

table4b %>% 
  pivot_longer(c(`1999`, `2000`), names_to = "ano", values_to = "populacao")
## # A tibble: 6 x 3
##   country     ano    populacao
##   <chr>       <chr>      <int>
## 1 Afghanistan 1999    19987071
## 2 Afghanistan 2000    20595360
## 3 Brazil      1999   172006362
## 4 Brazil      2000   174504898
## 5 China       1999  1272915272
## 6 China       2000  1280428583

As colunas dinâmicas são eliminadas e obtemos novas colunas de ano e de populacao. Entretanto, as relações entre as variáveis originais são preservadas.

Obs: Observe que “1999” e “2000” são nomes não sintáticos (porque eles não começam com uma letra), então temos que colocá-los entre crases.

pivot_longer() torna os datasets mais longos, aumentando o número de linhas e diminuindo o número de colunas.

2.2.2 Wide data

pivot_wider() é o oposto de pivot_longer(). Você o usa quando uma observação está espalhada por várias linhas. Como por exemplo a tabela 2: onde uma observação é um país em um ano, mas cada observação é distribuída por duas linhas.

table2
## # A tibble: 12 x 4
##    country      year type            count
##    <chr>       <int> <chr>           <int>
##  1 Afghanistan  1999 cases             745
##  2 Afghanistan  1999 population   19987071
##  3 Afghanistan  2000 cases            2666
##  4 Afghanistan  2000 population   20595360
##  5 Brazil       1999 cases           37737
##  6 Brazil       1999 population  172006362
##  7 Brazil       2000 cases           80488
##  8 Brazil       2000 population  174504898
##  9 China        1999 cases          212258
## 10 China        1999 population 1272915272
## 11 China        2000 cases          213766
## 12 China        2000 population 1280428583

Para arrumar isso, primeiro analisamos a representação de maneira semelhante a pivot_longer(). Desta vez, no entanto, precisamos apenas de dois parâmetros:

  • A coluna da qual obter os nomes das variáveis. Aqui, é tipo.

  • A coluna da qual obter valores. Aqui está a contagem.

table2 %>%
    pivot_wider(names_from = type, values_from = count)
## # A tibble: 6 x 4
##   country      year  cases population
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Como você deve ter adivinhado por seus nomes, pivot_wider() e pivot_longer() são complementos. pivot_longer() torna as tabelas amplas em estreitas e mais longas. Já a função pivot_wider() torna as tabelas longas em curtas e amplas.

2.3 Separando e unindo

2.3.1 Separando informações de uma mesma célula

separate() é a função que separa uma coluna em várias colunas, dividindo onde quer que apareça um caractere separador. Para ilustrar vamos usar a tabela3:

table3
## # A tibble: 6 x 3
##   country      year rate             
## * <chr>       <int> <chr>            
## 1 Afghanistan  1999 745/19987071     
## 2 Afghanistan  2000 2666/20595360    
## 3 Brazil       1999 37737/172006362  
## 4 Brazil       2000 80488/174504898  
## 5 China        1999 212258/1272915272
## 6 China        2000 213766/1280428583

A coluna rate contém casos e população de forma simultânea, e precisamos dividi-la em duas variáveis. Então vamos usar o comando separate() que pega o nome da coluna a ser separada e os nomes das colunas nas quais se quer separar, conforme mostrado código abaixo.

table3 %>% 
  separate(rate, into = c("Casos", "Populacao"))
## # A tibble: 6 x 4
##   country      year Casos  Populacao 
##   <chr>       <int> <chr>  <chr>     
## 1 Afghanistan  1999 745    19987071  
## 2 Afghanistan  2000 2666   20595360  
## 3 Brazil       1999 37737  172006362 
## 4 Brazil       2000 80488  174504898 
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Por padrão, separate() dividirá os valores sempre que vir um caractere não alfanumérico (ou seja, um caractere que não é um número ou letra). Por exemplo, no código acima, foi divido os valores da coluna rate no caractere de barra. Se você deseja usar um caractere específico para separar uma coluna, você pode passar o caractere para o argumento sep de separate(). Por exemplo, podemos reescrever o código acima como:

table3 %>% 
  separate(rate, into = c("Casos", "Populacao"), sep = "/")
## # A tibble: 6 x 4
##   country      year Casos  Populacao 
##   <chr>       <int> <chr>  <chr>     
## 1 Afghanistan  1999 745    19987071  
## 2 Afghanistan  2000 2666   20595360  
## 3 Brazil       1999 37737  172006362 
## 4 Brazil       2000 80488  174504898 
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

(Na verdade, sep é uma expressão regular, sobre a qual você aprenderá mais em strings.)

Se observarmos cuidadosamente os tipos das variáveis. Podemos notar que Casos e Populacao são colunas de caracteres. Este é o comportamento padrão em separate() deixa o tipo da coluna como está. Aqui, no entanto, não é muito útil, pois esses são realmente números. Podemos pedir a separate() para tentar converter para tipos melhores usando convert = TRUE.

table3 %>% 
  separate(rate, into = c("Casos", "Populacao"), convert = TRUE)
## # A tibble: 6 x 4
##   country      year  Casos  Populacao
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Você também pode passar um vetor de inteiros para sep. separate() vai interpretar os inteiros como posições nas quais dividir a informação. - Os valores positivos começam em 1 na extremidade esquerda das strings. - O valor negativo começa em -1 na extrema direita das strings. Ao usar inteiros para separar strings, o comprimento de sep deve ser um a menos que o número de nomes.

Você pode usar esse arranjo para separar os dois últimos dígitos de cada ano. Isso torna esses dados menos organizados, mas pode ser bem útil.

table_separate <- table3 %>%
  separate(rate, 
           into = c("Casos", "Populacao"), convert = TRUE) %>% 
  separate(year, into = c("Seculo", "Ano"), sep = 2)

table_separate
## # A tibble: 6 x 5
##   country     Seculo Ano    Casos  Populacao
##   <chr>       <chr>  <chr>  <int>      <int>
## 1 Afghanistan 19     99       745   19987071
## 2 Afghanistan 20     00      2666   20595360
## 3 Brazil      19     99     37737  172006362
## 4 Brazil      20     00     80488  174504898
## 5 China       19     99    212258 1272915272
## 6 China       20     00    213766 1280428583

2.3.2 Unindo informações em uma unica celula

unite() é o inverso de separate(): ele combina várias colunas em uma única coluna. è provavel que você irá precisar dele com muito menos frequência do que separate(), mas ainda é uma ferramenta útil para se ter.

Podemos usar unite() para reunir novamente as colunas de seculo e ano que criamos no último exemplo. A função unite() pega um conjunto de dados, o nome da nova variável a ser criada e um conjunto de colunas a serem combinadas.

table_separate %>% 
  unite(Ano_Cheio, Seculo, Ano)
## # A tibble: 6 x 4
##   country     Ano_Cheio  Casos  Populacao
##   <chr>       <chr>      <int>      <int>
## 1 Afghanistan 19_99        745   19987071
## 2 Afghanistan 20_00       2666   20595360
## 3 Brazil      19_99      37737  172006362
## 4 Brazil      20_00      80488  174504898
## 5 China       19_99     212258 1272915272
## 6 China       20_00     213766 1280428583

Nesse caso, também precisamos usar o argumento sep. O padrão colocará um underscore (_) entre os valores de diferentes colunas. Neste caso não queremos nenhum separador, então usamos "" como nosso separador.

table_separate %>% 
  unite(Ano_Cheio, Seculo, Ano, sep = "")
## # A tibble: 6 x 4
##   country     Ano_Cheio  Casos  Populacao
##   <chr>       <chr>      <int>      <int>
## 1 Afghanistan 1999         745   19987071
## 2 Afghanistan 2000        2666   20595360
## 3 Brazil      1999       37737  172006362
## 4 Brazil      2000       80488  174504898
## 5 China       1999      212258 1272915272
## 6 China       2000      213766 1280428583

2.4 Dados faltantes

Alterar a representação de um conjunto de dados traz à tona uma importante sutileza de valores ausentes. Surpreendentemente, um valor pode estar faltando em uma das duas maneiras possíveis:

  • Explicitamente, ou seja, sinalizado com NA.
  • Implicitamente, ou seja, simplesmente não está presente nos dados.

Vamos ilustrar essa ideia com um conjunto de dados muito simples:

stocks <- tibble(
  Anos   = c(2015, 2015, 2015, 2015, 2016, 2016, 2016),
  Tri    = c(   1,    2,    3,    4,    2,    3,    4),
  Retornos = c(1.88, 0.59, 0.35,   NA, 0.92, 0.17, 2.66)
)

Existem dois valores ausentes neste conjunto de dados:

O retorno para o quarto trimestre de 2015 está explicitamente ausente, porque a célula onde seu valor deveria estar contém NA.

O retorno para o primeiro trimestre de 2016 está implicitamente ausente, porque simplesmente não aparece no conjunto de dados.

Uma maneira de pensar sobre a diferença é com este koan do tipo Zen: - Um valor ausente explícito é a presença de uma ausência - Um valor ausente implícito é a ausência de uma presença.

A maneira como um conjunto de dados é representado pode tornar explícitos os valores implícitos. Por exemplo, podemos tornar explícito o valor ausente implícito colocando Anos nas colunas:

stocks %>% 
  pivot_wider(names_from = Anos, values_from = Retornos)
## # A tibble: 4 x 3
##     Tri `2015` `2016`
##   <dbl>  <dbl>  <dbl>
## 1     1   1.88  NA   
## 2     2   0.59   0.92
## 3     3   0.35   0.17
## 4     4  NA      2.66

Como esses valores ausentes explícitos podem não ser importantes em outras representações dos dados, você pode definir values_drop_na = TRUE em pivot_longer() para tornar implícitos os valores ausentes explícitos:

stocks %>% 
  pivot_wider(names_from = Anos, values_from = Retornos) %>% 
  pivot_longer(
    cols = c(`2015`, `2016`), 
    names_to = "Anos", 
    values_to = "Retornos", 
    values_drop_na = TRUE
  )
## # A tibble: 6 x 3
##     Tri Anos  Retornos
##   <dbl> <chr>    <dbl>
## 1     1 2015      1.88
## 2     2 2015      0.59
## 3     2 2016      0.92
## 4     3 2015      0.35
## 5     3 2016      0.17
## 6     4 2016      2.66

2.4.1 complete()

Outra função importante para tornar explícitos os valores ausentes em dados organizados é complete():

stocks %>% 
  complete(Anos, Tri)
## # A tibble: 8 x 3
##    Anos   Tri Retornos
##   <dbl> <dbl>    <dbl>
## 1  2015     1     1.88
## 2  2015     2     0.59
## 3  2015     3     0.35
## 4  2015     4    NA   
## 5  2016     1    NA   
## 6  2016     2     0.92
## 7  2016     3     0.17
## 8  2016     4     2.66

O comando complete() pega um conjunto de colunas e encontra todas as combinações únicas. Em seguida, garante que o conjunto de dados original contém todos esses valores, preenchendo NAs explícitos quando necessário.

2.4.2 fill()

Há uma outra ferramenta importante que você deve saber para trabalhar com valores ausentes. Às vezes, quando uma fonte de dados foi usada principalmente para entrada de dados, os valores ausentes indicam que o valor anterior deve ser utilizado:

tratamento <- tribble(
  ~ Pessoa,       ~ Tratamento, ~Resposta,
  "Roberto Silva",    2,           10,
  NA,                 3,           3,
  NA,                 1,           7,
  "Luciana Medeiros", 2,           9
)

Você pode preencher esses valores ausentes com fill(). É necessário um conjunto de colunas onde você deseja que os valores ausentes sejam substituídos pelo valor não ausente mais recente (às vezes chamado observação acima do dado faltante).

tratamento %>% 
  fill(Pessoa)
## # A tibble: 4 x 3
##   Pessoa           Tratamento Resposta
##   <chr>                 <dbl>    <dbl>
## 1 Roberto Silva             2       10
## 2 Roberto Silva             3        3
## 3 Roberto Silva             1        7
## 4 Luciana Medeiros          2        9

2.5 Dados aninhados

2.5.1 Nest()

Vamos agora apresentar uma nova estrutura de dados, os dados aninhados. Para criar um dataset aninhado, começamos com um dataset de dados agrupado e o “aninhamos”:

library(gapminder)

by_country <- gapminder %>% 
  group_by(country, continent) %>% 
  nest()

by_country
## # A tibble: 142 x 3
## # Groups:   country, continent [142]
##    country     continent data                 
##    <fct>       <fct>     <list>               
##  1 Afghanistan Asia      <tibble[,4] [12 x 4]>
##  2 Albania     Europe    <tibble[,4] [12 x 4]>
##  3 Algeria     Africa    <tibble[,4] [12 x 4]>
##  4 Angola      Africa    <tibble[,4] [12 x 4]>
##  5 Argentina   Americas  <tibble[,4] [12 x 4]>
##  6 Australia   Oceania   <tibble[,4] [12 x 4]>
##  7 Austria     Europe    <tibble[,4] [12 x 4]>
##  8 Bahrain     Asia      <tibble[,4] [12 x 4]>
##  9 Bangladesh  Asia      <tibble[,4] [12 x 4]>
## 10 Belgium     Europe    <tibble[,4] [12 x 4]>
## # ... with 132 more rows

Isso cria um dataset que possui uma linha por grupo (by_country) e uma coluna bastante incomum: data. A coluna data é uma lista de dataframes. Parece uma ideia maluca: temos um dataframe com uma coluna que é uma lista de outros dataframe! Vou vocês vão ver em breve porque esta é uma boa ideia.

A coluna de data é um pouco complicada de olhar porque é uma lista razoavelmente complexa e ainda estamos trabalhando em boas ferramentas para explorar esses objetos. Infelizmente, o uso de str() não é recomendado, pois geralmente produz uma saída muito longa. Mas se você retirar um único elemento da coluna de dados, verá que ele contém todos os dados desse país (neste caso, Brasil).

by_country$data[[15]]
## # A tibble: 12 x 4
##     year lifeExp       pop gdpPercap
##    <int>   <dbl>     <int>     <dbl>
##  1  1952    50.9  56602560     2109.
##  2  1957    53.3  65551171     2487.
##  3  1962    55.7  76039390     3337.
##  4  1967    57.6  88049823     3430.
##  5  1972    59.5 100840058     4986.
##  6  1977    61.5 114313951     6660.
##  7  1982    63.3 128962939     7031.
##  8  1987    65.2 142938076     7807.
##  9  1992    67.1 155975974     6950.
## 10  1997    69.4 168546719     7958.
## 11  2002    71.0 179914212     8131.
## 12  2007    72.4 190010647     9066.

Observe a diferença entre um dataframe agrupados padrão e um dataframe aninhado: em um dataset agrupado, cada linha é uma observação, e em um dataset aninhado, cada linha é um grupo. Outra maneira de pensar sobre um dataframe aninhado é que agora temos uma meta-observação: uma linha que representa o curso de tempo completo de um país, em vez de um único ponto no tempo.

2.5.2 Removendo aninhamento dos dados

A função unnest() funciona repetindo as colunas regulares uma vez para cada elemento da coluna-lista. Por exemplo, no exemplo a seguir, repetimos a primeira linha 4 vezes (porque lá o primeiro elemento de y tem comprimento quatro) e a segunda linha uma vez:

tibble(x = 1:2, y = list(1:4, 1)) 
## # A tibble: 2 x 2
##       x y        
##   <int> <list>   
## 1     1 <int [4]>
## 2     2 <dbl [1]>
tibble(x = 1:2, y = list(seq(1,10,2), 1)) %>% unnest(y)
## # A tibble: 6 x 2
##       x     y
##   <int> <dbl>
## 1     1     1
## 2     1     3
## 3     1     5
## 4     1     7
## 5     1     9
## 6     2     1