Pular para o conteúdo principal

Long para wide no R

Entenda por que transformar seus dados entre os formatos long e wide e veja como fazer isso no R com melt() e dcast!
· 15 min leer

Explore com IA

Em muitos momentos você vai precisar converter dados do formato long ("long-form" — o que isso significa ficará claro adiante) para o formato wide e vice-versa. Na prática, os dados chegam em todo tipo de formato, mas a maioria das funções do R foi pensada para um formato específico.

Por exemplo, lme4::lmer() para modelos lineares mistos e ggplot2::ggplot() para visualização foram projetadas para dados em long-form. Já outras funções esperam dados em wide-form, como mosaicplot(). Isso significa que você precisa converter o formato para analisar ou visualizar, a menos que os dados já tenham sido processados e estejam no formato certo.

Existem várias opções no R para conversão de formato, mas para quem está começando pode ser bem confuso.

Este tutorial mostra a mecânica de converter dados entre long-form e wide-form. Você vai ver:

  • O que é o formato long.
  • Depois, o que é o formato wide.
  • Um resumo das diferenças entre os formatos long e wide;
  • Os passos necessários para ir de um formato ao outro.
  • Por fim, como fazer tudo isso no R. Você vai aprender a ir de long para wide com as funções melt() e dcast().

Os dados

Tabela 01
namegenderyear2011year2012year2013year2014year2015
Jackson SmithM74.6984.9991.73105.11111.04
Sophia JohnsonFNANANANA75.89
Emma WilliamsFNANA75.7486.5091.50
Aiden JonesMNANANA71.8981.42
Liam BrownM88.2496.91101.85108.13112.45
Lucas DavisM70.6083.7894.17100.03106.35
Olivia MillerF64.7880.7687.3097.13103.80
Ava WilsonF88.7796.45104.72112.84NA

Esta tabela mostra a altura de bebês em uma pequena região. As alturas foram medidas de 2011 a 2015. Como você já deve saber, NA significa "Not Available" (não disponível). Pela posição dos NAs, dá para inferir a idade dos bebês, mas a idade exata não está disponível na tabela. Ela parece bem organizada, mas para quem quer estudar crescimento infantil existe um problema: o que importa não é o momento absoluto da medição, e sim a idade do bebê no momento da medição. Dá para inserir essa informação na tabela? Abaixo está uma forma:

Tabela 02
namegenderage0age1age2age3age4age5age6
Jackson SmithMNA76.6984.9197.06105.73107.32NA
Sophia JohnsonFNA76.05NANANANANA
Emma WilliamsF72.6582.4691.76NANANANA
Aiden JonesM69.7679.89NANANANANA
Liam BrownMNANA85.3493.22105.78105.84114.82
Lucas DavisM71.5984.8792.10100.73104.21NANA
Olivia MillerF62.7481.3992.0298.32106.44NANA
Ava WilsonFNANA88.39100.67107.58111.52NA

O número de NAs aumentou. ## Dados em wide-form Mas como você faria para indicar quantos meses o bebê tinha? Seria algo assim:

Tabela 03
namegenderm4m6m7m10m12...m55m60m65m72
Jackson SmithMNANANANA76.69...NA107.32NANA
Sophia JohnsonFNANANANA76.05...NANANANA
Emma WilliamsFNANANA72.65NA...NANANANA
Aiden JonesMNA69.76NANANA...NANANANA
Liam BrownMNANANANANA...NA105.84NA114.82
Lucas DavisMNANA71.59NANA...104.21NANANA
Olivia MillerF62.74NANANANA...NANANANA
Ava WilsonFNANANANANA...NANA111.52NA

Há inúmeros NAs na tabela acima. Se os bebês diferem nos momentos relativos das medições, o formato wide pode desperdiçar muito espaço. Outra opção seria incluir as idades dos bebês referentes a 2015, mas isso só funciona quando os tempos absolutos de medição são os mesmos. Se as datas variam por meses e você quer idades em meses, não funciona. Neste caso, o formato long é uma alternativa melhor. ## Dados em long-form Enquanto no wide-form todas as medições de um bebê ficam em uma linha, no long-form cada linha traz uma única medição de um bebê. A forma mais simples seria exibir o nome da criança, o momento da medição e a medição (altura) em cada linha. É exatamente o que esta tabela mostra:

