Reformar data.frame de formato ancho a largo

Resuelto mropa asked hace 54 años • 9 respuestas

Tengo algunos problemas para convertir data.frameuna mesa ancha a una mesa larga. Por el momento 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 esto data.frameen un largo 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

Miré y ya intenté usar las funciones melt()y reshape()como sugerían algunas personas en preguntas similares. Sin embargo, hasta ahora sólo obtengo resultados desordenados.

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

mropa avatar Jan 01 '70 08:01 mropa
Aceptado

Dos soluciones alternativas:

1) contabla de datos:

Puedes usar la meltfunción:

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

lo que da:

> long
    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

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

2) conordenado:

Usar pivot_longer():

library(tidyr)

long <- wide %>% 
  pivot_longer(
    cols = `1950`:`1954`, 
    names_to = "year",
    values_to = "value"
)

Nota:

  • names_toy values_toel valor predeterminado es "name"y "value", respectivamente, por lo que podría escribir esto de manera más sucinta como wide %>% pivot_longer(`1950`:`1954`).
  • El colsargumento utiliza el DSL tidyselect altamente flexible , por lo que puede seleccionar las mismas columnas usando una selección negativa ( !c(Code, Country)), un ayudante de selección ( starts_with("19"); matches("^\\d{4}$")), índices numéricos ( 3:7) y más.
  • tidyr::pivot_longer()es el sucesor de tidyr::gather()y reshape2::melt(), que ya no están en desarrollo.

Transformando valores

Otro problema con los datos es que R leerá los valores como valores de caracteres (como resultado de los ,números). Puedes reparar con gsuby as.numeric, ya sea antes de remodelar:

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

O durante la remodelación, con data.tableo tidyr:

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

# tidyr
long <- wide %>%
  pivot_longer(
    cols = `1950`:`1954`, 
    names_to = "year",
    values_to = "value",
    values_transform = ~ as.numeric(gsub(",", "", .x))
  )

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)
Jaap avatar Sep 15 '2014 20:09 Jaap

reshape()Toma un tiempo acostumbrarse, al igual que melt/ cast. Aquí hay una solución con remodelación, suponiendo que su marco de datos se llame d:

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

Con tidyr_1.0.0, otra opción espivot_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))
akrun avatar Sep 14 '2019 22:09 akrun