Ajouter une nouvelle ligne à dataframe, à un index de ligne spécifique, non ajouté?

160

Le code suivant combine un vecteur avec un dataframe:

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

Cependant, ce code insère toujours la nouvelle ligne à la fin du dataframe.

Comment puis-je insérer la ligne à un point spécifié dans la trame de données? Par exemple, disons que le dataframe a 20 lignes, comment puis-je insérer la nouvelle ligne entre les lignes 10 et 11?

Luciano
la source
Utilisez un index et un tri pratique?
Roland
22
existingDF = rbind(existingDF[1:10,],newrow,existingDF[-(1:10),])
Pop
Avec une simple boucle et une condition si nécessaire, les lignes peuvent être ajoutées d'une trame de données à une autre. Un exemple de code est comme indiqué ci-dessousnewdataframe[nrow(newdataframe)+1,] <- existingdataframe[i,]
kirancodify

Réponses:

156

Voici une solution qui évite l' rbindappel (souvent lent) :

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 vitesse est moins importante que la clarté, alors la solution de @ Simon fonctionne 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

(Notez que nous indexons rdifféremment).

Et enfin, des benchmarks:

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

Benchmarks

Comme @MatthewDowle me le fait toujours remarquer, des repères doivent être examinés pour la mise à l'échelle à mesure que la taille du problème augmente. On y va alors:

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 solution de @ Roland évolue assez bien, même avec l'appel à 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

Tracé sur une échelle linéaire:

linéaire

Et une échelle log-log:

log-log

Ari B. Friedman
la source
3
Insérer une ligne à la fin donne un comportement étrange!
Maarten
@Maarten Avec quelle fonction?
Ari B.Friedman
Je suppose que c'est le même comportement étrange que je décris ici: stackoverflow.com/questions/19927806
...
1
Le comportement étrange ne se produit pas avec insertRow2, dans mon bloc de données et ma ligne particuliers.
PatrickT
Comment ajouter simplement une ligne de nombres à un df? J'ai dfavec des colonnes a,b,c,det je veux ajouter la ligne 1,2,3,4. Comment je fais ça?
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
la source
3
C'est une solution cool. Je n'arrive toujours pas à comprendre pourquoi c'est tellement plus rapide que l'appel simultané rbind, mais je suis intrigué.
Ari
Les réponses avec des repères devraient avoir une certaine réputation supplémentaire appliquée automatiquement à l'OMI. Merci!
Alex
10

Vous devriez essayer le package 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)
}

})

Production

   user  system elapsed 
   0.25    0.00    0.25

Contrairement à l'utilisation de la fonction 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)
    }

})

Production

   user  system elapsed 
   0.49    0.00    0.49 

Il y a un gain de performance.

Naimish Agarwal
la source
-4

par exemple, vous voulez ajouter des lignes de la variable 2 à la variable 1 d'une donnée nommée "bords", faites-le comme ceci

allEdges <- data.frame(c(edges$V1,edges$V2))
user3670684
la source