Matlab: 从数组中删除元素

7

我有一个相当大的数组。为了简化问题,让我们将其简化为:

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

现在有一组1(4个元素)、2(2个元素)、3(4个元素)、4(2个元素)和5(8个元素)。现在,我想只保留属于3个或更多元素组的列。因此结果将如下所示:

B = [1 1 1 1 3 3 3 3 5 5 5 5 5 5 5 5];

我曾尝试使用for循环来单独扫描1、2、3等数字,但在大数组上执行非常缓慢... 感谢任何提供更高效方法的建议 :) Art.


你能确定数组总是排序的吗?它只包含整数吗?值之间的步长始终为1吗? - Rody Oldenhuis
  1. 是的,数组始终是排序的。但请注意,我仅展示了数组的第一行。还有其他值存在,但它们不会被考虑,因为仅使用此第一行进行消除(但如果不满足条件,则整列将被删除)。
  2. 在我的情况下,只有整数。
  3. 不,步长可以与1不同。
- Art
感谢大家提供的出色解决方案,你们真棒!我现在得去测试它们了... - Art
4个回答

8

一般方法

如果您的向量不一定是排序的,则需要运行以计算向量中每个元素出现的次数。您可以使用histc来完成这个任务:

elem = unique(A);
counts = histc(A, elem);
B = A;
B(ismember(A, elem(counts < 3))) = []

最后一行选取出现次数少于3次的元素并删除它们。

分组向量的方法

如果你的向量是“半排序”的,也就是说,如果向量中相似的元素被分组在一起(就像你的例子一样),你可以通过以下方式加快速度:

start_idx = find(diff([0, A]))
counts = diff([start_idx, numel(A) + 1]);
B = A;
B(ismember(A, A(start_idx(counts < 3)))) = []

需要注意的是,向量并不一定要完全排序,只需要相似的元素相邻即可。


只是一般性的评论:这个解决方案只适用于A由整数值组成的情况,对吧?(是的,我知道,OP没有说明A是否也可以包含非整数)。不过,无论如何都是一个好的解决方案... - H.Muster
@H.Muster 感谢您指出这一点。我已经改进它,使其适用于非整数值 :) - Eitan T
+1 你的方法对于A中的非整数有效,但代价是需要额外读取一次A :) 这是一个权衡。 - angainor

7

这是我的两行简介

counts = accumarray(A', 1);
B = A(ismember(A, find(counts>=3)));

accumarray被用来计算A中每个成员的个数。find提取符合“3个或更多元素”标准的元素。最后,ismember告诉你它们在A中的位置。请注意,A不需要排序。当然,accumarray只适用于A中的整数值。


+1:我相信这对非整数不起作用。 - Eitan T

3
你所描述的内容被称为行程长度编码
在Matlab的FileExchange中有相应的软件。或者你可以直接按照以下步骤进行操作:
len = diff([ 0 find(A(1:end-1) ~= A(2:end)) length(A) ]);
val = A(logical([ A(1:end-1) ~= A(2:end) 1 ]));

一旦您完成了行程长度编码,就可以根据长度删除元素,例如:
idx = (len>=3)
len = len(idx);
val = val(idx);

然后解码以获取您想要的数组:
i = cumsum(len);
j = zeros(1, i(end));
j(i(1:end-1)+1) = 1; 
j(1) = 1; 
B = val(cumsum(j));

1

这里有另一种使用Matlab内置函数的方法。

% Set up
A=[1 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5];
threshold=2;

% Get the unique elements of the array
uniqueElements=unique(A);

% Count haw many times each unique element occurs
counts=histc(A,uniqueElements);

% Write which elements should be kept
toKeep=uniqueElements(counts>threshold);

% Make a logical index
indexer=false(size(A));
for i=1:length(toKeep)
    % For every unique element we want to keep select the indices in A that
    % are equal
    indexer=indexer|(toKeep(i)==A);
end

% Apply index
B=A(indexer);

如果有相同元素的不同组,则此方法将无法正常工作。 - robince
@robince 我不太确定问题的意思,如果那是OP想要的,那么请忽略我的答案。我的方法不关心顺序。 - denahiro

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