Tabela 04
namegendermonthheight
Olivia MillerF462.74
Aiden JonesM669.76
Lucas DavisM771.59
Emma WilliamsF1072.65
Jackson SmithM1276.69
............
Liam BrownM72114.82

Se você ordenar por nome, terá algo como a tabela abaixo. E ela ocupa bem menos espaço que a Tabela 3!

Tabela 05
namegendermonthheight
Jackson SmithM1276.69
Jackson SmithM2484.91
Jackson SmithM3697.06
Jackson SmithM48105.73
Jackson SmithM60107.32
Sophia JohnsonF1276.05
Emma WilliamsF1072.65
............
Ava WilsonF65111.52

Long versus wide

Repare de novo nas diferenças fundamentais entre wide-form e long-form: uma diferença simples é que o wide-form mostra várias medições de um indivíduo em uma linha, e os nomes das colunas indicam o que são essas medições.

Esta tabela mostra as alturas de Liam Brown em 2011-2015 em uma única linha:

Tabela 06
namegenderyear2011year2012year2013year2014year2015
Liam BrownM88.2496.91101.85108.13112.45

No long-form, as alturas de Liam Brown aparecem em uma coluna:

Tabela 07
namegender_
Liam BrownM85.34
Liam BrownM93.22
Liam BrownM105.78
Liam BrownM105.84
Liam BrownM114.82

Assim, você não sabe o que essas medições representam. Se anotar o nome da coluna, ficaria assim:

Tabela 08
namegenderyear2011, 2012, 2013, 2014, 2015
Liam BrownM85.34
Liam BrownM93.22
Liam BrownM105.78
Liam BrownM105.84
Liam BrownM114.82

Mas isso também tem um problema. As alturas de Sophia Johnson não foram medidas de 2011 a 2014. Então, como você vê abaixo, o nome da coluna não pode ser definido com facilidade:

Tabela 09
namegender?
Liam BrownM85.34
Liam BrownM93.22
Liam BrownM105.78
Liam BrownM105.84
Liam BrownM114.82
Sophia JohnsonF76.05

De wide-form para long-form

Converter de wide para long pode ser pensado como um processo em etapas. Antes de transformar várias medições de uma linha em uma coluna, você pode reestruturar a tabela para que haja apenas uma medição por linha. Vamos fazer isso com esta tabela:

Tabela 10
namegenderage0age1age2age3age4age5age6
Emma WilliamsF72.6582.4691.76NANANANA
Aiden JonesM69.7679.89NANANANANA

O resultado fica assim:

Tabela 11
namegenderage0age1age2age3age4age5age6
Emma WilliamsF72.65  NANANANA
Emma WilliamsF 82.46    NA
Emma WilliamsF  91.76    
Aiden JonesM69.76 NANANANANA
Aiden JonesM 79.89     

Antes de juntar essas medições espalhadas pelas colunas em uma única coluna, você precisa criar uma coluna adicional dizendo o que é a medição, como abaixo:

Tabela 12
namegendermeasureage0age1age2age3age4age5age6
Emma WilliamsFage072.65  NANANANA
Emma WilliamsFage1 82.46    NA
Emma WilliamsFage2  91.76    
Aiden JonesMage069.76 NANANANANA
Aiden JonesMage1 79.89     

O nome dessa coluna geralmente é "measure" ou "key". Note na tabela acima que aparecem "age0", "age1", "age2", "age3", "age4", "age5" tanto nos nomes das colunas quanto em uma coluna. Se você omitir os nomes das colunas, a tabela fica assim:

Tabela 13
namegendermeasure____________________________
Emma WilliamsFage072.65  NANANANA
Emma WilliamsFage1 82.46     
Emma WilliamsFage2  91.76    
Aiden JonesMage069.76 NANANANANA
Aiden JonesMage1 79.89     

Como agora há apenas uma medição por linha, você pode juntá-las em uma coluna, resultando em:

