逐行创建R数据帧


107

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

尽管此方法有效,但似乎非常麻烦。有没有更简单的方法可以实现相同的目标?

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


1
您可以使用append()[可能应该命名为insert]或c()将项目添加到列表的末尾,尽管此处无济于事。
hatmatrix 2010年

有没有R中许多功能,包括返回的数据帧,除非你回报他们[逐行]从lapply()Map()和等,但你也可能想看看aggregate()dapply() {heR.Misc}cast() {reshape}看看你的任务无法通过这些处理功能(所有这些都返回数据帧)。
hatmatrix

Answers:


96

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

那并不意味着你应该。动态增长的结构是用R进行编码的效率最低的方法之一。

如果可以,请预先分配整个data.frame:

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

这应该适用于任意data.frame,并且效率更高。如果您超过N,则总是可以在最后缩小空行。


6
您不是要用N代替10,然后用list(1.4,“ foo”)代替c(1.4,“ foo”)来使1.4不能进入字符模式吗?
hatmatrix

是的,我打算在data.frame创建中使用N。另外,非常好的捕获将胁迫变成了聊天-我错过了。
Dirk Eddelbuettel 2010年

1
编辑答案要好于将其保留在评论中。我很迷茫,想知道这个答案。
用户

4
data.table似乎比使用data.frames进行预分配还要快。在此处进行测试:stackoverflow.com/a/11486400/636656
Ari B. Friedman 2012年

在R 3.1中仍然正确吗?
userJT 2014年

49

可以将行添加到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)

3
它输出矩阵,而不是数据帧
Olga 2013年

1
@Olga仅当您绑定相同类型的元素行时-在这种情况下,BTW更好sapply(或向量化)和转置。
mbq 2013年

1
@mbq我在做什么。我还发现,如果使用df <-data.frame()对其进行初始化,它将输出一个数据帧。
奥尔加

9

这是一个愚蠢的例子,说明如何do.call(rbind,)Map()[类似于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"

我经常使用这种构造。


8

我之所以如此喜欢Rcpp的原因是,我并不总是了解R Core的想法,而有了Rcpp,我常常不必这样做。

哲学上说,你在罪与问候的功能模式,它试图以确保每一个值的状态出现独立每隔价值; 更改一个值永远不会导致另一个值的可见变化,这是使用指针共享C语言表示形式的方式。

当函数式编程向小型飞船发出信号时,就会出现问题,而小型飞船会回答“我是灯塔”。同时,对要处理的大对象进行一系列小改动,可以使您进入灯塔领域。

在C ++ STL中,这push_back()是一种生活方式。它不会尝试是功能性的,但它确实尽力满足常见的编程风格有效

有了一些聪明的幕后知识,您有时可以安排每个世界都站着一只脚。基于快照的文件系统就是一个很好的例子(它是从联合安装等概念发展而来的,该概念也同时包含了双方)。

如果R Core想要执行此操作,则底层向量存储可以像联合安装那样起作用。对矢量存储的一个引用可能对下标有效1:N,而对相同存储的另一引用对下标有效1:(N+1)。可能有保留的存储空间,除了方便快捷之外,还没有任何有效的参考push_back()。在任何现有参考认为有效的范围之外进行附加时,都不会违反功能概念。

最终,以增量方式追加行,将耗尽保留的存储空间。您需要创建所有内容的新副本,并将存储空间乘以一定的增量。在扩展分配时,我使用的STL实现往往会将存储空间乘以2。我以为我在R Internals中读到了一种内存结构,其中存储增加了20%。不管哪种方式,增长操作都以相对于附加元素总数的对数频率进行。按摊销基准,这通常是可以接受的。

随着幕后花招的流逝,我看到了更糟的情况。每次push_back()在数据框中添加新行时,都需要复制一个顶层索引结构。新行可以追加到共享表示中,而不会影响任何旧功能值。我什至不认为这会使垃圾收集器复杂化。因为我不建议push_front()所有引用都是已分配向量存储区前面的前缀引用。


2

德克·埃德比布特尔的答案是最好的。在这里,我只是注意到您可以不必预先指定数据框的尺寸或数据类型,而在您具有多种数据类型和许多列的情况下,这有时很有用:

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<-rbind(df, row2)
蒂莫西·奎因

1

我发现这种通过原始矩阵创建数据框的方法。

带自动列名

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

0

如果您将向量指定为行,请使用进行连接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))

诚然,我看到两个主要限制:(1)仅适用于单模式数据,(2)您必须知道最后的#列才能起作用(即,我假设您不使用最大行长度是先验未知参差不齐的数组)。

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


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.