La remodelación hoja.de.datos de gran angular a formato largo
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.
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 r- faq , sentí que sería útil para compartir otra alternativa de la base R:. stack
Tenga en cuenta, sin embargo, que stack
no funciona con factor
s - 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 . DevuelveFALSE
lo contrario.
Estoy usando los datos de la muestra de @ respuesta de Jaap, donde los valores de las columnas son factor
s 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 sqldf 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))