Tabela 14
namegendermeasure 
Emma WilliamsFage072.65
Emma WilliamsFage182.46
Emma WilliamsFage291.76
Aiden JonesMage069.76
Aiden JonesMage179.89

Vamos dar um nome para a nova coluna de medições. Normalmente ela se chama "value":

Tabela 15
namegendermeasurevalue
Emma WilliamsFage072.65
Emma WilliamsFage182.46
Emma WilliamsFage291.76
Aiden JonesMage069.76
Aiden JonesMage179.89

Observe a tabela acima. Percebeu que "age" se repete? Você pode remover o prefixo repetido, chegando a:

Tabela 16
namegenderagevalue
Emma WilliamsF072.65
Emma WilliamsF182.46
Emma WilliamsF291.76
Aiden JonesM069.76
Aiden JonesM179.89

Age, que se repetia em todas as linhas, agora é um nome de coluna. O nome value pode ser mais específico. Vamos trocá-lo por height, que comunica melhor. Veja o resultado:

Tabela 17
namegenderageheight
Emma WilliamsF072.65
Emma WilliamsF182.46
Emma WilliamsF291.76
Aiden JonesM069.76
Aiden JonesM179.89

Compare a Tabela 15 com a Tabela 17. value pode significar qualquer coisa, enquanto height tem significado específico: altura. Já value na Tabela 15 poderia ser altura, peso, velocidade ou cor. Outra alternativa seria esta:

Tabela 18
namegendermeasurevalue
Emma WilliamsFheight.age072.65
Emma WilliamsFheight.age182.46
Emma WilliamsFheight.age291.76
Aiden JonesMheight.age069.76
Aiden JonesMheight.age179.89

Considere agora que as medições são diversas. Se forem altura, peso e IMC, teríamos algo como a tabela abaixo. Mas aí a coluna value reúne valores em escalas diferentes, o que pode confundir a leitura. Nesta tabela, altura está em centímetros e peso em quilogramas:

Tabela 19
namegendermeasurevalue
Emma WilliamsFheight72.65
Emma WilliamsFweight8.22
Emma WilliamsFBMI22.60
Aiden JonesMheight69.76
Aiden JonesMweight8.51

Portanto, ao converter de wide para long, você precisa decidir quais valores serão reunidos em uma coluna. Depois, definir o nome dessa coluna.

Resumindo: para converter de wide para long você precisa decidir quais colunas serão combinadas em uma única coluna. Ao fazer isso, várias colunas viram uma só, então é necessário um nome para ela, como mostram as próximas tabelas:

Tabela 20
id1id2age0age1age2
Emma WilliamsFNA82.4691.76
Aiden JonesM69.7679.89NA
Emma WilliamsFNA82.4691.76
Tabela 21
id1id2age0age1age2
Emma WilliamsFNA  
Emma WilliamsF 82.46 
Emma WilliamsF  91.76
Aiden JonesM69.76  
Aiden JonesM 79.89 
Aiden JonesM  NA
Emma WilliamsFNA  
Emma WilliamsF 82.46 
Emma WilliamsF  91.76
Tabela 22
id1id2age_age0age1age2
Emma WilliamsFage0NA  
Emma WilliamsFage1 82.46 
Emma WilliamsFage2  91.76
Aiden JonesMage069.76  
Aiden JonesMage1 79.89 
Aiden JonesMage2  NA
Emma WilliamsFage0NA  
Emma WilliamsFage1 82.46 
Emma WilliamsFage2  91.76
Tabela 23
id1id2age_ 
Emma WilliamsFage0NA
Emma WilliamsFage182.46
Emma WilliamsFage291.76
Aiden JonesMage069.76
Aiden JonesMage179.89
Aiden JonesMage2NA
Emma WilliamsFage0NA
Emma WilliamsFage182.46
Emma WilliamsFage291.76
Tabela 24
id1id2keyvalue
Emma WilliamsFage0NA
Emma WilliamsFage182.46
Emma WilliamsFage291.76
Aiden JonesMage069.76
Aiden JonesMage179.89
Aiden JonesMage2NA
Emma WilliamsFage0NA
Emma WilliamsFage182.46
Emma WilliamsFage291.76
Tabela 25
id1id2keyvalue
Emma WilliamsFage182.46
Emma WilliamsFage291.76
Aiden JonesMage069.76
Aiden JonesMage179.89
Emma WilliamsFage182.46
Emma WilliamsFage291.76

