2010-02-02 11 views
77

Ho qualche problema a convertire il mio data.frame da un ampio tavolo a un lungo tavolo. Al momento sembra che questo:Reshaping data.frame dal formato wide al formato lungo

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 di trasformare questo data.frame in una lunga data.frame. Qualcosa di simile a 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 guardato e provato già con il melt() e le funzioni reshape() come alcune persone sono state suggerendo a domande simili. Tuttavia, finora ho solo risultati disordinati.

Se possibile, mi piacerebbe farlo con la funzione reshape() dal sembra un po 'più bello da gestire.

+1

Non so se questo era il problema, ma le funzioni nel pacchetto Reshape sono fondere –

+0

E il pacchetto Reshape è stata sostituita da reshape2. –

+2

E ora reshape2 è stato sostituito da tidyr. – drhagen

risposta

54

reshape() prende un po 'per abituarsi, proprio come melt/cast. Ecco una soluzione con Nuova forma, supponendo che il frame di dati viene chiamato d:

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

Utilizzando Reshape pacchetto:

#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

tre soluzioni alternative:

1: Con reshape2

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

dono:

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

È possibile utilizzare la stessa funzione melt come nel pacchetto reshape2 (che è un esteso & migliore attuazione). melt da data.table ha anche più parametri che il melt da reshape2.È possibile per exaple 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 NA valori, è possibile aggiungere na.rm = TRUE al melt così come le gather funzioni.


Un altro problema con i dati è che i valori vengono letti da R come caratteri valori (in conseguenza della , in numeri). È possibile riparare quello con gsub e as.numeric:

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

O 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) 
+0

ottima risposta, solo un altro piccolo promemoria: non inserire variabili diverse da 'id' e'time' nel tuo frame di dati,' melt' non potrebbe dire cosa vuoi fare in questo caso. –

+1

@JasonGoal Potresti approfondire? Mentre sto interpretando il tuo commento, non dovrebbe essere un problema. Basta specificare sia 'id.vars' che' measure.vars'. – Jaap

+0

, allora è un bene per me, non so 'id.vars' e' measure.vars' può essere specificato nella prima alternativa, mi dispiace per il casino, è colpa mia. –

1

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

Si noti che, per gestire le virgole (e X del aggiunti, se check.names = FALSE non è impostato), Sono anche utilizzando dplyr 's mutare con parse_number da readr per convertire i valori di testo di nuovo a numeri. Questi sono tutti parte della tidyverse e così possono essere caricati insieme library(tidyverse)

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

Returns:

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

Dal momento che questa risposta è codificata con , ho pensato che sarebbe stato utile per condividere un'altra alternativa da base R: stack.

Si noti, tuttavia, che stack non funziona con factor s - funziona solo se è is.vectorTRUE, e dalla documentazione per is.vector, troviamo che:

is.vector rendimenti TRUE se x è un vettore della modalità specificata senza attributi diversi dai nomi. Restituisce altrimenti FALSE.

sto utilizzando i dati di esempio from @Jaap's answer, dove i valori nelle colonne anno sono factor s.

Ecco l'approccio stack: (. E rifusione)

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 
Problemi correlati