如何在不使用MATLAB循环的情况下找到向量中元素子集的出现次数?

时间:2016-10-13 16:52:04

标签: matlab frequency

说X是给定的向量:

   X=[1
      2
      4
      2
      3
      1
      4
      5
      2
      4
      5];

Y是X中给定元素的子集:

Y=[3
   4
   5];

所需输出是Y中元素出现在X中的次数:

out=[1
     3
     2];

我这样做的解决方案是使用for循环:

for i=1:size(X,1)
    temp = X(X(:,1)==Y(i,1),:);
    out(i,1) = size(temp,1);
end

但是当X和Y很大时,效率很低。那么,如何更快地利用矢量化呢?我知道histhistc,但我无法想到如何在这种情况下使用它们来获得所需的输出。

1 个答案:

答案 0 :(得分:5)

快速选项

您可以使用bsxfunsum相结合来计算此

sum(bsxfun(@eq, Y, X.'), 2)

<强>解释

在此示例中,bsxfunXY中的每个元素组合执行给定操作。我们要使用的操作是eq(检查相等性)。结果是一个矩阵,Y中的每个元素都有一行,X中的每个元素都有一列。如果1中的元素等于X中与给定行对应的元素,则它将具有Y值。

bsxfun(@eq, Y, X.')

%    0     0     0     0     1     0     0     0     0     0     0
%    0     0     1     0     0     0     1     0     0     1     0
%    0     0     0     0     0     0     0     1     0     0     1

然后,我们可以对列进行求和,以计算X中与Y中的给定值相等的元素数。

sum(bsxfun(@eq, Y, X.'), 2)

%   1
%   3
%   2

在较新版本的MATLAB上(自R2016b起),您可以省略bsxfun,因为相等操作将自动广播。

sum(Y - X.', 2)

内存有效选项

第一个选项效率不高,因为它需要创建一个[numel(Y), numel(X)]元素大的矩阵。另一种可能更高效记忆的方法可能是使用ismember的第二项输出结合accumarray

[tf, ind] = ismember(X, Y);
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], @numel);

<强>解释

ismember用于确定一个数组中的值是否在另一个数组中。第一个输入告诉我们 if 第一个输入的每个元素是否在第二个输入中,第二个输出告诉你第二个输入中的 where 第一个输入的每个元素都被找到

[tf, ind] = ismember(X, Y);

%   0 0 1 0 1 0 1 1 0 1 1
%   0 0 2 0 1 0 2 3 0 2 3

我们可以使用第二个输入将相同的值“分组”在一起。 accumarray函数就是这样做的,它使用上面的ind变量来确定组,然后将给定的操作应用于每个组。在我们的例子中,我们想要简单地确定每个组中的元素数量。所以要做到这一点,我们可以传递第二个输入,其大小为ind输入(减去那些不匹配的输入),然后使用numel作为操作(计算每个输入中的数字)基)

counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], @numel);

%   1
%   3
%   2