2012-01-06 2 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 результат назад. Имена ростов автоматически изменяются для запуска от 1: ноль.

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

+1

Спасибо, я прогнал это (я всегда думаю, что это seq_along и не прилагал усилий). Я ценю головы. – mdsumner

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? Возможно, вставьте некоторые результаты? –

16

Для data.frame объектов, это решение несколько раз быстрее, чем @ mdsummer's и @ wojciech-sobala's.

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(), которая непосредственно объединяет все кадры данных в списке, так что нет необходимости использовать do.call() вместе с rbind():

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

+1

Хороший! Когда я сравниваю это, 'slice (df, rep (row_number(), 3))' оказывается немного меньшим, чем 'bind_rows (replicate (...))' (1.9 против 2.1 ms). В любом случае, я подумал, что полезно иметь также «dplyr'-решение ... – Stibu

+1

@Frank Возможно, вы правы. Я не проверял, что происходит для больших кадров данных, так как я использовал только тот, который был предоставлен в вопросе. – Stibu

Смежные вопросы