Поэлементная репликация массива по подсчету

Мой вопрос похож на этот один, но я хотел бы воспроизвести каждый элемент согласно счетчику, указанному во втором массиве того же размера.

Пример этого, скажем, у меня есть массив v = [3 1 9 4], я хочу использовать rep = [2 3 1 5] для репликации первого элемента 2 раза, второго три раза и так далее, чтобы получить [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

Однако я все еще чувствую, что должен быть более умный способ сделать это ... Спасибо


person merv    schedule 04.03.2010    source источник
comment
см. stackoverflow.com/questions/1975772/matlab-array-manipulation   -  person Doresoom    schedule 04.03.2010
comment
@Doresoom: Я думал, что уже отвечал на подобный вопрос раньше, но не смог его найти. Я наконец выследил его одновременно с тобой. Заголовок и теги были совершенно разными, поэтому их было сложно найти.   -  person gnovice    schedule 04.03.2010
comment
если кто-то приходит сюда в поисках решения для numpy, проверьте docs.scipy .org / doc / numpy / reference / created / numpy.repeat.html   -  person Homero Esmeraldo    schedule 23.05.2019


Ответы (4)


Вот один из способов, которым мне нравится это делать:

>> 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 с удаленным последним элементом и единицей, помещенной в начало, я получаю вектор индексов в index, показывающий, где будут начинаться группы реплицированных значений. Эти точки отмечены единицами. Когда кумулятивная сумма выполняется для index, я получаю окончательный вектор индекса, который я могу использовать для индексации в v для создания вектора гетерогенно реплицированных значений.

person gnovice    schedule 04.03.2010
comment
не могли бы вы добавить несколько комментариев о том, как это работает? - person Nathan Fellman; 04.03.2010
comment
@ Натан: Уже впереди тебя. знак равно - person gnovice; 04.03.2010
comment
определенно умный способ использования cumsum .. Спасибо! - person merv; 04.03.2010
comment
Будьте осторожны, это решение работает, только если все элементы rep положительны. Если вы не хотите повторять некоторые элементы, установив некоторые элементы rep на нули, это не удастся. v = [3 1 9 4] и rep = [2 3 1 0] приводят к [3 3 1 1 1 9 4], давая дополнительный элемент. - person fdermishin; 17.10.2014

Чтобы добавить к списку возможных решений, рассмотрим это:

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

Это намного медленнее, чем у gnovice < / а> ..

person Amro    schedule 04.03.2010

Вы пытаетесь декодировать длину серий. Надежная / векторизованная утилита высокого уровня - это FEX submission rude():

% example inputs
counts = [2, 3, 1];
values = [24,3,30];

результат

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

Обратите внимание, что эта функция также выполняет противоположную операцию, то есть кодирует по длине серии вектор или, другими словами, возвращает values и соответствующий counts.

person Oleg    schedule 21.08.2013

Функция 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
person fdermishin    schedule 17.10.2014