I am trying to plot a data table using a function where each series is taken as a set of numbers of a certain criteria, as follows:
function plotter_fun(Table)
aList = unique(Table.a);
bList = unique(Table.b);
cList = unique(Table.c);
for a = aList
for b = bList
for c = cList
Slice = Table(Table.a==a & Table.b==b & Table.c==c, :);
plot(Slice.x, Slice.y);
end
end
end
end
I want to make this so that the table header parameters ('a', 'b', 'c') can be passed in as an argument, and can be any number of parameters. The desired result would be something like:
function plotter_fun(Table, headerNames)
for i = 1:numel(headerNames)
loopList{i} = unique(Table.(headerNames{i}));
end
% do nested looping
end
Where it is called like:
plotter_fun(Table, {'a', 'b', 'c'});
I'm not sure how to have a recursive looping algorithm, such that the number of nested loops can be changed dynamically?
Looks like each slice is a unique combination of your header variables' values. With this in mind, we can use findgroups
and unique
to eliminate the loops entirely. Then the "dynamic nesting" situation isn't a problem...
This function works as you describe:
function plotter_fun(Table, headerNames)
% Create a matrix of group indices for each specified header
grps = zeros(size(Table,1), numel(headerNames));
for i = 1:numel(headerNames)
grps(:,i) = findgroups(Table.(headerNames{i}));
end
% Get the unique rows for the grouping variables
[~, ~, idx] = unique(grps, 'rows');
% Loop over each unique row index and slice out the specified rows.
for i = 1:max(idx)
Slice = Table( idx == i, : );
plot( Slice.x, Slice.y );
end
end
Test (this works when the plot
line is removed, since I haven't specified x
or y
columns):
tbl = cell2table( {'a', 1, 'dog';
'a', 2, 'cat';
'b', 3, 'cat';
'a', 2, 'cat'}, ...
'variablenames', {'char','num','pet'} )
plotter_fun( tbl, {'char', 'num', 'pet'} ) % output slices are rows [1], [2,4] and [3].
plotter_fun( tbl, {'char'} ) % output slices are rows [1,2,4] and [3].
Edit:
Here is a flexible way to auto-generate the "filter" label from the Slice
. We can concatenate =
between the headerNames
and table values (converted using num2str
in case there are numeric values), and then use strjoin
to generate our label separated by ,
for each heading.
The one liner looks like this, and would be used within the loop where Slice
is defined:
label = strjoin( strcat( headerNames', '=', ...
cellfun(@num2str,table2cell(Slice(1,headerNames)),'uni',0)' ), ', ');
% Output for Slice 1 of example 1 above: 'char=a, num=1, pet=dog'
% Output for Slice 1 of example 2 above: 'char=a'
% cellfun(@num2str, __ ) conversion not needed if there is no numeric data in the table