Ответы на вопросы кажутся немного сложными. Факториал - это уже существующая функция, и она векторизована как таковая, если у вас есть какие-то данные, вы можете просто поместить их в функцию. Если вы хотите определить отрицательные числа для возврата 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
}
Несколько замечаний:
- Сначала выделите весь вектор, используя
output <- numeric(length)
, где длина - это количество выходов (например, здесь length(x)
или в более общем смысле NROW(x)
).
- Используйте константу R
NA
для отсутствия числовых значений вместо "NA"
. Первый распознается как число, а последний изменяет ваш вектор в векторе символов.
Теперь альтернативные ответы предполагают lapply или vapply. Это более или менее похоже на перебор каждого значения в векторе и использование функции для каждого значения. По сути, это часто медленный (но очень читаемый!) Способ векторизации функции. Однако, если этого можно избежать, вы часто можете получить прирост скорости. Для циклов и применения это не обязательно плохо, но в целом намного медленнее по сравнению с векторизованными функциями. См. эту страницу stackoverflow, где очень легко понять, почему. Дополнительная альтернатива - использование предложенной функции Vectorize
. Это быстрое и грязное решение. По моему опыту, это часто медленнее, чем выполнение простого цикла, и может иметь некоторые неожиданные побочные эффекты для функций с несколькими аргументами. Это не обязательно плохо, так как часто становится понятнее базовый код.
Сравнение скорости
Теперь векторизованная версия намного быстрее по сравнению с альтернативными ответами. Используя функцию microbenchmark
из пакета 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
Как видно, R_factorial примерно в 11-12 раз быстрее по сравнению с vapply или lapply (2428,8 / 212,96 = 11,4). Это довольно большой прирост скорости. Можно было бы сделать дополнительные улучшения, чтобы еще больше ускорить его (например, используя алгоритмы факториальной аппроксимации, Rcpp и другие параметры), но для этого примера этого может быть достаточно.
person
Oliver
schedule
24.02.2019