如何在R中为Monte Carlo创建更高效的模拟循环

如何在R中为Monte Carlo创建更高效的模拟循环,r,loops,simulation,montecarlo,performance,R,Loops,Simulation,Montecarlo,Performance,本练习的目的是创建营养摄入值的人口分布。先前的数据中有重复的度量,这些度量已被删除,因此每一行在数据框中都是唯一的人 我有这段代码,当使用少量数据帧行进行测试时,它运行得非常好。对于所有7135行,速度都非常慢。我试图计时,但当我的机器运行时间超过15小时时,我崩溃了。system.time结果是计时停止在:55625.08 2985.39 58673.87 如果您对加快模拟速度有任何意见,我将不胜感激: Male.MC <-c() for (j in 1:100)

本练习的目的是创建营养摄入值的人口分布。先前的数据中有重复的度量,这些度量已被删除,因此每一行在数据框中都是唯一的人

我有这段代码,当使用少量数据帧行进行测试时,它运行得非常好。对于所有7135行,速度都非常慢。我试图计时,但当我的机器运行时间超过15小时时,我崩溃了。
system.time
结果是
计时停止在: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))
    }
}
头部(男性发行版)

'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 ...
    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))
但将该值作为值输入,然后运行相同的(?)计算会得到一个结果,因此在进行手动计算时,我忽略了这一点:

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

我现在有了(我认为)使用矢量化的工作代码,而且速度非常快。为了防止其他人有这个问题,我在下面发布了工作代码。我必须添加一个最小值来防止,这里有一个方法可以解决两个最大的速度问题:

  • 我们不是循环观察(
    i
    ),而是一次计算它们
  • 我们使用
    replicate
    ,这是一种简化的
    apply
    ,而不是在MC复制(
    j
    )上循环
  • 首先,我们加载数据集并为您正在做的事情定义一个函数

    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
    }
    

    然后可以重新格式化、添加ID等,但这是主要计算部分的想法。祝你好运

    我怀疑这将减少到一个使用
    replicate
    和矩阵+数组数学一次完成所有观察的一行程序。不过,您能否发布一个可复制的小示例,以便我们能给您提供更具体的建议?使用
    rbind()
    来增长对象非常昂贵。您可以更好地在开始时创建一个emtpy数据帧(例如,用虚拟变量填充它)并将其填充到循环中。除了@SachaEpskamp所说的,不需要内部循环。您使用的所有函数都是矢量化的;好好利用这一点。我同意@JohnColby。我认为从“boot”包中复制或
    boot
    都可以完成这项工作,并且可能比您现在所做的更加有效。根据您希望
    max(0,…)
    做什么,您可以使用
    max(0,…,na.rm=TRUE)
    或单独测试
    (1-Lambda.Value)
    temp
    组件。谢谢John,这看起来确实是一个不错的选择,但是我得到了每个复制的
    NaN
    结果,我不知道为什么。它在测试数据上运行良好,但在整个数据帧上运行时失败。请注意,用
    replicate
    替换外部循环是一种装饰性的做法,没有速度增益。速度增益来自于避免
    rbind
    和元素操作。其中唯一能明显给出
    NaN
    结果的操作是将负数(
    temp
    )提高到分数幂(
    1/Lambda.Value
    1/Lambda.Value-2
    )。Post
    summary
    来自
    Male.Distrib
    的结果?哎呀,你确实发布了
    str
    (与
    summary
    一样好)。你的标准差是2.65,所以你可以期望
    u2
    定期下降到-5或-6,这可能会使
    mc_bca
    @BenBolker有一个很好的提示!我自己从来没有调查过。
    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
    
    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