Matlab: явная или именованная ассоциация аргументов `splitapply` с таблицей VariableNames - PullRequest
0 голосов
/ 04 мая 2018

Я прикусил лишний код и бухгалтерию, необходимые в Matlab для выполнения обычных операций SQL. Вот пример типичного шаблона кода SQL для генерации метрик, которые суммируют таблицу данных tDat:

SELECT vGrouping, MEAN( x - y ) AS rollup1, VAR(y+z) AS rollup2
INTO tRollups FROM tDat GROUP BY vGrouping

Мой SQL немного ржавый, но общая идея должна быть ясна для SQLers. Вот эквивалент Matlab:

% Create test data
tDat = array2table( floor(10*rand(5,3)) , ...
                    'VariableNames',{'x','y','z'} );
tDat.vGrouping = ( rand(5,1) > 0.5 )

% Calculate summary metrics for each group of data
[vGroup,grps] = findgroups(tDat.vGrouping)
fRollup = @(a,b,c)[ mean(a-b) var(b+c) ] % Calculates summary metric
rollups = splitapply( fRollup, tDat(:,{'x','y','z'}), vGroup )

% Code pattern 1 to assemble results
tRollups = [ array2table( grps , 'VariableNames',{'group'} ) ...
             array2table( rollups , ...
                          'VariableNames',{'rollup1','rollup2'} ) ]

% Code pattern 2 to assemble results
tRollups = array2table( [grps rollups], ...
                        'VariableNames',{'group','rollup1','rollup2'} )

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

За исключением объема кода, одна вещь, которая меня беспокоит, заключается в том, что выражения свертки в fRollup явно не связаны с именами столбцов входных или выходных данных. Аргументы являются фиктивными, а фактические столбцы входных данных из tDat указываются в вызове splitapply. Ассоциация с аргументами fRollup является позиционной, поэтому сами имена полей / переменных не могут обеспечить правильную связь. Аналогично, выходные столбцы в tRollups указываются в вызове array2table, снова позиционно связанного с выводом fRollup.

Это делает довольно простые отношения в операторе SQL очень трудно увидеть в коде Matlab. Есть ли альтернативная модель или идиома дизайна, у которой нет этого недостатка, но, надеюсь, не слишком много других недостатков?

AFTERNOTE: По какой-то причине, хотя следующее не решает именованную / явную связь splitapply аргументов ввода / вывода с фактическими переменными ввода / вывода, я все еще нахожу это легче увидеть отношения. Код определенно выглядит менее шумным. Ключевым моментом является то, что функция fRollup для генерации сводных метрик для данных теперь возвращает несколько выходных данных, а не объединяет их в один выходной массив. Это позволяет мне явно указывать свойства скаляра struct ssRollups в качестве цели назначения. Мне не нужны все виды преобразований в таблицы с дополнительным кодом для обозначения VariableNames, просто для объединения результатов с указанными группами. Вместо этого групповые идентификаторы начинаются с как просто другое свойство grps в тех же struct (ssRollups), что и результаты splitapply - фактически это первое свойство, которое приносит struct в существование.

% File tmp.m
%-----------
function tmp

   % Create test data
   tDat = array2table( floor(10*rand(5,3)) , ...
                       'VariableNames',{'x','y','z'} );
   tDat.vGrouping = ( rand(5,1) > 0.5 )

   % Find the groups
   [ vGroup, ssRollups.grps ] = findgroups(tDat.vGrouping)

   % Calculate summary metrics for each group of data
   [ ssRollups.rollup1 ssRollups.rollup2 ] = ...
      splitapply( @fRollup, tDat(:,{'x','y','z'}), vGroup );

   % Display use nice table formatting
   struct2table( ssRollups )

end % function tmp

function [rollup1 rollup2] = fRollup(a,b,c)
   rollup1 = mean(a-b);
   rollup2 = var(b+c);
end % function fRollup

