Как создать более эффективный цикл моделирования для Монте-Карло в R - PullRequest
4 голосов
/ 25 января 2012

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

У меня есть этот код, который довольно хорошо работает при тестировании с небольшим количеством строк моего фрейма данных. Для всех 7135 строк это очень медленно. Я попытался рассчитать время, но я потерпел крах, когда истекшее время работы на моей машине составило 15 часов. system.time результаты были Timing stopped at: 55625.08 2985.39 58673.87.

Буду признателен за любые комментарии по ускорению моделирования:

Male.MC <-c()
for (j in 1:100)            {
for (i in 1:nrow(Male.Distrib))  {
    u2        <- Male.Distrib$stddev_u2[i] * rnorm(1, mean = 0, sd = 1)
    mc_bca    <- Male.Distrib$FixedEff[i] + u2
    temp      <- Lambda.Value*mc_bca+1
    ginv_a    <- temp^(1/Lambda.Value)
    d2ginv_a  <- max(0,(1-Lambda.Value)*temp^(1/Lambda.Value-2))
    mc_amount <- ginv_a + d2ginv_a * Male.Resid.Var / 2
z <- data.frame(
     RespondentID = Male.Distrib$RespondentID[i], 
     Subgroup     = Male.Distrib$Subgroup[i], 
     mc_amount    = mc_amount,
     IndvWeight   = Male.Distrib$INDWTS[i]/100
     )

Male.MC <- as.data.frame(rbind(Male.MC,z))
    }
}

Для каждого из 7135 наблюдений в моем наборе данных создается 100 смоделированных значений питательных веществ, которые затем преобразуются в исходный уровень измерения (для моделирования используются результаты из нелинейной модели смешанного эффекта на преобразованных в BoxCox значениях питательных веществ). *

Я бы предпочел не использовать циклы for, так как я читал, что они неэффективны в R, но я недостаточно разбираюсь в опциях, основанных на apply, чтобы использовать их в качестве альтернативы. R запускается на автономных компьютерах, обычно это стандартный настольный компьютер типа Dell, работающий под управлением Windows 7, если это влияет на рекомендации по изменению кода.

Обновление: чтобы воспроизвести это для тестирования, Lambda.Value = 0,4 и Male.Resid.Var = 12.1029420429778, а Male.Distrib$stddev_u2 является постоянным значением для всех наблюдений.

str(Male.Distrib) это

'data.frame':   7135 obs. of  14 variables:
 $ RndmEff     : num  1.34 -5.86 -3.65 2.7 3.53 ...
 $ RespondentID: num  9966 9967 9970 9972 9974 ...
 $ Subgroup    : Ord.factor w/ 6 levels "3"<"4"<"5"<"6"<..: 4 3 2 4 1 4 2 5 1 2 ...
 $ RespondentID: int  9966 9967 9970 9972 9974 9976 9978 9979 9982 9993 ...
 $ Replicates  : num  41067 2322 17434 21723 375 ...
 $ IntakeAmt   : num  33.45 2.53 9.58 43.34 55.66 ...
 $ RACE        : int  2 3 2 2 3 2 2 2 2 1 ...
 $ INDWTS      : num  41067 2322 17434 21723 375 ...
 $ TOTWTS      : num  1.21e+08 1.21e+08 1.21e+08 1.21e+08 1.21e+08 ...
 $ GRPWTS      : num  41657878 22715139 10520535 41657878 10791729 ...
 $ NUMSUBJECTS : int  1466 1100 1424 1466 1061 1466 1424 1252 1061 1424 ...
 $ TOTSUBJECTS : int  7135 7135 7135 7135 7135 7135 7135 7135 7135 7135 ...
 $ FixedEff    : num  6.09 6.76 7.08 6.09 6.18 ...
 $ stddev_u2   : num  2.65 2.65 2.65 2.65 2.65 ...

head(Male.Distrib) -

    RndmEff RespondentID Subgroup RespondentID Replicates IntakeAmt RACE INDWTS    TOTWTS   GRPWTS NUMSUBJECTS TOTSUBJECTS  FixedEff stddev_u2
1  1.343753         9966        6         9966      41067 33.449808    2  41067 120622201 41657878        1466        7135  6.089918  2.645938
2 -5.856516         9967        5         9967       2322  2.533528    3   2322 120622201 22715139        1100        7135  6.755664  2.645938
3 -3.648339         9970        4         9970      17434  9.575439    2  17434 120622201 10520535        1424        7135  7.079757  2.645938
4  2.697533         9972        6         9972      21723 43.340180    2  21723 120622201 41657878        1466        7135  6.089918  2.645938
5  3.531878         9974        3         9974        375 55.660607    3    375 120622201 10791729        1061        7135  6.176319  2.645938
6  6.627767         9976        6         9976      48889 91.480049    2  48889 120622201 41657878        1466        7135  6.089918  2.645938

Обновление 2: строка функции, которая вызывает результаты NaN, равна

d2ginv_a  <- max(0,(1-Lambda.Value)*temp^(1/Lambda.Value-2))

Спасибо всем за помощь и комментарии, а также за оперативность ответов.

