2012-02-16 87 views
5

我使用下面的脚本分析大型数据集:使嵌套循环更有效?

M <- c_alignment 
c_check <- function(x){ 
    if (x == c_1) { 
     1 
    }else{ 
     0 
    } 
} 
both_c_check <- function(x){ 
    if (x[res_1] == c_1 && x[res_2] == c_1) { 
     1 
    }else{ 
     0 
    } 
} 
variance_function <- function(x,y){ 
    sqrt(x*(1-x))*sqrt(y*(1-y)) 
} 
frames_total <- nrow(M) 
cols <- ncol(M) 
c_vector <- apply(M, 2, max) 
freq_vector <- matrix(nrow = sum(c_vector)) 
co_freq_matrix <- matrix(nrow = sum(c_vector), ncol = sum(c_vector)) 
insertion <- 0 
res_1_insertion <- 0 
for (res_1 in 1:cols){ 
    for (c_1 in 1:conf_vector[res_1]){ 
     res_1_insertion <- res_1_insertion + 1 
     insertion <- insertion + 1 
     res_1_subset <- sapply(M[,res_1], c_check) 
     freq_vector[insertion] <- sum(res_1_subset)/frames_total 
     res_2_insertion <- 0 
     for (res_2 in 1:cols){ 
      if (is.na(co_freq_matrix[res_1_insertion, res_2_insertion + 1])){ 
       for (c_2 in 1:max(c_vector[res_2])){ 
        res_2_insertion <- res_2_insertion + 1 
        both_res_subset <- apply(M, 1, both_c_check) 
        co_freq_matrix[res_1_insertion, res_2_insertion] <- sum(both_res_subset)/frames_total 
        co_freq_matrix[res_2_insertion, res_1_insertion] <- sum(both_res_subset)/frames_total 
       } 
      } 
     } 
    } 
} 
covariance_matrix <- (co_freq_matrix - crossprod(t(freq_vector))) 
variance_matrix <- matrix(outer(freq_vector, freq_vector, variance_function), ncol = length(freq_vector)) 
correlation_coefficient_matrix <- covariance_matrix/variance_matrix 

模型输入会是这样的:

1 2 1 4 3 
1 3 4 2 1 
2 3 3 3 1 
1 1 2 1 2 
2 3 4 4 2 

什么我计算是每个国家的二项式方差在M[,i]中找到,每个州在M[,j]中找到。每一行都是该试验的状态,我想看看列的状态是如何变化的。澄清:我找到两个多项式分布的协方差,但我通过二项式比较来完成。

输入是一个4200 x 510矩阵,每列的c值平均约为15。我知道for循环在R中非常缓慢,但我不确定在这里如何使用apply函数。如果有人有建议,在这里正确使用apply,我真的很感激。现在脚本需要几个小时。谢谢!

+0

请问您可以添加一个小的数据集,你试图得到什么? – aatrujillob 2012-02-16 22:12:58

+0

@AndresT添加了更多信息。 – 2012-02-16 22:22:24

+0

你有没有试过在编译器中打开'loop unrolling'优化器? – 2012-02-16 22:36:34

回答

3

这不是真正的4路嵌套循环,而是你的代码在每次迭代中增长内存的方式。这发生了4次,我在cbind和行上放置了# **。在这种情况下,R(以及Matlab和Python)中的标准建议是预先分配并填充它。这就是apply函数所做的。只要已知数量的结果,他们就会分配一个list,将每个结果分配给每个槽,然后将所有结果合并在一起。在你的情况下,你可以预先分配正确的大小矩阵,并在这4点(粗略地说)分配。这应该和apply系列一样快,并且您可能会发现编码更容易。

15

我想写评论,但我有太多话要说。

首先,如果您认为申请更快,请看Is R's apply family more than syntactic sugar?。它可能是,但它远没有保证。

接下来,请不要在您移动代码时增长矩阵,这会让代码难以置信地变慢。预分配矩阵并填充它,这可以将您的代码速度提高十倍以上。你通过你的代码越来越多不同的向量和矩阵,这太疯狂了(原谅我的强烈语音)

然后,看看?subset帮助页面,并给予警告有:

这是一种便捷功能旨在交互使用。对于 编程,最好使用标准子集函数,如 [,特别是参数子集 的非标准评估可能会有意想不到的后果。

总是。使用。指数。

此外,您一次又一次重新计算相同的值。例如fre_res_2针对每个res_2和state_2计算的次数与组合res_1state_1的次数相同。这只是资源的浪费。从循环中取出不需要重新计算的内容,并将其保存在矩阵中,然后再次访问。

哎呀,现在我在:请使用矢量化函数。再想想,看看你可以拖动循环了:这是我看到的你的计算的核心:

cov <- (freq_both - (freq_res_1)*(freq_res_2))/
(sqrt(freq_res_1*(1-freq_res_1))*sqrt(freq_res_2*(1-freq_res_2))) 

当我看到它,你可以构造一个矩阵freq_both,freq_res_1和freq_res_2并使用它们作为那一行的输入。这将是整个协方差矩阵(不要称之为cov,cov是一个函数)。退出循环。输入快速代码。

鉴于我不知道什么是在c_alignment,我不会重写你的代码你,但你一定要摆脱思想的C-方式开始思考R.

让这作为开始:The R Inferno

+1

我希望我能给你+2!很好的答案和链接! – Justin 2012-02-16 23:13:42

+0

特别注意'The R Inferno'的圆圈2。 – 2012-02-17 09:26:00

+0

我第二+2 – 2012-02-17 10:33:18