Использование 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, я изо всех сил пытаюсь понять, какие термины искать.


person Jo.    schedule 05.07.2014    source источник
comment
Всем спасибо - еще изучаю систему. И предложение Луиса Мендо, и предложение Амро работают, но первое кажется проще.   -  person Jo.    schedule 08.07.2014


Ответы (3)


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

r = accumarray(category,data,[],@(v) {(min(v)==v)});
r = vertcat(r{:});
groupminallrows = NaN(size(data));
groupminallrows(r) = data(r);
person Luis Mendo    schedule 05.07.2014
comment
Вау, это достигает того, что мне нужно, всего за 4 строки - теперь нужно понять, что он на самом деле делает! Благодарю вас! - person Jo.; 05.07.2014
comment
@Jo Посмотрите промежуточные результаты и спросите меня, если вам нужно. Ключом является анонимная функция внутри accumarray, которая возвращает ячейку, содержащую вектор - person Luis Mendo; 05.07.2014

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

% 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:

минарг.м

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

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

person Amro    schedule 05.07.2014
comment
Спасибо, Амро, кажется, это очень хорошо работает с моим фактическим набором данных (я все еще читаю, чтобы понять, что на самом деле делают команды!). Однако команда утверждения каждый раз сообщает об ошибке утверждения. Должен ли я беспокоиться? Данные выглядят правильно при построении графика и отображаются в нужном месте... - person Jo.; 05.07.2014
comment
как я понял, у вас много значений NaN в данных, поэтому попробуйте заменить isequal выше на isequaln (потому что, если категория содержит только значения NaN, тогда минимальное значение также равно NaN, и, как вы, возможно, знаете, мы не можем сравнивать NaN на равенство , вам нужен специальный isequaln или более старый isequalwithequalnans) - person Amro; 05.07.2014
comment
Большой! Сообщение об ошибке исчезло с isequalwithnans - кажется, у меня старая версия Matlab. Большое вам спасибо за вашу помощь! - person Jo.; 05.07.2014
comment
Рад, что смог помочь. Я должен был упомянуть одну вещь: category должен быть отсортирован перед вызовом accumarray, в противном случае вы должны явно сделать это самостоятельно (не забудьте упорядочить соответствующие значения data, чтобы они совпадали). Это потому, что мы зависим от детерминированного порядка. Вы можете прочитать об этой детали здесь: stackoverflow.com/questions/17536558/ - person Amro; 05.07.2014

Используйте 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, который я не знаю, возможно ли получить при использовании с accumarray. Но есть следующий обходной путь:

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, как правило, неправильный путь.

person thewaywewalk    schedule 05.07.2014
comment
Спасибо! Это очень хорошо работает с тестовыми данными, которые я предоставил для простоты, и небольшими фрагментами моего фактического набора данных, но когда я пробую это со всеми 820 000 строк моих фактических данных, команда groupidx = accumarray( category, data, [], @(x ) найти (x == min(x) )).' возвращает сообщение об ошибке: Error using accumarray Функция '@(x)find(x==min(x))' вернула нескалярное значение. Я думаю, это потому, что у меня есть некоторые NaN в моих данных. Любые идеи? - person Jo.; 05.07.2014
comment
Спасибо. Я все еще получаю возвращенную ошибку нескалярного значения даже с find(x == min(x),1) для моего фактического набора данных... Пока что код Амро, кажется, работает для меня... Большое спасибо для вашей помощи! - person Jo.; 05.07.2014