有人可以根据上面的实现示例向我解释向量化函数和非向量化函数之间的区别吗? ? 我使用了 4 种方法(for 循环、映射、应用、向量化)来向量化函数。
我还有另一个关于 benchmark() 的问题,我读到在应用它来评估每个函数的性能之前不需要 set.seed() 。但是,我无法解释我的结果,因为每次运行它都会给我不同的值。
特别是我实现的最后一个向量化函数,我无法与上面的 4 种方法进行比较,因为我有不同的值。
PS:即使我使用 set.seed 它也会给出不同的值。
你能帮我吗?非常感谢你:)
这是我的代码(不需要数据):
#non-vectorized function
glass_champagne_non_vectorized <- function(x) {
if (!is.numeric(x)) {
stop("Input must be numeric", call. = FALSE)
}
result <- numeric(length(x))
result[x < 0] <- 0
result[x >= 0 & x < 0.5] <- 15
result[x >= 0.5 & x < 10] <- 2
result[x >= 10 & x < 15] <- 8 * log2(x[x >= 10 & x < 15] - 9) + 2
result[x >= 15 & x < 20] <- 8 * log2(6) + 2
result[x >= 20] <- 0
return(result)
}
#vectorized function using for loop
glass_champagne_for_loop <- function(x) {
if (!is.numeric(x)) {
stop("Input must be numeric", call. = FALSE)
}
result <- numeric(length(x))
for (i in seq_along(x)) {
if (x[i] < 0) {
result[i] <- 0
} else if (x[i] >= 0 & x[i] < 0.5) {
result[i] <- 15
} else if (x[i] >= 0.5 & x[i] < 10) {
result[i] <- 2
} else if (x[i] >= 10 & x[i] < 15) {
result[i] <- 8 * log2(x[i] - 9) + 2
} else if (x[i] >= 15 & x[i] < 20) {
result[i] <- 8 * log2(6) + 2
} else if (x[i] >= 20) {
result[i] <- 0
}
}
return(result)
}
# vectorized function using map_dbl()
glass_champagne_map <- function(x) {
if (!is.numeric(x)) {
stop("Input must be numeric", call. = FALSE)
}
map_dbl(x, function(val) {
if (val < 0) {
return(0)
} else if (val >= 0 & val < 0.5) {
return(15)
} else if (val >= 0.5 & val < 10) {
return(2)
} else if (val >= 10 & val < 15) {
return(8 * log2(val - 9) + 2)
} else if (val >= 15 & val < 20) {
return(8 * log2(6) + 2)
} else if (val >= 20) {
return(0)
}
})
}
#vectorized function using sapply
glass_champagne_sapply <- function(x) {
if (!is.numeric(x)) {
stop("Input must be numeric", call. = FALSE)
}
sapply(x, function(val) {
if (val < 0) {
return(0)
} else if (val >= 0 & val < 0.5) {
return(15)
} else if (val >= 0.5 & val < 10) {
return(2)
} else if (val >= 10 & val < 15) {
return(8 * log2(val - 9) + 2)
} else if (val >= 15 & val < 20) {
return(8 * log2(6) + 2)
} else if (val >= 20) {
return(0)
}
})
}
#vectorize using Vectorize
glass_champagne_vectorized <- Vectorize(FUN = glass_champagne_for_loop, vectorize.args = "x")
#performance comparision
x <- 10^(1:4)
set.seed(123)
benchmark_results <- microbenchmark::microbenchmark(
glass_champagne_for_loop(x),
glass_champagne_map(x),
glass_champagne_sapply(x),
glass_champagne_vectorized(x),
times = 100
)
benchmark_results
benchmark_results %>%
summary() %>%
kable() %>%
kable_styling()
The function using the 'for loop' is the most efficient with the lowest average execution time.
#Vectorized version of f
glass_champagne_vectorized_1 <- function(x) {
if (!is.numeric(x)) {
stop("Input must be numeric", call. = FALSE)
}
result <- numeric(length(x))
result[x < 0] <- 0
result[x >= 0 & x < 0.5] <- 15
result[x >= 0.5 & x < 10] <- 2
result[x >= 10 & x < 15] <- 8 * log2(x[x >= 10 & x < 15] - 9) + 2
result[x >= 15 & x < 20] <- 8 * log2(6) + 2
result[x >= 20] <- 0
return(result)
}
如评论中所述,向量化是指 R 函数接受向量并使用编译代码循环该向量。这篇博客是一个很好的入门读物,介绍了其中的内容。 https://www.noamross.net/archives/2014-04-16-vectorization-in-r-why/
dplyr
提供 case_when
函数,即向量化 switch 语句。这些案例是按顺序评估的,因此如果我们对案例的顺序进行一些思考,我们可以更简单地表达每个案例,作为我们想要捕获的剩余案例的一部分。
我发现这更容易阅读,并且比这里的任何其他方法快 10 倍。通过使用高性能软件包(如
data.table
、collapse
、r-polars
等)当然可以获得进一步的收益。
glass_champagne_case_when <- function(x) {
if (!is.numeric(x)) {
stop("Input must be numeric", call. = FALSE)
}
dplyr::case_when(
x < 0 ~ 0,
x < 0.5 ~ 15,
x < 10 ~ 2,
x < 15 ~ 8 * log2(x - 9) + 2,
x < 20 ~ 8 * log2(6) + 2,
TRUE ~ 0)
}
性能比较
set.seed(123)
x <- runif(1e5, 0, 100)
benchmark_results <- microbenchmark::microbenchmark(
glass_champagne_for_loop(x),
glass_champagne_map(x),
glass_champagne_sapply(x),
glass_champagne_vectorized(x),
glass_champagne_case_when(x),
times = 10
)
benchmark_results
Unit: milliseconds
expr min lq mean median uq max neval cld
glass_champagne_for_loop(x) 51.834864 53.552969 95.291091 55.091700 57.289635 435.28669 10 b
glass_champagne_map(x) 87.178386 88.892446 100.290595 92.270288 115.752287 125.47068 10 b
glass_champagne_sapply(x) 89.159797 94.287119 107.667650 96.523616 126.795462 146.83633 10 b
glass_champagne_vectorized(x) 248.161232 257.706691 267.975942 267.984442 277.122519 284.25981 10 c
glass_champagne_case_when(x) 6.647669 6.760983 7.754931 7.868059 8.505078 8.92574 10 a