Wiederholen Sie die Zeilen eines data.frame N-mal

76

Ich habe folgenden Datenrahmen:

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

Ich möchte die Zeilen n- mal wiederholen . Hier werden die Zeilen beispielsweise dreimal wiederholt:

  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

Gibt es eine einfache Funktion, um dies in R zu tun? Vielen Dank!

Michael
quelle

Antworten:

136

BEARBEITEN: aktualisiert auf eine bessere moderne R-Antwort.

Sie können replicate()dann rbinddas Ergebnis wieder zusammen verwenden. Die Rownamen werden automatisch so geändert, dass sie von 1: nrows ablaufen.

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

Ein traditionellerer Weg ist die Verwendung der Indizierung, aber hier ist die Änderung des Rownamens nicht ganz so ordentlich (aber informativer):

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

Hier sind Verbesserungen gegenüber den oben genannten, die ersten beiden mit purrrfunktionaler Programmierung, idiomatisches Schnurren:

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

und weniger idiomatisches Schnurren (identisches Ergebnis, wenn auch umständlicher):

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

und schließlich über Indizierung statt Liste anwenden mit dplyr:

d %>% slice(rep(row_number(), 3))
mdsumner
quelle
4
Achten Sie auf nullzeilige Datenrahmen. seq_len ist wahrscheinlich eine bessere Option
Hadley
1
Danke, ich habe das vage gemacht (ich denke immer, es ist seq_along und ich habe mich nicht bemüht). Ich schätze die Köpfe hoch.
Mdsumner
tidyr :: expand und tidyr :: uncount sind ebenfalls gute Optionen
Arthur Yip
32

Für data.frameObjekte ist diese Lösung um ein Vielfaches schneller als die von @ mdsummer und @ wojciech-sobala.

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

Bei data.tableObjekten ist @ mdsummer's nach dem Konvertieren in etwas schneller als das Anwenden der oben genannten data.frame. Für große n könnte dies umdrehen. Mikrobenchmark.

Vollständiger Code:

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))
Max Ghenis
quelle
16

Das Paket dplyrenthält die Funktion bind_rows(), die alle Datenrahmen in einer Liste direkt kombiniert, sodass keine Verwendung do.call()zusammen mit rbind():

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

Denn eine große Anzahl von Wiederholungen bind_rows()ist auch viel schneller als 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 
Stibu
quelle
1
Ich denke, es slice(rep(row_number(), 3))ist besser, laut Max 'Benchmark. Oh, ich habe gerade Ihre Bank gesehen ... persönlich würde ich denken, dass eine Vergrößerung der DF etwas die richtige Richtung wäre, anstatt die Anzahl der Tische, aber ich weiß es nicht.
Frank
1
Schön! Wenn ich es vergleiche, slice(df, rep(row_number(), 3))stellt sich heraus, dass es ein bisschen langsamer ist als bind_rows(replicate(...))(1,9 vs. 2,1 ms). Auf jeden Fall fand ich es nützlich, auch eine dplyrLösung zu haben ...
Stibu
2
@ Frank Du hast wahrscheinlich recht. Ich habe nicht überprüft, was bei großen Datenrahmen passiert, da ich nur den in der Frage angegebenen verwendet habe.
Stibu
7

Mit dem -Paket, Sie könnten das spezielle Symbol .Izusammen mit verwenden 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)]

was gibt:

   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
Jaap
quelle
Gibt es eine Möglichkeit, mit dieser Methode spaltenweise zu duplizieren?
Stephen
1
@ Stephen für einen Datenrahmen könnten Sie etwas tun wie : df[, rep(seq_along(df), n)]; Für eine Datentabelle könnten Sie Folgendes tun: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)])
Wojciech Sobala
quelle
4
Möchten Sie näher erläutern, was Sie gerade getan haben und wie es mit der Antwort von mdsumner verglichen wird? Vielleicht einige Ergebnisse einfügen?
Roman Luštrik
2

Verwenden Sie einfach die einfache Indizierung mit Wiederholungsfunktion.

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
Lerner
quelle
Ich denke, dass dies die gleiche @ Max Ghenis-Lösung ist
Simon C.
2

Noch einfacher:

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)
Arturo Sbr
quelle
1
Aus data.tablePaket
Mostafa