Pacote R tidyr e suas novas funções pivot_longer e pivot_wider

Pacote arrumador incluído no núcleo de uma das bibliotecas mais populares da linguagem R - arrumado.
O principal objetivo do pacote é trazer os dados de forma precisa.

Já disponível no Habré publicação dedicado a este pacote, mas remonta a 2015. E quero falar sobre as mudanças mais atuais, que foram anunciadas há poucos dias por seu autor, Hedley Wickham.

Pacote R tidyr e suas novas funções pivot_longer e pivot_wider

SJK: Reunir() e espalhar() serão obsoletos?

Hadley Wickham: Até certo ponto. Não recomendaremos mais o uso dessas funções e corrigiremos bugs nelas, mas elas continuarão presentes no pacote em seu estado atual.

Conteúdo

Se você estiver interessado em análise de dados, talvez esteja interessado em meu telegrama и Youtube canais. A maior parte do conteúdo é dedicada à linguagem R.

Conceito TidyData

Meta arrumador - ajudá-lo a trazer os dados para um formato chamado organizado. Dados puros são dados onde:

  • Cada variável está em uma coluna.
  • Cada observação é uma string.
  • Cada valor é uma célula.

É muito mais fácil e conveniente trabalhar com dados apresentados de forma organizada ao realizar análises.

Principais funções incluídas no pacote tidyr

tidyr contém um conjunto de funções projetadas para transformar tabelas:

  • fill() — preencher valores faltantes em uma coluna com valores anteriores;
  • separate() — divide um campo em vários usando um separador;
  • unite() — realiza a operação de combinar vários campos em um, a ação inversa da função separate();
  • pivot_longer() — uma função que converte dados de formato largo para formato longo;
  • pivot_wider() - uma função que converte dados de formato longo para formato largo. A operação inversa daquela realizada pela função pivot_longer().
  • gather()obsoleto — uma função que converte dados de formato largo para formato longo;
  • spread()obsoleto - uma função que converte dados de formato longo para formato largo. A operação inversa daquela realizada pela função gather().

Novo conceito para conversão de dados de formato largo para formato longo e vice-versa

Anteriormente, funções eram usadas para esse tipo de transformação gather() и spread(). Ao longo dos anos de existência destas funções, tornou-se óbvio que para a maioria dos utilizadores, incluindo o autor do pacote, os nomes destas funções e os seus argumentos não eram totalmente óbvios, e causavam dificuldades em encontrá-las e compreender qual destas funções converte um quadro de data do formato largo para o formato longo e vice-versa.

A este respeito, em arrumador Foram adicionadas duas funções novas e importantes, projetadas para transformar quadros de datas.

Novos recursos pivot_longer() и pivot_wider() foram inspirados por alguns dos recursos do pacote cdata, criado por John Mount e Nina Zumel.

Instalando a versão mais atual do tidyr 0.8.3.9000

Para instalar a versão nova e mais atual do pacote arrumador 0.8.3.9000, onde novos recursos estiverem disponíveis, use o código a seguir.

devtools::install_github("tidyverse/tidyr")

No momento em que este artigo foi escrito, essas funções estavam disponíveis apenas na versão dev do pacote no GitHub.

Transição para novos recursos

Na verdade, não é difícil transferir scripts antigos para trabalhar com novas funções, para melhor compreensão pegarei um exemplo da documentação de funções antigas e mostrarei como as mesmas operações são realizadas usando novas pivot_*() funções.

Converta formato largo em formato longo.

Código de exemplo da documentação da função de coleta

# example
library(dplyr)
stocks <- data.frame(
  time = as.Date('2009-01-01') + 0:9,
  X = rnorm(10, 0, 1),
  Y = rnorm(10, 0, 2),
  Z = rnorm(10, 0, 4)
)

# old
stocks_gather <- stocks %>% gather(key   = stock, 
                                   value = price, 
                                   -time)

# new
stocks_long   <- stocks %>% pivot_longer(cols      = -time, 
                                       names_to  = "stock", 
                                       values_to = "price")

Convertendo formato longo em formato largo.

Código de exemplo da documentação da função spread

# old
stocks_spread <- stocks_gather %>% spread(key = stock, 
                                          value = price) 

# new 
stock_wide    <- stocks_long %>% pivot_wider(names_from  = "stock",
                                            values_from = "price")

Porque nos exemplos acima de trabalho com pivot_longer() и pivot_wider(), na tabela original AÇÕES nenhuma coluna listada nos argumentos nomes_para и valores_para seus nomes devem estar entre aspas.

