Как да приложа функция за агрегиране към групи от редове на таблица (или набор от данни)?

Да предположим, че имам някои table (или < href="http://www.mathworks.com/help/stats/dataset.html" rel="nofollow">dataset) обект A, който включва някакъв фактор X (имащ n отделни стойности или „нива“) сред своите променливи. Да предположим също, че имам някаква персонализирана функция за агрегиране agg, която приема като вход поредица от редове и връща едно обобщено число (известно още като "агрегат") като изход.

Пример за изключително често срещана операция в статистическия анализ на данни би било да се групират редовете на A според стойността на фактора X и да се приложи agg към масивите, състоящи се от редовете в групата. Резултатът от такава операция трябва да бъде нов table (или dataset, в зависимост от класа на A), с n редове и две променливи. Първата от тези променливи трябва да бъде наречена X и трябва да съдържа n отделните стойности на фактора X в A, а втората трябва да има някакво подходящо (за предпочитане зададено от потребителя) име и трябва да съдържа резултатът от прилагането на agg към групите редове за съответните X нива.

Както казах, това е много стандартна операция за изпълнение на структури от данни като обектите table и dataset на MATLAB, така че очаквах да има вграден начин да го направя, но не го намирам.


Например, нека A е както е дефинирано по-долу:

% "data" table
A = cell2table({
                'even', 'red', 'spades', 38, 0.9837;
                'even', 'red', 'hearts', 19, 0.5695;
                'even', 'red', 'diamonds', 89, 0.2629;
                'even', 'red', 'diamonds', 98, 0.3578;
                'even', 'red', 'diamonds', 92, 0.2596;
                'even', 'red', 'diamonds', 69, 0.5751;
                'even', 'red', 'diamonds', 77, 0.6318;
                'even', 'yellow', 'clubs', 22, 0.6917;
                'even', 'green', 'spades', 35, 0.6674;
                'even', 'green', 'hearts', 67, 0.7896;
                'even', 'green', 'hearts', 49, 0.5025;
                'even', 'green', 'hearts', 64, 0.5318;
                'odd', 'red', 'spades', 22, 0.5587;
                'odd', 'red', 'hearts', 51, 0.9122;
                'odd', 'red', 'diamonds', 74, 0.3343;
                'odd', 'red', 'diamonds', 69, 0.2911;
                'odd', 'yellow', 'spades', 33, 0.2653;
                'odd', 'yellow', 'spades', 38, 0.2549;
                'odd', 'yellow', 'diamonds', 1, 0.2064;
                'odd', 'yellow', 'diamonds', 25, 0.8257;
                'odd', 'green', 'spades', 64, 0.4348;
                'odd', 'green', 'hearts', 59, 0.8644;
                'odd', 'green', 'hearts', 4, 0.6374;
                'odd', 'green', 'hearts', 11, 0.3354
               }, 'VariableNames', ...
               {'Parity', 'TrafficLight', 'Suit', 'order', 'prevalence'});

Освен това нека X е TrafficLight и agg е

agg = @(t) size(t, 1);

(Разбира се, използвам това agg тук, само за да запазя примера възможно най-опростен. На практика agg ще бъде много по-малко простодушен.)

Типичната group_aggregate функция, за която мисля, обикновено приема като входни аргументи (в някакъв ред) агрегираща функция, името на колоната в изхода за изчислените агрегати, table (или dataset) и имената на един или повече променливи, по които да групирате. Следователно в този пример извикването на такава функция и нейният изход ще изглеждат по следния начин

>> group_aggregate(agg, 'nrows', A, {'TrafficLight'})

ans =

    TrafficLight    nrows
    ____________    _____

    'green'          8   
    'red'           11   
    'yellow'         5   

Между другото, за да получа резултата по-горе, измислих този отчаян малък звяр:

>> tmp = cellfun(@(s) {s agg(A(strcmp(A.TrafficLight, s), :))}, ...
unique(A.TrafficLight), 'un', 0);
>> cell2table(cat(1, tmp{:}), 'VariableNames', {'TrafficLight' 'nrows'})

Надявам се, че вграденото решение е по-стабилно за, например, различни класове стойности за променливата X и т.н.


person kjo    schedule 07.02.2014    source източник
comment
Но това е толкова прекрасен малък звяр!   -  person chappjc    schedule 07.02.2014


Отговори (2)


Не знам за table метод само за тази цел, но за агрегиране вижте accumarray:

>> [lights,ia,ic]=unique(A.TrafficLight);
>> nrows = accumarray(ic,1);
>> cell2table([lights num2cell(nrows)],'VariableNames', {'TrafficLight' 'nrows'})
ans = 
    TrafficLight    nrows
    ____________    _____
    'green'          8   
    'red'           11   
    'yellow'         5   
person chappjc    schedule 07.02.2014
comment
Както се оказа, за съжаление, accumarray е сериозен бъг. - person kjo; 11.02.2014
comment
@kjo Любопитно. Можете ли да разширите? Едно нещо, което трябва да се отбележи, е, че първият вход трябва да бъде сортиран. Просто трябва да се уверите, че входът е сортиран, за да сте сигурни. - person chappjc; 11.02.2014
comment
Да, в коментара си направих връзка към публикация, която току-що изпратих на SO, с всички кървави подробности. - person kjo; 11.02.2014

Знам, че въпросът е от 1 година...

Използвам различен начин, този accumarray. Използвам таблица http://www.mathworks.com/help/stats/tabulate.html?s_tid=gn_loc_drop

Освен това все още имам по-стара версия на Matlab и затова използвам набор от данни. В този случай (но мисля, че може да работи и за „таблица“), вашият код ще бъде:

TrafficLight_stats=tabulate(A.TrafficLight);
Agg_table=mat2dataset(TrafficLight_stats,'VarNames',{'TrafficLight','nrows','Perc'});

Agg_table = 

TrafficLight    nrows       Perc         
'red'           [11]        [45.8333]
'yellow'        [ 5]        [20.8333]
'green'         [ 8]        [33.3333]
person Nico    schedule 25.02.2015