Neue Zeile zum Datenrahmen hinzufügen, an einem bestimmten Zeilenindex, nicht angehängt?

160

Der folgende Code kombiniert einen Vektor mit einem Datenrahmen:

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

Dieser Code fügt jedoch immer die neue Zeile am Ende des Datenrahmens ein.

Wie kann ich die Zeile an einer bestimmten Stelle im Datenrahmen einfügen? Angenommen, der Datenrahmen hat 20 Zeilen. Wie kann ich die neue Zeile zwischen den Zeilen 10 und 11 einfügen?

luciano
quelle
Verwenden Sie einen bequemen Index und sortieren Sie?
Roland
22
existingDF = rbind(existingDF[1:10,],newrow,existingDF[-(1:10),])
Pop
Mit einer einfachen Schleife und einer Bedingung, falls erforderlich, können Zeilen von einem Datenrahmen in einen anderen angehängt werden. Ein Beispielcode ist wie unten gezeigtnewdataframe[nrow(newdataframe)+1,] <- existingdataframe[i,]
kirancodify

Antworten:

156

Hier ist eine Lösung, die den (oft langsamen) rbindAufruf vermeidet :

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

Wenn Geschwindigkeit weniger wichtig als Klarheit ist, funktioniert die Lösung von @ Simon gut:

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

(Beachten Sie, dass wir randers indizieren ).

Und schließlich 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

Wie @MatthewDowle mich immer darauf hinweist, müssen Benchmarks für die Skalierung untersucht werden, wenn das Problem größer wird. Los geht's dann:

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()

Die Lösung von @ Roland lässt sich auch mit dem Aufruf an 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

Auf einer linearen Skala dargestellt:

linear

Und eine Log-Log-Skala:

log-log

Ari B. Friedman
quelle
3
Das Einfügen einer Zeile am Ende führt zu seltsamem Verhalten!
Maarten
@ Maarten Mit welcher Funktion?
Ari B. Friedman
Ich denke, es ist das seltsame Verhalten, das ich hier beschreibe: stackoverflow.com/questions/19927806/…
PatrickT
1
Das seltsame Verhalten tritt bei insertRow2 in meinem speziellen Datenrahmen und meiner Zeile nicht auf.
PatrickT
Wie fügt man einem df einfach eine Reihe von Zahlen hinzu? Ich habe dfmit Spalten a,b,c,dund ich möchte die Zeile hinzufügen 1,2,3,4. Wie mache ich das?
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
quelle
3
Dies ist eine coole Lösung. rbindIch kann immer noch nicht herausfinden, warum es so viel schneller ist als der gleichzeitige Anruf , aber ich bin fasziniert.
Ari B. Friedman
Bei Antworten mit Benchmarks sollte IMO automatisch eine zusätzliche Reputation angewendet werden. Vielen Dank!
Alex
10

Sie sollten versuchen, dplyr Paket

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)
}

})

Ausgabe

   user  system elapsed 
   0.25    0.00    0.25

Im Gegensatz zur Verwendung der Rbind-Funktion

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)
    }

})

Ausgabe

   user  system elapsed 
   0.49    0.00    0.49 

Es gibt einen gewissen Leistungsgewinn.

Naimish Agarwal
quelle
-4

Zum Beispiel möchten Sie Zeilen von Variable 2 zu Variable 1 von Daten mit dem Namen "Kanten" hinzufügen. Machen Sie es einfach so

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