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.
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()
epivot_longer()
: para pivotar a base.separate()
eunite()
: para separar variáveis concatenadas em uma única coluna ou uni-las.nest()
eunnest()
: 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.
<- table3 %>%
table_separate 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:
<- tibble(
stocks 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:
<- tribble(
tratamento ~ 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)
<- gapminder %>%
by_country 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).
$data[[15]] by_country
## # 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