Pregunta

Tengo algunos problemas para convertir mi data.frame de una tabla de ancho a una larga mesa. En el momento en que se ve así:

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 gustaría 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 mirado y ya intentado usar las funciones del melt() reshape() y ya que algunas personas estaban sugiriendo en cuestiones similares. Sin embargo, hasta ahora solo me dan resultados desordenados.

Si es posible me gustaría hacerlo con la función reshape() desde se ve un poco más agradable poco a mango.

¿Fue útil?

Solución

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)

Otros consejos

Tres soluciones alternativas:

1: Con 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

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 melt como en el paquete reshape2 (que es una implementación extendida y mejorada). melt de data.table tiene también más parámetros que la función de melt-reshape2. Por ejemplo, puede especificar también el nombre de la variable de columnas:

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 los valores NA, se puede añadir a la na.rm = TRUE melt, 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)

Con 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"]))

Dado que esta respuesta es etiquetada con , sentí que sería útil para compartir otra alternativa de la base R:. stack

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

  

retornos is.vector TRUE si x es un vector del modo especificado no tener atributos aparte de nombres . Devuelve FALSE lo contrario.

Estoy usando los datos de la muestra de @ respuesta de Jaap, donde los valores de las columnas son factors año.

Este es el enfoque 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

Este es otro ejemplo que muestra el uso de gather de tidyr. Puede seleccionar las columnas a gather ya sea mediante la eliminación de ellos de forma individual (como lo hago aquí), o mediante la inclusión de los años que quieren de forma explícita.

Tenga en cuenta que, para manejar las comas (y de X añadido si no se establece check.names = FALSE), También estoy usando mutan de dplyr con parse_number de readr para convertir los valores de texto de nuevo a los números. Estos son todos 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))

Las devoluciones:

   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

Aquí hay una solución:

sqldf("Select Code, Country, '1950' As Year, `1950` As Value From wide
        Union All
       Select Code, Country, '1951' As Year, `1951` As Value From wide
        Union All
       Select Code, Country, '1952' As Year, `1952` As Value From wide
        Union All
       Select Code, Country, '1953' As Year, `1953` As Value From wide
        Union All
       Select Code, Country, '1954' As Year, `1954` As Value From wide;")

Para realizar la consulta sin necesidad de escribir en todo, se puede utilizar el siguiente:

G. Grothendieck de su aplicación.

ValCol <- tail(names(wide), -2)

s <- sprintf("Select Code, Country, '%s' As Year, `%s` As Value from wide", ValCol, ValCol)
mquery <- paste(s, collapse = "\n Union All\n")

cat(mquery) #just to show the query
 # Select Code, Country, '1950' As Year, `1950` As Value from wide
 #  Union All
 # Select Code, Country, '1951' As Year, `1951` As Value from wide
 #  Union All
 # Select Code, Country, '1952' As Year, `1952` As Value from wide
 #  Union All
 # Select Code, Country, '1953' As Year, `1953` As Value from wide
 #  Union All
 # Select Code, Country, '1954' As Year, `1954` As Value from wide

sqldf(mquery)
 #    Code     Country Year  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

Por desgracia, no creo que PIVOT y UNPIVOT funcionaría para R SQLite. Si desea escribir seguimiento de la consulta de una manera más sofisticada, también puede echar un vistazo a estos mensajes:

Usando sprintf redacción de SQL consultas o las variables Pass de sqldf

Con tidyr_1.0.0, otra opción es pivot_longer

library(tidyr)
pivot_longer(df1, -c(Code, Country), values_to = "Value", names_to = "Year")
# A tibble: 10 x 4
#   Code  Country     Year  Value 
#   <fct> <fct>       <chr> <fct> 
# 1 AFG   Afghanistan 1950  20,249
# 2 AFG   Afghanistan 1951  21,352
# 3 AFG   Afghanistan 1952  22,532
# 4 AFG   Afghanistan 1953  23,557
# 5 AFG   Afghanistan 1954  24,555
# 6 ALB   Albania     1950  8,097 
# 7 ALB   Albania     1951  8,986 
# 8 ALB   Albania     1952  10,058
# 9 ALB   Albania     1953  11,123
#10 ALB   Albania     1954  12,246

datos

df1 <- structure(list(Code = structure(1:2, .Label = c("AFG", "ALB"), class = "factor"), 
    Country = structure(1:2, .Label = c("Afghanistan", "Albania"
    ), class = "factor"), `1950` = structure(1:2, .Label = c("20,249", 
    "8,097"), class = "factor"), `1951` = structure(1:2, .Label = c("21,352", 
    "8,986"), class = "factor"), `1952` = structure(2:1, .Label = c("10,058", 
    "22,532"), class = "factor"), `1953` = structure(2:1, .Label = c("11,123", 
    "23,557"), class = "factor"), `1954` = structure(2:1, .Label = c("12,246", 
    "24,555"), class = "factor")), class = "data.frame", row.names = c(NA, 
-2L))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top