¿Agregar nueva fila al marco de datos, en un índice de fila específico, no agregado?

160

El siguiente código combina un vector con un marco de datos:

newrow = c(1:4)
existingDF = rbind(existingDF,newrow)

Sin embargo, este código siempre inserta la nueva fila al final del marco de datos.

¿Cómo puedo insertar la fila en un punto específico dentro del marco de datos? Por ejemplo, supongamos que el marco de datos tiene 20 filas, ¿cómo puedo insertar la nueva fila entre las filas 10 y 11?

luciano
fuente
Utilice un índice conveniente y ordenar?
Roland
22
existingDF = rbind(existingDF[1:10,],newrow,existingDF[-(1:10),])
Pop
Con un bucle simple y una condición si es necesario, se pueden agregar filas de un marco de datos a otro. Un código de muestra es el que se muestra a continuaciónnewdataframe[nrow(newdataframe)+1,] <- existingdataframe[i,]
kirancodify

Respuestas:

156

Aquí hay una solución que evita la rbindllamada (a menudo lenta) :

existingDF <- as.data.frame(matrix(seq(20),nrow=5,ncol=4))
r <- 3
newrow <- seq(4)
insertRow <- function(existingDF, newrow, r) {
  existingDF[seq(r+1,nrow(existingDF)+1),] <- existingDF[seq(r,nrow(existingDF)),]
  existingDF[r,] <- newrow
  existingDF
}

> insertRow(existingDF, newrow, r)
  V1 V2 V3 V4
1  1  6 11 16
2  2  7 12 17
3  1  2  3  4
4  3  8 13 18
5  4  9 14 19
6  5 10 15 20

Si la velocidad es menos importante que la claridad, entonces la solución de @ Simon funciona bien:

existingDF <- rbind(existingDF[1:r,],newrow,existingDF[-(1:r),])
> existingDF
   V1 V2 V3 V4
1   1  6 11 16
2   2  7 12 17
3   3  8 13 18
4   1  2  3  4
41  4  9 14 19
5   5 10 15 20

(Tenga en cuenta que indexamos de manera rdiferente).

Y finalmente, puntos de referencia:

library(microbenchmark)
microbenchmark(
  rbind(existingDF[1:r,],newrow,existingDF[-(1:r),]),
  insertRow(existingDF,newrow,r)
)

Unit: microseconds
                                                    expr     min       lq   median       uq       max
1                       insertRow(existingDF, newrow, r) 660.131 678.3675 695.5515 725.2775   928.299
2 rbind(existingDF[1:r, ], newrow, existingDF[-(1:r), ]) 801.161 831.7730 854.6320 881.6560 10641.417

Puntos de referencia

Como @MatthewDowle siempre me señala, los puntos de referencia deben examinarse para determinar la escala a medida que aumenta el tamaño del problema. Aquí vamos entonces:

benchmarkInsertionSolutions <- function(nrow=5,ncol=4) {
  existingDF <- as.data.frame(matrix(seq(nrow*ncol),nrow=nrow,ncol=ncol))
  r <- 3 # Row to insert into
  newrow <- seq(ncol)
  m <- microbenchmark(
   rbind(existingDF[1:r,],newrow,existingDF[-(1:r),]),
   insertRow(existingDF,newrow,r),
   insertRow2(existingDF,newrow,r)
  )
  # Now return the median times
  mediansBy <- by(m$time,m$expr, FUN=median)
  res <- as.numeric(mediansBy)
  names(res) <- names(mediansBy)
  res
}
nrows <- 5*10^(0:5)
benchmarks <- sapply(nrows,benchmarkInsertionSolutions)
colnames(benchmarks) <- as.character(nrows)
ggplot( melt(benchmarks), aes(x=Var2,y=value,colour=Var1) ) + geom_line() + scale_x_log10() + scale_y_log10()

La solución de @ Roland escala bastante bien, incluso con la llamada a rbind:

                                                              5       50     500    5000    50000     5e+05
insertRow2(existingDF, newrow, r)                      549861.5 579579.0  789452 2512926 46994560 414790214
insertRow(existingDF, newrow, r)                       895401.0 905318.5 1168201 2603926 39765358 392904851
rbind(existingDF[1:r, ], newrow, existingDF[-(1:r), ]) 787218.0 814979.0 1263886 5591880 63351247 829650894

Trazado en una escala lineal:

lineal

Y una escala log-log:

log-log

Ari B. Friedman
fuente
3
¡Insertar una fila al final da un comportamiento extraño!
Maarten
@Maarten ¿Con qué función?
Ari B. Friedman
Supongo que es el mismo comportamiento extraño que estoy describiendo aquí: stackoverflow.com/questions/19927806/…
PatrickT
1
El comportamiento extraño no ocurre con insertRow2, en mi marco de datos particular y fila.
PatrickT
¿Cómo se agrega una fila de números a un df? Tengo dfcon columnas a,b,c,dy quiero agregar la fila 1,2,3,4. ¿Cómo puedo hacer eso?
Travis Heeter
44
insertRow2 <- function(existingDF, newrow, r) {
  existingDF <- rbind(existingDF,newrow)
  existingDF <- existingDF[order(c(1:(nrow(existingDF)-1),r-0.5)),]
  row.names(existingDF) <- 1:nrow(existingDF)
  return(existingDF)  
}

insertRow2(existingDF,newrow,r)

  V1 V2 V3 V4
1  1  6 11 16
2  2  7 12 17
3  1  2  3  4
4  3  8 13 18
5  4  9 14 19
6  5 10 15 20

microbenchmark(
+   rbind(existingDF[1:r,],newrow,existingDF[-(1:r),]),
+   insertRow(existingDF,newrow,r),
+   insertRow2(existingDF,newrow,r)
+ )
Unit: microseconds
                                                    expr     min       lq   median       uq      max
1                       insertRow(existingDF, newrow, r) 513.157 525.6730 531.8715 544.4575 1409.553
2                      insertRow2(existingDF, newrow, r) 430.664 443.9010 450.0570 461.3415  499.988
3 rbind(existingDF[1:r, ], newrow, existingDF[-(1:r), ]) 606.822 625.2485 633.3710 653.1500 1489.216
Roland
fuente
3
Esta es una solución genial. Todavía no puedo entender por qué es mucho más rápido que la llamada simultánea rbind, pero estoy intrigado.
Ari B. Friedman
Las respuestas con puntos de referencia deberían tener cierta reputación adicional aplicada automáticamente IMO. ¡Gracias!
Alex
10

Deberías probar el paquete dplyr

library(dplyr)
a <- data.frame(A = c(1, 2, 3, 4),
               B = c(11, 12, 13, 14))


system.time({
for (i in 50:1000) {
    b <- data.frame(A = i, B = i * i)
    a <- bind_rows(a, b)
}

})

Salida

   user  system elapsed 
   0.25    0.00    0.25

En contraste con el uso de la función rbind

a <- data.frame(A = c(1, 2, 3, 4),
                B = c(11, 12, 13, 14))


system.time({
    for (i in 50:1000) {
        b <- data.frame(A = i, B = i * i)
        a <- rbind(a, b)
    }

})

Salida

   user  system elapsed 
   0.49    0.00    0.49 

Hay alguna ganancia de rendimiento.

Naimish Agarwal
fuente
-4

por ejemplo, desea agregar filas de la variable 2 a la variable 1 de un dato llamado "bordes" simplemente hágalo así

allEdges <- data.frame(c(edges$V1,edges$V2))
usuario3670684
fuente