2010-02-02 7 views
77

J'ai quelques problèmes pour convertir mon data.frame d'une table large à une longue table. le moment, il ressemble à ceci:Remodeler data.frame du format large au format long

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'aime transformer cette data.frame en une longue data.frame. Quelque chose comme ceci:

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 

Je l'ai regardé et déjà essayé avec le melt() et les fonctions reshape() que certaines personnes suggéraient des questions similaires. Cependant, jusqu'à présent, je n'ai que des résultats désordonnés.

Si c'est possible je voudrais le faire avec la fonction reshape() depuis il semble un peu plus agréable à gérer.

+1

Je ne sais pas si cela était le problème, mais les fonctions dans le package de Reshape sont à l'état fondu et coulé –

+0

Et le paquet Reshape a été remplacé par reshape2. –

+2

Et maintenant reshape2 a été remplacé par tidyr. – drhagen

Répondre

54

reshape() prend un certain temps pour s'y habituer, comme melt/cast. Voici une solution avec Reshape, en supposant que votre trame de données est appelé d:

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

En utilisant Reshape package:

#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

Trois solutions alternatives:

1: Avec reshape2

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

donner:

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 paquet reshape2 (qui est une extension & mise en œuvre améliorée). melt de data.table a également plus de paramètres que le melt de reshape2.Vous pouvez pour exaple spécifier le nom de la colonne de la 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") 

3: 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 NA les valeurs, vous pouvez ajouter à la na.rm = TRUEmelt ainsi que les gather fonctions.


Un autre problème avec les données est que les valeurs sont lues par R comme caractère de valeurs (à la suite de la , du nombre). Vous pouvez réparer cela 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))) 

données:

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

bonne réponse, juste un petit rappel: ne mettez pas de variables autres que 'id' et' time' dans votre cadre de données, 'melt' ne pourrait pas dire ce que vous voulez faire dans ce cas. –

+1

@JasonGoal Pourriez-vous élaborer sur ce point? Pendant que j'interprète votre commentaire, cela ne devrait pas poser de problème. Spécifiez simplement 'id.vars' et' measure.vars'. – Jaap

+0

, alors c'est bon pour moi, je ne sais pas 'id.vars' et le' measure.vars' peut être spécifié dans la première alternative, désolé pour le désordre, c'est ma faute. –

1

Voici un autre exemple montrant la utilisation de gather de tidyr. Vous pouvez sélectionner les colonnes à gather soit en les supprimant individuellement (comme je le fais ici), soit en incluant les années que vous voulez explicitement.

Notez que, pour gérer les virgules (et X est ajoutée si check.names = FALSE est pas défini), je suis également en utilisant l » muter dplyr avec parse_number de readr pour convertir les valeurs de texte à des numéros. Ils font tous partie du 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)) 

Retours:

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

Depuis cette réponse est marquée avec , je pense qu'il serait utile de partager une autre alternative de 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 is.vector, nous constatons que:

is.vector retours TRUE si x est un vecteur du mode spécifié n'ayant pas d'attributs autres que les noms. Il renvoie FALSE sinon.

J'utilise les données d'échantillon from @Jaap's answer, où les valeurs dans les colonnes de l'année sont factor s.

est ici l'approche stack: (. Et refondre)

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 
Questions connexes