Я прикусил лишний код и бухгалтерию, необходимые в 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-файл. Я предпочитаю не засорять мою файловую систему такими крошечными фрагментами функций, которые должны использоваться в одном месте.