Observe as colunas id1 e id2 na tabela acima. id2 não identifica um indivíduo; é um atributo (gênero) do indivíduo. Em outras palavras, pode ser tratado como uma medição. Portanto, você pode converter para um formato ainda mais long. O formato mais "longo" possível tem três colunas: id, key e value.

Tabela 26
idkeyvalue
Emma WilliamsgenderF
Emma Williamsage182.46
Emma Williamsage291.76
Aiden JonesgenderF
Aiden Jonesage069.76
Aiden Jonesage179.89
Emma WilliamsgenderF
Emma Williamsage182.46
Emma Williamsage291.76

A tabela acima mostra o formato mais longo possível da Tabela 25. Porém, pessoas diferentes podem ter o mesmo nome.

Nesse caso, o long-form pode gerar ambiguidade, já que id não identifica unicamente a pessoa. Você pode adicionar um número após o nome para diferenciar (por exemplo, com make.unique() no R), mas não há como distinguir um nome que originalmente já tinha número. Pense em alguém que realmente se chame "John.2"!

A saída óbvia é atribuir um identificador numérico distinto para cada pessoa. Nesse cenário, o nome passa a ser mais uma medição.

A próxima tabela mostra que até os nomes entram como medições. Em certo sentido, este é o formato verdadeiramente mais longo.

Tabela 27
idkeyvalue
1nameEmma Williams
1genderF
1age182.46
1age291.76
2nameAiden Jones
2genderF
2age069.76
2age179.89
3nameEmma Williams
3genderF
3age182.46
3age291.76

O formato mais longo é o mais fácil de transformar de volta em wide-form. Se você reverter o processo mostrado nas Tabelas 20 a 25, chega ao formato wide. As tabelas a seguir mostram esse processo:

Tabela 28. As medições na coluna key viram nomes de colunas
idkeynamegenderage0age1age2
1nameEmma Williams    
1gender F   
1age1   82.46 
1age2    91.76
2nameAiden Jones    
2gender F   
2age0  69.76  
2age1   79.89 
3nameEmma Williams    
3gender F   
3age1   82.46 
3age2    91.76
Tabela 29. A coluna key pode ser removida
idnamegenderage0age1age2
1Emma Williams    
1 F   
1   82.46 
1    91.76
2Aiden Jones    
2 F   
2  69.76  
2   79.89 
3Emma Williams    
3 F   
3   82.46 
3    91.76
Tabela 30. O mesmo id pode ser reunido em uma linha
idnamegenderage0age1age2
1Emma WilliamsF 82.4691.76
1     
1     
1     
2Aiden JonesF69.7679.89 
2     
2     
2     
3Emma WilliamsF 82.4691.76
3     
3     
3     
Tabela 31. Exclua linhas sem medições
idnamegenderage0age1age2
1Emma WilliamsF 82.4691.76
2Aiden JonesF69.7679.89 
3Emma WilliamsF 82.4691.76

O ponto-chave ao converter de wide para long é decidir quais colunas serão reunidas em uma só.

Mas não precisa ser apenas uma. Você pode usar duas colunas, se necessário, como na tabela abaixo:

Tabela 32
namegenderh2011h2012w2011w2012
Jackson SmithM74.6984.999.6012.0
Olivia MillerFNA80.767.1510.7
Ava WilsonF88.7796.45NA15.0

Você consegue levá-la para o long-form?

As colunas h2011, h2012, w2011, w2012 significam alturas e pesos em 2011 e 2012. height e weight estão em escalas diferentes — cm e kg. Você pode usar uma coluna só para ambos ou duas colunas, como nesta tabela:

Tabela 33
namegenderyearheightweight
Jackson SmithM201174.699.60
Jackson SmithM201284.9912.00
Olivia MillerF2011NA80.76
Olivia MillerF20127.1510.70
Ava WilsonF201188.7796.45
Ava WilsonF2012NA15.00