Uma tabela que o ajudará a descobrir mais facilmente como passar a trabalhar com um novo conceito arrumador.

Pacote R tidyr e suas novas funções pivot_longer e pivot_wider

Nota do autor

Todo o texto abaixo é adaptativo, diria até tradução livre vinhetas do site oficial da biblioteca tidyverse.

Um exemplo simples de conversão de dados de formato largo para formato longo

pivot_longer () — torna os conjuntos de dados mais longos, reduzindo o número de colunas e aumentando o número de linhas.

Pacote R tidyr e suas novas funções pivot_longer e pivot_wider

Para executar os exemplos apresentados no artigo, primeiro você precisa conectar os pacotes necessários:

library(tidyr)
library(dplyr)
library(readr)

Digamos que temos uma tabela com os resultados de uma pesquisa que (entre outras coisas) perguntou às pessoas sobre sua religião e renda anual:

#> # A tibble: 18 x 11
#>    religion `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k`
#>    <chr>      <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
#>  1 Agnostic      27        34        60        81        76       137
#>  2 Atheist       12        27        37        52        35        70
#>  3 Buddhist      27        21        30        34        33        58
#>  4 Catholic     418       617       732       670       638      1116
#>  5 Don’t k…      15        14        15        11        10        35
#>  6 Evangel…     575       869      1064       982       881      1486
#>  7 Hindu          1         9         7         9        11        34
#>  8 Histori…     228       244       236       238       197       223
#>  9 Jehovah…      20        27        24        24        21        30
#> 10 Jewish        19        19        25        25        30        95
#> # … with 8 more rows, and 4 more variables: `$75-100k` <dbl>,
#> #   `$100-150k` <dbl>, `>150k` <dbl>, `Don't know/refused` <dbl>

Esta tabela contém os dados religiosos dos entrevistados em linhas, e os níveis de renda estão espalhados pelos nomes das colunas. O número de entrevistados de cada categoria é armazenado nos valores das células na intersecção da religião e do nível de renda. Para deixar a tabela em um formato limpo e correto, basta usar pivot_longer():

pew %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")

pew %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")
#> # A tibble: 180 x 3
#>    religion income             count
#>    <chr>    <chr>              <dbl>
#>  1 Agnostic <$10k                 27
#>  2 Agnostic $10-20k               34
#>  3 Agnostic $20-30k               60
#>  4 Agnostic $30-40k               81
#>  5 Agnostic $40-50k               76
#>  6 Agnostic $50-75k              137
#>  7 Agnostic $75-100k             122
#>  8 Agnostic $100-150k            109
#>  9 Agnostic >150k                 84
#> 10 Agnostic Don't know/refused    96
#> # … with 170 more rows

Argumentos de função pivot_longer()

  • Primeiro argumento cols, descreve quais colunas precisam ser mescladas. Neste caso, todas as colunas, exceto tempo.
  • Argumento nomes_para dá o nome da variável que será criada a partir dos nomes das colunas que concatenamos.
  • valores_para dá o nome de uma variável que será criada a partir dos dados armazenados nos valores das células das colunas mescladas.

Especificações

Esta é uma nova funcionalidade do pacote arrumador, que anteriormente não estava disponível ao trabalhar com funções legadas.

Uma especificação é um quadro de dados, cada linha corresponde a uma coluna no novo quadro de data de saída e duas colunas especiais que começam com:

  • . Nome contém o nome da coluna original.
  • .valor contém o nome da coluna que conterá os valores das células.

As colunas restantes da especificação refletem como a nova coluna exibirá o nome das colunas compactadas de . Nome.

A especificação descreve os metadados armazenados em um nome de coluna, com uma linha para cada coluna e uma coluna para cada variável, combinados com o nome da coluna, esta definição pode parecer confusa no momento, mas depois de olhar alguns exemplos ela se tornará muito mais claro.

O objetivo da especificação é que você pode recuperar, modificar e definir novos metadados para o dataframe que está sendo convertido.

Para trabalhar com especificações ao converter uma tabela de formato largo para formato longo, use a função pivot_longer_spec().

O funcionamento dessa função é que ela pega qualquer período de data e gera seus metadados da maneira descrita acima.

Como exemplo, vamos pegar o conjunto de dados who fornecido com o pacote arrumador. Este conjunto de dados contém informações fornecidas pela organização internacional de saúde sobre a incidência da tuberculose.

