Aggiungi una nuova riga al frame di dati, in corrispondenza dell'indice di riga specifico, non aggiunto?


160

Il codice seguente combina un vettore con un frame di dati:

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

Tuttavia, questo codice inserisce sempre la nuova riga alla fine del frame di dati.

Come posso inserire la riga in un punto specificato all'interno del frame di dati? Ad esempio, supponiamo che il frame di dati abbia 20 righe, come posso inserire la nuova riga tra le righe 10 e 11?


Utilizzare un indice conveniente e ordinare?
Roland,

22
existingDF = rbind(existingDF[1:10,],newrow,existingDF[-(1:10),])
Pop

Con un semplice ciclo e una condizione, se necessario, è possibile aggiungere righe da un frame di dati a un altro. Un codice di esempio è mostrato di seguitonewdataframe[nrow(newdataframe)+1,] <- existingdataframe[i,]
kirancodify

Risposte:


156

Ecco una soluzione che evita la rbindchiamata (spesso 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

Se la velocità è meno importante della chiarezza, la soluzione di @ Simon funziona bene:

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

(Nota indicizziamo rdiversamente).

E infine, i benchmark:

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

Punti di riferimenti

Come mi indica sempre @MatthewDowle, i benchmark devono essere esaminati per il ridimensionamento all'aumentare della dimensione del problema. Eccoci quindi:

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 soluzione di @ Roland si adatta abbastanza bene, anche con la chiamata 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

Tracciato su una scala lineare:

lineare

E una scala log-log:

log-log


3
L'inserimento di una riga alla fine dà un comportamento strano!
Maarten,

@Maarten Con quale funzione?
Ari B. Friedman,

Credo che sia lo stesso comportamento bizzarro che sto descrivendo qui: stackoverflow.com/questions/19927806/...
PatrickT

1
Lo strano comportamento non si verifica con insertRow2, nel mio particolare frame di dati e riga.
Patrick,

Come si aggiunge semplicemente una riga di numeri a un df? Ho dfcon le colonne a,b,c,de voglio aggiungere la riga 1,2,3,4. Come lo faccio?
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

3
Questa è una soluzione interessante. Non riesco ancora a capire perché sia ​​molto più veloce della chiamata simultanea a rbind, ma sono incuriosito.
Ari B. Friedman,

Alle risposte con parametri di riferimento dovrebbe essere applicata automaticamente una reputazione extra IMO. Grazie!
Alex,

10

Dovresti provare il pacchetto 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)
}

})

Produzione

   user  system elapsed 
   0.25    0.00    0.25

Contrariamente all'uso della funzione 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)
    }

})

Produzione

   user  system elapsed 
   0.49    0.00    0.49 

C'è un certo miglioramento delle prestazioni.


-4

per esempio, vuoi aggiungere righe della variabile 2 alla variabile 1 di un dato chiamato "bordi", fallo così

allEdges <- data.frame(c(edges$V1,edges$V2))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.