2012-07-27 13 views
22

Ho un data.frame df e voglio che ogni riga in questa df viene duplicato lengthTime volte e che viene aggiunta una nuova colonna che conta da 1 a lengthTime per ogni riga df .Alternativa a expand.grid per data.frames

Lo so, sembra piuttosto complicato, ma quello che voglio in pratica è applicare expand.grid a df. Ecco un soluzione brutto e ho la sensazione che ci massimo essere una soluzione più semplice (forse anche una funzione di base-R?):

df <- data.frame(ID = rep(letters[1:3], each=3), 
       CatA = rep(1:3, times = 3), 
       CatB = letters[1:9]) 
lengthTime <- 3 
nrRow <- nrow(df) 
intDF <- df 
for (i in 1:(lengthTime - 1)) { 
    df <- rbind(df, intDF) 
} 
df$Time <- rep(1:lengthTime, each=nrRow) 

ho pensato che avrei potuto semplicemente usare expand.grid(df, 1:lengthTime), ma che non funziona. outer non ha portato fortuna. Quindi qualcuno conosce una buona soluzione?

risposta

13

Perché non solo qualcosa come df[rep(1:nrow(df),times = 3),] per estendere il frame di dati e quindi aggiungere la colonna in più proprio come sopra, con df$Time <- rep(1:lengthTime, each=nrRow)?

2

Questo funziona:

REP <- rep(1:nrow(df), 3) 
df2 <- data.frame(df[REP, ], Time = rep(1:3, each = 9)) 
rownames(df2) <- NULL 
df2 
+0

Il mio non è diverso da quello di joran che mi ha battuto di 40 secondi, ma lo lascerò perché è leggermente più esplicito. –

+0

Primo arrivato, prima cosa, quindi ho accettato la sua risposta ;-) Ma +1 per entrambi. Questa è una soluzione molto accurata! –

36

E 'stato un po' che questa domanda è stata pubblicata, ma di recente mi sono imbattuto in essa cercando solo la cosa nel titolo, vale a dire, un expand.grid che funziona per frame di dati. Le risposte postato affrontano domanda più specifica del PO, così nel caso qualcuno è alla ricerca di una soluzione più generale per i frame di dati, ecco un approccio leggermente più generale:

expand.grid.df <- function(...) Reduce(function(...) merge(..., by=NULL), list(...)) 

# For the example in the OP 
expand.grid.df(df, data.frame(1:lengthTime)) 

# More generally 
df1 <- data.frame(A=1:3, B=11:13) 
df2 <- data.frame(C=51:52, D=c("Y", "N")) 
df3 <- data.frame(E=c("+", "-")) 
expand.grid.df(df1, df2, df3) 
+1

Bella soluzione. – Ariel

+1

Wow. Questo è un grande lavoro. – jknowles

9

Si può anche solo fare un semplice merge utilizzando NULL come unire colonna (che causerà merge fare semplice replica dei dati combinatoria):

data.frame(time=1:lengthTime) %>% merge(iris, by=NULL) 

la tubazione operatore %>% viene dal pacchetto magrittr (dplyr sarà anche allegare automaticamente) ed è stato appena usato per impro ve leggibilità. Si può anche semplice fare merge(iris, data.frame(...), by=NULL)

+0

Penso che tu voglia menzionare dove hai preso '%>%' da a ... –

+0

@DavidArenburg grazie per il suggerimento. –

1

Una soluzione data.table:

> library(data.table) 
> (df <- data.frame(ID = rep(letters[1:3], each=3), 
+     CatA = rep(1:3, times = 3), 
+     CatB = letters[1:9])) 
    ID CatA CatB 
1 a 1 a 
2 a 2 b 
3 a 3 c 
4 b 1 d 
5 b 2 e 
6 b 3 f 
7 c 1 g 
8 c 2 h 
9 c 3 i 
> (DT <- data.table(df)[, lapply(.SD, function(x) rep(x,3))][, Time:=rep(1:3, each=nrow(df0))]) 
    ID CatA CatB Time 
1: a 1 a 1 
2: a 2 b 1 
3: a 3 c 1 
4: b 1 d 1 
5: b 2 e 1 
6: b 3 f 1 
7: c 1 g 1 
8: c 2 h 1 
9: c 3 i 1 
10: a 1 a 2 
11: a 2 b 2 
12: a 3 c 2 
13: b 1 d 2 
14: b 2 e 2 
15: b 3 f 2 
16: c 1 g 2 
17: c 2 h 2 
18: c 3 i 2 
19: a 1 a 3 
20: a 2 b 3 
21: a 3 c 3 
22: b 1 d 3 
23: b 2 e 3 
24: b 3 f 3 
25: c 1 g 3 
26: c 2 h 3 
27: c 3 i 3 

Un altro:

> library(data.table) 
> (df <- data.frame(ID = rep(letters[1:3], each=3), 
+     CatA = rep(1:3, times = 3), 
+     CatB = letters[1:9])) 
> DT <- data.table(df) 
> rbindlist(lapply(1:3, function(i) cbind(DT, Time=i))) 
    ID CatA CatB Time 
1: a 1 a 1 
2: a 2 b 1 
3: a 3 c 1 
4: b 1 d 1 
5: b 2 e 1 
6: b 3 f 1 
7: c 1 g 1 
8: c 2 h 1 
9: c 3 i 1 
10: a 1 a 2 
11: a 2 b 2 
12: a 3 c 2 
13: b 1 d 2 
14: b 2 e 2 
15: b 3 f 2 
16: c 1 g 2 
17: c 2 h 2 
18: c 3 i 2 
19: a 1 a 3 
20: a 2 b 3 
21: a 3 c 3 
22: b 1 d 3 
23: b 2 e 3 
24: b 3 f 3 
25: c 1 g 3 
26: c 2 h 3 
27: c 3 i 3 
5

aggiornamento rapido

V'è ora anche la funzione di attraversamento() nella confezione tidyr che può essere usato invece di unire, è un po 'più veloce e restituisce un tbl_df/tibble.

data.frame(time=1:10) %>% merge(iris, by=NULL) 

data.frame(time=1:10) %>% tidyr::crossing(iris)