合并向量中相邻重复的值所对应的值

3

我需要翻译两个数据向量,它们看起来像这样:

A = [1 2 3 3 4 5 6 6 5 4 4 3 3 3 3];
B = [1 5 9 6 4 6 8 2 1 5 7 8 3 2 6];

我希望能够去除 A 中所有相邻重复的值,并将对应的 B 值求和,结果为

A = [1 2 3  4 5 6  5 4  3];
B = [1 5 15 4 6 10 1 12 19];

我可以像这个答案中描述的那样使用unique,但是这将组合所有重复的值和重复值,而不考虑顺序。我还可以使用diff,如这个问题中所述,但我不知道应该如何记录被组合的索引。

我可以一直遍历向量,但这似乎太繁琐了,我认为应该有更优雅的解决方案。是否有一种只需几行代码就能实现这一点的方法?


我回答了您的问题吗? - rayryeng
@rayryeng 终于抽空尝试了一下,它完全符合我的需求。 cumsumaccumarray 结合使用的技巧真是太棒了! - David K
呵呵,一点问题也没有。accumarraycumsum是我最喜欢的两个函数之一。很高兴能与你分享这些! - rayryeng
1个回答

7
您可以使用diff来找到不唯一的相邻位置,然后将其与cumsum结合使用,以生成应归属于彼此的不同组。在差异结果中查找任何非零值将找到连续的非唯一值。当您将cumsum应用于此结果时,将生成一个ID数组,该数组从1变化到许多组,其中属于同一ID的所有值都属于同一连续组。这应该作为输入到accumarray的理想输入,我们可以对属于每个组的所有值进行求和:
Aval = A(:); % Unroll into a column to ensure shape compliance
ind = diff([Inf; Aval]) ~= 0; % Find all unique locations
IDs = cumsum(ind); % Create ID array
Aout = Aval(ind).'; % Determine all unique values per group
Bout = accumarray(IDs(:), B(:)).'; % Find their sum

我承认这不是几行代码就能概括的,大部分内容都是设置,但核心答案可以在第二、第三和最后一行代码中看到。请注意,在使用accumarray时需要输入列向量,要强制将输入数据转换为列向量,我使用(:)将向量展开成列向量,无论它们的形状如何,特别是在第一行代码中。最后,我将结果进行转置,因为accumarray在这种情况下会输出一个列向量,而转置会创建一个行向量,而你想要的是行向量作为所需的结果。
对于您的测试输入:
A = [1 2 3 3 4 5 6 6 5 4 4 3 3 3 3];
B = [1 5 9 6 4 6 8 2 1 5 7 8 3 2 6];
diff 的输出结果如下所示:
>> ind.'

ind =

     1     1     1     0     1     1     1     0     1     1     0     1     0     0     0

您可以准确地看到值为零的位置对应于非唯一连续位置。运行cumsum后,ID数组的输出如下:
>> IDs.'

IDs =

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

执行 cumsum 函数对 IDs 数组进行操作,可以将这个 diff 数组转换成每个连续组都给出一个唯一 ID 的形式。我们也可以使用 ind 索引到 A 中的每个唯一值所在的组,这是第三行代码。最后一行代码对每个组进行求和。请注意,第三行代码被转置为行向量,因为我展开了数据,使其成为一个列向量来处理。

我们得到了期望的输出结果:

>> Aout

Aout =

     1     2     3     4     5     6     5     4     3

>> Bout

Bout =

     1     5    15     4     6    10     1    12    19

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