who
#> # A tibble: 7,240 x 60
#>    country iso2  iso3   year new_sp_m014 new_sp_m1524 new_sp_m2534
#>    <chr>   <chr> <chr> <int>       <int>        <int>        <int>
#>  1 Afghan… AF    AFG    1980          NA           NA           NA
#>  2 Afghan… AF    AFG    1981          NA           NA           NA
#>  3 Afghan… AF    AFG    1982          NA           NA           NA
#>  4 Afghan… AF    AFG    1983          NA           NA           NA
#>  5 Afghan… AF    AFG    1984          NA           NA           NA
#>  6 Afghan… AF    AFG    1985          NA           NA           NA
#>  7 Afghan… AF    AFG    1986          NA           NA           NA
#>  8 Afghan… AF    AFG    1987          NA           NA           NA
#>  9 Afghan… AF    AFG    1988          NA           NA           NA
#> 10 Afghan… AF    AFG    1989          NA           NA           NA
#> # … with 7,230 more rows, and 53 more variables

Vamos construir sua especificação.

spec <- who %>%
  pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")

#> # A tibble: 56 x 3
#>    .name        .value name        
#>    <chr>        <chr>  <chr>       
#>  1 new_sp_m014  count  new_sp_m014 
#>  2 new_sp_m1524 count  new_sp_m1524
#>  3 new_sp_m2534 count  new_sp_m2534
#>  4 new_sp_m3544 count  new_sp_m3544
#>  5 new_sp_m4554 count  new_sp_m4554
#>  6 new_sp_m5564 count  new_sp_m5564
#>  7 new_sp_m65   count  new_sp_m65  
#>  8 new_sp_f014  count  new_sp_f014 
#>  9 new_sp_f1524 count  new_sp_f1524
#> 10 new_sp_f2534 count  new_sp_f2534
#> # … with 46 more rows

campos país, iso2, iso3 já são variáveis. Nossa tarefa é inverter as colunas com novo_sp_m014 em newrel_f65.

Os nomes dessas colunas armazenam as seguintes informações:

  • Prefixo new_ indica que a coluna contém dados sobre novos casos de tuberculose, o quadro de datas atual contém informações apenas sobre novas doenças, portanto este prefixo no contexto atual não tem qualquer significado.
  • sp/rel/sp/ep descreve um método para diagnosticar uma doença.
  • m/f sexo do paciente.
  • 014/1524/2535/3544/4554/65 faixa etária do paciente.

Podemos dividir essas colunas usando a função extract()usando expressão regular.

spec <- spec %>%
        extract(name, c("diagnosis", "gender", "age"), "new_?(.*)_(.)(.*)")

#> # A tibble: 56 x 5
#>    .name        .value diagnosis gender age  
#>    <chr>        <chr>  <chr>     <chr>  <chr>
#>  1 new_sp_m014  count  sp        m      014  
#>  2 new_sp_m1524 count  sp        m      1524 
#>  3 new_sp_m2534 count  sp        m      2534 
#>  4 new_sp_m3544 count  sp        m      3544 
#>  5 new_sp_m4554 count  sp        m      4554 
#>  6 new_sp_m5564 count  sp        m      5564 
#>  7 new_sp_m65   count  sp        m      65   
#>  8 new_sp_f014  count  sp        f      014  
#>  9 new_sp_f1524 count  sp        f      1524 
#> 10 new_sp_f2534 count  sp        f      2534 
#> # … with 46 more rows

Observe a coluna . Nome deve permanecer inalterado, pois este é o nosso índice nos nomes das colunas do conjunto de dados original.

Gênero e idade (colunas gênero и idade) possuem valores fixos e conhecidos, por isso é recomendado converter essas colunas em fatores:

spec <-  spec %>%
            mutate(
              gender = factor(gender, levels = c("f", "m")),
              age = factor(age, levels = unique(age), ordered = TRUE)
            ) 

Finalmente, para aplicar a especificação que criamos ao período original que precisamos usar um argumento especulação em função pivot_longer().

who %>% pivot_longer(spec = spec)

#> # A tibble: 405,440 x 8
#>    country     iso2  iso3   year diagnosis gender age   count
#>    <chr>       <chr> <chr> <int> <chr>     <fct>  <ord> <int>
#>  1 Afghanistan AF    AFG    1980 sp        m      014      NA
#>  2 Afghanistan AF    AFG    1980 sp        m      1524     NA
#>  3 Afghanistan AF    AFG    1980 sp        m      2534     NA
#>  4 Afghanistan AF    AFG    1980 sp        m      3544     NA
#>  5 Afghanistan AF    AFG    1980 sp        m      4554     NA
#>  6 Afghanistan AF    AFG    1980 sp        m      5564     NA
#>  7 Afghanistan AF    AFG    1980 sp        m      65       NA
#>  8 Afghanistan AF    AFG    1980 sp        f      014      NA
#>  9 Afghanistan AF    AFG    1980 sp        f      1524     NA
#> 10 Afghanistan AF    AFG    1980 sp        f      2534     NA
#> # … with 405,430 more rows

