向量化和非向量化函数+ R 中的基准应用

问题描述 投票:0回答:1

有人可以根据上面的实现示例向我解释向量化函数和非向量化函数之间的区别吗? ? 我使用了 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 function vectorization benchmarking
1个回答
0
投票

如评论中所述,向量化是指 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  
© www.soinside.com 2019 - 2024. All rights reserved.