Как накапливать (усредненные) данные на основе нескольких критериев

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

Исходная таблица

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

Желаемый результат

Это то, что я знаю, как сделать в основном в Excel (т.е. используя сводную таблицу), но я не уверен, как сделать то же самое в MATLAB. Я пытался использовать accumarray, но мне сложно понять, как по существу включить несколько условий. Мне нужно было бы создать массив subs, где его номер соответствует каждому уникальному набору из 3 точек данных. Путем грубой силы я мог бы создать такой массив, как:

subs = [1 1 1; 2 2 2; 3 3 3; 4 4 4; ...]'

используя некоторый цикл и иметь его в качестве моего массива сабвуферов, но поскольку он не привязан к самим данным, и могут быть странные сбои повсюду (например, более 3 точек данных на набор или отсутствующие данные и т. д.). Я знаю, что должен быть какой-то способ группировать что-то вроде сводной таблицы для чего-то подобного, но мне нужна помощь, чтобы сдвинуть его с мертвой точки. Спасибо.

Вот входные данные в текстовом виде:

Subject  Flow   On/Off   Values
1        10     1        2.20
1        10     1        2.50
1        10     1        2.60
1        20     1        5.50
1        20     1        6.10
1        20     1        5.90
1        30     1        10.10
1        30     1        10.50
1        30     1        10.50
1        10     0        1.90
1        10     0        2.20
1        10     0        2.30
1        20     0        5.20
1        20     0        5.80
1        20     0        5.60
1        30     0        9.80
1        30     0        10.20
1        30     0        10.20
2        10     1        5.70
2        10     1        6.00
2        10     1        6.10
2        20     1        9.00
2        20     1        9.60
2        20     1        9.40
2        30     1        13.60
2        30     1        14.00
2        30     1        14.00
2        10     0        5.40
2        10     0        5.70
2        10     0        5.80
2        20     0        8.70
2        20     0        9.30
2        20     0        9.10
2        30     0        13.30
2        30     0        13.70
2        30     0        13.70

person teepee    schedule 16.05.2017    source источник
comment
Не могли бы вы также вставить входные данные в виде текста? Кроме того, как вы, например, выводите 2.13? Какие числа там усредняются?   -  person Luis Mendo    schedule 17.05.2017
comment
Привет @LuisMendo, спасибо, что рассмотрели этот вопрос. Я только что загрузил текстовые данные.   -  person teepee    schedule 17.05.2017


Ответы (4)


я предполагаю, что

  • Вы хотите усреднять на основе уникальных значений первых трех столбцов (а не групп из трех строк, хотя в вашем примере два критерия совпадают);
  • Порядок определяется столбцом 1, затем 3, затем 2.

Затем, обозначив ваши данные как x,

[~, ~, subs] = unique(x(:, [1 3 2]), 'rows', 'sorted');
result = accumarray(subs, x(:,end), [], @mean);

дает

result =
    2.1333
    5.5333
   10.0667
    2.4333
    5.8333
   10.3667
    5.6333
    9.0333
   13.5667
    5.9333
    9.3333
   13.8667

Как видите, я использую третий вывод unique с 'rows' и 'sorted' варианты. Это создает вектор группировки subs на основе первых трех столбцов ваших данных в нужном порядке. Затем, передав это в accumarray, вычисляются средние значения.

person Luis Mendo    schedule 16.05.2017
comment
Спасибо за ваш ответ! В чем преимущество использования типа 'sorted' по сравнению с использованием 'stable'? - person teepee; 17.05.2017
comment
Без 'sorted' вы не получите результаты в том же порядке, который вы указали в своем вопросе. - person Luis Mendo; 17.05.2017
comment
Ах, хорошо, большое спасибо; очень четкий и лаконичный способ сделать это. Спасибо! - person teepee; 17.05.2017
comment
Мне также интересно, @LuisMendo, если бы я включил еще один столбец - например, значения дат - как я мог бы выполнить ту же процедуру, учитывая, что я не могу объединить столбец дат в массив с остальными данными? - person teepee; 17.05.2017

