Remodelando o data.frame do formato wide para o longo

Eu tenho alguns problemas para converter meus data.frame de uma tabela ampla para uma tabela longa. No momento, parece assim:

 Code Country 1950 1951 1952 1953 1954 AFG Afghanistan 20,249 21,352 22,532 23,557 24,555 ALB Albania 8,097 8,986 10,058 11,123 12,246 

Agora eu gosto de transformar esse data.frame em um data.frame longo. Algo assim:

 Code Country Year Value AFG Afghanistan 1950 20,249 AFG Afghanistan 1951 21,352 AFG Afghanistan 1952 22,532 AFG Afghanistan 1953 23,557 AFG Afghanistan 1954 24,555 ALB Albania 1950 8,097 ALB Albania 1951 8,986 ALB Albania 1952 10,058 ALB Albania 1953 11,123 ALB Albania 1954 12,246 

Eu olhei e tentei já com o melt() e as funções de reshape() como algumas pessoas estavam sugerindo perguntas semelhantes. No entanto, até agora eu só tenho resultados confusos.

Se for possível, eu gostaria de fazer isso com a function reshape() , já que parece um pouco mais legal de lidar.

reshape() leva um tempo para se acostumar, da mesma forma que melt / cast . Aqui está uma solução com reformulação, assumindo que seu quadro de dados é chamado d :

 reshape(d, direction = "long", varying = list(names(d)[3:7]), v.names = "Value", idvar = c("Code","Country"), timevar = "Year", times = 1950:1954) 

Três soluções alternativas:

1: com reshape2

 library(reshape2) long <- melt(wide, id.vars = c("Code", "Country")) 

dando:

  Code Country variable value 1 AFG Afghanistan 1950 20,249 2 ALB Albania 1950 8,097 3 AFG Afghanistan 1951 21,352 4 ALB Albania 1951 8,986 5 AFG Afghanistan 1952 22,532 6 ALB Albania 1952 10,058 7 AFG Afghanistan 1953 23,557 8 ALB Albania 1953 11,123 9 AFG Afghanistan 1954 24,555 10 ALB Albania 1954 12,246 

Algumas notações alternativas que dão o mesmo resultado:

 # you can also define the id-variables by column number melt(wide, id.vars = 1:2) # as an alternative you can also specify the measure-variables # all other variables will then be used as id-variables melt(wide, measure.vars = 3:7) melt(wide, measure.vars = as.character(1950:1954)) 

2: com data.table

Você pode usar a mesma function de melt como no pacote reshape2 (que é uma implementação estendida e aprimorada). melt do data.table também possui mais parâmetros que a function melt da reshape2 . Por exemplo, você também pode especificar o nome da coluna-variável:

 library(data.table) long <- melt(setDT(wide), id.vars = c("Code","Country"), variable.name = "year") 

Algumas notações alternativas:

 melt(setDT(wide), id.vars = 1:2, variable.name = "year") melt(setDT(wide), measure.vars = 3:7, variable.name = "year") melt(setDT(wide), measure.vars = as.character(1950:1954), variable.name = "year") 

3: Com o tidyr

 library(tidyr) long <- wide %>% gather(year, value, -c(Code, Country)) 

Algumas notações alternativas:

 wide %>% gather(year, value, -Code, -Country) wide %>% gather(year, value, -1:-2) wide %>% gather(year, value, -(1:2)) wide %>% gather(year, value, -1, -2) wide %>% gather(year, value, 3:7) wide %>% gather(year, value, `1950`:`1954`) 

Se você quiser excluir valores de NA , você pode adicionar na.rm = TRUE às funções de melt , bem como de gather .


Outro problema com os dados é que os valores serão lidos por R como valores de caracteres (como resultado de , nos números). Você pode reparar isso com gsub e as.numeric :

 long$value <- as.numeric(gsub(",", "", long$value)) 

Ou diretamente com data.table ou dplyr :

 # data.table long <- melt(setDT(wide), id.vars = c("Code","Country"), variable.name = "year")[, value := as.numeric(gsub(",", "", value))] # tidyr and dplyr long <- wide %>% gather(year, value, -c(Code,Country)) %>% mutate(value = as.numeric(gsub(",", "", value))) 

Dados:

 wide <- read.table(text="Code Country 1950 1951 1952 1953 1954 AFG Afghanistan 20,249 21,352 22,532 23,557 24,555 ALB Albania 8,097 8,986 10,058 11,123 12,246", header=TRUE, check.names=FALSE) 

Usando o pacote de reformulação :

 #data x <- read.table(textConnection( "Code Country 1950 1951 1952 1953 1954 AFG Afghanistan 20,249 21,352 22,532 23,557 24,555 ALB Albania 8,097 8,986 10,058 11,123 12,246"), header=TRUE) library(reshape) x2 <- melt(x, id = c("Code", "Country"), variable_name = "Year") x2[,"Year"] <- as.numeric(gsub("X", "" , x2[,"Year"])) 

Como essa resposta é marcada com r-faq , senti que seria útil compartilhar outra alternativa da base R: stack .

Note, no entanto, que a stack não funciona com factor s – ela só funciona se is.vector for TRUE e, a partir da documentação de is.vector , descobrirmos que:

is.vector retorna TRUE se x for um vetor do modo especificado sem atributos além de nomes . Ele retorna FALSE caso contrário.

Estou usando os dados de amostra da resposta do @ Jaap , em que os valores nas colunas do ano são factor s.

Aqui está a abordagem da stack :

 cbind(wide[1:2], stack(lapply(wide[-c(1, 2)], as.character))) ## Code Country values ind ## 1 AFG Afghanistan 20,249 1950 ## 2 ALB Albania 8,097 1950 ## 3 AFG Afghanistan 21,352 1951 ## 4 ALB Albania 8,986 1951 ## 5 AFG Afghanistan 22,532 1952 ## 6 ALB Albania 10,058 1952 ## 7 AFG Afghanistan 23,557 1953 ## 8 ALB Albania 11,123 1953 ## 9 AFG Afghanistan 24,555 1954 ## 10 ALB Albania 12,246 1954 

Aqui está outro exemplo mostrando o uso de gather do tidyr . Você pode selecionar as colunas para gather removendo-as individualmente (como faço aqui) ou incluindo os anos desejados explicitamente.

Note que, para lidar com as vírgulas (e X’s adicionadas se check.names = FALSE não estiver setado), eu também estou usando o mutate de parse_number com parse_number de readr para converter os valores de texto de volta para números. Estes são todos parte do tidyverse e assim podem ser carregados junto com a library(tidyverse)

 wide %>% gather(Year, Value, -Code, -Country) %>% mutate(Year = parse_number(Year) , Value = parse_number(Value)) 

Retorna:

  Code Country Year Value 1 AFG Afghanistan 1950 20249 2 ALB Albania 1950 8097 3 AFG Afghanistan 1951 21352 4 ALB Albania 1951 8986 5 AFG Afghanistan 1952 22532 6 ALB Albania 1952 10058 7 AFG Afghanistan 1953 23557 8 ALB Albania 1953 11123 9 AFG Afghanistan 1954 24555 10 ALB Albania 1954 12246