2010-02-02 8 views
77

Tengo algunos problemas para convertir mi data.frame de una tabla ancha a una tabla larga. En el momento en que se ve así:Remodelación de data.frame de formato ancho a largo

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 

Ahora me gusta transformar este data.frame en una larga data.frame. Algo como esto:

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 

he buscado y probado ya con el melt() y las funciones reshape() ya que algunas personas estaban sugiriendo a preguntas similares. Sin embargo, hasta ahora solo obtengo resultados desordenados.

Si es posible, me gustaría hacerlo con la función reshape() ya que se ve un poco más agradable de manejar.

+1

no sabemos si ese era el problema, pero las funciones del paquete de cambio de forma son la fusión y fundición –

+0

Y el paquete reshape ha sido sustituido por reshape2. –

+2

Y ahora reshape2 ha sido reemplazado por tidyr. – drhagen

Respuesta

54

reshape() toma un tiempo para acostumbrarse a ellos, al igual que melt/cast. Aquí es una solución con cambio de forma, asumiendo que su trama de datos se llama d:

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

Usando reshape paquete:

#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"])) 
64

tres soluciones alternativas:

1: Con reshape2

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

entrega:

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 

Algunas notaciones alternativas que dan el mismo 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: con data.table

Se puede utilizar la misma función que en el meltreshape2 paquete (que es un & mejor aplicación extendida). melt de data.table también tiene más parámetros que el melt de reshape2.Se puede, por exaple también especificar el nombre de la columna de la variable:

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

Algunas notaciones 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: Con tidyr

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

Algunas notaciones 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`) 

Si desea excluir NA valores, se puede añadir a la na.rm = TRUEmelt, así como las funciones gather.


Otro problema con los datos es que los valores serán leídos por R como valores de caracteres (como resultado de la , en los números). Puede reparar que con gsub y as.numeric:

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

o directamente con data.table o 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))) 

datos:

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) 
+0

gran respuesta, solo un pequeño recordatorio más: no coloque ninguna variable que no sea 'id' y 'time' en su marco de datos,' melt' no podría decir lo que quiere hacer en este caso. –

+1

@JasonGoal ¿Podrías profundizar en eso? Como estoy interpretando tu comentario, no debería ser un problema. Simplemente especifique los 'id.vars' y' measure.vars'. – Jaap

+0

, entonces eso es bueno para mí, no sé 'id.vars' y' measure.vars' se puede especificar en la primera alternativa, lo siento por el desastre, es mi culpa. –

1

Este es otro ejemplo que muestra el uso de gather de tidyr. Puede seleccionar las columnas al gather ya sea eliminándolas individualmente (como hago aquí), o incluyendo los años que desea explícitamente.

Tenga en cuenta que, para manejar las comas (y X de añadir si check.names = FALSE no está configurado), También estoy usando dplyr 's mutar con parse_number de readr para convertir los valores de texto de nuevo a los números. Todos ellos forman parte de la tidyverse y así pueden cargarse junto con library(tidyverse)

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

Devuelve:

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 
3

Dado que esta respuesta se etiqueta con , sentí que sería útil compartir otra alternativa de base R: stack.

Tenga en cuenta, sin embargo, que stack no funciona con factor s - sólo funciona si es is.vectorTRUE, y de la documentación para is.vector, nos encontramos con que:

is.vector vuelve TRUE si x es una vector del modo especificado que no tiene atributos que no sean nombres. Devuelve FALSE de lo contrario.

estoy usando los datos de la muestra from @Jaap's answer, en donde los valores de las columnas año son factor s.

Aquí está la stack enfoque: (. Y refundida)

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 
Cuestiones relacionadas