Skip to content

Commit

Permalink
Fix getRow function for dynamic table if row data is an array where n…
Browse files Browse the repository at this point in the history
…dims is 3 or higher (#590)

* Permute nd arrays to get correct shape for table

* Update getRow.m

Ensure fix also works if a dynamic table is created raw by raw using the addRow method

* Add test for previous failure point
  • Loading branch information
ehennestad authored Sep 12, 2024
1 parent fd34d3d commit 4b280cb
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 3 deletions.
28 changes: 28 additions & 0 deletions +tests/+system/DynamicTableTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,34 @@ function toTableTest(testCase)
);
end
end

function toTableNdArrayTest(testCase)
% toTableNdArrayTest Test to table for a plane segmentation table

% Generate fake image_mask data
imaging_shape = [100, 100];
x = imaging_shape(1);
y = imaging_shape(2);

n_rois = 20;
image_mask = zeros(y, x, n_rois);
for i = 1:n_rois
start = randi(90,2,1);
image_mask(start(1):start(1)+10, start(2):start(2)+10, 1) = 1;
end

% add data to NWB structures
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'image_mask'}, ...
'description', 'output from segmenting my favorite imaging plane', ...
'id', types.hdmf_common.ElementIdentifiers('data', int64(0:19)'), ...
'image_mask', types.hdmf_common.VectorData('data', image_mask, 'description', 'image masks') ...
);

T = plane_segmentation.toTable();
testCase.verifyClass(T, 'table');
testCase.verifySize(T, [n_rois, 2]); % 2 columns, id and image_mask
end

function DynamicTableCheckTest(testCase)
% Verify that the checkConfig utility function
Expand Down
42 changes: 39 additions & 3 deletions +types/+util/+dynamictable/getRow.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,39 @@

row{i} = select(DynamicTable, indexNames, ind);

% transpose row vectors
if ~istable(row{i}) && isrow(row{i})
row{i} = row{i} .';
if ~istable(row{i})
% transpose row vectors
if isrow(row{i})
row{i} = row{i} .';
% or permute arrays to place last dimension first
elseif ~ismatrix(row{i}) % i.e nd array where n >= 3
array_size = size(row{i});
num_rows = numel(ind);

is_row_dim = array_size == num_rows;
if sum(is_row_dim) == 1
if ~(is_row_dim(1) || is_row_dim(end))
throw( InvalidVectorDataShapeError(cn) )
end
elseif sum(is_row_dim) > 1
if is_row_dim(1) && is_row_dim(end)
% Last dimension takes precedence
is_row_dim(1:end-1) = false;
warning(...
['The length of the first and last dimensions of ', ...
'VectorData for column "%s" match the number of ', ...
'rows in the dynamic table. Data is rearranged based on ', ...
'the last dimension, assuming it corresponds with the table rows.'], cn)
elseif is_row_dim(1)
is_row_dim(2:end) = false;
elseif is_row_dim(end)
is_row_dim(1:end-1) = false;
else
throw( InvalidVectorDataShapeError(cn) )
end
end
row{i} = permute( row{i}, [find(is_row_dim), find(~is_row_dim)]);
end
end

% cell-wrap single multidimensional matrices to prevent invalid
Expand Down Expand Up @@ -186,4 +216,10 @@
[idMatch, ind] = ismember(id, ids);
assert(all(idMatch), 'NWB:DynamicTable:GetRow:InvalidId',...
'Invalid ids found. If you wish to use row indices directly, remove the `useId` flag.');
end

function ME = InvalidVectorDataShapeError(column_name)
ME = MException('NWB:DynamicTable:InvalidVectorDataShape', ...
sprintf( ['Array data for column "%s" has a shape which do ', ...
'not match the number of rows in the dynamic table.'], column_name ));
end

0 comments on commit 4b280cb

Please sign in to comment.