如何在MATLAB中遍历矩阵的列并将它们每个添加到MATLAB中一个特定列的总和矩阵中?

4
假设有一个矩阵。
A =

 1     3     2    4
 4     2     5    8
 6     1     4    9

我有一个向量,包含了这个矩阵每一列的“类别”,比如:
v = [1 , 1 , 2 , 3]

我该如何将矩阵的列加总至一个新矩阵,并使每个向量对应其所在的类别列?例如,在此示例中,A 的第 1 列和第 2 列将被加至新矩阵的第一列,第 2 列和第 3 列将被加至第二列,第 4 列将被加至第三列。
类似于:
SUM =

4    2    4
6    5    8
7    4    9

不使用循环是否可能实现这个功能?


不要害怕MATLAB循环!现在它非常快,而且通常很容易阅读! - knedlsepp
2
@knedlsepp ... 但是会削弱大部分的乐趣 :-P - Luis Mendo
1
@LuisMendo:好吧,我和下一个人一样喜欢一个好的向量化解决方案,但如果 for 循环胜过所有其他方法,为什么要避免它呢?;-) - knedlsepp
@user3563898,以下任何答案是否有帮助?请标记其中一个为已接受。谢谢! - Benoit_11
4个回答

4

结合accumarraybsxfun的完美场景之一是-

%// Since we are to accumulate columns, first step would be to transpose A
At = A.'  %//'

%// Create a vector of linear IDs for use with ACCUMARRAY later on
idx = bsxfun(@plus,v(:),[0:size(A,1)-1]*max(v))

%// Use ACCUMARRAY to accumulate rows from At, i.e. columns from A based on the IDs
out = reshape(accumarray(idx(:),At(:)),[],size(A,1)).'

示例运行 -
A =
     1     3     2     4     6     0
     4     2     5     8     9     2
     6     1     4     9     8     9
v =
     1     1     2     3     3     2
out =
     4     2    10
     6     7    17
     7    13    17

不错。我一直在尝试使用accumarray来解决这个问题。+1。 - rayryeng
3
没有“bsxfun”,没有完美的解决方案!;) - Divakar
3
bsxfunaccumarray和非共轭转置:精彩的答案!! :-)(翻译说明:这句话已经是中文了,不需要进行翻译。) - Luis Mendo

3

使用 accumarray 在二维情况下的一个替代方案。用向量 v 生成一个网格,然后应用 accumarray

A = A.';

v = [1 1 2 3]; 

[X, Y] = ndgrid(v,1:size(A,2));

这里的XY长成这样:

X =

     1     1     1
     1     1     1
     2     2     2
     3     3     3


Y =

     1     2     3
     1     2     3
     1     2     3
     1     2     3

然后应用 accumarray
B=accumarray([X(:) Y(:)],A(:)),

SUM = B.'

SUM =

     4     2     4
     6     5     8
     7     4     9

你会发现,使用[X(:) Y(:)]可以创建如下数组:
ans =

     1     1
     1     1
     2     1
     3     1
     1     2
     1     2
     2     2
     3     2
     1     3
     1     3
     2     3
     3     3

其中包含“class”的向量v被复制了3次,因为有3个唯一的类需要汇总在一起。

编辑:

正如knedlsepp指出的那样,你可以这样消除对AB的转置:

[X2, Y2] = ndgrid(1:size(A,1),v);

B = accumarray([X2(:) Y2(:)],A(:))

最终实现了相同的效果。使用转置方式更容易理解,但结果相同。


1
很好,这对我来说更有意义。 - rayryeng
为什么要转置?你不能只使用 [X, Y] = ndgrid(1:size(A,1),v); 吗? - knedlsepp
是的,确实如此;我发现通过转置A更容易进行可视化,但你是完全正确的@knedlsepp。 - Benoit_11
4
我希望Mathworks能够扩展accumarray函数,使其能够使用矩阵作为第二个参数... - Luis Mendo
@LuisMendo - 我也希望如此。这会让我开发的很多算法更容易编写。 - rayryeng

2
一句话概括呢?
result = full(sparse(repmat(v,size(A,1),1), repmat((1:size(A,1)).',1,size(A,2)), A));

@knedlsepp,Benoit_11已经介绍了使用accumarray的方法 :-) 使用sparse的优点是可以直接对_列_进行累加,而不仅仅是像accumarray强制要求的数字。但仔细想想,实际上它们非常相似。 - Luis Mendo
@knedlsepp 或许我应该删除它? - Luis Mendo
@knedlsepp 我不确定你的意思 :-) 我的想法是“使用sparse允许处理列,而accumarray则不行”。但最终的代码肯定几乎相同。 - Luis Mendo
1
我刚刚感觉到了一股力量的干扰,那位排名第一的SO accumarray高手会选择使用full(sparse)。好吧,当R2015a终于被安装在大多数机器上时,你可以使用repelem代替repmat,从而使用accumarray实现一行代码,而不需要丑陋的reshape(,[],1)。 :-) - knedlsepp
@knedlsepp 哈哈哈。实际上,#1的accumarrayer是chappjc! 可惜他最近在Matlab方面不太活跃。 - Luis Mendo
显示剩余4条评论

1

不要过早地进行优化!

for循环对于您的问题表现良好:

out = zeros(size(A,1), max(v));
for i = 1:numel(v)
    out(:,v(i)) = out(:,v(i)) + A(:,i);
end

顺便提一下:fine 意思是:快速、快速、快速!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接