Оптимизация производительности при итерации функции по вектору, если каждый элемент пропущен один раз - PullRequest
0 голосов
/ 13 февраля 2019

Мне нужно рассчитать несколько статистических параметров для вектора, опуская при этом каждое значение в нем один раз.Поскольку это происходит в большом наборе данных со многими параметрами, я ищу общий подход для оптимизации производительности.Простым примером может быть:

v <- c(9, 14, 8, 12, 5, 10, 6, 9, 9, 9, 9, 10, 8, 11, 9, 9, 10, 6, 10, 10)

sapply(1:length(v), function(x){
    var(v[-x])
})

В результате получается желаемый результат с вектором, содержащим общую дисперсию v, если каждый элемент пропущен один раз:

 [1] 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211 4.134211
[17] 4.134211 4.134211 4.134211 4.134211

Как указаноэто приводит к снижению производительности при работе с большими наборами данных и несколькими параметрами.Поскольку циклы иногда позорятся за медлительность , я ищу эффективные альтернативы, то есть векторизованные функции.

Спасибо!

РЕДАКТИРОВАТЬ: Оба предложенных решения значительно повышают производительность.В то время как решение Dominiks побеждает в гонке за скоростью, подход Rolands является более общим и может использоваться более широко.Поэтому ответ Rolands помечен как правильный, а я буду использовать решение Dominiks для этой конкретной ситуации.Спасибо обоим!

Results with N = 2000
    Unit: milliseconds
                      expr      min        lq       mean    median        uq      max neval
         original approach 117.2269 122.38290 130.933014 124.95565 128.69030 453.0770   100
      approach from Roland  57.1625  64.75505  96.255364  67.88550 168.55915 204.6941   100
     approach from Dominik   2.7083   2.89440   3.395894   2.99545   3.24165  30.0510   100

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

Мы можем использовать формулу для дисперсии: sum((v-m)^2)/(n-1)

, где

n <- length(v)
m <- mean(v)

Пусть i будет любым индексом от 1 до n.Затем с небольшой математикой мы получим:

#x = v[i]
 #var(v[-i]) is equal to (sum(v^2)-x^2-(sum(v)-x)^2/(n-1))/(n-2)

После того, как вы нарисовали некоторую часть в скобках, вот ваш код:

a <- sum(v^2)/(n-2) - sum(v)^2/(n-1)/(n-2)
b <- n/(n-1)/(n-2)
d <- 2*sum(v)/((n-1)*(n-2))

apply(X = as.matrix(v), MARGIN = 1, FUN = function(x){
  a -b*x^2 + d*x
})
0 голосов
/ 13 февраля 2019

Вы можете использовать combn для создания матрицы всех комбинаций, а затем использовать быструю реализацию вычисления дисперсии по столбцам.Это должно быть эффективно, пока вектор не велик и у вас достаточно памяти.

library(microbenchmark)

library(matrixStats)

microbenchmark(loop = {
  res1 <- sapply(1:length(v), function(x){
    var(v[-x])
  })
},
combn = {res2 <- colVars(combn(v, length(v) - 1))}
)

#Unit: microseconds
# expr     min       lq     mean  median       uq      max neval cld
# loop 633.528 646.0755 736.6643 654.526 675.9085 5652.840   100   b
#combn  58.641  62.4820  67.7778  66.067  69.1400  173.106   100  a 


all.equal(sort(res1), sort(res2))
#[1] TRUE
...