【问题标题】:Sum up every nth row in Matlab在Matlab中总结每n行
【发布时间】:2014-07-09 16:32:54
【问题描述】:

有没有简单的方法来总结 Matlab 中的每 n 行? 假设我有:

A =

 1     2     3
 4     5     6
 7     8     9
10    11    12
13    14    15
16    17    18

我希望每 2 行添加一次,像这样:row1+row3+row5,然后是 row2+row4+row6,所以我的输出是:

B =

21    24    27
30    33    36

我知道sum(A(1:2:end,:)) 可以做到这一点,但如果您有大矩阵和许多第 n 行,这将无济于事,for 循环是另一种选择,但到目前为止我无法让它工作。您有任何建议/解决方案如何使用for 循环解决这个问题,或者是否有内置函数?

【问题讨论】:

  • sum(A(1:2:end,:)) for large matrices 有什么问题?
  • 从我的例子中sum(A(1:2:end,:) 给了我21 24 27 而要获得30 33 36 我必须使用sum(A(2:2:end,:) 如果你有很多这样的第n 行那么它就变得有点棘手了。也许我的问题不清楚。
  • 只是好奇,A 中的行数和列数可以有多大?
  • @vaitas 虽然给定的解决方案很好,但我提供的解决方案基本上是对您已经做过的事情的概括。

标签: matlab for-loop matrix indexing vectorization


【解决方案1】:

所有解决方案的基准

Thanks to Amro 用于基准代码)

function [t,v] = nthSum()
    n = 3; 
    A = randi(100,10000*n,3);

    % functions to compare
    fcns = {
        @() sum1(A,n);
        @() sum2(A,n);
        @() sum3(A,n);
        @() sum4(A,n);
        @() sum5(A,n);
        @() sum6(A,n);
    };

    % timeit
    t = zeros(6,1);
    for ii = 1:100;
        t = t + cellfun(@timeit, fcns);
    end

    % check results
    v = cellfun(@feval, fcns, 'UniformOutput',false);
    assert(isequal(v{:}));
end

function B = sum1(A,n)   %thewaywewalk#1
    [a,b] = size(A);
    idx = bsxfun(@plus, 1:b,repmat([0:b:b*n-1]',a/n,1)); 
    B = reshape(accumarray(idx(:),A(:)),b,[]).';
end

function B = sum2(A,n)  %thewaywewalk#2
    B = cell2mat(arrayfun(@(x) sum(A(x:n:end,:)),1:n,'uni',0)');
end

function B = sum3(A,n) %Dennis Jaheruddin
    B=zeros(n,size(A,2));

    for k=1:n
        B(k,:)=sum(A(k:n:end,:),1);
    end
end

function B = sum4(A,n) %Luis Mendo
    B = squeeze(sum(reshape(permute(A, [1 3 2]), n,[] ,size(A,2)), 2));
end

function B = sum5(A,n) % Bentoy13
    [k,l] = size(A);
    B = sum(reshape([A',zeros(l,mod(-k,n))],l,n,ceil(k/n)),3)';
end

function B = sum6(A,n) % Divakar
    [M,N] = size(A); 
    rowd = reshape(1:M,n,M/n)'; 
    A1 = A(rowd(:),:);
    A2 = reshape(A1,M/n,[]); 
    B = reshape(sum(A2),[],N);
end

每个运行 100 次的结果和一个 30000x3 矩阵 A

0.1616s   %// thewaywewalk#1
0.0667s   %// thewaywewalk#2
0.0499s   %// Dennis Jaheruddin
0.0211s   %// Luis Mendo
0.0791s   %// Bentoy13
0.0784s   %// Divakar   

胜负分明,其余非常接近。 特别是丹尼斯最简单的解决方案(for循环)是一流的;)

有趣的是,大行长度会如何变化

每个运行 100 次的结果和一个 3000x1000 矩阵 A

6.5646s   %// thewaywewalk#1
2.6314s   %// thewaywewalk#2
2.5939s   %// Dennis Jaheruddin
0.6412s   %// Luis Mendo
4.1996s   %// Bentoy13
1.9975s   %// Divakar

沿行数的性能

1000 次运行的平均结果,输入矩阵大小从 10 * 25 到 1e6 * 25 个元素。

数据:

N               10          20          50          100         200         500         1e+03       2e+03       5e+03       1e+04       2e+04       5e+04   1e+05   2e+05   5e+05   1e+06
thewaywewalk #1     0.000282    0.000401    0.000399    0.000341    0.000276    0.000306    0.000358    0.000538    0.00109     0.0015      0.00283     0.0111  0.021   0.0427  0.112   0.224
thewaywewalk #2     7.15e-05    0.000106    0.000129    0.000137    0.000149    0.000262    0.000433    0.000929    0.00313     0.00581     0.0122      0.0289  0.0567  0.121   0.327   0.635
Divakar             3.21e-05    4.36e-05    4.65e-05    4.63e-05    4.52e-05    6.86e-05    0.000116    0.00024     0.000668    0.00179     0.00378     0.00962 0.0193  0.0442  0.116   0.245
Bentoy13            4.58e-05    6.48e-05    7.43e-05    7.31e-05    7.16e-05    0.000103    0.000192    0.000387    0.00115     0.0028      0.00585     0.015   0.0303  0.0623  0.158   0.298
Dennis Jaheruddin   3.76e-05    5.54e-05    6.07e-05    6.25e-05    6.47e-05    0.000114    0.000183    0.000376    0.000999    0.00165     0.00345     0.0162  0.0318  0.0657  0.163   0.326
Luis Mendo          7.44e-05    0.000108    0.00011     9.45e-05    7.83e-05    8.56e-05    0.000102    0.000154    0.000323    0.000452    0.000892    0.00203 0.00374 0.00741 0.0186  0.0368

对于小型矩阵(1000 行以下),Divakar 的解决方案是最快的;对于大型矩阵,Luis Mendo 的解决方案显然是最好的解决方案。

【讨论】:

  • 我认为这个答案将归社区维基所有。除此之外,伟大的工作;我同时对自己进行了另一个基准测试,得出的结论有点不同,如果您允许,我会在帖子中添加信息。
  • @Bentoy13 继续 ;)
  • 谢谢。我需要更多时间,我重新运行基准以添加 Luis Mendo 解决方案的时间。
  • @thewaywewalk 谢谢你的结果!!我也很好奇如果列数也增加了,有可能吗?或者太多的工作可能有两个变量?不过,非常感谢!哦,你刚刚做到了,库尔!
  • @thewaywewalk 感谢您的努力!像这样的答案太棒了
【解决方案2】:

置换维度和重塑:

B = squeeze(sum(reshape(permute(A, [1 3 2]), n,[],size(A,2)), 2));

【讨论】:

  • 不错但很艰难,我在这里通过permute 学到了一些东西。
【解决方案3】:

我仍然认为 for 循环在这里会很简单,如果你有长矩阵,可能会相当有效:

A = [ 1     2     3
 4     5     6
 7     8     9
10    11    12
13    14    15
16    17    18];

n=2;
result=zeros(n,size(A,2));

for k=1:n
   result(k,:)=sum(A(k:n:end,:),1);
end

【讨论】:

    【解决方案4】:

    即使已经有一个公认的答案,让我们尝试以不同的方式来做。

    这里的想法是将原始矩阵重塑为 3D 数组,以便沿一维求和。但我们必须注意一个事实,即 A 的大小可能不是 n 的倍数。

    让我们从一些符号开始:

    [k,l] = size(A);
    

    为了重塑,我们必须有k=n*x(x 整数)。如果不是这种情况,我们必须用 0 的行填充 A。如果k=n*x+y,y 是 k 除以 n 的欧几里德除法的提示,我们必须将 0 的 ny 行连接到 A(或者 0 是 y== 0)。使用 Matlab 的mod 函数,转换为:

    A1 = [A;zeros(mod(-k,n),l)];
    

    回想一下mod(-k,n) 是肯定的。

    现在,您想要对行而不是列求和:我们想要获得一个 3D 数组,其中行保持不变,列分布在行上。所以我必须转置数组,并重塑它:

    A2 = reshape(A1',l,n,ceil(k/n));
    

    然后,您的结果就是沿第 3 维的总和,然后转回:

    B = sum(A2,3)';
    

    作为结论,让我们构建一个近乎单行的(为了清楚起见,我把 k 和 l 分开了......好吧,为了简洁):

    [k,l] = size(A);
    B = sum(reshape([A',zeros(l,mod(-k,n))],l,n,ceil(k/n)),3)';
    

    【讨论】:

      【解决方案5】:

      怎么样?

      B = cell2mat(arrayfun(@(x) sum(A(x:n:end,:)),1:n,'uni',0)')
      

      我首先考虑使用accumarray,但它需要一个向量作为输入。如果你关注this answer,它仍然是可能的。

      另一个 accumarray 替代方案:

      [a,b] = size(A);
      idx = bsxfun(@plus, 1:b,repmat([0:b:b*n-1]',a/n,1)) 
      B = reshape(accumarray(idx(:),A(:)),b,[]).'
      

      【讨论】:

      • accumarray + bsxfun 看起来很快! +1
      • 这个解决方案似乎要求Mn 的整数倍。这不一定是个问题,但应该牢记在心。
      • @DennisJaheruddin 是的,这是真的,第一个解决方案应该可以工作。但如果没有这个条件,整个总结对我来说毫无意义,因为所有组的结果都没有可比性。
      • @Divakar 但事实并非如此!一点也不;)
      • @thewaywewalk 是的,说得太早了,但仍然是简洁的代码:)
      【解决方案6】:

      如果您想避免arrayfun 用于一般的n 情况,您可以使用一些重塑方法。一种这样的方法可能是这样 -

      [M,N] = size(A); %// Get the size of A for later usage
      rowd = reshape(1:M,n,M/n)'; %// Get new row indices based on every nth selection
      A1 = A(rowd(:),:); %// Reshaped A that has all the nth rows packed up consecutively for easing summing up
      A2 = reshape(A1,M/n,[]); %// Reshape into a matrix with the number of rows equal to number of rows in each nth grouping
      out = reshape(sum(A2),[],N); %// Get the final output by summing across columns and reshaping into the N-column format as with A
      

      【讨论】:

      • @vaitas 太好了!!由于您拥有大型矩阵,因此在解决方案中提到的技术中看到一些基准测试结果会很有趣!
      • @Divakar 这次我让你做那个任务...... ;)
      • @Bentoy13!我只是用30 的列数做了一些基准测试,而我的看起来更快:D 好吧,关于发布这样的基准测试结果..目前不确定 :)
      • @Divakar 好的,我想玩! :P 让我们等待更多解决方案。
      • 这个解决方案似乎要求Mn 的整数倍。这不一定是个问题,但应该牢记在心。
      猜你喜欢
      • 2012-11-09
      • 1970-01-01
      • 1970-01-01
      • 2017-08-22
      • 2014-12-04
      • 1970-01-01
      • 1970-01-01
      • 2018-04-24
      • 2021-10-21
      相关资源
      最近更新 更多