Оценка стандартного отклонения от среднего и доверительного интервалов с помощью гамма-распределения в R - PullRequest
0 голосов
/ 08 мая 2020

У меня есть следующая проблема, которую я хотел бы решить в R и применить к большему рабочему процессу. Мне нужно оценить стандартное отклонение от гамма-распределения, для которого известны среднее значение и 95% доверительный интервал.

state = c("group1", "group2", "group3")
mean = c(0.970, 0.694, 0.988)
lowers = c(0.527, 0.381, 0.536)
uppers = c(1.87, 1.37, 1.90)

df = data.frame(state=state, mean=mean, lower=lower, upper=upper)

Используя Excel и инструмент «решатель», я могу настроить стандартное отклонение, чтобы минимизировать сумму возведенные в квадрат разницы между целевым 2,5 (нижнее) и 97,5 (верхнее) процентилями распределения с фактическими данными. Проблема в том, что это нужно масштабировать до довольно большого набора данных и задействовать в моем рабочем процессе фрейма данных R. Есть идеи, как это решить?

1 Ответ

0 голосов
/ 10 мая 2020

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

Этот процесс не оптимизация: он расширяет определенный диапазон возможных параметров k (форма) и находит комбинацию формы / масштаба (с учетом вашего среднего значения), которая наиболее близко соответствует вашим верхним и нижним квантилям. Вы контролируете степень детализации k, которая настолько хороша, насколько вы собираетесь получить допуск (что было бы подходящим для оптимизации).

Таким образом, этот процесс будет несовершенным . Я надеюсь, что это даст вам достаточно быстрый процесс для достаточно хорошей точности.

Я собираюсь сначала продемонстрировать процесс, который обрабатывает одну строку за раз, как guesser1. Затем я разверну его, чтобы выполнить ту же операцию с произвольным числом mean, lower и upper.

Данные с известными ответами

Но сначала я хочу чтобы сгенерировать мои собственные образцы, чтобы мы знали «правду» для этого угадателя.

library(dplyr)
set.seed(42)
n <- 4
randks <- tibble(
  k = sample(1:10, size = n, replace = TRUE),
  scale = sample(seq(0.5, 2.5, by = 0.5), size = n, replace = TRUE)
) %>%
  mutate(
    samp = map2(k, scale, ~ rgamma(1000, shape = .x, scale = .y)),
    theor_mu = k*scale,
    mu = map_dbl(samp, ~ mean(.x)),
    lwr = map_dbl(samp, ~ quantile(.x, 0.025)),
    upr = map_dbl(samp, ~ quantile(.x, 0.975))
  ) %>%
  select(-samp)
randks
# # A tibble: 4 x 6
#       k scale theor_mu    mu   lwr   upr
#   <int> <dbl>    <dbl> <dbl> <dbl> <dbl>
# 1    10   2       20   19.9   9.47 33.7 
# 2    10   1.5     15   15.1   7.36 25.0 
# 3     3   2        6    5.85  1.08 14.5 
# 4     9   0.5      4.5  4.51  1.99  7.72

Guesser1

По одной строке за раз:

guesser1 <- function(mu, lwr, upr, k.max = 10, k.by = 0.01) {
  stopifnot(length(mu) == 1, length(lwr) == 1, length(upr) == 1)
  ks <- seq(0, k.max, by = k.by)[-1]
  L <- sapply(ks, function(k) qgamma(0.025, shape = k, scale = mu / k))
  U <- sapply(ks, function(k) qgamma(0.975, shape = k, scale = mu / k))
  dists <- sqrt((L-lwr)^2 + (U-upr)^2)
  ind <- which.min(dists)
  data.frame(
    k     = ks[ind],
    scale = mu / ks[ind],
    dist  = min(dists),
    lwr   = L[ind],
    upr   = U[ind]
  )
}

В действии :

out1 <- do.call(rbind, Map(guesser1, randks$mu, randks$lwr, randks$upr))
cbind(subset(randks, select = -theor_mu), out1)
#    k scale    mu  lwr   upr     k scale  dist  lwr   upr
# 1 10   2.0 19.88 9.47 33.67 10.00 1.988 0.304 9.53 33.97
# 2 10   1.5 15.06 7.36 25.02 10.00 1.506 0.727 7.22 25.73
# 3  3   2.0  5.85 1.08 14.50  2.76 2.120 0.020 1.10 14.50
# 4  9   0.5  4.51 1.99  7.72  9.55 0.472 0.142 2.12  7.79
###  \____ randks __________/  \____ guessed ____________/

