MATLAB - 查找并标记数组中的重复项

3

我有一个数值数组,其中一些具有重复项,例如:

a = [5;5;4;7;7;3;3;9;5;7]

我希望找出哪些是重复的,然后按顺序为每个重复项编号,同时将非重复项变为零。例如:

b = [1;1;0;2;2;3;3;0;1;2]

目前我采用的方法非常低效且不完整,使用了unique函数和各种for循环和if语句,但我觉得应该有一个简单的答案。

什么是最有效的方法来获得这个答案?


4
[5;5;4;7;7;3;3;9;5;5;4;7] 这样的输入是可能的吗?结果会是什么? - Luis Mendo
@LuisMendo 是的,那种输入也是可能的。我已经修改了问题以包括非连续重复项。 - user3743235
4个回答

3

以下是另一种方法:

a = [5;5;4;7;7;3;3;9;5;7];
[u, ~, w] = unique(a, 'stable');
s = find(sum(bsxfun(@eq, a, u.'), 1) > 1);
b = sum(bsxfun(@times, bsxfun(@eq, w, s), 1:numel(s)), 2);

从R2016b开始,你可以简化语法:
a = [5;5;4;7;7;3;3;9;5;7];
[u, ~, w] = unique(a, 'stable');
s = find(sum(a==u.', 1) > 1);
b = sum((w==s).*(1:numel(s)), 2);

2
你可以结合使用 uniqueaccumarrayismember 来进行必要的调整:
a = [5;5;4;7;7;3;3;9];

% Identify unique values and their counts
[uniquevals, ~, ia] = unique(a, 'stable');  % Stable keeps it in the same order
bincounts = accumarray(ia, 1);  % Count the frequency of each index in ia

% Zero out singles
singles = uniquevals(bincounts <= 1);
[~, singleidx] = intersect(a, singles);
a(singleidx) = 0;

% Overwrite repeats
repeats = uniquevals(bincounts > 1);
[~, a] = ismember(a, repeats);

这将返回一个新的a,其中包含:

a =

     1     1     0     2     2     3     3     0

步骤

我们使用unique来查找输入数组a中的所有唯一值。我们还存储了可选的第三个输出,即将a的值映射到其在唯一值数组中的索引。请注意,我们使用stable选项以按a中首次遇到的顺序获取唯一值;默认情况下,unique的结果是排序的。

然后我们使用accumarray累加从unique得到的下标,这给我们每个索引的计数。使用逻辑索引,我们首先使用这些计数将单个实例归零。这些被归零后,我们可以使用ismember的第二个输出来返回最终答案。


2

这里有一个基于索引、逻辑运算符和 cumsum 的解决方案:

x = [false; a(2:end)==a(1:end-1)]; %logical indexes of repeated elements except the first element of each block 
y = [x(2:end)|x(1:end-1) ;x(end)]; %logical indexes of repeated elements
result = cumsum(~x&y).*y           %cumsum(...):number all elements sequentially and (... .* y): making non-duplicates zero

编辑:

由于问题已经编辑,为了处理非连续的重复项,您可以这样做:

[s ii] = sort(a);
x = [false ;s(2:end)==s(1:end-1)];
y = [x(2:end)|x(1:end-1) ;x(end)];
first = ~x&y;
[~,ix]=sort(ii(first));
un(ix,1)=1:numel(ix);
result(ii,1)=un(cumsum(first)).*y;

我喜欢这种高效的方法。虽然它只能找到连续的重复项。 - Y. Chang
@Y.Chang 谢谢!看起来 OP 想要连续的重复,除非我收到新的反馈。 - rahnema1
1
无法确定问题出在哪里,但是如果存在超过2个相同的元素,则您的第二种方法会出现错误。例如对于a = [5;5;5;4;7;7;3;3;3;3;9];。仍然非常整洁。 - Leander Moesinger
@LeanderMoesinger 谢谢,你是对的,第二种方法已经移除。 - rahnema1

1
这是一个适用于非连续重复项的两行代码。
[c, ia, ic] = unique(a, 'stable');
[~, b] = ismember(a, a(ia(accumarray(ic,1)>1)));

我使用了@excaza的答案中的一些想法,并进行了修改。


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