Este é long-form ou wide-form?

De long para wide no R

Vamos fazer a conversão entre long e wide no R. Existem várias funções e pacotes para isso.

Tabela 34
funcpackageto_long_formto_wide_form
stack/unstackutilsstackunstack
reshapestatsreshape(direction="long", ...)reshape(direction="wide", ...)
melt/dcastreshape2meltdcast
gather/spreadtidyrgatherspread

Aqui, vamos ver como usar as funções melt() e dcast() do pacote reshape2. O motivo é que stack() e unstack() são funções básicas e normalmente exigem pós-processamento. Já gather() e spread() do tidyr também são muito populares, mas não serão abordadas neste tutorial.

Outra função possível é reshape(), do pacote stats. Não confunda com o pacote reshape! Esse pacote, junto com o reshape2, foi criado para quem tinha dificuldades com reshape(). Além disso, reshape() parece assumir dados longitudinais (medições repetidas ao longo do tempo).

Vamos converter os dados da Tabela 35 para long-form. Suponha que a tabela abaixo esteja em um data.frame chamado dat.

Tabela 35
namegenderyear2011year2012year2013
Jackson SmithM74.6984.9991.73
Emma WilliamsFNANA75.74
Liam BrownM88.24NA101.85
Ava WilsonF88.7796.45NA

babies = data.frame(name= c("Jackson Smith", "Emma Williams", "Liam Brown", "Ava Wilson"), gender = c("M", "F", "M", "F"), year2011= c(74.69, NA, 88.24, 88.77), year2012=c(84.99, NA, NA, 96.45), year2013=c(91.73, 75.74, 101.83, NA)) babies

##            name gender year2011 year2012 year2013
## 1 Jackson Smith      M    74.69    84.99    91.73
## 2 Emma Williams      F       NA       NA    75.74
## 3    Liam Brown      M    88.24       NA   101.83
## 4    Ava Wilson      F    88.77    96.45       NA

A pergunta é: "quais colunas combinar em uma só?".

Neste caso, year2011, year2012, year2013. Ao usar melt(), liste essas colunas em measure.vars:

melt(babies, measure.vars = c("year2011", "year2012", "year2013"))

##             name gender variable  value
## 1  Jackson Smith      M year2011  74.69
## 2  Emma Williams      F year2011     NA
## 3     Liam Brown      M year2011  88.24
## 4     Ava Wilson      F year2011  88.77
## 5  Jackson Smith      M year2012  84.99
## 6  Emma Williams      F year2012     NA
## 7     Liam Brown      M year2012     NA
## 8     Ava Wilson      F year2012  96.45
## 9  Jackson Smith      M year2013  91.73
## 10 Emma Williams      F year2013  75.74
## 11    Liam Brown      M year2013 101.83
## 12    Ava Wilson      F year2013     NA

year2011, year2012, year2013 estão nas colunas 3, 4 e 5, então você pode usar:

melt(babies, measure.vars = 3:5)

##             name gender variable  value
## 1  Jackson Smith      M year2011  74.69
## 2  Emma Williams      F year2011     NA
## 3     Liam Brown      M year2011  88.24
## 4     Ava Wilson      F year2011  88.77
## 5  Jackson Smith      M year2012  84.99
## 6  Emma Williams      F year2012     NA
## 7     Liam Brown      M year2012     NA
## 8     Ava Wilson      F year2012  96.45
## 9  Jackson Smith      M year2013  91.73
## 10 Emma Williams      F year2013  75.74
## 11    Liam Brown      M year2013 101.83
## 12    Ava Wilson      F year2013     NA

O resultado é o mostrado acima. Note os nomes das colunas variable e value. As Tabelas 23 e 24 mostraram que novos nomes de colunas são necessários. A função melt() usa por padrão variable e value. Você pode sobrescrever com variable.name= e value.name=. Veja:

melt(babies, measure.vars=3:5, variable.name="year", value.name="height")

