几种SQL语言(我主要使用postgreSQL)有一个名为coalesce的函数,它返回每行的第一个非空列元素.当表中包含大量NULL
元素时,这可以非常有效地使用.
我在R中的许多场景中都遇到过这种情况,当处理不太结构化的数据时,其中包含很多NA.
我自己做了一个天真的实现,但它的速度非常慢.
coalesce <- function(...) {
apply(cbind(...), 1, function(x) {
x[which(!is.na(x))[1]]
})
}
Run Code Online (Sandbox Code Playgroud)
a <- c(1, 2, NA, 4, NA)
b <- c(NA, NA, NA, 5, 6)
c <- c(7, 8, NA, 9, 10)
coalesce(a,b,c)
# [1] 1 2 NA 4 6
Run Code Online (Sandbox Code Playgroud)
coalesce
在R中有没有有效的方法?
mri*_*rip 41
在我的机器上,使用Reduce
性能提高了5倍:
coalesce2 <- function(...) {
Reduce(function(x, y) {
i <- which(is.na(x))
x[i] <- y[i]
x},
list(...))
}
> microbenchmark(coalesce(a,b,c),coalesce2(a,b,c))
Unit: microseconds
expr min lq median uq max neval
coalesce(a, b, c) 97.669 100.7950 102.0120 103.0505 243.438 100
coalesce2(a, b, c) 19.601 21.4055 22.8835 23.8315 45.419 100
Run Code Online (Sandbox Code Playgroud)
Mar*_*gan 21
貌似coalesce1仍然可用
coalesce1 <- function(...) {
ans <- ..1
for (elt in list(...)[-1]) {
i <- is.na(ans)
ans[i] <- elt[i]
}
ans
}
Run Code Online (Sandbox Code Playgroud)
哪个更快(但是或多或少的手重写Reduce
,所以不那么一般)
> identical(coalesce(a, b, c), coalesce1(a, b, c))
[1] TRUE
> microbenchmark(coalesce(a,b,c), coalesce1(a, b, c), coalesce2(a,b,c))
Unit: microseconds
expr min lq median uq max neval
coalesce(a, b, c) 336.266 341.6385 344.7320 355.4935 538.348 100
coalesce1(a, b, c) 8.287 9.4110 10.9515 12.1295 20.940 100
coalesce2(a, b, c) 37.711 40.1615 42.0885 45.1705 67.258 100
Run Code Online (Sandbox Code Playgroud)
或者对于较大的数据比较
coalesce1a <- function(...) {
ans <- ..1
for (elt in list(...)[-1]) {
i <- which(is.na(ans))
ans[i] <- elt[i]
}
ans
}
Run Code Online (Sandbox Code Playgroud)
显示which()
有时可能有效,即使它意味着第二次通过索引.
> aa <- sample(a, 100000, TRUE)
> bb <- sample(b, 100000, TRUE)
> cc <- sample(c, 100000, TRUE)
> microbenchmark(coalesce1(aa, bb, cc),
+ coalesce1a(aa, bb, cc),
+ coalesce2(aa,bb,cc), times=10)
Unit: milliseconds
expr min lq median uq max neval
coalesce1(aa, bb, cc) 11.110024 11.137963 11.145723 11.212907 11.270533 10
coalesce1a(aa, bb, cc) 2.906067 2.953266 2.962729 2.971761 3.452251 10
coalesce2(aa, bb, cc) 3.080842 3.115607 3.139484 3.166642 3.198977 10
Run Code Online (Sandbox Code Playgroud)
zx8*_*754 15
使用dplyr包:
library(dplyr)
coalesce(a, b, c)
# [1] 1 2 NA 4 6
Run Code Online (Sandbox Code Playgroud)
Benchamark,没有公认解决方案那么快:
coalesce2 <- function(...) {
Reduce(function(x, y) {
i <- which(is.na(x))
x[i] <- y[i]
x},
list(...))
}
microbenchmark::microbenchmark(
coalesce(a, b, c),
coalesce2(a, b, c)
)
# Unit: microseconds
# expr min lq mean median uq max neval cld
# coalesce(a, b, c) 21.951 24.518 27.28264 25.515 26.9405 126.293 100 b
# coalesce2(a, b, c) 7.127 8.553 9.68731 9.123 9.6930 27.368 100 a
Run Code Online (Sandbox Code Playgroud)
但是在更大的数据集上,它具有可比性:
aa <- sample(a, 100000, TRUE)
bb <- sample(b, 100000, TRUE)
cc <- sample(c, 100000, TRUE)
microbenchmark::microbenchmark(
coalesce(aa, bb, cc),
coalesce2(aa, bb, cc))
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# coalesce(aa, bb, cc) 1.708511 1.837368 5.468123 3.268492 3.511241 96.99766 100 a
# coalesce2(aa, bb, cc) 1.474171 1.516506 3.312153 1.957104 3.253240 91.05223 100 a
Run Code Online (Sandbox Code Playgroud)
我有一个coalesce.na
在我的misc包中调用的即用型实现.它看起来很有竞争力,但不是最快的.它也适用于不同长度的载体,并对长度为1的载体进行特殊处理:
expr min lq median uq max neval
coalesce(aa, bb, cc) 990.060402 1030.708466 1067.000698 1083.301986 1280.734389 10
coalesce1(aa, bb, cc) 11.356584 11.448455 11.804239 12.507659 14.922052 10
coalesce1a(aa, bb, cc) 2.739395 2.786594 2.852942 3.312728 5.529927 10
coalesce2(aa, bb, cc) 2.929364 3.041345 3.593424 3.868032 7.838552 10
coalesce.na(aa, bb, cc) 4.640552 4.691107 4.858385 4.973895 5.676463 10
Run Code Online (Sandbox Code Playgroud)
这是代码:
coalesce.na <- function(x, ...) {
x.len <- length(x)
ly <- list(...)
for (y in ly) {
y.len <- length(y)
if (y.len == 1) {
x[is.na(x)] <- y
} else {
if (x.len %% y.len != 0)
warning('object length is not a multiple of first object length')
pos <- which(is.na(x))
x[pos] <- y[(pos - 1) %% y.len + 1]
}
}
x
}
Run Code Online (Sandbox Code Playgroud)
当然,正如凯文指出的那样,Rcpp解决方案可能会快几个数量级.
从data.table >= 1.12.3
您可以使用fcoalesce
。
library(data.table)
fcoalesce(a, b, c)
# [1] 1 2 NA 4 6
Run Code Online (Sandbox Code Playgroud)
有关更多信息(包括基准),请参阅新闻项#18(开发版本1.12.3)。
一个非常简单的解决方案是使用包ifelse
中的函数base
:
coalesce3 <- function(x, y) {
ifelse(is.na(x), y, x)
}
Run Code Online (Sandbox Code Playgroud)
虽然看起来比coalesce2
上面慢:
test <- function(a, b, func) {
for (i in 1:10000) {
func(a, b)
}
}
system.time(test(a, b, coalesce2))
user system elapsed
0.11 0.00 0.10
system.time(test(a, b, coalesce3))
user system elapsed
0.16 0.00 0.15
Run Code Online (Sandbox Code Playgroud)
您可以使用Reduce
它来使其适用于任意数量的向量:
coalesce4 <- function(...) {
Reduce(coalesce3, list(...))
}
Run Code Online (Sandbox Code Playgroud)