2016-11-04 6 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] 
    } 
} 

nlm 기능을 위해 사용되는 제 2 내부 루프 for 함수 func1 (t 오류가 분산 될 때 찾을 MLE). R에서 parallel 패키지를 사용하고 싶었지만 유용한 기능을 찾지 못했습니다.

+0

달성하고자하는 것에 대한 설명을 제공 할 수 있습니까? 즉 루프 **가 수행해야하는 작업과 출력을 기대하는 작업은 무엇입니까? – SymbolixAU

+0

루프 내에서 func1을 정의하는 이유는 무엇입니까? j, x를 매개 변수로 전달하지 마십시오. – dww

+0

'lineprof'가 가장 느린 단계를 찾는 데 도움이 될 수 있습니다. – mt1022

답변

2

R에서 더 빠르게 실행하는 데있어 핵심은 for 루프를 벡터화 된 기능 (예 : apply 제품군)으로 대체하는 것입니다. 또한 모든 프로그래밍 언어에 대해 동일한 매개 변수를 사용하여 값 비싼 함수 (예 : 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) 
} 

이제 실제 작업을 수행합니다. 중첩 된 루프가 아니라 중첩 된 적용 문을 사용합니다. 외부 lapplyS의 각 값에 대해 (beta0.mle, beta1.mle, beta0.sle, beta1.lse) 싶어 네 개의 값에 대한 매트릭스를 만들 것입니다 v과 내부 vapply의 각 값에 대한 목록을 만들 것입니다.

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 
) 

마지막으로 모든 것을 4 개의 행렬로 재구성 할 수 있습니다.

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 인덱스를 사용하는 이유에 따라 더 빨리 실행하려면이를 재구성하는 것이 가능하다. x을 생성하기 위해 사용 된 시드가 rnorm 인 것을 아는 것이 중요하다면 최선을 다할 수 있습니다. 그러나 v의 모든 값이 동일한 값인 x에서 테스트되고 있는지 확인하기 위해이 작업을 수행하는 경우 replicate을 사용하여 속도를 높일 수있는 더 많은 재구성이 필요할 수 있습니다.

+0

'apply'는 벡터 숨김이 아닌 루프 숨기기입니다. 'for'에서 'apply'로 이동하면 약간의 속도 향상을 가져올 수 있지만 true 또는 false로 이동하는 속도 향상과 혼동되어서는 안됩니다. 더 커. 예를 들어, [R은 가족 통사론을 적용합니까?] (http://stackoverflow.com/a/2276001/903061) 또는 [R Inferno 서클 4] (http://www.burns-stat.com/)를 참조하십시오. 페이지/교사/R_inferno.pdf). – Gregor

+0

'for' 루프 숨기기 및 벡터화에 대한 요점이 있지만, 이것이 정의 벡터화가 퍼지게되는 곳이라고 생각합니다. 벡터화 된 함수를 호출하여 입력 벡터를 받아들이고 최적화하면 해당 함수가 적합합니다. 전체 벡터를 동시에 평가하는 것을 호출한다면, 그렇지 않습니다. 인용 한 SO 게시물은'for's에서'apply's로 전환 할 때 3-10 배의 속도 향상을 보이는 답변으로 가득 차 있습니다. 'apply'와의 가장 큰 차이점은 루핑과 변수 생성이'R'보다는'C'에서 일어나는데 훨씬 빨라질 수 있다는 것입니다. – Barker

+0

사실이지만, 적용으로 전환 할 때 가끔 볼 수있는 극적인 속도 향상은 루프 내부에서 일어나는 일이 사소한 경우에 발생하는 경향이 있습니다 (John의 대답 참조). 대부분의 실제적인 경우에,이 경우처럼, 루프 몸체는 사소한 일을합니다. 이 코드는 코드를 재구성 한 방식으로 효율성이 크게 향상되었습니다. 나는 단지 당신이 인상의 대다수가'vapply '를 사용하는 것에 기인한다고 생각한다고 생각하는데, 그것은 단지 케이크 위에 장식하는 것입니다. – Gregor