2016-11-04 56 views
-1

我正在做一個模擬研究,我寫了下面的R代碼。無論如何編寫這個代碼,而不使用兩個for循環,或使其更有效率(運行速度更快)?如何使此R代碼(for循環)更有效?

S = 10000 
n = 100 
v = c(5,10,50,100) 
beta0.mle = matrix(NA,S,length(v)) #creating 4 S by n NA matrix 
beta1.mle = matrix(NA,S,length(v)) 
beta0.lse = matrix(NA,S,length(v)) 
beta1.lse = matrix(NA,S,length(v)) 
for (j in 1:length(v)){ 
    for (i in 1:S){ 
    set.seed(i) 
    beta0 = 50 
    beta1 = 10 
    x = rnorm(n) 
    e.t = rt(n,v[j]) 
    y.t = e.t + beta0 + beta1*x 
    func1 = function(betas){ 
     beta0 = betas[1] 
     beta1 = betas[2] 
     sum = sum(log(1+1/v[j]*(y.t-beta0-beta1*x)^2)) 
     return((v[j]+1)/2*sum) 
    } 
    beta0.mle[i,j] = nlm(func1,c(1,1),iterlim = 1000)$estimate[1] 
    beta1.mle[i,j] = nlm(func1,c(1,1),iterlim = 1000)$estimate[2] 
    beta0.lse[i,j] = lm(y.t~x)$coef[1] 
    beta1.lse[i,j] = lm(y.t~x)$coef[2] 
    } 
} 

第二for迴路用於nlm功能內部的功能func1(查找MLE當誤差噸分佈)。 我想在R中使用parallel包,但我沒有找到任何有用的功能。

+0

你能描述一下你想達到的目標嗎?即循環**應該做什麼,以及你期望的輸出是什麼? – SymbolixAU

+0

一點也不清楚爲什麼你在循環中定義func1,而不是將j,x作爲參數傳遞給它 – dww

+0

'lineprof'可能有助於找到最慢的步驟。 – mt1022

回答

2

在R中使任何東西運行得更快的關鍵是用矢量化函數(如apply系列)替換for循環。此外,對於任何編程語言,您應該使用相同的參數多次查找要調用昂貴函數的地方(如nlm),並查看可以在哪裏存儲結果,而不是每次重新計算。

在這裏我開始像你所做的那樣定義參數。此外,因爲beta0beta1總是5010我將在這裏定義這些。

S <- 10000 
n <- 100 
v <- c(5,10,50,100) 
beta0 <- 50 
beta1 <- 10 

接下來,我們將定義func1外循環,以避免每次都重新定義。 func1現在有兩個額外的參數vy.t,以便可以用新值調用它。

func1 <- function(betas, v, y.t, x){ 
    beta0 <- betas[1] 
    beta1 <- betas[2] 
    sum <- sum(log(1+1/v*(y.t-beta0-beta1*x)^2)) 
    return((v+1)/2*sum) 
} 

現在我們真的做了真正的工作。我們使用嵌套的apply語句,而不是嵌套循環。外lapply將使的v和內vapply每個值列表將使矩陣爲你想(beta0.mlebeta1.mlebeta0.slebeta1.lse)爲S每個值的四個值。

values <- lapply(v, function(j) vapply(1:S, function(s) { 
    # This should look familiar, it is taken from your code 
    set.seed(s) 
    x <- rnorm(n) 
    e.t <- rt(n,j) 
    y.t <- e.t + beta0 + beta1*x 
    # Rather than running `nlm` and `lm` twice, we run it once and store the results 
    nlmmod <- nlm(func1,c(1,1), j, y.t, x, iterlim = 1000) 
    lmmod <- lm(y.t~x) 
    # now we return the four values of interest 
    c(beta0.mle = nlmmod$estimate[1], 
    beta1.mle = nlmmod$estimate[2], 
    beta0.lse = lmmod$coef[1], 
    beta1.lse = lmmod$coef[2]) 
}, numeric(4)) # this tells `vapply` what to expect out of the function 
) 

最後,我們可以將所有事情重組爲四個矩陣。

beta0.mle <- vapply(values, function(x) x["beta0.mle", ], numeric(S)) 
beta1.mle <- vapply(values, function(x) x["beta1.mle", ], numeric(S)) 
beta0.lse <- vapply(values, function(x) x["beta0.lse.(Intercept)", ], numeric(S)) 
beta1.lse <- vapply(values, function(x) x["beta1.lse.x", ], numeric(S)) 

最後要注意,有可能重組這跑得取決於你爲什麼要使用S指標設置種子。如果知道用什麼種子生成xrnorm很重要,那麼這可能是我能做的最好的。但是,如果只是爲了確保v的所有值都在x的相同值上進行測試,那麼可能會進行更多的重組,我們可以使用replicate來提高速度。

+0

'apply'是循環隱藏,而不是矢量化。雖然可以通過從'for'移動到'apply'來實現小的加速,但不應該將其與從'for'或'apply'移動到真實矢量化的加速相混淆 - 通常這很快更大。請參閱[例如R是否應用家庭語法糖?](http://stackoverflow.com/a/2276001/903061)或[R地獄之環](http://www.burns-stat.com/頁/導師/ R_inferno.pdf)。 – Gregor

+0

你對'循環隱藏和向量化有一點意見,但我覺得這是定義向量化模糊的地方。如果你調用一個向量化的函數,它接受並針對一個輸入向量進行了優化,它就有資格。如果你稱它爲一個同時評估整個向量的方法,那麼它不會。您引用的SO帖子中充滿了顯示從「for」到「apply」切換速度提升3-10倍的答案。與'apply'的最大區別在於循環和變量創建發生在'C'而不是'R',它可以更快。 – Barker

+0

確實如此,但是在切換到應用程序時有時出現的戲劇性加速往往是在循環內部發生的事情微不足道的情況下(參見John的答案)。在大多數實際情況下,正如在這種情況下,循環體做了一些不重要的事情。在你重構代碼的方式中,你已經在這個答案中提高了效率。我只是覺得你給人的印象是,大部分的改進都是由於使用'vapply',而這只是錦上添花。 – Gregor