Повторіть рядки data.frame N разів


77

У мене є такий фрейм даних:

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

Я хочу повторити рядки n разів. Наприклад, тут рядки повторюються 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

Чи є проста функція для цього в R? Дякую!

Відповіді:


141

EDIT: оновлено до кращої сучасної відповіді R.

Ви можете використовувати replicate(), а потім rbindрезультат знову разом. Назви рядків автоматично змінюються для запуску з 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))

Більш традиційним способом є використання індексації, але тут зміна імені рядка не зовсім акуратна (але більш інформативна):

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

Ось вдосконалення вищезазначеного, перші два з використанням purrrфункціонального програмування, ідіоматичне муркотіння:

purrr::map_dfr(seq_len(3), ~d)

і менш ідіоматичне бурчання (ідентичний результат, хоча і незручніший):

purrr::map_dfr(seq_len(3), function(x) d)

і, нарешті, за допомогою індексації, а не списку застосовувати, використовуючи dplyr:

d %>% slice(rep(row_number(), 3))

4
Остерігайтеся нульових рядків даних. seq_len, мабуть, кращий варіант
hadley

1
Дякую, я розмився з цим (я завжди думаю, що це seq_along і не докладав зусиль). Я ціную голови.
mdsumner

tidyr :: expand і tidyr :: uncount - теж хороші варіанти
Артур Іп

33

Для data.frameоб'єктів це рішення в кілька разів швидше, ніж @ mdsummer's та @ wojciech-sobala.

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

Для data.tableоб'єктів @ mdsummer's трохи швидший, ніж застосування вищезазначеного після перетворення в data.frame. Для великих n це може перевернутися. мікровизначення.

Повний код:

packages <- c("data.table", "ggplot2", "RUnit", "microbenchmark")
lapply(packages, require, character.only=T)

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)

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
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)
ggsave("repeat_microbenchmark.png", autoplot(res))

16

Пакет 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))

Для великої кількості повторень 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 

1
Думаю, slice(rep(row_number(), 3))це краще, за тестом Макса. О, я щойно побачив вашу лавку ... особисто, я б подумав, що збільшення розміру ДФ було б правильним напрямком, а не кількістю таблиць, але я не знаю.
Frank

1
Хороший! Коли я тестую його, slice(df, rep(row_number(), 3))виявляється трохи крихітнішим, ніж bind_rows(replicate(...))(1,9 проти 2,1 мс). У будь-якому випадку, я вважав, що було б корисно також мати dplyr-рішення ...
Стібу

2
@Frank Ти, мабуть, маєш рацію. Я не перевіряв, що відбувається з великими кадрами даних, оскільки я просто використовував той, який був наданий у питанні.
Стібу

7

За допомогою -пакет, ви можете використовувати спеціальний символ .Iразом із rep:

df <- data.frame(a = c(1,2,3), b = c(1,2,3))
dt <- as.data.table(df)

n <- 3

dt[rep(dt[, .I], n)]

що дає:

   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

Чи є спосіб використовувати цей метод для копіювання по колонці?
Стівен

1
@Stephen для dataframe ви могли б зробити що - щось на кшталт: df[, rep(seq_along(df), n)]; для data.table ви можете зробити:cols <- rep(seq_along(mydf), n); mydf[, ..cols]
Jaap

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

4
Хочете детально розказати, що ви щойно зробили, і як це порівнює з відповіддю mdsumner? Можливо, вкладіть якісь результати?
Роман Луштрік

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

Я здогадуюсь, що це те саме рішення @Max Ghenis
Саймон С.

2

Ще простіше:

library(data.table)
my_data <- data.frame(a = c(1,2,3),b = c(1,2,3))
rbindlist(replicate(n = 3, expr = my_data, simplify = FALSE)

1
З data.tableпосилки
Мостафа
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.