Однако, как функция с несколькими выходами, fRollup лучше подходит для неанонимной функции. Мне кажется, что на самом деле лучше документировать несколько выходов, несмотря на менее компактный код. Это может быть только одна из тех ситуаций, когда большая компактность менее читаема, из-за чего становится сложнее увидеть взаимосвязи данных. Однако для этого требуется, чтобы весь фрагмент кода был превращен в функцию (tmp в данном случае), если только вы не возражаете разбить fRollup на ее собственную функцию и m-файл. Я предпочитаю не засорять мою файловую систему такими крошечными фрагментами функций, которые должны использоваться в одном месте.

1 Ответ

0 голосов
/ 07 мая 2018

Этот «ответ» не имеет непосредственного отношения к явной именованной связи между фактическими переменными ввода / вывода и аргументами дескриптора функции, предоставленными splitapply. Однако это значительно упрощает код в первоначальном примере, и, надеюсь, сделает его более понятным, чтобы увидеть связь между аргументами функции и переменными ввода / вывода. Это решение изначально было включено в AFTERNOTE в вопросе. Поскольку лучшие ответы не появятся в ближайшее время, я решил включить их в качестве ответа. Он использует deal для реализации анонимной функции множественного вывода для splitapply для использования в группах данных, которые определены его аргументом группировки.

% Create test data
tDat = array2table( floor(10*rand(5,3)) , ...
                    'VariableNames',{'x','y','z'} );
tDat.vGrouping = ( rand(5,1) > 0.5 )

% Find the groups
[ vGroup, ssRollups.grps ] = findgroups(tDat.vGrouping)

% Calculate summary metrics for each group of data
fRollup = @(a,b,c) deal( mean(a-b), var(b+c) )
[ ssRollups.rollup1 ssRollups.rollup2 ] = ...
   splitapply( fRollup, tDat(:,{'x','y','z'}), vGroup );

% Display use nice table formatting
struct2table( ssRollups )

Пока не будет найдено лучшее решение, этот подход будет моей идеей для splitapply.

Вот вариант, который использует табличную переменную для вывода splitapply. Это может быть более удобно при использовании нескольких группирующих переменных, поскольку findgroups будет передавать имена группирующих переменных в выходную переменную tRollups на LHS:

% Create test data
tDat = array2table( floor(10*rand(8,3)) , ...
                    'VariableNames',{'x','y','z'} );
tDat = [ tDat ...
         array2table( rand(8,2)>0.5 , ...
                      'VariableNames',{'vGrpng1','vGrpng2'} ) ];

% Find the groups
[ vGroup, tRollups ] = findgroups(tDat(:,{'vGrpng1','vGrpng2'}));

% Calculate summary metrics for each group of data
fRollup = @(a,b,c) deal( mean(a-b), var(b+c) )
[ tRollups.rollup1 tRollups.rollup2 ] = ...
   splitapply( fRollup, tDat(:,{'x','y','z'}), vGroup );

tRollups

А вот версия, которая использует несколько группирующих переменных и использует скалярную структуру вместо таблицы для выходных данных findgroup и splitapply:

% Create test data
tDat = array2table( floor(10*rand(8,3)) , ...
                    'VariableNames',{'x','y','z'} );
tDat.vGrpng1 = rand(8,1)>0.5 ;
tDat.vGrpng2 = rand(8,1)>0.5

% Find the groups
[ vGroup, ssRollups.vGrpng1, ssRollups.vGrpng2 ] = ...
    findgroups( tDat.vGrpng1, tDat.vGrpng2 );

% Calculate summary metrics for each group of data
fRollup = @(a,b,c) deal( mean(a-b), var(b+c) )
[ ssRollups.rollup1 ssRollups.rollup2 ] = ...
   splitapply( fRollup, tDat(:,{'x','y','z'}), vGroup );

% Display using nice table formatting
struct2table( ssRollups )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...