2012-07-27 14 views
22

Mam data.frame df i chcę, aby każdy rząd w tym df jest powielany lengthTime razy i że nowa kolumna dodaje, że liczy się od 1 do lengthTime dla każdego wiersza w df .Alternatywa expand.grid dla data.frames

Wiem, to brzmi dość skomplikowanie, ale to, czego zasadniczo chcę, to zastosowanie expand.grid do df. Oto brzydki obejście i mam wrażenie, że najbardziej być łatwiejszy rozwiązanie (może nawet funkcję 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) 

Myślałem, że mogę po prostu użyć expand.grid(df, 1:lengthTime), ale to nie działa. outer też nie przynosiła szczęścia. Czy ktoś zna dobre rozwiązanie?

Odpowiedz

13

Dlaczego nie po prostu coś df[rep(1:nrow(df),times = 3),] rozszerzyć ramkę danych, a następnie dodać dodatkowe kolumny tylko jak masz powyżej, z df$Time <- rep(1:lengthTime, each=nrRow)?

2

to działa:

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

Mój naprawdę nie różni się niczym od jorana, który pokonał mnie o 40 sekund, ale zostawię to, ponieważ jest nieco bardziej wyraźny. –

+0

Najpierw przyjadę, służę, więc przyjąłem jego odpowiedź ;-) Ale +1 dla was obojga. To bardzo miłe rozwiązanie! –

36

Minęło trochę czasu odkąd ta kwestia została wysłana, ale ostatnio natknąłem nim szuka tylko rzecz w tytule, a mianowicie expand.grid że pracuje dla ramek danych. Delegowani odpowiedzi rozwiązać bardziej konkretne pytanie OP, więc w przypadku gdy ktoś szuka bardziej ogólnego rozwiązania dla ramek danych, tutaj jest nieco bardziej ogólne podejście:

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

Piękne rozwiązanie. – Ariel

+1

Wow. To jest świetna robota. – jknowles

9

Można też po prostu zrobić prosty merge użyciu NULL jako scalania kolumny (co spowoduje merge do prostych replikacji kombinatoryczne danych)

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

rurociąg operatora %>% pochodzi z opakowania magrittr (dplyr również dołączyć go automatycznie) i został użyty do popraw ve czytelność. Można również proste zrobić merge(iris, data.frame(...), by=NULL)

+0

Myślę, że możesz wspomnieć, skąd wziąłeś '%>%' od do ... –

+0

@DavidArenburg dzięki za podpowiedź. –

1

data.table rozwiązanie:

> 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 

Inny:

> 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

Szybka zmiana

Jest teraz również funkcja przejście() w pakiet tidyr który może być użyty zamiast scalania, jest nieco szybszy i zwraca plik tbl_df/tibble.

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

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