Обновление: @Ben Bolker правильно, что именно отрицательные значения temp вызывают проблему NaN. Я пропустил это во время некоторого тестирования (после закомментирования функции, чтобы возвращались только значения temp, и вызова моего фрейма данных результатов Test). Этот код воспроизводит проблему NaN:

> min(Test)
[1] -2.103819
> min(Test)^(1/Lambda.Value)
[1] NaN

Но если ввести значение в качестве значения и затем выполнить то же (?) Вычисление, я получу результат, поэтому я пропустил это при выполнении ручных вычислений:

> -2.103819^(1/Lambda.Value) 
[1] -6.419792

Теперь у меня есть рабочий код, который (я думаю) использует векторизацию, и он невероятно быстр. На всякий случай, если у кого-то еще есть эта проблема, я публикую ниже рабочий код. Мне пришлось добавить минимум, чтобы предотвратить проблему <0 с вычислением. Спасибо всем, кто помог, и кофе. Я попытался поместить результаты <code>rnorm в кадр данных, и это действительно замедлило процесс, создав их таким образом, а затем используя cbind очень быстро. Male.Distrib - это мой полный фрейм данных с 7135 наблюдениями, но этот код должен работать на урезанной версии, которую я выложил ранее (не тестировался).

Min_bca <- ((.5*min(Male.AddSugar$IntakeAmt))^Lambda.Value-1)/Lambda.Value
Test <- Male.Distrib[rep(seq.int(1,nrow(Male.Distrib)), 100), 1:ncol(Male.Distrib)]
RnormOutput <- rnorm(nrow(Test),0,1)
Male.Final <- cbind(Test,RnormOutput)
Male.Final$mc_bca    <- Male.Final$FixedEff + (Male.Final$stddev_u2 *     Male.Final$RnormOutput)
Male.Final$temp      <- ifelse(Lambda.Value*Male.Final$mc_bca+1 > Lambda.Value*Min_bca+1,
                           Lambda.Value*Male.Final$mc_bca+1, Lambda.Value*Min_bca+1)
Male.Final$ginv_a    <- Male.Final$temp^(1/Lambda.Value)
Male.Final$d2ginv_a  <- ifelse(0 > (1-Lambda.Value)*Male.Final$temp^(1/Lambda.Value-2),
                           0, (1-Lambda.Value)*Male.Final$temp^(1/Lambda.Value-2))
Male.Final$mc_amount <- Male.Final$ginv_a + Male.Final$d2ginv_a * Male.Resid.Var / 2

Уроки на день:

  • функция распределения не будет пересчитываться в цикле, если вы попытаетесь сделать то, что я пробовал ранее
  • вы не можете использовать max() так, как я пытался, так как он возвращает максимальное значение из столбца, тогда как я хотел получить максимальное из двух значений. Оператор ifelse является заменой.

1 Ответ

4 голосов
/ 26 января 2012

Вот подход, который решает 2 самые большие проблемы со скоростью:

  1. Вместо того, чтобы зацикливаться на наблюдениях (i), мы вычисляем их все сразу.
  2. Вместоциклически повторяя репликации MC (j), мы используем replicate, что является упрощенным apply, предназначенным для этой цели.

Сначала мы загружаем набор данных и определяем функцию для того, кем вы былиделать.

Male.Distrib = read.table('MaleDistrib.txt', check.names=F)

getMC <- function(df, Lambda.Value=0.4, Male.Resid.Var=12.1029420429778) {
  u2        <- df$stddev_u2 * rnorm(nrow(df), mean = 0, sd = 1)
  mc_bca    <- df$FixedEff + u2
  temp      <- Lambda.Value*mc_bca+1
  ginv_a    <- temp^(1/Lambda.Value)
  d2ginv_a  <- max(0,(1-Lambda.Value)*temp^(1/Lambda.Value-2))
  mc_amount <- ginv_a + d2ginv_a * Male.Resid.Var / 2
  mc_amount
}

Затем мы копируем это несколько раз.

> replicate(10, getMC(Male.Distrib))
         [,1]      [,2]     [,3]     [,4]      [,5]     [,6]     [,7]     [,8]     [,9]    [,10]
[1,] 36.72374 44.491777 55.19637 23.53442 23.260609 49.56022 31.90657 25.26383 25.31197 20.58857
[2,] 29.56115 18.593496 57.84550 22.01581 22.906528 22.15470 29.38923 51.38825 13.45865 21.47531
[3,] 61.27075 10.140378 75.64172 28.10286  9.652907 49.25729 23.82104 31.77349 16.24840 78.02267
[4,] 49.42798 22.326136 33.87446 14.00084 25.107143 25.75241 30.20490 33.14770 62.86563 27.33652
[5,] 53.45546  9.673162 22.66676 38.76392 30.786100 23.42267 28.40211 35.95015 43.75506 58.83676
[6,] 34.72440 23.786004 63.57919  8.08238 12.636745 34.11844 14.88339 21.93766 44.53451 51.12331

Затем вы можете переформатировать, добавить идентификаторы и т. д., но это идея для основной вычислительной части,Удачи!

...