2014-09-28 161 views
0

我有大約3000個文件。每個文件都有大約55000行/標識符和大約100列。我需要計算每個文件的行方向相關性或加權協方差(取決於文件中的列數)。所有文件中的行數相同。我想知道爲每個文件計算相關矩陣的最有效方法是什麼?我已經嘗試過Perl和C++,但是它需要花費很多時間來處理一個文件 - Perl需要6天,C需要一天以上的時間。通常情況下,我不想每個文件花費15-20分鐘以上。行計算相關/協方差矩陣的有效方法

現在,我想知道如果我可以使用一些技巧或東西更快地處理它。這裏是我的僞代碼:

while (using the file handler) 
    reading the file line by line 
    Storing the column values in hash1 where the key is the identifier 
    Storing the mean and ssxx (Sum of Squared Deviations of x to the mean) to the hash2 and hash3 respectively (I used hash of hashed in Perl) by calling the mean and ssxx function 
end 
close file handler 

for loop traversing the hash (this is nested for loop as I need values of 2 different identifiers to calculate correlation coefficient) 
    calculate ssxxy by calling the ssxy function i.e. Sum of Squared Deviations of x and y to their mean 
    calculate correlation coefficient. 
end 

現在,我計算一對的相關係數只有一次,我沒有計算相同標識符的相關係數。我已經採取我的嵌套for循環照顧。你認爲是否有辦法更快地計算相關係數?任何提示/建議都會很棒。謝謝!

EDIT1: 我輸入文件看起來是這樣的 - 前10個標識符:

"Ident_01" 6453.07 8895.79 8145.31 6388.25 6779.12 
"Ident_02" 449.803 367.757 302.633 318.037 331.55 
"Ident_03" 16.4878 198.937 220.376 91.352 237.983 
"Ident_04" 26.4878 398.937 130.376 92.352 177.983 
"Ident_05" 36.4878 298.937 430.376 93.352 167.983 
"Ident_06" 46.4878 498.937 560.376 94.352 157.983 
"Ident_07" 56.4878 598.937 700.376 95.352 147.983 
"Ident_08" 66.4878 698.937 990.376 96.352 137.983 
"Ident_09" 76.4878 798.937 120.376 97.352 117.983 
"Ident_10" 86.4878 898.937 450.376 98.352 127.983 

EDIT2:這裏是段/子程序或者說,我在Perl寫的功能

## Pearson Correlation Coefficient 
sub correlation { 
    my($arr1, $arr2) = @_; 
    my $ssxy = ssxy($arr1->{string}, $arr2->{string}, $arr1->{mean}, $arr2->{mean}); 
    my $cor = $ssxy/sqrt($arr1->{ssxx} * $arr2->{ssxx}); 
    return $cor ; 
} 

## Mean 
sub mean { 
    my $arr1 = shift; 
    my $mu_x = sum(@$arr1) /scalar(@$arr1); 
    return($mu_x); 
} 

## Sum of Squared Deviations of x to the mean i.e. ssxx 
sub ssxx { 
    my ($arr1, $mean_x) = @_; 
    my $ssxx = 0; 

    ## looping over all the samples 
    for(my $i = 0; $i < @$arr1; $i++){ 
     $ssxx = $ssxx + ($arr1->[$i] - $mean_x)**2; 
    } 
    return($ssxx); 
} 

## Sum of Squared Deviations of xy to the mean i.e. ssxy 
sub ssxy { 
    my($arr1, $arr2, $mean_x, $mean_y) = @_; 
    my $ssxy = 0; 

    ## looping over all the samples 
    for(my $i = 0; $i < @$arr1; $i++){ 
     $ssxy = $ssxy + ($arr1->[$i] - $mean_x) * ($arr2->[$i] - $mean_y); 
    } 
    return ($ssxy); 
} 
+0

您能否提供典型輸入文件的摘錄? – MBo 2014-09-28 05:44:44

+0

已添加文件的前10行。 – snape 2014-09-28 06:34:18

+0

除了性能問題,您的計算可能不正確。 – 2014-09-28 12:43:40

回答

0

有你搜索CPAN?用於計算皮爾遜相關的方法gsl_stats_correlation。這一個是在Math::GSL::Statisics。該模塊綁定到GNU科學圖書館。

gsl_stats_correlation($data1, $stride1, $data2, $stride2, $n) - 該函數有效地計算陣列之間的Pearson相關係數引用$data1$data2必須既具有相同的長度$n的。 r = cov(x, y)/(\Hat\sigma_x \Hat\sigma_y) = {1/(n-1) \sum (x_i - \Hat x) (y_i - \Hat y) \over \sqrt{1/(n-1) \sum (x_i - \Hat x)^2} \sqrt{1/(n-1) \sum (y_i - \Hat y)^2} }

0

@Sinan和@Praveen對於如何在Perl中執行此操作有正確的想法。我會建議perl固有的開銷意味着你永遠不會得到你正在尋找的效率。我建議你在優化你的C代碼。

第一步是設置-O3標誌以實現最大代碼優化。

從那裏,我會更改您的ssxx代碼,以便它從各個數據點減去均值:x[i] -= mean。這意味着您不再需要減去ssxy代碼中的平均值,因此您只需進行一次減法運算55001次。

我會檢查反彙編,以確保(x-mean)**2編譯爲乘法而不是2^(2 * log(x - mean)),或者只是用這種方式編寫。

你使用什麼樣的數據結構來處理數據? A double**與分配給每行的內存將導致額外的呼叫(慢速功能)malloc。而且,分配的內存位於不同的地方更容易導致內存抖動。理想情況下,您應儘可能少地調用malloc以獲得儘可能大的內存塊,並使用指針運算來遍歷數據。

應該有更多的優化。如果你發佈你的代碼,我可以提出一些建議。

+0

感謝您的意見。 – snape 2014-10-01 21:59:58