如何将行附加到R数据框

我已经看了一下周围的StackOverflow,但我找不到解决scheme特定于我的问题,其中涉及将行附加到R数据框。

我正在初始化一个空的2列数据框,如下所示。

df = data.frame(x = numeric(), y = character()) 

然后,我的目标是迭代值列表,并在每次迭代中,将一个值附加到列表的末尾。 我从下面的代码开始。

 for (i in 1:10) { df$x = rbind(df$x, i) df$y = rbind(df$y, toString(i)) } 

我也尝试了函数cappend ,并没有成功merge 。 如果您有任何build议,请让我知道。

更新

不知道你在做什么,我会再分享一个build议:为每一列预先分配你想要的types的向量,在这些向量中插入值,最后创build你的data.frame

继续Julian的f3 (预分配data.frame )作为目前为止最快的选项,定义为:

 # pre-allocate space f3 <- function(n){ df <- data.frame(x = numeric(n), y = character(n), stringsAsFactors = FALSE) for(i in 1:n){ df$x[i] <- i df$y[i] <- toString(i) } df } 

这里有一个类似的方法,但最后一步是创builddata.frame

 # Use preallocated vectors f4 <- function(n) { x <- numeric(n) y <- character(n) for (i in 1:n) { x[i] <- i y[i] <- i } data.frame(x, y, stringsAsFactors=FALSE) } 

来自“microbenchmark”软件包的microbenchmark将给我们比system.time更全面的洞察力system.time

 library(microbenchmark) microbenchmark(f1(1000), f3(1000), f4(1000), times = 5) # Unit: milliseconds # expr min lq median uq max neval # f1(1000) 1024.539618 1029.693877 1045.972666 1055.25931 1112.769176 5 # f3(1000) 149.417636 150.529011 150.827393 151.02230 160.637845 5 # f4(1000) 7.872647 7.892395 7.901151 7.95077 8.049581 5 

f1() (下面的方法)效率非常低,因为它调用data.frame是多less,而且因为增长的对象通常在R中很慢data.frame f3()由于预分配而得到很大的改善,但是data.frame结构本身可能成为这里瓶颈的一部分。 f4()试图绕过这个瓶颈而不影响你想采取的方法。


原始答案

这真的不是一个好主意,但如果你想这样做,我想你可以试试:

 for (i in 1:10) { df <- rbind(df, data.frame(x = i, y = toString(i))) } 

请注意,在您的代码中,还有一个问题:

  • 如果您希望字符不能转换为因素,则应该使用stringsAsFactors 。 使用: df = data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE)

我们来分析一下提出的三个解决scheme

 # use rbind f1 <- function(n){ df <- data.frame(x = numeric(), y = character()) for(i in 1:n){ df <- rbind(df, data.frame(x = i, y = toString(i))) } df } # use list f2 <- function(n){ df <- data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE) for(i in 1:n){ df[i,] <- list(i, toString(i)) } df } # pre-allocate space f3 <- function(n){ df <- data.frame(x = numeric(1000), y = character(1000), stringsAsFactors = FALSE) for(i in 1:n){ df$x[i] <- i df$y[i] <- toString(i) } df } system.time(f1(1000)) # user system elapsed # 1.33 0.00 1.32 system.time(f2(1000)) # user system elapsed # 0.19 0.00 0.19 system.time(f3(1000)) # user system elapsed # 0.14 0.00 0.14 

最好的解决scheme是预先分配空间(按照R的预期)。 次最好的解决scheme是使用list ,而最糟糕的解决scheme(至less基于这些时序结果)似乎是rbind

假设你根本不知道data.frame的大小。 它可以是几行,或几百万。 你需要有一些容器,dynamic增长。 考虑到我的经验和所有相关的答案,所以我有四个不同的解决scheme:

  1. rbindlist到data.frame

  2. 使用data.table的快速set操作,并在需要时手动将表加倍。

  3. 使用RSQLite并追加到内存中保存的表。

  4. data.frame自己的能力,增长和使用自定义的环境(其中有引用语义)来存储data.frame所以它不会被复制回来。

下面是对大量附加行的所有方法的testing。 每种方法都有3个相关的function:

  • create(first_element)返回create(first_element)的适当的后台对象。

  • append(object, element)element追加到表格的末尾(用object表示)。

  • access(object)获取所有插入元素的data.frame

rbindlist到data.frame

这很简单直接:

 create.1<-function(elems) { return(as.data.table(elems)) } append.1<-function(dt, elems) { return(rbindlist(list(dt, elems),use.names = TRUE)) } access.1<-function(dt) { return(dt) } 

data.table::set +在需要时手动将表加倍。

