J'ai quelques difficultés à convertir mon data.frame
D'une table large à une table longue. Pour le moment ça ressemble à ça:
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
Maintenant, j'aimerais transformer ce data.frame
En un long data.frame
. Quelque chose comme ça:
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
J'ai déjà étudié et essayé d'utiliser les fonctions melt()
et reshape()
, comme certaines personnes le suggéraient dans des questions similaires. Cependant, jusqu'à présent, je n'ai que des résultats en désordre.
S'il est possible, j'aimerais le faire avec la fonction reshape()
car elle est un peu plus agréable à manipuler.
reshape()
prend un certain temps pour s'y habituer, tout comme melt
/cast
. Voici une solution avec reshape, en supposant que votre cadre de données s'appelle d
:
reshape(d, direction = "long", varying = list(names(d)[3:7]), v.names = "Value",
idvar = c("Code","Country"), timevar = "Year", times = 1950:1954)
Trois solutions alternatives:
1: avec reshape2
library(reshape2)
long <- melt(wide, id.vars = c("Code", "Country"))
donnant:
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
Quelques notations alternatives qui donnent le même résultat:
# 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: avec data.table
Vous pouvez utiliser la même fonction melt
que dans le reshape2
package (qui est une implémentation étendue et améliorée). melt
de data.table
possède également plus de paramètres que la fonction melt
- de reshape2
. Vous pouvez par exemple également spécifier le nom de la colonne de variable:
library(data.table)
long <- melt(setDT(wide), id.vars = c("Code","Country"), variable.name = "year")
Quelques notations alternatives:
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")
: avec tidyr
library(tidyr)
long <- wide %>% gather(year, value, -c(Code, Country))
Quelques notations alternatives:
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 vous voulez exclure les valeurs NA
, vous pouvez ajouter na.rm = TRUE
aux fonctions melt
ainsi qu'aux fonctions gather
.
Un autre problème avec les données est que les valeurs seront lues par R comme des valeurs de caractère (à la suite du ,
dans les chiffres). Vous pouvez réparer ça avec gsub
et as.numeric
:
long$value <- as.numeric(gsub(",", "", long$value))
Ou directement avec 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)))
Data:
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)
Utiliser le package reshape:
#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"]))
Puisque cette réponse est étiquetée avec r-faq , j’ai pensé qu’il serait utile de partager une autre alternative à partir de la base R: stack
.
Notez cependant que stack
ne fonctionne pas avec factor
s - il ne fonctionne que si is.vector
est TRUE
et de la documentation pour is.vector
, on constate que:
is.vector
retourneTRUE
si x est un vecteur du mode spécifié ne comportant aucun attribut autre que les noms . Il retourneFALSE
sinon.
J'utilise les exemples de données de la réponse de @ Jaap , où les valeurs dans les colonnes de l'année sont factor
s.
Voici l'approche 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
Voici un autre exemple montrant l'utilisation de gather
de tidyr
. Vous pouvez sélectionner les colonnes à gather
en les supprimant individuellement (comme je le fais ici) ou en incluant explicitement les années souhaitées.
Notez que, pour gérer les virgules (et les X ajoutés si check.names = FALSE
N'est pas défini), j'utilise également la mutation de dplyr
avec parse_number
De readr
pour convertir le texte retourne aux nombres. Ils font tous partie de tidyverse
et peuvent donc être chargés avec library(tidyverse)
wide %>%
gather(Year, Value, -Code, -Country) %>%
mutate(Year = parse_number(Year)
, Value = parse_number(Value))
Résultats:
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
Voici une solution sqldf :
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;")
Pour créer la requête sans tout saisir, vous pouvez utiliser les éléments suivants:
Merci à G. Grothendieck pour l’avoir appliqué.
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
Malheureusement, je ne pense pas que PIVOT
et UNPIVOT
fonctionneraient pour R
SQLite
. Si vous souhaitez rédiger votre requête de manière plus sophistiquée, vous pouvez également consulter ces publications:
tilisation de sprintf
écriture de requêtes SQL OuPasser les variables à sqldf