R中的ifelse()对于确定在大向量上调用哪个函数是否有效?
我目前正在编写一个调用特定函数的代码,具体取决于向量中元素的值。那么,我的问题是这是否有效。如果我正确理解了R中的ifelse()对于确定在大向量上调用哪个函数是否有效?,r,performance,if-statement,R,Performance,If Statement,我目前正在编写一个调用特定函数的代码,具体取决于向量中元素的值。那么,我的问题是这是否有效。如果我正确理解了ifelse算法,那么我作为函数的第二个和第三个参数输入的任何值都将全部计算,然后根据我的条件的TRUE或FALSE值进行子集设置。这与我们在编码中看到的典型的if/else结构形成对比,在这种结构中,我们会评估一个条件,然后在元素上运行一个函数,直到我们知道要运行哪个函数。为了验证这一点,我尝试使用以下方法: test1 <- function() { x <- samp
ifelse
算法,那么我作为函数的第二个和第三个参数输入的任何值都将全部计算,然后根据我的条件的TRUE
或FALSE
值进行子集设置。这与我们在编码中看到的典型的if/else
结构形成对比,在这种结构中,我们会评估一个条件,然后在元素上运行一个函数,直到我们知道要运行哪个函数。为了验证这一点,我尝试使用以下方法:
test1 <- function() {
x <- sample(1:1e9, 1e6, replace = TRUE)
y <- ifelse(x %% 2 == 0, x**2, x/2)
return(y)
}
test2 <- function() {
x <- sample(1:1e9, 1e6, replace = TRUE)
y <- numeric(length(x))
for (i in 1:length(x)) {
if (x[i] %% 2 == 0) {
y[i] <- x[i]**2
} else {
y[i] <- x[i]/2
}
}
return(y)
}
microbenchmark::microbenchmark(test1(), test2(), times = 1000)
Unit: milliseconds
expr min lq mean median uq max neval
test1() 2.366067 2.494746 8.27343 2.580164 2.706826 1690.049 1000
test2() 21.773385 23.050818 29.70450 23.712907 29.468783 3169.008 1000
test1您的编码方式比ifelse
更糟糕,但正如的警告部分所建议的那样,ifelse
可能做得更好。使用简单的函数,x^2
和x/2
,下面的test3()
函数速度更快—大约是ifelse
的2到3倍,比test2()快30倍。如果函数的计算量更大(但仍然是矢量化的!),边界可能会更大
速度增加(我认为)主要是由于两个原因:
ifelse
执行test3()
跳过的输入检查和错误处理ifelse
更通用、更灵活test3()
硬编码为仅返回一个数值(矢量)
如中所示,ifelse
将计算其整个TRUE
响应向量,前提是至少有1个TRUE
测试值,同样地,其FALSE
也会计算test3()
通过创建TRUE
和FALSE
子向量绕过额外的计算
我修改了您的test1()
和test2()
以简化一点,拉出了数据模拟(因为这不是我们想要测试的)。我添加了使用逻辑子集的test3
。我还大大减小了测试向量的大小,因此它运行得相当快
set.seed(47)
x <- sample(1:1e6, 1e4, replace = TRUE)
test1 <- function(x) {
ifelse(x %% 2 == 0, x**2, x/2)
}
test2 <- function(x) {
y <- numeric(length(x))
for (i in seq_along(x)) {
if (x[i] %% 2 == 0) {
y[i] <- x[i]**2
} else {
y[i] <- x[i]/2
}
}
return(y)
}
test3 <- function(x) {
y = numeric(length(x))
cond = x %% 2 == 0
y[cond] = x[cond] ^ 2
y[!cond] = x[!cond] / 2
return(y)
}
identical(test1(x), test2(x))
# TRUE
identical(test1(x), test3(x))
# TRUE
microbenchmark::microbenchmark(test1(x), test2(x), test3(x), times = 1000)
# Unit: microseconds
# expr min lq mean median uq max neval cld
# test1(x) 1563.270 1642.3540 1701.3877 1669.2180 1697.894 3159.743 1000 b
# test2(x) 17909.833 18788.9635 23682.1516 19882.8600 20679.436 116206.536 1000 c
# test3(x) 627.241 668.7445 691.8433 680.6675 696.061 1340.507 1000 a
set.seed(47)
x您的编码方式比ifelse
更糟糕,但正如的警告部分所建议的那样,ifelse
可能做得更好。使用简单的函数,x^2
和x/2
,下面的test3()
函数速度更快—大约是ifelse
的2到3倍,比test2()快30倍。如果函数的计算量更大(但仍然是矢量化的!),边界可能会更大
速度增加(我认为)主要是由于两个原因:
ifelse
执行test3()
跳过的输入检查和错误处理ifelse
更通用、更灵活test3()
硬编码为仅返回一个数值(矢量)
如中所示,ifelse
将计算其整个TRUE
响应向量,前提是至少有1个TRUE
测试值,同样地,其FALSE
也会计算test3()
通过创建TRUE
和FALSE
子向量绕过额外的计算
我修改了您的test1()
和test2()
以简化一点,拉出了数据模拟(因为这不是我们想要测试的)。我添加了使用逻辑子集的test3
。我还大大减小了测试向量的大小,因此它运行得相当快
set.seed(47)
x <- sample(1:1e6, 1e4, replace = TRUE)
test1 <- function(x) {
ifelse(x %% 2 == 0, x**2, x/2)
}
test2 <- function(x) {
y <- numeric(length(x))
for (i in seq_along(x)) {
if (x[i] %% 2 == 0) {
y[i] <- x[i]**2
} else {
y[i] <- x[i]/2
}
}
return(y)
}
test3 <- function(x) {
y = numeric(length(x))
cond = x %% 2 == 0
y[cond] = x[cond] ^ 2
y[!cond] = x[!cond] / 2
return(y)
}
identical(test1(x), test2(x))
# TRUE
identical(test1(x), test3(x))
# TRUE
microbenchmark::microbenchmark(test1(x), test2(x), test3(x), times = 1000)
# Unit: microseconds
# expr min lq mean median uq max neval cld
# test1(x) 1563.270 1642.3540 1701.3877 1669.2180 1697.894 3159.743 1000 b
# test2(x) 17909.833 18788.9635 23682.1516 19882.8600 20679.436 116206.536 1000 c
# test3(x) 627.241 668.7445 691.8433 680.6675 696.061 1340.507 1000 a
set.seed(47)
xifelse
有一大堆输入检查等,而您的test2
函数没有这些检查。这可能是纳秒级差异的主要原因。ifelse
的核心本质上是一个if/else
,与您对NA
值的调整相同。您的test2
中有一个错误;试着自己运行它。另外,您的基准测试命令应该调用test1()
和test2()
,否则它实际上不会运行代码。如果您确实想看到性能差异,请预先模拟x
,并将其传递给您的函数。@aaron很好的调用!我将更新问题以反映这一点。另请参阅帮助文件中警告
部分下的建议,其中给出了一个可能首选的构造类型的示例。ifelse
有一整套检查输入等功能,而test2
函数没有这些功能。这可能是纳秒级差异的主要原因。ifelse
的核心本质上是一个if/else
,与您对NA
值的调整相同。您的test2
中有一个错误;试着自己运行它。另外,您的基准测试命令应该调用test1()
和test2()
,否则它实际上不会运行代码。如果您确实想看到性能差异,请预先模拟x
,并将其传递给您的函数。@aaron很好的调用!我将更新问题以反映这一点。另外,请参阅帮助文件中“警告”部分下的建议,其中给出了一个可能首选的构造类型示例。答案似乎是ifelse
比if/else
快得多,但代码通常可以以比ifelse更快的方式重写,这对我来说完全有意义。谢谢似乎答案是ifelse
比if/else
快得多,但代码通常可以以比ifelse
更快的方式重写,这对我来说完全有意义。谢谢