我将在rowcount属性中存储表的真正长度。

 create.2<-function(elems) { return(as.data.table(elems)) } append.2<-function(dt, elems) { n<-attr(dt, 'rowcount') if (is.null(n)) n<-nrow(dt) if (n==nrow(dt)) { tmp<-elems[1] tmp[[1]]<-rep(NA,n) dt<-rbindlist(list(dt, tmp), fill=TRUE, use.names=TRUE) setattr(dt,'rowcount', n) } pos<-as.integer(match(names(elems), colnames(dt))) for (j in seq_along(pos)) { set(dt, i=as.integer(n+1), pos[[j]], elems[[j]]) } setattr(dt,'rowcount',n+1) return(dt) } access.2<-function(elems) { n<-attr(elems, 'rowcount') return(as.data.table(elems[1:n,])) } 

SQL应该针对快速logging插入进行优化,所以我最初对RSQLite解决scheme寄予厚望

这基本上是复制和粘贴Karsten W.类似的线程上的答案 。

 create.3<-function(elems) { con <- RSQLite::dbConnect(RSQLite::SQLite(), ":memory:") RSQLite::dbWriteTable(con, 't', as.data.frame(elems)) return(con) } append.3<-function(con, elems) { RSQLite::dbWriteTable(con, 't', as.data.frame(elems), append=TRUE) return(con) } access.3<-function(con) { return(RSQLite::dbReadTable(con, "t", row.names=NULL)) } 

data.frame自己的行追加+自定义环境。

 create.4<-function(elems) { env<-new.env() env$dt<-as.data.frame(elems) return(env) } append.4<-function(env, elems) { env$dt[nrow(env$dt)+1,]<-elems return(env) } access.4<-function(env) { return(env$dt) } 

testing套件:

为了方便起见,我将使用一个testing函数以间接调用来覆盖它们。 (我检查:使用do.call而不是直接调用函数不会使代码运行更长时间)。

 test<-function(id, n=1000) { n<-n-1 el<-list(a=1,b=2,c=3,d=4) o<-do.call(paste0('create.',id),list(el)) s<-paste0('append.',id) for (i in 1:n) { o<-do.call(s,list(o,el)) } return(do.call(paste0('access.', id), list(o))) } 

让我们看看n = 10插入的性能。

我还添加了一个“安慰剂”function(后缀为0 ),它们不执行任何操作,只是为了测量testing设置的开销。

 r<-microbenchmark(test(0,n=10), test(1,n=10),test(2,n=10),test(3,n=10), test(4,n=10)) autoplot(r) 

添加n = 10行的时间

计时n = 100行 计时n = 1000行

对于1E5行(在Intel(R)Core(TM)i7-4710HQ CPU @ 2.50GHz上完成的测量):

 nr function time 4 data.frame 228.251 3 sqlite 133.716 2 data.table 3.059 1 rbindlist 169.998 0 placebo 0.202 

它看起来像基于SQLite的数据泄露,尽pipe在大数据上恢复了一些速度,但是还远没有data.table +手动指数增长。 差别几乎是两个数量级!

概要

如果您知道您将追加相当less量的行(n <= 100),请继续使用最简单的解决scheme:使用括号表示将行分配给data.frame,并忽略data.frame没有预先填充。

对于其他所有的东西,使用data.table::set并以指数forms增长data.table(例如使用我的代码)。

更通用的解决scheme可能是以下内容。

  extendDf <- function (df, n) { withFactors <- sum(sapply (df, function(X) (is.factor(X)) )) > 0 nr <- nrow (df) colNames <- names(df) for (c in 1:length(colNames)) { if (is.factor(df[,c])) { col <- vector (mode='character', length = nr+n) col[1:nr] <- as.character(df[,c]) col[(nr+1):(n+nr)]<- rep(col[1], n) # to avoid extra levels col <- as.factor(col) } else { col <- vector (mode=mode(df[1,c]), length = nr+n) class(col) <- class (df[1,c]) col[1:nr] <- df[,c] } if (c==1) { newDf <- data.frame (col ,stringsAsFactors=withFactors) } else { newDf[,c] <- col } } names(newDf) <- colNames newDf } 

extendDf()函数用n行扩展dataframe。

举个例子:

 aDf <- data.frame (l=TRUE, i=1L, n=1, c='a', t=Sys.time(), stringsAsFactors = TRUE) extendDf (aDf, 2) # linct # 1 TRUE 1 1 a 2016-07-06 17:12:30 # 2 FALSE 0 0 a 1970-01-01 01:00:00 # 3 FALSE 0 0 a 1970-01-01 01:00:00 system.time (eDf <- extendDf (aDf, 100000)) # user system elapsed # 0.009 0.002 0.010 system.time (eDf <- extendDf (eDf, 100000)) # user system elapsed # 0.068 0.002 0.070 

让我们看一个从1到5的vector“点”

point = c(1,2,3,4,5)

如果我们想在vector的任何地方附加一个数字6,那么下面的命令可能会派上用场

i) vector

new_var = append(point, 6 ,after = length(point))

ii) 一张桌子的列

new_var = append(point, 6 ,after = length(mtcars$mpg))

append命令有三个参数:

  1. 要修改的向量/列。
  2. 值被包括在修改的向量中。
  3. 一个下标,在此之后值被附加。

简单…!! 道歉的情况下,任何…!