Como remodelar dados de um formato longo para um formato amplo?

Estou tendo problemas para reorganizar o seguinte quadro de dados:

set.seed(45) dat1 <- data.frame( name = rep(c("firstName", "secondName"), each=4), numbers = rep(1:4, 2), value = rnorm(8) ) dat1 name numbers value 1 firstName 1 0.3407997 2 firstName 2 -0.7033403 3 firstName 3 -0.3795377 4 firstName 4 -0.7460474 5 secondName 1 -0.8981073 6 secondName 2 -0.3347941 7 secondName 3 -0.5013782 8 secondName 4 -0.1745357 

Eu quero reformulá-lo para que cada variável de “nome” exclusiva seja um nome novo, com os “valores” como observações ao longo dessa linha e os “números” como nomes de cole. Mais ou menos assim:

  name 1 2 3 4 1 firstName 0.3407997 -0.7033403 -0.3795377 -0.7460474 5 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357 

Eu olhei para melt e cast e algumas outras coisas, mas nenhuma parece fazer o trabalho.

   

    Usando a function reshape :

     reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide") 

    O novo (em 2014) pacote tidyr também faz isso simplesmente, com gather() / spread() sendo os termos para melt / cast .

     library(tidyr) spread(dat1, key = numbers, value = value) 

    Do github ,

    tidyr é uma reformulação da reshape2 projetada para acompanhar a estrutura de dados organizada e para trabalhar lado a lado com magrittr e dplyr para construir um pipeline sólido para a análise de dados.

    Assim como o reshape2 fez menos que reformular, o tidyr faz menos que reshape2 . Ele é projetado especificamente para arrumar dados, não a reformulação geral que a reformulação2 faz ou a agregação geral que a reformulação fez. Em particular, os methods tidyr funcionam apenas para frameworks de dados, e o tidyr não fornece margens ou agregação.

    Você pode fazer isso com a function reshape() ou com as funções melt() / cast() no pacote reshape. Para a segunda opção, o código de exemplo é

     library(reshape) cast(dat1, name ~ numbers) 

    Ou usando reshape2

     library(reshape2) dcast(dat1, name ~ numbers) 

    Outra opção se o desempenho é uma preocupação é usar a extensão de reshape2 das funções melt & dcast do reshape2

    ( Referência: reformulação eficiente usando data.tables )

     library(data.table) setDT(dat1) dcast(dat1, name ~ numbers, value.var = "value") # name 1 2 3 4 # 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078 # 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814 

    E, a partir do data.table v1.9.6, podemos lançar várias colunas

     ## add an extra column dat1[, value2 := value * 2] ## cast multiple value columns dcast(dat1, name ~ numbers, value.var = c("value", "value2")) # name value_1 value_2 value_3 value_4 value2_1 value2_2 value2_3 value2_4 # 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078 0.3672866 -1.6712572 3.190562 0.6590155 # 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814 -1.6409368 0.9748581 1.476649 1.1515627 

    Usando seu dataframe de exemplo, poderíamos:

     xtabs(value ~ name + numbers, data = dat1) 

    Outras duas opções:

    Pacote base:

     df < - unstack(dat1, form = value ~ numbers) rownames(df) <- unique(dat1$name) df 

    pacote sqldf :

     library(sqldf) sqldf('SELECT name, MAX(CASE WHEN numbers = 1 THEN value ELSE NULL END) x1, MAX(CASE WHEN numbers = 2 THEN value ELSE NULL END) x2, MAX(CASE WHEN numbers = 3 THEN value ELSE NULL END) x3, MAX(CASE WHEN numbers = 4 THEN value ELSE NULL END) x4 FROM dat1 GROUP BY name') 

    Usando aggregate function aggregate base R:

     aggregate(value ~ name, dat1, I) # name value.1 value.2 value.3 value.4 #1 firstName 0.4145 -0.4747 0.0659 -0.5024 #2 secondName -0.8259 0.1669 -0.8962 0.1681 

    Existe um novo pacote muito poderoso de cientistas de dados geniais da Win-Vector (pessoal que fez vtreat , seplyr e replyr ) chamado cdata . Ele implementa os princípios de “dados coordenados” descritos neste documento e também neste post . A ideia é que, independentemente de como você organizar seus dados, deve ser possível identificar pontos de dados individuais usando um sistema de “coordenadas de dados”. Aqui está um trecho da postagem recente do blog de John Mount:

    Todo o sistema é baseado em duas primitivas ou operadores cdata :: moveValuesToRowsD () e cdata :: moveValuesToColumnsD (). Esses operadores têm codificação pivot, un-pivot, one-hot, transpose, movendo várias linhas e colunas, e muitas outras transformações como casos especiais simples.

    É fácil escrever muitas operações diferentes em termos das primitivas cdata. Esses operadores podem trabalhar na memory ou em grande escala de dados (com bancos de dados e Apache Spark; para big data, use as variantes cdata :: moveValuesToRowsN () e cdata :: moveValuesToColumnsN ()). As transformações são controladas por uma tabela de controle que em si é um diagrama de (ou imagem da) transformação.

    Primeiro, criaremos a tabela de controle (consulte a postagem do blog para obter detalhes) e, em seguida, executaremos a movimentação de dados de linhas para colunas.

     library(cdata) # first build the control table pivotControlTable < - buildPivotControlTableD(table = dat1, # reference to dataset columnToTakeKeysFrom = 'numbers', # this will become column headers columnToTakeValuesFrom = 'value', # this contains data sep="_") # optional for making column names # perform the move of data to columns dat_wide <- moveValuesToColumnsD(tallTable = dat1, # reference to dataset keyColumns = c('name'), # this(these) column(s) should stay untouched controlTable = pivotControlTable# control table above ) dat_wide #> name numbers_1 numbers_2 numbers_3 numbers_4 #> 1 firstName 0.3407997 -0.7033403 -0.3795377 -0.7460474 #> 2 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357 

    A function de reshape base funciona perfeitamente bem:

     df < - data.frame( year = c(rep(2000, 12), rep(2001, 12)), month = rep(1:12, 2), values = rnorm(24) ) df_wide <- reshape(df, idvar="year", timevar="month", v.names="values", direction="wide", sep="_") df_wide 

    Onde

    • idvar é a coluna de classs que separa linhas
    • timevar é a coluna de classs para lançar ampla
    • v.names é a coluna que contém valores numéricos
    • direction especifica o formato amplo ou longo
    • o argumento sep opcional é o separador usado entre nomes de classs v.names e v.names na saída data.frame .

    Se não houver nenhum idvar , crie um antes de usar a function idvar reshape() :

     df$id < - c(rep("year1", 12), rep("year2", 12)) df_wide <- reshape(df, idvar="id", timevar="month", v.names="values", direction="wide", sep="_") df_wide 

    Apenas lembre-se que idvar é necessário! A parte timevar e v.names é fácil. A saída dessa function é mais previsível do que algumas das outras, já que tudo é explicitamente definido.