Tudo o que acabamos de fazer pode ser esquematicamente representado da seguinte forma:

Pacote R tidyr e suas novas funções pivot_longer e pivot_wider

Especificação usando vários valores (.value)

No exemplo acima, a coluna de especificação .valor continha apenas um valor; na maioria dos casos, esse é o caso.

Mas ocasionalmente pode surgir uma situação em que você precisa coletar dados de colunas com diferentes tipos de dados em valores. Usando uma função legada spread() isso seria muito difícil de fazer.

O exemplo abaixo foi retirado de vinhetas para o pacote Tabela de dados.

Vamos criar um dataframe de treinamento.

family <- tibble::tribble(
  ~family,  ~dob_child1,  ~dob_child2, ~gender_child1, ~gender_child2,
       1L, "1998-11-26", "2000-01-29",             1L,             2L,
       2L, "1996-06-22",           NA,             2L,             NA,
       3L, "2002-07-11", "2004-04-05",             2L,             2L,
       4L, "2004-10-10", "2009-08-27",             1L,             1L,
       5L, "2000-12-05", "2005-02-28",             2L,             1L,
)
family <- family %>% mutate_at(vars(starts_with("dob")), parse_date)

#> # A tibble: 5 x 5
#>   family dob_child1 dob_child2 gender_child1 gender_child2
#>    <int> <date>     <date>             <int>         <int>
#> 1      1 1998-11-26 2000-01-29             1             2
#> 2      2 1996-06-22 NA                     2            NA
#> 3      3 2002-07-11 2004-04-05             2             2
#> 4      4 2004-10-10 2009-08-27             1             1
#> 5      5 2000-12-05 2005-02-28             2             1

O período criado contém dados sobre os filhos de uma família em cada linha. As famílias podem ter um ou dois filhos. Para cada criança, os dados são fornecidos sobre a data de nascimento e sexo, e os dados de cada criança estão em colunas separadas; nossa tarefa é trazer esses dados para o formato correto para análise.

Observe que temos duas variáveis ​​com informações sobre cada criança: seu sexo e data de nascimento (colunas com o prefixo rolha contém data de nascimento, colunas com prefixo gênero conter o sexo da criança). O resultado esperado é que eles apareçam em colunas separadas. Podemos fazer isso gerando uma especificação na qual a coluna .value terá dois significados diferentes.

spec <- family %>%
  pivot_longer_spec(-family) %>%
  separate(col = name, into = c(".value", "child"))%>%
  mutate(child = parse_number(child))

#> # A tibble: 4 x 3
#>   .name         .value child
#>   <chr>         <chr>  <dbl>
#> 1 dob_child1    dob        1
#> 2 dob_child2    dob        2
#> 3 gender_child1 gender     1
#> 4 gender_child2 gender     2

Então, vamos dar uma olhada passo a passo nas ações executadas pelo código acima.

  • pivot_longer_spec(-family) — crie uma especificação que comprima todas as colunas existentes, exceto a coluna da família.
  • separate(col = name, into = c(".value", "child")) - dividir a coluna . Nome, que contém os nomes dos campos de origem, usando o sublinhado e inserindo os valores resultantes nas colunas .valor и criança.
  • mutate(child = parse_number(child)) — transformar os valores do campo criança do tipo de dados de texto para numérico.

Agora podemos aplicar a especificação resultante ao dataframe original e trazer a tabela para o formato desejado.

family %>% 
    pivot_longer(spec = spec, na.rm = T)

#> # A tibble: 9 x 4
#>   family child dob        gender
#>    <int> <dbl> <date>      <int>
#> 1      1     1 1998-11-26      1
#> 2      1     2 2000-01-29      2
#> 3      2     1 1996-06-22      2
#> 4      3     1 2002-07-11      2
#> 5      3     2 2004-04-05      2
#> 6      4     1 2004-10-10      1
#> 7      4     2 2009-08-27      1
#> 8      5     1 2000-12-05      2
#> 9      5     2 2005-02-28      1

Usamos argumento na.rm = TRUE, porque a forma atual dos dados força a criação de linhas extras para observações inexistentes. Porque a família 2 tem apenas um filho, na.rm = TRUE garante que a família 2 terá uma linha na saída.

