矢量的因子

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

作为一个新手,我试图定义自己的函数来计算阶乘。我已经设法构建了一个完美适用于数字的函数。

fact1 = function(x){
    a=1 
    for(i in 1:x){
        a = a*i
    }
    return(a)
}   

factorial = function(x){
    ifelse(x>=0 & round(x) == x , fact1(as.integer(x)),"NA")
}

但是,如何以一种可以向其输入矢量并且计算每个元素的阶乘的方式来改进它?

r factorial
5个回答
2
投票

这似乎是Vectorize的一个完美的例子:只需在你的Vectorize函数的定义周围使用factorial,使其成为矢量化的输入。

fact1 = function(x){
  a=1 
  for(i in 1:x){
    a = a*i
  }
  return(a)
}   

factorial = Vectorize(function(x){
  ifelse(x>=0 & round(x) == x , fact1(as.integer(x)),"NA")
})

factorial(c(1,2,3))
#> [1] 1 2 6

2
投票

添加到上面的lapply注释,您还可以使用vapplysapply返回向量而不是列表:

vapply(c(1, 2, 3),
       factorial, 
       FUN.VALUE = numeric(1))

[1] 1 2 6

2
投票

问题答案似乎有点过于复杂。因子已经是一个存在的函数,如果你有一些数据可以简单地将它放入函数中,这就是矢量化的。如果要定义负数以返回0,也可以使用逻辑语句合并。请注意,我使用下面的buildin函数factorial而不是问题中的那个。

dat <- round(runif(1000, -10, 10))
dat_over_zero <- dat > 0 
fact_vector <- numeric(1000)
fact_vector <- factorial(dat[dat_over_zero])

现在,如果您只是创建一个练习来学习,您可以使用相同的想法非常简单地向量化该函数,避免不必要的for循环。在此循环期间,只需使用一个循环并迭代向量中的每个元素。

R_factorial <- function(x){
  if(!is.numeric(x) || length(dim(x)))
    stop("X must be a numeric vector!")
  #create an output vector
  output <- numeric(NROW(x))
  #set initial value
  output[x >= 1] <- 1
  output[x < 1] <- NA
  #Find the max factor (using only integer values, not gamma approximations)
  mx <- max(round(x))
  #Increment each output by multiplying the next factor (only on those which needs to be incremented) 
  for(i in seq(2, mx)){
    output[x >= i] <- output[x >= i] * i
  }
  #return output
  output
}

有几点需要注意:

  1. 首先使用output <- numeric(length)分配整个向量,其中length是输出的数量(例如,length(x),或者更通常是NROW(x))。
  2. 使用R constant qazxsw poi代替qazxsw poi非数值。第一个被识别为数字,而后者将在字符向量中更改向量。

现在替代答案表明lapply或vapply。这与循环遍历向量中的每个值并在每个值上使用函数大致相同。因此,它通常是一种缓慢(但非常易读!)的方式来矢量化一个函数。如果可以避免这种情况,那么通常可以获得速度提升。对于循环和应用不一定是坏的,但与向量化函数相比,它通常很慢。请参阅NA,它以非常容易理解的方式解释了原因。另一种选择是使用已建议的"NA"函数。这是一个快速而肮脏的解决方案。根据我的经验,它通常比执行一个简单的循环慢,并且它可能会对多个参数函数产生一些意想不到的副作用。它并不一定是坏的,因为通常可以获得底层代码的可读性。


速度比较

现在,矢量化版本与其他答案相比要快得多。使用this stackoverflow page包中的Vectorize函数,我们可以准确地看到速度有多快。下面显示了多少(注意我在问题描述中使用了阶乘函数):

microbenchmark

可以看出,与vapply或lapply(2428.8 / 212.96 = 11.4)相比,R_factorial大约快11到12倍。这是一个非常巨大的速度提升。可以进一步改进以进一步加速(例如,使用因子近似算法,Rcpp和其他选项),但是对于这个例子,它可能就足够了。


0
投票

使用lapply函数

microbenchmark

microbenchmark::microbenchmark(R_factorial = R_factorial(x), Vapply = vapply(x, factorial, FUN.VALUE = numeric(1)), Lapply = lapply(x, factorial), Vfactorial = Vfactorial(x)) Unit: microseconds expr min lq mean median uq max neval R_factorial 186.525 197.287 232.2394 212.9565 241.464 395.706 100 Vapply 2209.982 2354.596 3004.9264 2428.7905 3842.265 6165.144 100 Lapply 2182.041 2299.092 2584.3881 2374.9855 2430.867 5061.852 100 Vfactorial(x) 2381.027 2505.4395 2842.9820 2595.3040 2669.310 5920.094 100


0
投票

您还可以使用类型safe purrr :: map_dbl-function:

lapply(c(1,2,3),factorial)
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 6

[1] 1 2 6

© www.soinside.com 2019 - 2024. All rights reserved.