根据两列中的数字拆分矩阵

时间:2015-02-12 02:17:34

标签: matlab

我有一个大矩阵,我想根据两列的值分隔行。例如:

M=[ 1 3 5 6; 3 6 5 4; 1 8 5 1; 4 6 5 7; 3 6 4 5; 3 6 4 4]

我希望行与第1列和第3列中的常见值分开。这是预期的输出:

A= [1 3 5 6; 1 8 5 1]
B= [3 6 5 4]
C= [4 6 5 7]
D= [3 6 4 5; 3 6 4 4]

我尝试过以下方法。但是,它只用一列分隔:

A = arrayfun(@(x) M(M(:,1) == x, :), unique(M(:,1)), 'uniformoutput', false)

2 个答案:

答案 0 :(得分:2)

我的理解是你想要提取矩阵的行,它们在第一列和第三列共享共同的值。您当然可以使用arrayfun方法,但是您需要修改调用unique的方式。具体来说,在要检查的列上使用'rows'标记,然后在arrayfun调用中,使用bsxfunall结合使用来检查包含每个列的行您想要的列元素的唯一组合,所以:

>> M=[ 1 3 5 6; 3 6 5 4; 1 8 5 1; 4 6 5 7; 3 6 4 5; 3 6 4 4];
>> r = unique(M(:,[1 3]), 'rows', 'stable');
>> A = arrayfun(@(x) M(all(bsxfun(@eq, M(:,[1 3]), r(x,:)),2), :), 1:size(r,1),'uni', 0);
>> celldisp(A);

A{1} =

     1     3     5     6
     1     8     5     1

A{2} =

     3     6     5     4

A{3} =

     4     6     5     7

A{4} =

     3     6     4     5
     3     6     4     4

请注意,我使用'stable'标记非常重要,因为unique默认情况下排序唯一条目。 'stable'可确保我们在遇到它们的顺序中找到唯一条目,例如您问题中所需的输出。顺便说一句,'uni''UniformOutput'的缩写。从长远来看,它可以帮助您输入:)。

第三行相当满口,但很容易解释。首先,看一下这句话:

bsxfun(@eq, M(:,[1 3]), r(x,:))

我们将在此处执行的是r的每一行,它是从M的第1列和第3列中获取的唯一值的配对,并查看{ {1}}等于M行中的相应位置。为了找到匹配项,我们需要确保此结果的一行中的所有值等于1,这就是为什么我们需要在此使用r并查看列:

all

一旦我们发现这些行与all(bsxfun(@eq, M(:,[1 3]), r(x,:)),2) 中的特定行匹配,我们就会使用这些行并将其索引到我们的矩阵r中并提取满足M中行的行寻找r,因此:

M

M(all(bsxfun(@eq, M(:,[1 3]), r(x,:)),2), :) 从1到x中的行数进行迭代,并且在每次迭代时,我们每次从r中提取出一个唯一的行。最终结果将是一个单元数组,它根据第一列和第三列之间的公共元素对r行进行分组。


如果您希望提高效率,可以执行Divakar建议的操作,并仅使用M的第三个输出。这样也会让事情变得更容易阅读:

unique

当然更具可读性!现在发生的事情是,>> M=[ 1 3 5 6; 3 6 5 4; 1 8 5 1; 4 6 5 7; 3 6 4 5; 3 6 4 4]; >> [~,~,r] = unique(M(:,[1 3]), 'rows', 'stable'); >> A = arrayfun(@(x) M(r == x, :), 1:max(r),'uni', 0); >> celldisp(A); A{1} = 1 3 5 6 1 8 5 1 A{2} = 3 6 5 4 A{3} = 4 6 5 7 A{4} = 3 6 4 5 3 6 4 4 的第三个输出会将1中的唯一ID分配给您拥有的唯一值作为unique的输入。然后,我们简单地找到最大ID,并从1到最大ID进行迭代,其中在每次迭代时,该索引用作从矩阵中提取对应于第一列和第三列的每个唯一组合的行的方式。 unique

答案 1 :(得分:1)

对我来说,分区意味着在MATLAB中accumarray。第一步与 rayryeng 的解决方案基本相同:使用unique获取每行的新位置。第二步是:基于这些新位置:获取M的行并将它们放在一个单元格中。

[~,~,I] = unique(M(:,[1 3]), 'rows', 'stable');
A = accumarrayStable(I, 1:length(I), [], @(J) {M(J,:)});

由于这些行位置不会被排序,我们需要accumarray的稳定版本,我从this answer获取。 如果您不关心每个A{i}中行的顺序,则您不会需要这样做,但可以立即使用更快的accumarray

function A = accumarrayStable(subs, val, varargin)
[subs(:,end:-1:1), I] = sortrows(subs(:,end:-1:1));
A = accumarray(subs, val(I), varargin{:});

总体来说,使用此解决方案应该可以为arrayfun - 解决方案提供5-10倍的加速,具体取决于您的矩阵大小。