Gya*_*eda 109 merge r append rows dataframe
我查看了StackOverflow,但我找不到特定于我的问题的解决方案,这涉及将行附加到R数据帧.
我正在初始化一个空的2列数据帧,如下所示.
df = data.frame(x = numeric(), y = character())
Run Code Online (Sandbox Code Playgroud)
然后,我的目标是迭代一个值列表,并在每次迭代中,将值附加到列表的末尾.我从以下代码开始.
for (i in 1:10) {
df$x = rbind(df$x, i)
df$y = rbind(df$y, toString(i))
}
Run Code Online (Sandbox Code Playgroud)
我也试图功能c
,append
以及merge
没有成功.如果您有任何建议,请告诉我.
A5C*_*2T1 107
不知道你想要做什么,我将再分享一个建议:为每个列预分配所需类型的向量,将值插入到这些向量中,然后在最后创建你的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
}
Run Code Online (Sandbox Code Playgroud)
这是一种类似的方法,但是将data.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)
}
Run Code Online (Sandbox Code Playgroud)
microbenchmark
来自"microbenchmark"的软件包将为我们提供比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
Run Code Online (Sandbox Code Playgroud)
f1()
(下面的方法)是非常低效的,因为它调用的频率data.frame
和由于f3()
预分配而在R中通常缓慢增长的对象大大改善,但data.frame
结构本身可能是这里瓶颈的一部分.f4()
试图绕过这个瓶颈而不影响你想采取的方法.
这真的不是一个好主意,但如果你想这样做,我想你可以试试:
for (i in 1:10) {
df <- rbind(df, data.frame(x = i, y = toString(i)))
}
Run Code Online (Sandbox Code Playgroud)
请注意,在您的代码中,还有一个问题:
stringsAsFactors
如果您希望字符不转换为因子,则应使用.使用:df = data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE)
Jul*_*ano 31
让我们对三种解决方案进行基准测试:
# 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
Run Code Online (Sandbox Code Playgroud)
最好的解决方案是预先分配空间(如R中所预期的).下一个最佳解决方案是使用list
,并且最坏的解决方案(至少基于这些时序结果)似乎是rbind
.
Ada*_*ski 13
假设您事先并不知道data.frame的大小.它可以是几行,或几百万.你需要有一些动态增长的容器.考虑到我的经验和所有相关的答案,我提出了4个不同的解决方案:
rbindlist
到data.frame
使用data.table
快速set
操作并在需要时手动将表加倍.
使用RSQLite
并附加到内存中的表中.
data.frame
自己增长和使用自定义环境(具有引用语义)来存储data.frame的能力,因此它不会在返回时被复制.
这是对小数量和大量附加行的所有方法的测试.每种方法都有3个与之相关的功能:
create(first_element)
返回带有first_element
put 的适当后备对象.
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)
}
Run Code Online (Sandbox Code Playgroud)
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,]))
}
Run Code Online (Sandbox Code Playgroud)
RSQLite
解决方案寄予厚望这基本上是复制和粘贴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))
}
Run Code Online (Sandbox Code Playgroud)
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)
}
Run Code Online (Sandbox Code Playgroud)
为方便起见,我将使用一个测试功能通过间接调用覆盖它们.(我检查过:使用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)))
}
Run Code Online (Sandbox Code Playgroud)
让我们看看n = 10次插入的表现.
我还添加了一个"安慰剂"功能(带后缀0
),它们不执行任何操作 - 只是为了测量测试设置的开销.
r<-microbenchmark(test(0,n=10), test(1,n=10),test(2,n=10),test(3,n=10), test(4,n=10))
autoplot(r)
Run Code Online (Sandbox Code Playgroud)
对于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
Run Code Online (Sandbox Code Playgroud)
它看起来像基于SQLite的问题,尽管在大数据上恢复了一些速度,但远不及data.table +手动指数式增长.差异几乎是两个数量级!
如果您知道将追加相当少量的行(n <= 100),请继续使用最简单的解决方案:只需使用括号表示法将行分配给data.frame,并忽略data.frame是的事实没有预先填充.
对于其他一切使用data.table::set
和增长data.table指数(例如使用我的代码).
由于问题已经过时(6 年),因此答案缺少带有较新软件包 tidyr 和 purrr 的解决方案。因此,对于使用这些软件包的人,我想为之前的答案添加一个解决方案 - 都很有趣,尤其是 .
purrr 和 tidyr 的最大优点是更好的可读性恕我直言。purrr 用更灵活的 map() 系列替换了 lapply,tidyr 提供了超级直观的方法 add_row - 只是按照它说的做:)
map_df(1:1000, function(x) { df %>% add_row(x = x, y = toString(x)) })
Run Code Online (Sandbox Code Playgroud)
此解决方案简短易读,而且速度相对较快:
system.time(
map_df(1:1000, function(x) { df %>% add_row(x = x, y = toString(x)) })
)
user system elapsed
0.756 0.006 0.766
Run Code Online (Sandbox Code Playgroud)
它几乎呈线性扩展,因此对于 1e5 行,性能为:
system.time(
map_df(1:100000, function(x) { df %>% add_row(x = x, y = toString(x)) })
)
user system elapsed
76.035 0.259 76.489
Run Code Online (Sandbox Code Playgroud)
这将使其在@Adam Ryczkowski 的基准测试中紧随 data.table(如果您忽略安慰剂)之后排名第二:
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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
314116 次 |
最近记录: |