加快矩阵录入

时间:2015-02-02 22:05:51

标签: performance matlab for-loop matrix

我的matlab计算速度有点问题。我能够在matlab中编写一个代码来运行小矩阵的计算,但它使用嵌套的for循环,并且对于我正在使用的大型数据集,matlab无法完成计算。

注意:我对Matlab并不十分熟悉,所以虽然该程序有效但效率极低。

简而言之,我正在尝试创建一个矩阵,其条目描述了一组唯一位置之间的关系。作为一个具体的例子,我们从这个矩阵开始:

B = 

5873 4 1

5873 7 1

5873 1 1

2819 8 2

2819 1 2

9771 4 3

9771 2 3

9771 5 3

9771 6 3

5548 7 4

第三列是唯一的位置标识符,第二列是恰好位于该位置的“段”的编号。请注意,多个细分可以分为不同的位置。

我想要的是创建一个描述不同位置之间关系的矩阵。具体来说,对于位置i& j,我希望新矩阵的(i,j)条目是i&的分段数。 j的共同点除以i&的段总数。 j合并。

目前我的代码如下:

C = zeros(max(B.data(:,3)), max(B.data(:,3)));

for i = 1:max(B.data(:,3))

  for j = 1:max(B.data(:,3))

    vi = B.data(:,3) == i;
    vj = B.data(:,3) == j;
    C(i,j) = numel(intersect(B.data(vi,2), B.data(vj,2))) / numel(union(B.data(vi,2), B.data(vj,2)));
  end

end

但它非常慢。有没有人有加快计算的建议?

非常感谢!!

2 个答案:

答案 0 :(得分:2)

使用分组和循环

的方法
  1. 根据段(下面的代码中的单元阵列groups)对位置进行分组。具有自定义功能的accumarray用于该任务。
  2. 大小等于最大位置的方阵C初始化为零。对于每个组,属于该组的所有位置对都使其C中的条目增加1
  3. 矩阵C已标准化。
  4. 代码:

    groups = accumarray(B.data(:,2), B.data(:,3), [], @(x) {x});  %// step 1
    C = zeros(max(B.data(:,3)));                                  %// step 2
    for n = 1:numel(groups);
        ind = groups{n};
        C(ind,ind) = C(ind,ind)+1;
    end
    d = diag(C);                                                  %// step 3
    C = C./(bsxfun(@plus,d,d.')-C);
    

    使用3D数组的矢量化方法

    这种方法很耗费内存;根据@Divakar在下面的评论,对于非常大的数据集,它并不比循环方法快:

    1. 构建3D logical数组TT(m,n,s) 1当且仅当地点mn共享细分{ {1}}。这可以通过bsxfun高效完成。
    2. 沿第三个维度汇总s会产生与上一个方法相同的T
    3. 矩阵C的标准化方式与之前相同。
    4. 代码:

      C

      基准

      以下基准测试和解释将归功于@Divakar:

      以下是一些将T = full(sparse(B.data(:,3), B.data(:,2), 1)); %// step 1 T = bsxfun(@and, permute(T, [1 3 2]), permute(T, [3 1 2])); C = sum(T, 3); %// step 2 d = diag(C); %// step 3 C = C./(bsxfun(@plus,d,d.')-C); 方法与后者loop-based方法进行比较的运行时 -

      vectorized

      可以注意到,对于***** Parameters: No. of rows in B ~= 10000 and No. of groups = 10 ***** ---------------------------------- With loopy approach Elapsed time is 0.242635 seconds. ---------------------------------- With vectorized approach Elapsed time is 0.022174 seconds. ***** Parameters: No. of rows in B ~= 10000 and No. of groups = 100 ***** ---------------------------------- With loopy approach Elapsed time is 0.318268 seconds. ---------------------------------- With vectorized approach Elapsed time is 0.451242 seconds. ***** Parameters: No. of rows in B ~= 100000 and No. of groups = 100 ***** ---------------------------------- With loopy approach Elapsed time is 1.173182 seconds. ---------------------------------- With vectorized approach Elapsed time is 0.464339 seconds. ***** Parameters: No. of rows in B ~= 100000 and No. of groups = 1000 ***** ---------------------------------- With loopy approach Elapsed time is 10.310780 seconds. ---------------------------------- With vectorized approach Elapsed time is 54.216923 seconds. 中相同的行数,组数的增加意味着向量化方法的性能大幅下降。

      因此,在为问题案例选择适当的方法时,请记住这一点。

答案 1 :(得分:1)

这可能是一种使用单循环的方法 -

%// Store column-2 and 3 values in separate variables for easy access
col2 = B(:,2);
col3 = B(:,3);

%// Start and end indices of the groups w.r.t column-3 of B
ends = [find(diff(col3)) ; numel(col3)];
starts = [1 ; ends(1:end-1)+1];

ngrp = max(col3); %// number of groups
intersectM = zeros(ngrp); %// initialize 2D matrix to store intersect counts
for k = 1:ngrp-1

    %// Matches for each group with respect to each other group 
    %// but one less after each iteration
    matches = ismember(col2(starts(k+1):end),col2(starts(k):ends(k)));
    %// OR matches =
    %// any(bsxfun(@eq,col2(starts(k):ends(k)),col2(starts(k+1):end).'),1)'

    %// Store intersect counts
    intersectM(k,k+1:end) = accumarray(col3(starts(k+1):end)-k,matches,[]);
end

grp_counts = histc(col3,1:ngrp); %// counts of group elements

%// Peform numel(intersect)/numel(union)
C = intersectM./(bsxfun(@plus,grp_counts,grp_counts') - intersectM); %//'

%// Since the output is diagonal symmetric, so just copy over the upper
%// diagonal elements into the lower diagonal places
C = triu(C)' + C; %//'
C(1:ngrp+1:end) = 1; %// Set diagonal elements as ones