Convertendo quadros de data de formato longo para largo

pivot_wider() - é a transformação inversa e vice-versa aumenta o número de colunas do quadro de data reduzindo o número de linhas.

Pacote R tidyr e suas novas funções pivot_longer e pivot_wider

Esse tipo de transformação raramente é usado para deixar os dados em um formato preciso; no entanto, essa técnica pode ser útil para criar tabelas dinâmicas usadas em apresentações ou para integração com outras ferramentas.

Na verdade as funções pivot_longer() и pivot_wider() são simétricos e produzem ações inversas entre si, ou seja: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) retornará o df original.

O exemplo mais simples de conversão de uma tabela para um formato amplo

Para demonstrar como a função funciona pivot_wider() usaremos o conjunto de dados encontros_peixe, que armazena informações sobre como diferentes estações registram o movimento dos peixes ao longo do rio.

#> # A tibble: 114 x 3
#>    fish  station  seen
#>    <fct> <fct>   <int>
#>  1 4842  Release     1
#>  2 4842  I80_1       1
#>  3 4842  Lisbon      1
#>  4 4842  Rstr        1
#>  5 4842  Base_TD     1
#>  6 4842  BCE         1
#>  7 4842  BCW         1
#>  8 4842  BCE2        1
#>  9 4842  BCW2        1
#> 10 4842  MAE         1
#> # … with 104 more rows

Na maioria dos casos, esta tabela será mais informativa e mais fácil de usar se você apresentar informações para cada estação em uma coluna separada.

fish_encounters %>% pivot_wider(names_from = station, values_from = seen)

fish_encounters %>% pivot_wider(names_from = station, values_from = seen)
#> # A tibble: 19 x 12
#>    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE
#>    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int>
#>  1 4842        1     1      1     1       1     1     1     1     1     1
#>  2 4843        1     1      1     1       1     1     1     1     1     1
#>  3 4844        1     1      1     1       1     1     1     1     1     1
#>  4 4845        1     1      1     1       1    NA    NA    NA    NA    NA
#>  5 4847        1     1      1    NA      NA    NA    NA    NA    NA    NA
#>  6 4848        1     1      1     1      NA    NA    NA    NA    NA    NA
#>  7 4849        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#>  8 4850        1     1     NA     1       1     1     1    NA    NA    NA
#>  9 4851        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#> 10 4854        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#> # … with 9 more rows, and 1 more variable: MAW <int>

Este conjunto de dados apenas registra informações quando peixes são detectados pela estação, ou seja, se algum peixe não foi registrado por alguma estação, então esse dado não estará na tabela. Isso significa que a saída será preenchida com NA.

Porém, neste caso sabemos que a ausência de registro significa que o peixe não foi avistado, então podemos usar o argumento valores_preenchimento em função pivot_wider() e preencha esses valores ausentes com zeros:

fish_encounters %>% pivot_wider(
  names_from = station, 
  values_from = seen,
  values_fill = list(seen = 0)
)

#> # A tibble: 19 x 12
#>    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE
#>    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int>
#>  1 4842        1     1      1     1       1     1     1     1     1     1
#>  2 4843        1     1      1     1       1     1     1     1     1     1
#>  3 4844        1     1      1     1       1     1     1     1     1     1
#>  4 4845        1     1      1     1       1     0     0     0     0     0
#>  5 4847        1     1      1     0       0     0     0     0     0     0
#>  6 4848        1     1      1     1       0     0     0     0     0     0
#>  7 4849        1     1      0     0       0     0     0     0     0     0
#>  8 4850        1     1      0     1       1     1     1     0     0     0
#>  9 4851        1     1      0     0       0     0     0     0     0     0
#> 10 4854        1     1      0     0       0     0     0     0     0     0
#> # … with 9 more rows, and 1 more variable: MAW <int>

Gerando um nome de coluna a partir de diversas variáveis ​​de origem

Imagine que temos uma tabela contendo uma combinação de produto, país e ano. Para gerar um período de teste, você pode executar o seguinte código:

df <- expand_grid(
  product = c("A", "B"), 
  country = c("AI", "EI"), 
  year = 2000:2014
) %>%
  filter((product == "A" & country == "AI") | product == "B") %>% 
  mutate(value = rnorm(nrow(.)))

#> # A tibble: 45 x 4
#>    product country  year    value
#>    <chr>   <chr>   <int>    <dbl>
#>  1 A       AI       2000 -2.05   
#>  2 A       AI       2001 -0.676  
#>  3 A       AI       2002  1.60   
#>  4 A       AI       2003 -0.353  
#>  5 A       AI       2004 -0.00530
#>  6 A       AI       2005  0.442  
#>  7 A       AI       2006 -0.610  
#>  8 A       AI       2007 -2.77   
#>  9 A       AI       2008  0.899  
#> 10 A       AI       2009 -0.106  
#> # … with 35 more rows

