2012-01-06 13 views
39

Tengo la siguiente trama de datosdata.frame N veces

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

y quiero convertirlo en

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 

o repetir n veces. ¿Hay una función fácil de hacer esto en R? ¡Gracias!

Respuesta

66

Puede usar replicate(), luego rbind el resultado de nuevo. Los nombres de fila se modifican automáticamente para ejecutarse desde 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)) 

Una forma más tradicional es utilizar la indexación, pero aquí la alteración rowname no es tan puro (pero más informativo):

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

Cuidado con cero tramas de datos consecutivas. seq_len es probablemente una mejor opción – hadley

+1

Gracias, me vagué sobre eso (siempre pienso que es seq_along y no estaba haciendo el esfuerzo). Aprecio los avisos. – 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

¿Te interesa elaborar lo que acabas de hacer y cómo se compara con la respuesta de mdsumner? Tal vez pegar en algunos resultados? –

16

Para data.frame objetos, esta solución es de varios veces más rápido que @ mdsummer's y @ wojciech-sobala's.

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

Para data.table objetos, @ mdsummer de es un poco más rápido que la aplicación de las anteriores después de convertir a data.frame. Para n grande, esto podría voltearse. microbenchmark.

código completo:

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

Sólo tiene que utilizar la indexación sencilla con la función de repetición.

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

El paquete dplyr contiene la función bind_rows() que combina directamente todas las tramas de datos en una lista, de modo que no hay necesidad de usar do.call() junto con rbind():

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

Para un gran número de repetions bind_rows() es también mucho más rápido que 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

Supongo que 'slice (rep (row_number(), 3))' es mejor, según el punto de referencia de Max. Oh, acabo de ver tu banco ... personalmente, creo que ampliar el tamaño del DF sería la dirección correcta, en lugar de la cantidad de tablas, pero no sé. – Frank

+1

¡Bonito! Cuando lo comparo, 'slice (df, rep (row_number(), 3))' resulta ser un poco más lento que 'bind_rows (replicate (...))' (1.9 vs. 2.1 ms). En cualquier caso, pensé que era útil tener una solución 'dplyr' también ... – Stibu

+1

@Frank Probablemente tengas razón. No revisé qué ocurre con los marcos de datos grandes, ya que acabo de usar el que se proporcionó en la pregunta. – Stibu

Cuestiones relacionadas