2012-01-06 104 views
39

我有以下的数据帧重复data.frame N次

data.frame(a = c(1,2,3),b = c(1,2,3)) 
    a b 
1 1 1 
2 2 2 
3 3 3 

,我希望把它变成

a b 
1 1 1 
2 2 2 
3 3 3 
4 1 1 
5 2 2 
6 3 3 
7 1 1 
8 2 2 
9 3 3 

或重复了N次。在R中有这样一个简单的功能吗?谢谢!

回答

66

您可以使用replicate(),然后rbind将结果重新拼接在一起。 rownames会自动更改为从1:nrows运行。

d <- data.frame(a = c(1,2,3),b = c(1,2,3)) 
n <- 3 
do.call("rbind", replicate(n, d, simplify = FALSE)) 

更传统的方式是使用索引,但这里的rowname改变不是那么整齐(但更多的信息):

d[rep(seq_len(nrow(d)), n), ] 
+4

谨防零个数据帧。 seq_len可能是一个更好的选择 – hadley 2012-01-06 09:17:42

+1

谢谢,我对此表示遗憾(我总是认为这是seq_along,并没有付出努力)。我很欣赏那些头。 – mdsumner 2012-01-06 13:34:03

3
d <- data.frame(a = c(1,2,3),b = c(1,2,3)) 
r <- Reduce(rbind, list(d)[rep(1L, times=3L)]) 
+2

要小心详细说明你刚刚做了什么以及它如何与mdsumner的答案进行比较?也许粘贴一些结果? – 2012-01-07 01:28:51

16

对于data.frame对象,这个解决方案是几比@ mdsummer和@ wojciech-sobala的时间快了几倍。

d[rep(seq_len(nrow(d)), n), ] 

对于data.table对象,@ mdsummer的是有点不是转换成data.frame之后应用上述更快。对于大n可能翻转。 microbenchmark

全码:

Repeat1 <- function(d, n) { 
    return(do.call("rbind", replicate(n, d, simplify = FALSE))) 
} 

Repeat2 <- function(d, n) { 
    return(Reduce(rbind, list(d)[rep(1L, times=n)])) 
} 

Repeat3 <- function(d, n) { 
    if ("data.table" %in% class(d)) return(d[rep(seq_len(nrow(d)), n)]) 
    return(d[rep(seq_len(nrow(d)), n), ]) 
} 

Repeat3.dt.convert <- function(d, n) { 
    if ("data.table" %in% class(d)) d <- as.data.frame(d) 
    return(d[rep(seq_len(nrow(d)), n), ]) 
} 

# Try with data.frames 
mtcars1 <- Repeat1(mtcars, 3) 
mtcars2 <- Repeat2(mtcars, 3) 
mtcars3 <- Repeat3(mtcars, 3) 

library(RUnit) 
checkEquals(mtcars1, mtcars2) 
# Only difference is row.names having ".k" suffix instead of "k" from 1 & 2 
checkEquals(mtcars1, mtcars3) 

# Works with data.tables too 
mtcars.dt <- data.table(mtcars) 
mtcars.dt1 <- Repeat1(mtcars.dt, 3) 
mtcars.dt2 <- Repeat2(mtcars.dt, 3) 
mtcars.dt3 <- Repeat3(mtcars.dt, 3) 

# No row.names mismatch since data.tables don't have row.names 
checkEquals(mtcars.dt1, mtcars.dt2) 
checkEquals(mtcars.dt1, mtcars.dt3) 

# Time test 
library(microbenchmark) 
res <- microbenchmark(Repeat1(mtcars, 10), 
         Repeat2(mtcars, 10), 
         Repeat3(mtcars, 10), 
         Repeat1(mtcars.dt, 10), 
         Repeat2(mtcars.dt, 10), 
         Repeat3(mtcars.dt, 10), 
         Repeat3.dt.convert(mtcars.dt, 10)) 
print(res) 
library(ggplot2) 
ggsave("~/gdrive/repeat_microbenchmark.png", autoplot(res)) 
2

只要使用具有复读功能简单的索引。

mydata<-data.frame(a = c(1,2,3),b = c(1,2,3)) #creating your data frame 
n<-10   #defining no. of time you want repetition of the rows of your dataframe 

mydata<-mydata[rep(rownames(mydata),n),] #use rep function while doing indexing 
rownames(mydata)<-1:NROW(mydata) #rename rows just to get cleaner look of data 
8

封装dplyr包含函数bind_rows()直接结合所有的数据帧中的列表,以使得没有必要与rbind()一起使用do.call()

df <- data.frame(a = c(1, 2, 3), b = c(1, 2, 3)) 
library(dplyr) 
bind_rows(replicate(3, df, simplify = FALSE)) 

对于大量repetions的bind_rows()也比rbind()快得多:

library(microbenchmark) 
microbenchmark(rbind = do.call("rbind", replicate(1000, df, simplify = FALSE)), 
       bind_rows = bind_rows(replicate(1000, df, simplify = FALSE)), 
       times = 20) 
## Unit: milliseconds 
##  expr  min  lq  mean median  uq  max neval cld 
##  rbind 31.796100 33.017077 35.436753 34.32861 36.773017 43.556112 20 b 
## bind_rows 1.765956 1.818087 1.881697 1.86207 1.898839 2.321621 20 a 
+0

我猜'slice(rep(row_number(),3))'更好,根据Max的基准。哦,刚刚看到你的长凳......我个人认为扩大DF的大小会是正确的方向,而不是桌子的数量,但我不知道。 – Frank 2017-08-11 15:34:24

+1

不错的一个!当我对它进行基准测试时,'slice(df,rep(row_number(),3))'比bind_rows(replicate(...))'慢了1.9(比2.1 ms)。无论如何,我认为有一个'dplyr'解决方案以及... – Stibu 2017-08-11 15:42:42

+1

@Frank你可能是对的。我没有检查大数据框会发生什么,因为我只是使用了问题中提供的那个。 – Stibu 2017-08-11 15:45:42