Nossa tarefa é expandir o quadro de dados para que uma coluna contenha dados para cada combinação de produto e país. Para fazer isso, basta passar o argumento nomes_de um vetor contendo os nomes dos campos a serem mesclados.

df %>% pivot_wider(names_from = c(product, country),
                 values_from = "value")

#> # A tibble: 15 x 4
#>     year     A_AI    B_AI    B_EI
#>    <int>    <dbl>   <dbl>   <dbl>
#>  1  2000 -2.05     0.607   1.20  
#>  2  2001 -0.676    1.65   -0.114 
#>  3  2002  1.60    -0.0245  0.501 
#>  4  2003 -0.353    1.30   -0.459 
#>  5  2004 -0.00530  0.921  -0.0589
#>  6  2005  0.442   -1.55    0.594 
#>  7  2006 -0.610    0.380  -1.28  
#>  8  2007 -2.77     0.830   0.637 
#>  9  2008  0.899    0.0175 -1.30  
#> 10  2009 -0.106   -0.195   1.03  
#> # … with 5 more rows

Você também pode aplicar especificações a uma função pivot_wider(). Mas quando submetido a pivot_wider() a especificação faz a conversão oposta pivot_longer(): As colunas especificadas em . Nome, usando valores de .valor e outras colunas.

Para este conjunto de dados, você pode gerar uma especificação personalizada se quiser que cada combinação possível de país e produto tenha sua própria coluna, não apenas aquelas presentes nos dados:

spec <- df %>% 
  expand(product, country, .value = "value") %>% 
  unite(".name", product, country, remove = FALSE)

#> # A tibble: 4 x 4
#>   .name product country .value
#>   <chr> <chr>   <chr>   <chr> 
#> 1 A_AI  A       AI      value 
#> 2 A_EI  A       EI      value 
#> 3 B_AI  B       AI      value 
#> 4 B_EI  B       EI      value

df %>% pivot_wider(spec = spec) %>% head()

#> # A tibble: 6 x 5
#>    year     A_AI  A_EI    B_AI    B_EI
#>   <int>    <dbl> <dbl>   <dbl>   <dbl>
#> 1  2000 -2.05       NA  0.607   1.20  
#> 2  2001 -0.676      NA  1.65   -0.114 
#> 3  2002  1.60       NA -0.0245  0.501 
#> 4  2003 -0.353      NA  1.30   -0.459 
#> 5  2004 -0.00530    NA  0.921  -0.0589
#> 6  2005  0.442      NA -1.55    0.594

Vários exemplos avançados de trabalho com o novo conceito tidyr

Limpeza de dados usando o conjunto de dados de Renda e Aluguel do Censo dos EUA como exemplo.

Conjunto de dados us_rent_income contém informações sobre renda mediana e aluguel para todos os estados dos EUA em 2017 (conjunto de dados disponível no pacote arrumado).

us_rent_income
#> # A tibble: 104 x 5
#>    GEOID NAME       variable estimate   moe
#>    <chr> <chr>      <chr>       <dbl> <dbl>
#>  1 01    Alabama    income      24476   136
#>  2 01    Alabama    rent          747     3
#>  3 02    Alaska     income      32940   508
#>  4 02    Alaska     rent         1200    13
#>  5 04    Arizona    income      27517   148
#>  6 04    Arizona    rent          972     4
#>  7 05    Arkansas   income      23789   165
#>  8 05    Arkansas   rent          709     5
#>  9 06    California income      29454   109
#> 10 06    California rent         1358     3
#> # … with 94 more rows

Na forma em que os dados são armazenados no conjunto de dados us_rent_income trabalhar com eles é extremamente inconveniente, por isso gostaríamos de criar um conjunto de dados com colunas: alugar, rent_moe, como, renda_moe. Existem muitas maneiras de criar essa especificação, mas o ponto principal é que precisamos gerar cada combinação de valores de variáveis ​​e estimativa/moee, em seguida, gere o nome da coluna.

  spec <- us_rent_income %>% 
    expand(variable, .value = c("estimate", "moe")) %>% 
    mutate(
      .name = paste0(variable, ifelse(.value == "moe", "_moe", ""))
    )

