我想在R中逐行构建一个数据帧。我已经做了一些搜索,我想出的就是建议创建一个空列表,保留一个列表索引标量,然后每次添加列表中的单行数据帧并将列表索引前进一个。最后,列表上有do.call(rbind,)
。
虽然这有效,但看起来非常麻烦。是否有更简单的方法来实现同一目标?
显然,我指的是我不能使用某些apply
函数并明确需要逐行创建数据帧的情况。至少,有没有办法push
进入列表的末尾,而不是明确地跟踪最后使用的索引?
答案 0 :(得分:89)
您可以通过附加或使用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,你总是可以在最后收缩空行。
答案 1 :(得分:47)
可以向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)
答案 2 :(得分: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"
我经常使用这个结构。
答案 3 :(得分:8)
我非常喜欢Rcpp的原因是我并不总是了解R Core的想法,而且通常使用Rcpp,我不需要。
在哲学上说,你在功能范式方面处于一种罪恶状态,它试图确保每个值出现独立于其他所有价值;更改一个值不应该导致另一个值发生明显变化,指针在C中共享表示的方式。
当功能性编程发出信号表示小型飞行器移开时,问题就出现了,小型工艺人员回答“我是一座灯塔”。在此期间对要处理的大对象进行一系列小的更改会使您成为灯塔区域的正方形。
在C ++ STL中,push_back()
是一种生活方式。它并不试图发挥作用,但它确实试图适应常见的编程习惯 。
在幕后有些聪明,你有时可以在每个世界中安排一只脚。基于快照的文件系统就是一个很好的例子(它是从诸如联合安装之类的概念发展而来的,它也是双方的一部分)。
如果R Core想要这样做,底层矢量存储可以像union mount一样运行。对向量存储的一个引用可能对下标1:N
有效,而对同一存储的另一个引用对下标1:(N+1)
有效。除了方便快捷push_back()
之外,还有一些保留存储尚未被有效引用。在超出任何现有引用认为有效的范围之外时,不违反功能概念。
最终以递增方式追加行,您的预留存储空间不足。你需要创建所有东西的新副本,存储乘以一些增量。我使用的STL实现在扩展分配时倾向于将存储乘以2。我以为我在R Internals中读到有一个内存结构,其中存储增加了20%。无论哪种方式,增长操作都以相对于附加元素总数的对数频率发生。在摊销的基础上,这通常是可以接受的。
作为幕后花样,我看到了更糟。每次push_back()
在数据帧上添加新行时,都需要复制顶级索引结构。新行可以附加到共享表示,而不会影响任何旧的功能值。我甚至认为它不会使垃圾收集器复杂化;因为我不提议push_front()
所有引用都是对分配的向量存储的前面的前缀引用。
答案 4 :(得分:1)
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(d,row2) #now this works as you'd expect.
答案 5 :(得分:1)
我已经找到了这种通过原始方法创建数据帧而无需矩阵的方法。
具有自动列名
wb.save('G:\\path')
带有列名
df<-data.frame(
t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
,row.names = NULL,stringsAsFactors = FALSE
)
答案 6 :(得分: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)你必须知道你的最终#列才能工作(即,我假设你不是使用一个最大行长度未知的不规则数组 a priori )。
这个解决方案看起来很简单,但根据我在R中进行类型转换的经验,我确信它会带来新的挑战。有人可以对此发表评论吗?
答案 7 :(得分:0)
根据新行的格式,如果新行很简单并且可以在“值对”中指定,则可以使用tibble::add_row
。或者,您可以使用dplyr::bind_rows
,“ do.call(rbind,dfs)通用模式的有效实现”。