Creating an R dataframe row-by-row

我想在 R 中逐行构建一个数据框架,我做了一些搜索,得到的建议是创建一个空列表,保持一个列表索引标量,然后每次向列表中添加一个单行数据框架,并将列表索引提前一行。最后,名单上的 do.call(rbind,)

虽然这个方法很有效,但是看起来很麻烦。难道没有更简单的方法来实现同样的目标吗?

显然,我指的是不能使用某些 apply函数并显式需要逐行创建数据框架的情况。至少,有没有一种方法可以让 push进入列表的末尾,而不是显式地跟踪最后使用的索引?

151402 次浏览

可以将行添加到 NULL:

df<-NULL;
while(...){
#Some code that generates new row
rbind(df,row)->df
}

比如说

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)

可以通过追加或使用 rbind()逐行增长它们。

但这并不意味着你应该这么做。动态生长的结构是 R 语言中效率最低的编码方式之一。

如果可以的话,预先分配好你所有的数据:

N <- 1e4  # total number of rows to preallocate--possibly an overestimate


DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
stringsAsFactors=FALSE)          # you don't know levels yet

然后在操作期间一次插入一行

DF[i, ] <- list(1.4, "foo")

That should work for arbitrary data.frame and be much more efficient. If you overshot N you can always shrink empty rows out at the end.

这是一个愚蠢的例子,说明如何在 Map()的输出上使用 do.call(rbind,)[类似于 lapply()]

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

我经常使用这个结构。

我之所以这么喜欢 Rcpp 是因为我并不总是能理解 RCore 的想法,而对于 Rcpp 来说,通常情况下,我并不需要理解。

从哲学的角度讲,在函数范式方面,你正处于一种罪恶的状态,这种范式试图确保每个值 appears独立于其他值; 改变一个值不应该导致另一个值的可见变化,就像你在 C 语言中使用指针共享表示一样。

当函数式编程发出信号,要求小飞行器离开时,小飞行器回答“我是一座灯塔”,问题就出现了。对一个大的物体做一系列的小的改变,同时你想要处理这些改变,这样你就进入了灯塔的领域。

在 C + + STL 中,push_back()是一种生活方式。它不试图是函数式的,但它确实试图适应常见的编程习惯用法 有效率

在幕后运用一些聪明才智,你有时可以安排一只脚踏入每个世界。基于快照的文件系统就是一个很好的例子(它是从联合挂载等概念发展而来的,这些概念同样适用于两边)。

If R Core wanted to do this, underlying vector storage could function like a union mount. One reference to the vector storage might be valid for subscripts 1:N, while another reference to the same storage is valid for subscripts 1:(N+1). There could be reserved storage not yet validly referenced by anything but convenient for a quick push_back(). You don't violate the functional concept when appending outside the range that any existing reference considers valid.

Eventually appending rows incrementally, you run out of reserved storage. You'll need to create new copies of everything, with the storage multiplied by some increment. The STL implementations I've use tend to multiply storage by 2 when extending allocation. I thought I read in R Internals that there is a memory structure where the storage increments by 20%. Either way, growth operations occur with logarithmic frequency relative to the total number of elements appended. On an amortized basis, this is usually acceptable.

就幕后花招而言,我见过更糟的。每次 push_back()向数据框架添加新行时,都需要复制一个顶级索引结构。新行可以附加到共享表示上,而不会影响任何旧的函数值。我甚至不认为这会使垃圾收集器复杂化多少; 因为我没有提出 push_front(),所有的引用都是指向分配的向量存储前端的前缀引用。

如果向量注定要成为行,那么使用 c()将它们连接起来,将它们逐行传递给矩阵,并将该矩阵转换为数据框架。

例如,行

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

可转换为数据框架,从而:

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

Admittedly, I see 2 major limitations: (1) this only works with single-mode data, and (2) you must know your final # columns for this to work (i.e., I'm assuming that you're not working with a ragged array whose greatest row length is unknown 先验的).

这个解决方案看起来很简单,但是根据我在 R 中进行类型转换的经验,我确信它会带来新的挑战。有人对此有何评论吗?

Dirk Eddelbuettel 的回答是最好的; 在这里我只是提醒你不用预先指定数据框架的尺寸或者数据类型,如果你有多种数据类型和大量的列,这有时是很有用的:

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)


df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(df,row2) #now this works as you'd expect.

我已经找到了这种方法来创建数据框架的原始没有矩阵。

使用自动列名

df<-data.frame(
t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
,row.names = NULL,stringsAsFactors = FALSE
)

列名

df<-setNames(
data.frame(
t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
,row.names = NULL,stringsAsFactors = FALSE
),
c("col1","col2","col3")
)

根据新行的格式,如果新行很简单并且可以用“值对”指定,则可以使用 tibble::add_row。或者您可以使用 dplyr::bind_rows,“ do.call (rbind,dfs)公共模式的有效实现”。