#> # A tibble: 4 x 3
#>   variable .value   .name     
#>   <chr>    <chr>    <chr>     
#> 1 income   estimate income    
#> 2 income   moe      income_moe
#> 3 rent     estimate rent      
#> 4 rent     moe      rent_moe

Fornecendo esta especificação pivot_wider() nos dá o resultado que procuramos:

us_rent_income %>% pivot_wider(spec = spec)

#> # A tibble: 52 x 6
#>    GEOID NAME                 income income_moe  rent rent_moe
#>    <chr> <chr>                 <dbl>      <dbl> <dbl>    <dbl>
#>  1 01    Alabama               24476        136   747        3
#>  2 02    Alaska                32940        508  1200       13
#>  3 04    Arizona               27517        148   972        4
#>  4 05    Arkansas              23789        165   709        5
#>  5 06    California            29454        109  1358        3
#>  6 08    Colorado              32401        109  1125        5
#>  7 09    Connecticut           35326        195  1123        5
#>  8 10    Delaware              31560        247  1076       10
#>  9 11    District of Columbia  43198        681  1424       17
#> 10 12    Florida               25952         70  1077        3
#> # … with 42 more rows

O Banco Mundial

Às vezes, trazer um conjunto de dados para o formato desejado requer várias etapas.
Conjunto de dados banco_mundo_pop contém dados do Banco Mundial sobre a população de cada país entre 2000 e 2018.

#> # A tibble: 1,056 x 20
#>    country indicator `2000` `2001` `2002` `2003`  `2004`  `2005`   `2006`
#>    <chr>   <chr>      <dbl>  <dbl>  <dbl>  <dbl>   <dbl>   <dbl>    <dbl>
#>  1 ABW     SP.URB.T… 4.24e4 4.30e4 4.37e4 4.42e4 4.47e+4 4.49e+4  4.49e+4
#>  2 ABW     SP.URB.G… 1.18e0 1.41e0 1.43e0 1.31e0 9.51e-1 4.91e-1 -1.78e-2
#>  3 ABW     SP.POP.T… 9.09e4 9.29e4 9.50e4 9.70e4 9.87e+4 1.00e+5  1.01e+5
#>  4 ABW     SP.POP.G… 2.06e0 2.23e0 2.23e0 2.11e0 1.76e+0 1.30e+0  7.98e-1
#>  5 AFG     SP.URB.T… 4.44e6 4.65e6 4.89e6 5.16e6 5.43e+6 5.69e+6  5.93e+6
#>  6 AFG     SP.URB.G… 3.91e0 4.66e0 5.13e0 5.23e0 5.12e+0 4.77e+0  4.12e+0
#>  7 AFG     SP.POP.T… 2.01e7 2.10e7 2.20e7 2.31e7 2.41e+7 2.51e+7  2.59e+7
#>  8 AFG     SP.POP.G… 3.49e0 4.25e0 4.72e0 4.82e0 4.47e+0 3.87e+0  3.23e+0
#>  9 AGO     SP.URB.T… 8.23e6 8.71e6 9.22e6 9.77e6 1.03e+7 1.09e+7  1.15e+7
#> 10 AGO     SP.URB.G… 5.44e0 5.59e0 5.70e0 5.76e0 5.75e+0 5.69e+0  4.92e+0
#> # … with 1,046 more rows, and 11 more variables: `2007` <dbl>,
#> #   `2008` <dbl>, `2009` <dbl>, `2010` <dbl>, `2011` <dbl>, `2012` <dbl>,
#> #   `2013` <dbl>, `2014` <dbl>, `2015` <dbl>, `2016` <dbl>, `2017` <dbl>

Nosso objetivo é criar um conjunto de dados organizado com cada variável em sua própria coluna. Não está claro exatamente quais etapas são necessárias, mas começaremos com o problema mais óbvio: o ano está espalhado por várias colunas.

Para corrigir isso você precisa usar a função pivot_longer().

pop2 <- world_bank_pop %>% 
  pivot_longer(`2000`:`2017`, names_to = "year")

#> # A tibble: 19,008 x 4
#>    country indicator   year  value
#>    <chr>   <chr>       <chr> <dbl>
#>  1 ABW     SP.URB.TOTL 2000  42444
#>  2 ABW     SP.URB.TOTL 2001  43048
#>  3 ABW     SP.URB.TOTL 2002  43670
#>  4 ABW     SP.URB.TOTL 2003  44246
#>  5 ABW     SP.URB.TOTL 2004  44669
#>  6 ABW     SP.URB.TOTL 2005  44889
#>  7 ABW     SP.URB.TOTL 2006  44881
#>  8 ABW     SP.URB.TOTL 2007  44686
#>  9 ABW     SP.URB.TOTL 2008  44375
#> 10 ABW     SP.URB.TOTL 2009  44052
#> # … with 18,998 more rows

