Использование accumarray и @min для извлечения min из групп, а также для вывода соответствующих значений из другой переменной / столбца

У меня есть 3 столбца данных:

time     = [1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16];
category = [1;1;1;1;2;2;2;2;3; 3; 3; 3; 4; 4; 4; 4];
data     = [1;1;0;1;2;2;1;2;3; 3; 2; 3; 4; 4; 4; 3];

Я использую следующее, чтобы извлечь минимальные значения данных для каждой категории:

groupmin = accumarray(category,data,[],@min)

Какие выводы:

groupmin = [0;1;2;3]

Тем не менее, я действительно хотел бы иметь вывод, который также говорит мне, с какого момента времени минимумы, например,

timeofgroupmin  = [3;7;11;16]
groupmin        = [0;1; 2; 3]

В качестве альтернативы, я хотел бы получить минимумы в собственном векторе с NaN для любой строки, которая не является минимумом ее группы, например

groupminallrows = [NaN;NaN;0;NaN;NaN;NaN;1;NaN;NaN;NaN;2;NaN;NaN;NaN;NaN;3];

Любой подход решит мою проблему. Как новичок в Matlab, я изо всех сил пытаюсь узнать, какие термины искать.

3 ответа

Решение

Это работает, если все данные одной категории находятся в одном прогоне, а категории отсортированы, как в вашем примере. В каждой категории допускается несколько минимизаторов.

r = accumarray(category,data,[],@(v) {(min(v)==v)});
r = vertcat(r{:});
groupminallrows = NaN(size(data));
groupminallrows(r) = data(r);

Использование accumarray с пользовательской функцией:

time     = [1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16];
category = [1;1;1;1;2;2;2;2;3; 3; 3; 3; 4; 4; 4; 4];
data     = [1;1;0;1;2;2;1;2;3; 3; 2; 3; 4; 4; 4; 3];

groupmin = accumarray( A(:,1), A(:,2), [], @min)

Это то, что у вас есть, но для получения индексов минимумов и их времени вам понадобится второй вывод min функция, которую я не знаю, возможно ли получить при использовании с accmarray. Но есть следующий обходной путь:

groupidx = accumarray( category, data, [], @(x) find(x == min(x) )).'
occ = cumsum(hist(category,unique(category)))
idx = -occ(1)+occ+groupidx;
timeofgroupmin = time(idx).'
groupmin = data(idx).'

groupmin =

     0     1     2     3

timeofgroupmin =

     3     7    11    16

Желаемый NaN -вектор можно получить как:

groupminallrows = NaN(1,numel(data));
groupminallrows(idx) = data(idx)

По поводу вашего комментария:

Я предполагаю, что причина в том, что у вас есть несколько минимумов в каждой группе, то find возвращает массив. Чтобы решить, что вы можете заменить find(x == min(x)) с find(x == min(x),1), Но тогда вы просто получите первое вхождение каждого минимума в каждой группе.

Если это не желательно, я бы сказал, accumarray как правило, неправильный путь.

Попробуйте это решение:

% first we group the data into cell according to the group they belong to
grouped = accumarray(category, data, [], @(x){x});

% find the minimum and corresponding index of each group
[mn,idx] = cellfun(@min, grouped);

% fix index by offsetting the position to point the whole data vector
offset = cumsum([0;cellfun(@numel, grouped)]);
idx = idx + offset(1:end-1);

% result
[mn(:) idx(:)]
assert(isequal(mn, data(idx)))

% build the vector with NaNs
mnAll = nan(size(data));
mnAll(idx) = mn;

Полученные векторы:

>> mn'
ans =
     0     1     2     3
>> idx'
ans =
     3     7    11    16
>> mnAll'
ans =
   NaN   NaN     0   NaN   NaN   NaN     1   NaN   NaN   NaN     2   NaN   NaN   NaN   NaN     3

РЕДАКТИРОВАТЬ:

Вот альтернативное решение:

% find the position of min value in each category
idx = accumarray(category, data, [], @minarg);

% fix position in terms of the whole vector
offset = cumsum([0;accumarray(category,1)]);
idx = idx + offset(1:end-1);

% corresponding min values
mn = data(idx);

Я использую следующую пользовательскую функцию для извлечения второго выходного аргумента из min:

minarg.m

function idx = minarg(X)
    [~,idx] = min(X);
end

Результаты такие же, как указано выше.

Другие вопросы по тегам