Вы можете использовать unique и accumarray так, чтобы сохранить порядок ваших строк данных:

[newData, ~, subs] = unique(data(:, 1:3), 'rows', 'stable');
newData(:, 4) = accumarray(subs, data(:, 4), [], @mean);

newData =

    1.0000   10.0000    1.0000    2.4333
    1.0000   20.0000    1.0000    5.8333
    1.0000   30.0000    1.0000   10.3667
    1.0000   10.0000         0    2.1333
    1.0000   20.0000         0    5.5333
    1.0000   30.0000         0   10.0667
    2.0000   10.0000    1.0000    5.9333
    2.0000   20.0000    1.0000    9.3333
    2.0000   30.0000    1.0000   13.8667
    2.0000   10.0000         0    5.6333
    2.0000   20.0000         0    9.0333
    2.0000   30.0000         0   13.5667
person gnovice    schedule 16.05.2017
comment
Спасибо @gnovice за вашу помощь; это действительно здорово. Мне также интересно: знаете ли вы, что можно было бы сделать, если бы один из столбцов данных был другого типа (например, ячейки даты) и, следовательно, не мог быть объединен в массив data, как в этом примере? - person teepee; 17.05.2017
comment
@teepee: если у вас есть дата, вы можете преобразовать ее в числовое значение, используя datenum. Затем вы можете добавить его как еще один столбец в data. - person gnovice; 17.05.2017
comment
Ах, хорошо, это способ сделать это. Отлично, большое спасибо. Я уже применил обходной путь, скомпилировав данные в table, так как unique() принимает этот тип и может выдать нужный мне subs. Есть ли какой-либо особый недостаток в этом подходе в качестве альтернативы? - person teepee; 17.05.2017
comment
@teepee: Нет, таблицы — это хороший способ собирать и отображать различные типы данных. - person gnovice; 17.05.2017

accumarray действительно правильный путь. Во-первых, вам нужно присвоить индекс каждому набору значений с помощью unique :

[unique_subjects, ~, ind_subjects] = unique(vect_subjects);
[unique_flows, ~, ind_flows] = unique(vect_flows);
[unique_on_off, ~, ind_on_off] = unique(vect_on_off);

Таким образом, теперь у вас есть ind_subjects, ind_flows и ind_on_off, которые являются значениями в [1..2], [1..3] и [1..2].

Теперь вы можете вычислить средние значения в массиве [3x2x2] (в вашем примере):

mean_values = accumarray([ind_flows, ind_on_off, ind_subjects], vect_values, [], @mean);
mean_values = mean_values(:);

Примечание : порядок устанавливается в соответствии с вашим примером.

Затем вы можете построить резюме:

[ind1, ind2, ind3] = ndgrid(1:numel(unique_flows), 1:numel(unique_on_off), 1:numel(unique_subjects));
flows_summary = unique_flows(ind1(:));
on_off_summary = unique_on_off(ind2(:));
subjects_summary = unique_subjects(ind3(:));

Примечание . Также работает с нечисловыми значениями.

person G.J    schedule 16.05.2017

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

 >> T = array2table(data, 'VariableNames', { 'Subject', 'Flow', 'On_Off', 'Values'});
 >> [gid,Tgrp] = findgroups(T(:,1:3));
 >> Tgrp.MeanValue = splitapply(@mean, T(:,4), gid)
 Tgrp =
   12×4 table
     Subject    Flow    On_Off    MeanValue
     _______    ____    ______    _________
     1          10      0         2.1333   
     1          10      1         2.4333   
     1          20      0         5.5333   
     1          20      1         5.8333   
     1          30      0         10.067   
     1          30      1         10.367   
     2          10      0         5.6333   
     2          10      1         5.9333   
     2          20      0         9.0333   
     2          20      1         9.3333   
     2          30      0         13.567   
     2          30      1         13.867   
person CKT    schedule 17.05.2017