O próximo passo é observar a variável indicadora.
pop2 %>% count(indicator)

#> # A tibble: 4 x 2
#>   indicator       n
#>   <chr>       <int>
#> 1 SP.POP.GROW  4752
#> 2 SP.POP.TOTL  4752
#> 3 SP.URB.GROW  4752
#> 4 SP.URB.TOTL  4752

Onde SP.POP.GROW é o crescimento populacional, SP.POP.TOTL é a população total e SP.URB. * a mesma coisa, mas apenas para áreas urbanas. Vamos dividir esses valores em duas variáveis: área - área (total ou urbana) e uma variável contendo dados reais (população ou crescimento):

pop3 <- pop2 %>% 
  separate(indicator, c(NA, "area", "variable"))

#> # A tibble: 19,008 x 5
#>    country area  variable year  value
#>    <chr>   <chr> <chr>    <chr> <dbl>
#>  1 ABW     URB   TOTL     2000  42444
#>  2 ABW     URB   TOTL     2001  43048
#>  3 ABW     URB   TOTL     2002  43670
#>  4 ABW     URB   TOTL     2003  44246
#>  5 ABW     URB   TOTL     2004  44669
#>  6 ABW     URB   TOTL     2005  44889
#>  7 ABW     URB   TOTL     2006  44881
#>  8 ABW     URB   TOTL     2007  44686
#>  9 ABW     URB   TOTL     2008  44375
#> 10 ABW     URB   TOTL     2009  44052
#> # … with 18,998 more rows

Agora tudo o que precisamos fazer é dividir a variável em duas colunas:

pop3 %>% 
  pivot_wider(names_from = variable, values_from = value)

#> # A tibble: 9,504 x 5
#>    country area  year   TOTL    GROW
#>    <chr>   <chr> <chr> <dbl>   <dbl>
#>  1 ABW     URB   2000  42444  1.18  
#>  2 ABW     URB   2001  43048  1.41  
#>  3 ABW     URB   2002  43670  1.43  
#>  4 ABW     URB   2003  44246  1.31  
#>  5 ABW     URB   2004  44669  0.951 
#>  6 ABW     URB   2005  44889  0.491 
#>  7 ABW     URB   2006  44881 -0.0178
#>  8 ABW     URB   2007  44686 -0.435 
#>  9 ABW     URB   2008  44375 -0.698 
#> 10 ABW     URB   2009  44052 -0.731 
#> # … with 9,494 more rows

Lista de contatos

Um último exemplo, imagine que você tem uma lista de contatos que copiou e colou de um site:

contacts <- tribble(
  ~field, ~value,
  "name", "Jiena McLellan",
  "company", "Toyota", 
  "name", "John Smith", 
  "company", "google", 
  "email", "[email protected]",
  "name", "Huxley Ratcliffe"
)

Tabular esta lista é bastante difícil porque não existe uma variável que identifique quais dados pertencem a qual contato. Podemos corrigir isso observando que os dados de cada novo contato começam com “nome”, então podemos criar um identificador único e incrementá-lo em um cada vez que a coluna do campo contiver o valor “nome”:

contacts <- contacts %>% 
  mutate(
    person_id = cumsum(field == "name")
  )
contacts

#> # A tibble: 6 x 3
#>   field   value            person_id
#>   <chr>   <chr>                <int>
#> 1 name    Jiena McLellan           1
#> 2 company Toyota                   1
#> 3 name    John Smith               2
#> 4 company google                   2
#> 5 email   [email protected]          2
#> 6 name    Huxley Ratcliffe         3

Agora que temos um ID exclusivo para cada contato, podemos transformar o campo e o valor em colunas:

contacts %>% 
  pivot_wider(names_from = field, values_from = value)

#> # A tibble: 3 x 4
#>   person_id name             company email          
#>       <int> <chr>            <chr>   <chr>          
#> 1         1 Jiena McLellan   Toyota  <NA>           
#> 2         2 John Smith       google  [email protected]
#> 3         3 Huxley Ratcliffe <NA>    <NA>

Conclusão

Minha opinião pessoal é que o novo conceito arrumador verdadeiramente mais intuitivo e significativamente superior em funcionalidade às funções legadas spread() и gather(). Espero que este artigo tenha ajudado você a lidar com pivot_longer() и pivot_wider().

Fonte: habr.com

Adicionar um comentário