按照计数复制数组的每个元素

8
我的问题类似于这个问题,但我想要根据一个相同大小的第二个数组中指定的计数来复制每个元素。
例如,假设我有一个数组v = [3 1 9 4],我想使用rep = [2 3 1 5]将第一个元素复制2次,第二个元素复制3次,以此类推,得到[3 3 1 1 1 9 4 4 4 4 4]
到目前为止,我正在使用简单的循环来完成这项工作。这是我开始使用的内容:
vv = [];
for i=1:numel(v)
    vv = [vv repmat(v(i),1,rep(i))];
end

我通过预分配空间来提高性能:

vv = zeros(1,sum(rep));
c = cumsum([1 rep]);
for i=1:numel(v)
    vv(c(i):c(i)+rep(i)-1) = repmat(v(i),1,rep(i));
end

然而我仍然觉得有更聪明的方法来实现这个...谢谢。


5
请参见https://dev59.com/ynI-5IYBdhLWcg3wMFS0。 - Doresoom
1
@Doresoom:我以为我之前回答过这样的问题,但是找不到了。最后我和你一起找到了它。标题和标签非常不同,这让它有点难找。 - gnovice
如果有人在这里寻找numpy的解决方案,请查看https://docs.scipy.org/doc/numpy/reference/generated/numpy.repeat.html。 - Homero Esmeraldo
4个回答

16

这是我喜欢的一种实现方式:

>> index = zeros(1,sum(rep));
>> index(cumsum([1 rep(1:end-1)])) = 1;

index =

     1     0     1     0     0     1     1     0     0     0     0

>> index = cumsum(index)

index =

     1     1     2     2     2     3     4     4     4     4     4

>> vv = v(index)

vv =

     3     3     1     1     1     9     4     4     4     4     4

首先创建一个与最终值计数相同长度的零索引向量。通过对rep向量进行累积求和(去掉最后一个元素并在开头放置1),生成一个指向index的索引向量,显示重复值组将开始的位置。这些点用1标记。对index进行累积求和后,得到一个最终的索引向量,可以用来索引v,从而创建异构重复值向量。


你能添加一些关于这个如何工作的注释吗? - Nathan Fellman
@Nathan:我已经超前你了。=) - gnovice
1
绝对是使用cumsum的聪明方式..谢谢! - merv
1
请注意,此解决方案仅在rep的所有元素均为正数时才有效。如果您不想通过将rep的某些元素设置为零来重复某些元素,则该解决方案将失败。 v = [3 1 9 4]rep = [2 3 1 0] 的结果是 [3 3 1 1 1 9 4],多出了一个元素。 - fdermishin

2

作为可能的解决方案之一,考虑以下方法:

vv = cellfun(@(a,b)repmat(a,1,b), num2cell(v), num2cell(rep), 'UniformOutput',0);
vv = [vv{:}];

这比gnovice的方案要慢得多...


2
你实际上可以使用ARRAYFUN来避免调用NUM2CELL,但速度仍然会慢得多:https://dev59.com/ynI-5IYBdhLWcg3wMFS0#1975835。 - gnovice

0

accumarray 函数可用于使代码在 rep 数组中存在零时正常工作。

function vv = repeatElements(v, rep)
index = accumarray(cumsum(rep)'+1, 1);
vv = v(cumsum(index(1:end-1))+1);
end

这个方法与gnovice的解决方案类似,不同之处在于索引是累加而不是分配给1。这样可以跳过一些索引(如下面示例中的3和6),并从输出中删除相应的元素。

>> v = [3 1 42 9 4 42];
>> rep = [2 3 0 1 5 0];
>> index = accumarray(cumsum(rep)'+1, 1)'

index =

     0     0     1     0     0     2     1     0     0     0     0     2

>> cumsum(index(1:end-1))+1

ans =

     1     1     2     2     2     4     5     5     5     5     5

>> vv = v(cumsum(index(1:end-1))+1)

vv =

     3     3     1     1     1     9     4     4     4     4     4

0
你正在尝试做的是 run-length decode。一个高可靠性/向量化的实用程序是 FEX submission rude()
% example inputs
counts = [2, 3, 1];
values = [24,3,30];

结果

rude(counts, values)
ans =
    24    24     3     3     3    30

请注意,此函数还执行相反的操作,即对向量进行游程编码,换句话说返回和相应的计数

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