##             name gender     year height
## 1  Jackson Smith      M year2011  74.69
## 2  Emma Williams      F year2011     NA
## 3     Liam Brown      M year2011  88.24
## 4     Ava Wilson      F year2011  88.77
## 5  Jackson Smith      M year2012  84.99
## 6  Emma Williams      F year2012     NA
## 7     Liam Brown      M year2012     NA
## 8     Ava Wilson      F year2012  96.45
## 9  Jackson Smith      M year2013  91.73
## 10 Emma Williams      F year2013  75.74
## 11    Liam Brown      M year2013 101.83
## 12    Ava Wilson      F year2013     NA

Nas definições acima, especificamos as medições. Em outros cenários, as colunas de identificação (id) podem indiretamente especificar o restante. Assim, você pode escolher colunas de id em vez de medições.

Os dois comandos abaixo produzem o mesmo resultado. O primeiro escolhe as colunas de id; o segundo, as colunas a serem reunidas:

melt(babies, id.vars=c("name","gender"))

melt(babies, measure.vars = c("year2011", "year2012", "year2013"))

##             name gender variable  value
## 1  Jackson Smith      M year2011  74.69
## 2  Emma Williams      F year2011     NA
## 3     Liam Brown      M year2011  88.24
## 4     Ava Wilson      F year2011  88.77
## 5  Jackson Smith      M year2012  84.99
## 6  Emma Williams      F year2012     NA
## 7     Liam Brown      M year2012     NA
## 8     Ava Wilson      F year2012  96.45
## 9  Jackson Smith      M year2013  91.73
## 10 Emma Williams      F year2013  75.74
## 11    Liam Brown      M year2013 101.83
## 12    Ava Wilson      F year2013     NA

Se você especificar tanto id.vars quanto measure.vars, as colunas que não estiverem em nenhuma dessas listas serão excluídas do resultado. Exemplo:

melt(babies, id.vars=c("name"), measure.vars=c("year2011", "year2012"))

##            name variable value
## 1 Jackson Smith year2011 74.69
## 2 Emma Williams year2011    NA
## 3    Liam Brown year2011 88.24
## 4    Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 6 Emma Williams year2012    NA
## 7    Liam Brown year2012    NA
## 8    Ava Wilson year2012 96.45

Além disso, na.rm=T remove NAs:

melt(babies, id.vars=c("name"), measure.vars=c("year2011", "year2012"), na.rm=T)

##            name variable value
## 1 Jackson Smith year2011 74.69
## 3    Liam Brown year2011 88.24
## 4    Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8    Ava Wilson year2012 96.45

A função dcast() faz o inverso de melt(). id, variable e value são as colunas do formato mais longo. dcast(data, id ~ variable, value.var="value") leva do formato mais longo ao wide.

babiesLong <- melt(babies, id.vars=c("name"), measure.vars=c("year2011", "year2012"), na.rm=T)
babiesLong

##            name variable value
## 1 Jackson Smith year2011 74.69
## 3    Liam Brown year2011 88.24
## 4    Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8    Ava Wilson year2012 96.45

babiesLong tem name como id, variable como variável e value como valor. Logo, podemos usar dcast() para voltar ao wide:

dcast(babiesLong, name ~ variable, value.var="value")

##            name year2011 year2012
## 1    Ava Wilson    88.77    96.45
## 2 Jackson Smith    74.69    84.99
## 3    Liam Brown    88.24       NA

Conclusão

É isso! Neste tutorial, o foco foi entender a lógica e o processo de reshaping dos dados. Em geral, o long-form é preferido para análise, mas há exceções. A apresentação em wide-form pode economizar espaço quando faz sentido, porém, à medida que crescem os valores faltantes, vale mais a pena converter para long.

Na conversão de formatos, mantenha o foco no que são as medições. O lema do long-form é: uma medição por linha. No wide-form, várias medições por linha. Quando você reúne medições espalhadas por colunas em uma só coluna, os nomes originais dessas colunas precisam ser preservados em uma coluna adicional. Quando espalha os valores de uma coluna em várias colunas, é a partir da coluna variable que você define os nomes das novas colunas.

Depois que você entende o processo, usar funções como dcast() e melt() fica muito mais simples!

Temas

Relacionado

Tutorial

Tutorial de Pipes em R para iniciantes