Risagoma data.frame dal formato wide a long

Ho qualche problema a convertire my data.frame da una tabella estesa a una tabella lunga. Al momento sembra così:

 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 

Ora mi piace trasformare questo data.frame in un data.frame lungo. Qualcosa come questo:

 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 

Ho cercato e provato già con le funzioni melt() e reshape() poichè alcune persone suggerivano domande simili. Tuttavia, finora ho solo risultati disordinati.

Se è ansible mi piacerebbe farlo con la funzione reshape() poiché sembra un po ‘più piacevole da gestire.

reshape() richiede un po ‘di tempo per abituarsi, proprio come melt / cast . Ecco una soluzione con risagoma, assumendo che il tuo data frame sia chiamato d :

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

Tre soluzioni alternative:

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 

Alcune notazioni alternative che danno lo stesso risultato:

 # 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

È ansible utilizzare la stessa funzione di melt del pacchetto reshape2 (che è un'implementazione estesa e migliorata). melt da data.table ha anche più parametri che la funzione di melt da reshape2 . Ad esempio, puoi anche specificare il nome della colonna-variabile:

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

Alcune notazioni alternative:

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

Alcune notazioni alternative:

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

Se si desidera escludere i valori NA , è ansible aggiungere na.rm = TRUE alla melt e alle funzioni di gather .


Un altro problema con i dati è che i valori saranno letti da R come valori-carattere (come risultato del , nei numeri). Puoi ripararlo con gsub e as.numeric :

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

Oppure direttamente 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))) 

Dati:

 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) 

Utilizzo del pacchetto di risagoma :

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

Poiché questa risposta è contrassegnata da r-faq , ho ritenuto utile condividere un’altra alternativa dalla base R: stack .

Nota, tuttavia, che lo stack non funziona con factor s – funziona solo se is.vector è TRUE , e dalla documentazione di is.vector , troviamo che:

is.vector restituisce TRUE se x è un vettore della modalità specificata che non ha attributi diversi dai nomi . Restituisce FALSE contrario.

Sto usando i dati di esempio dalla risposta di @ Jaap , dove i valori nelle colonne dell’anno sono i factor s.

Ecco l’approccio dello 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 

Ecco un altro esempio che mostra l’uso di gather da tidyr . Puoi selezionare le colonne da gather rimuovendole singolarmente (come faccio qui) o includendo gli anni che desideri esplicitamente.

Nota che, per gestire le virgole (e le X aggiunte se check.names = FALSE non è impostato), sto anche usando il dplyr di parse_number con parse_number da readr per convertire i valori di testo in numeri. Questi sono tutti parte del tidyverse e quindi possono essere caricati insieme alla library(tidyverse)

 wide %>% gather(Year, Value, -Code, -Country) %>% mutate(Year = parse_number(Year) , Value = parse_number(Value)) 

Ritorna:

  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