2012-05-14 2 views
19

Non riesco a trovare il modo più elegante e flessibile per passare i dati dal formato lungo al formato grande quando ho più di una variabile di misura che voglio portare avanti.Convertire i dati dal formato lungo al formato largo con più colonne di misure

Ad esempio, ecco una semplice cornice dati in formato lungo. ID è il soggetto, il tempo è una variabile di tempo, e X e Y sono misurazioni effettuate di ID alle TIME:

> my.df <- data.frame(ID=rep(c("A","B","C"), 5), TIME=rep(1:5, each=3), X=1:15, Y=16:30) 
> my.df 

    ID TIME X Y 
1 A 1 1 16 
2 B 1 2 17 
3 C 1 3 18 
4 A 2 4 19 
5 B 2 5 20 
6 C 2 6 21 
7 A 3 7 22 
8 B 3 8 23 
9 C 3 9 24 
10 A 4 10 25 
11 B 4 11 26 
12 C 4 12 27 
13 A 5 13 28 
14 B 5 14 29 
15 C 5 15 30 

Se volevo solo girare i valori di tempo nelle intestazioni delle colonne che contengono il includono X, lo so posso usare getto dal pacchetto Reshape (o dcast da reshape2):

> cast(my.df, ID ~ TIME, value="X") 
    ID 1 2 3 4 5 
1 A 1 4 7 10 13 
2 B 2 5 8 11 14 
3 C 3 6 9 12 15 

Ma quello che voglio veramente fare è anche portare con sé Y come un'altra variabile di misura, e hanno i nomi delle colonne riflettere sia il nome della variabile di misura e il valore del tempo:

ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5 
1 A 1 4 7 10 13 16 19 22 25 28 
2 B 2 5 8 11 14 17 20 23 26 29 
3 C 3 6 9 12 15 18 21 24 27 30 

(FWIW, non mi importa se tutte le X vengono prima seguiti dalla Y, o se essi sono intercalati da X_1, Y_1, x_2, Y_2, etc.)

posso avvicinarmi a questo fondendo i dati lunghi due volte e unendo i risultati, anche se i nomi delle colonne richiedono un po 'di lavoro, e dovrei modificarlo se dovessi aggiungere una terza o quarta variabile oltre a X e Y:

merge(
cast(my.df, ID ~ TIME, value="X"), 
cast(my.df, ID ~ TIME, value="Y"), 
by="ID", suffixes=c("_X","_Y") 
) 

Sembra una combinazione di funzioni in reshape2 e/o plyr dovrebbe essere in grado di farlo in modo più elegante del mio tentativo, oltre a gestire più variabili di misura in modo più pulito. Qualcosa come cast (my.df, ID ~ TIME, value = c ("X", "Y")), che non è valido. Ma non sono stato in grado di capirlo.

I maghi R possono aiutarmi? Grazie.

risposta

14

Per gestire più variabili come desiderato, è necessario disporre di melt i dati che si hanno prima di trasmetterli.

library("reshape2") 

dcast(melt(my.df, id.vars=c("ID", "TIME")), ID~variable+TIME) 

che dà

ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5 
1 A 1 4 7 10 13 16 19 22 25 28 
2 B 2 5 8 11 14 17 20 23 26 29 
3 C 3 6 9 12 15 18 21 24 27 30 

EDIT basato su commento:

Il frame di dati

num.id = 10 
num.time=10 
my.df <- data.frame(ID=rep(LETTERS[1:num.id], num.time), 
        TIME=rep(1:num.time, each=num.id), 
        X=1:(num.id*num.time), 
        Y=(num.id*num.time)+1:(2*length(1:(num.id*num.time)))) 

dà un risultato diverso (tutte le voci sono 2) perché la ID/La combinazione TIME non indica una riga univoca. Infatti, ci sono due righe con ciascuna combinazione ID/TIME. reshape2 assume un valore singolo per ogni possibile combinazione di variabili e applicherà una funzione di riepilogo per creare una singola variabile se ci sono più voci. Questo è il motivo per cui v'è l'avvertimento

Aggregation function missing: defaulting to length 

È possibile ottenere qualcosa che funziona se si aggiunge un'altra variabile che rompe che la ridondanza.

my.df$cycle <- rep(1:2, each=num.id*num.time) 
dcast(melt(my.df, id.vars=c("cycle", "ID", "TIME")), cycle+ID~variable+TIME) 

Questo funziona perché cycle/ID/time ora definisce univocamente una riga in my.df.

+0

stavo cercando di valutare quale soluzione è più veloce, ma ha trovato un problema con la il tuo codice se il data frame è il seguente: num.id = 10 num.time = 10 my.df <- dati. frame (ID = rep (LETTERS [1: num.id], num.time), TIME = rep (1: num.time, each = num.id), X = 1: (num.id * num.time), Y = (num.id * num.time) +1: (2 * lunghezza (1: (num.id * num.time)))) –

+0

Grazie mille. –

+0

Perfetto, grazie Brian. Dal momento che il cast sembrava funzionare, non mi ero reso conto che lo scioglimento fosse ancora necessario. –

14
reshape(my.df, idvar = "ID", timevar = "TIME", direction = "wide") 

ID X.1 Y.1 X.2 Y.2 X.3 Y.3 X.4 Y.4 X.5 Y.5 
1 A 1 16 4 19 7 22 10 25 13 28 
2 B 2 17 5 20 8 23 11 26 14 29 
3 C 3 18 6 21 9 24 12 27 15 30 
+0

Questo è fantastico. Grazie! –

9

Utilizzando la data.table_1.9.5, questo può essere fatto senza il melt come si può gestire più value.var colonne. È possibile installarlo dal here

library(data.table) 
dcast(setDT(my.df), ID~TIME, value.var=c('X', 'Y')) 
# ID 1_X 2_X 3_X 4_X 5_X 1_Y 2_Y 3_Y 4_Y 5_Y 
#1: A 1 4 7 10 13 16 19 22 25 28 
#2: B 2 5 8 11 14 17 20 23 26 29 
#3: C 3 6 9 12 15 18 21 24 27 30 
4

Ecco una soluzione con il pacchetto tidyr, che ha sostanzialmente sostituito rimodellare e reshape2. Come con questi due pacchetti, la strategia per prendere il set di dati più a lungo prima e poi più ampia.

library(magrittr); requireNamespace("tidyr"); requireNamespace("dplyr") 
my.df %>% 
    tidyr::gather_(key="variable", value="value", c("X", "Y")) %>% # Make it even longer. 
    dplyr::mutate(             # Create the spread key. 
    time_by_variable = paste0(variable, "_", TIME) 
) %>% 
    dplyr::select(ID, time_by_variable, value) %>%     # Retain these three. 
    tidyr::spread(key=time_by_variable, value=value)    # Spread/widen. 

Dopo la chiamata tidyr::gather(), il set di dati intermedio è:

ID TIME variable value 
1 A 1  X  1 
2 B 1  X  2 
3 C 1  X  3 
... 
28 A 5  Y 28 
29 B 5  Y 29 
30 C 5  Y 30 

Il risultato finale è:

ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5 
1 A 1 4 7 10 13 16 19 22 25 28 
2 B 2 5 8 11 14 17 20 23 26 29 
3 C 3 6 9 12 15 18 21 24 27 30