假设a
和b
是两个数据框。目标是编写一个函数
生成合并数据框的f(a,b)
,与合并相同
merge(a,b,all=TRUE)
会这样做,即填充a
或b
中缺少的变量。 (问题是merge()
似乎非常慢。)
这可以如下完成(伪代码):
for each variable `var` found in either `a` or `b`, do:
unlist(list(a.srcvar, b.srcvar), recursive=FALSE, use.names=FALSE)
where:
x.srcvar is x$var if x$var exists, or else
rep(NA, nrow(x)) if y$var !is.factor, or else
as.factor(rep(NA, nrow(x)))
然后将所有内容都包装在数据框中。
这是一个“天真”的实现:
merge.datasets1 <- function(a, b) {
a.fill <- rep(NA, nrow(a))
b.fill <- rep(NA, nrow(b))
a.fill.factor <- as.factor(a.fill)
b.fill.factor <- as.factor(b.fill)
out <- list()
for (v in union(names(a), names(b))) {
if (!v %in% names(a)) {
b.srcvar <- b[[v]]
if (is.factor(b.srcvar))
a.srcvar <- a.fill.factor
else
a.srcvar <- a.fill
} else {
a.srcvar <- a[[v]]
if (v %in% names(b))
b.srcvar <- b[[v]]
else if (is.factor(a.srcvar))
b.srcvar <- b.fill.factor
else
b.srcvar <- b.fill
}
out[[v]] <- unlist(list(a.srcvar, b.srcvar),
recursive=FALSE, use.names=FALSE)
}
data.frame(out)
}
这是使用“矢量化”函数的不同实现:
merge.datasets2 <- function(a, b) {
srcvar <- within(list(var=union(names(a), names(b))), {
a.exists <- var %in% names(a)
b.exists <- var %in% names(b)
a.isfactor <- unlist(lapply(var, function(v) is.factor(a[[v]])))
b.isfactor <- unlist(lapply(var, function(v) is.factor(b[[v]])))
a <- ifelse(a.exists, var, ifelse(b.isfactor, 'fill.factor', 'fill'))
b <- ifelse(b.exists, var, ifelse(a.isfactor, 'fill.factor', 'fill'))
})
a <- within(a, {
fill <- NA
fill.factor <- factor(fill)
})
b <- within(b, {
fill <- NA
fill.factor <- factor(fill)
})
out <- mapply(function(x,y) unlist(list(a[[x]], b[[y]]),
recursive=FALSE, use.names=FALSE),
srcvar$a, srcvar$b, SIMPLIFY=FALSE, USE.NAMES=FALSE)
out <- data.frame(out)
names(out) <- srcvar$var
out
}
现在我们可以测试一下:
sample.datasets <- lapply(1:50, function(i) iris[,sample(names(iris), 4)])
system.time(invisible(Reduce(merge.datasets1, sample.datasets)))
>> user system elapsed
>> 0.192 0.000 0.190
system.time(invisible(Reduce(merge.datasets2, sample.datasets)))
>> user system elapsed
>> 2.292 0.000 2.293
所以,天真的版本比另一个快几个数量级。怎么能
这是?我一直以为for
循环很慢,那个应该是
而是使用lapply
和朋友,避开R中的循环。我欢迎任何关于如何在速度方面改进我的功能的想法。
答案 0 :(得分:3)
事实上,您根本没有尝试复制merge(a,b, all = TRUE)
,因为您没有尝试合并任何列。相反,您只是堆叠数据,填充NA
,其中列不存在。
# note that this is not what you want/
dim(merge(sample.datasets[[1]], sample.datasets[[2]], all = T))
[1] 314 5
merge(a,b, all = TRUE)
慢的原因是它默认通过名称的交集来合并。如果你转换为data.tables
那么merge.data.table
方法是快速的,但是对于你的测试数据,它会在每次成功合并时创建一个指数级增长的数据集(不是7500乘5,因为你希望你的结果是定)
一个简单的解决方案是使用rbind.fill
包中的plyr
。
library(plyr)
system.time({.x <- Reduce(rbind.fill, sample.datasets)})
## user system elapsed
## 0.16 0.00 0.15
# which is almost identical to
system.time(.old <- Reduce(merge.datasets1, sample.datasets))
## user system elapsed
## 0.14 0.00 0.14
在进一步考虑时,注意您可以将data.frames
列表传递给rbind.fill
非常有用
system.time(super_fast <- rbind.fill(sample.datasets))
## user system elapsed
## 0.02 0.00 0.02
identical(super_fast, .old)
[1] TRUE
花费Reduce
rbind.fill
的费用的大部分时间,{{1}}不需要。