Определенно есть некоторые различия, подчеркивающие мое первоначальное утверждение, что это несовершенно.

Гадалки

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

guessers <- function(mu, lwr, upr, k.max = 10, k.by = 0.01, include.size = FALSE) {
  stopifnot(length(mu) == length(lwr), length(mu) == length(upr))
  # count <- length(mu)
  ks <- seq(0, k.max, by = k.by)[-1]
  # 'ks'      dims: [mu]
  L <- outer(mu, ks, function(m, k) qgamma(0.025, shape = k, scale = m / k))
  U <- outer(mu, ks, function(m, k) qgamma(0.975, shape = k, scale = m / k))
  # 'L' & 'U' dims: [mu, ks]
  dists <- sqrt((L - lwr)^2 + (U - upr)^2)
  inds <- apply(dists, 1, which.min)
  mindists <- apply(dists, 1, min)
  i <- seq_along(mu)
  out <- data.frame(
    k = ks[inds],
    scale = mu / ks[inds],
    dist = mindists,
    lwr = L[cbind(i, inds)],
    upr = U[cbind(i, inds)]
  )
  size <- if (include.size) {
    message("guessers memory: ",
            object.size(list(ks, L, U, dists, inds, mindists, i, out)))
  }
  out
}

В действии:

outs <- guessers(randks$mu, randks$lwr, randks$upr, include.size = TRUE)
# guessers memory: 106400
cbind(subset(randks, select = -theor_mu), outs)
#    k scale    mu  lwr   upr     k scale  dist  lwr   upr
# 1 10   2.0 19.88 9.47 33.67 10.00 1.988 0.304 9.53 33.97
# 2 10   1.5 15.06 7.36 25.02 10.00 1.506 0.727 7.22 25.73
# 3  3   2.0  5.85 1.08 14.50  2.76 2.120 0.020 1.10 14.50
# 4  9   0.5  4.51 1.99  7.72  9.55 0.472 0.142 2.12  7.79
###  \____ randks __________/  \____ guessed (same) _____/

(Я включил сообщение памяти там, чтобы отследить, насколько это может масштабироваться. Сейчас это неплохо, и этот аргумент определенно не должен использоваться в производстве. К вашему сведению.)

Сравнение

Используя microbenchmark, мы повторяем каждую операцию несколько раз и сравните время их выполнения.

microbenchmark::microbenchmark(
  g1 = Map(guesser1, randks$mu, randks$lwr, randks$upr),
  gs = guessers(randks$mu, randks$lwr, randks$upr)
)
# Unit: milliseconds
#  expr  min   lq mean median   uq   max neval
#    g1 27.3 28.8 33.9   29.7 33.0 131.1   100
#    gs 13.3 13.6 14.4   13.8 14.6  20.3   100

Неудивительно, что all-at-once guessers работает немного быстрее. Когда этого не будет? Когда количество строк становится настолько большим, что потребление памяти становится проблемой. Я не знаю, что это такое.

Давайте попробуем то же самое, но увеличим randks с 4 строк до 1000 и повторим тест.

n <- 1000
# randks <- ...
nrow(randks)
# [1] 1000
microbenchmark::microbenchmark(
  g1 = Map(guesser1, randks$mu, randks$lwr, randks$upr),
  gs = guessers(randks$mu, randks$lwr, randks$upr),
  times = 10
)
# Unit: seconds
#  expr  min   lq mean median   uq   max neval
#    g1 8.50 8.99 9.59   9.31 9.64 11.72    10
#    gs 3.35 3.44 3.61   3.63 3.77  3.93    10

Так что это определенно быстрее. Среднее время выполнения для 1000 оценок составляет 3,63 секунды, поэтому кажется, что он составляет sh около 300 / se c.

guessers memory: 24066176

(24 MiB) На самом деле, это неплохо для все. Уменьшите k.by, чтобы повысить точность, или увеличьте k.by, чтобы ускорить это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...