Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor dynamic table #612

Draft
wants to merge 36 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9954644
add test python requirements
lawrence-mbf Sep 8, 2023
de7671a
migrate azure pipeline to github actions
lawrence-mbf Sep 8, 2023
39f6acd
Fix test workflow syntax
lawrence-mbf Sep 8, 2023
2089bac
attempt to fix python setup
lawrence-mbf Sep 8, 2023
c0806bb
switch run-test to run-command
lawrence-mbf Sep 8, 2023
b516aac
try to upload to codecov
lawrence-mbf Sep 8, 2023
45fac03
fix matlab syntax error
lawrence-mbf Sep 8, 2023
83e4cdd
retab functions
lawrence-mbf Sep 8, 2023
4ccde42
Refactor add column function pull out functions
lawrence-mbf Sep 8, 2023
cffd360
Fix CI testing
lawrence-mbf Sep 8, 2023
ef4832c
Refactor addTableColumn
lawrence-mbf Sep 8, 2023
28d9bf3
Fix addColumn usage in tests and tutorials
lawrence-mbf Sep 8, 2023
84ba20b
update error id chekcing
lawrence-mbf Sep 8, 2023
7f92bbe
check ci env setting
lawrence-mbf Sep 8, 2023
4a0f268
fix debug pipeline
lawrence-mbf Sep 8, 2023
fbe0ffc
more env fixing
lawrence-mbf Sep 8, 2023
51d194d
fix expected error test id
lawrence-mbf Sep 8, 2023
c3fbce7
remove debug prints from ci
lawrence-mbf Sep 8, 2023
e1d091a
add source files to download repo
lawrence-mbf Sep 8, 2023
f43c703
try different test suite
lawrence-mbf Sep 8, 2023
1923456
try different test suite (again)
lawrence-mbf Sep 8, 2023
ddac00c
Merge branch 'master' into convert-ci-to-actions
ehennestad Sep 30, 2024
5cfc738
Try using MATLAB R2024a for tests in workflow
ehennestad Sep 30, 2024
dd60ea2
Give test workflow a shorter name
ehennestad Sep 30, 2024
6d3d5d7
Add codecov settings file
ehennestad Sep 30, 2024
d4b9f94
Merge branch 'master' into convert-ci-to-actions
bendichter Sep 30, 2024
23c3933
Merge branch 'convert-ci-to-actions' of https://github.com/NeurodataW…
ehennestad Sep 30, 2024
026b5ad
Update tests.yml
ehennestad Sep 30, 2024
9365c27
Merge master into branch
ehennestad Nov 1, 2024
b4421e5
Merge branch 'add-code-section-read-remote-tutorial' into convert-ci-…
ehennestad Nov 1, 2024
6ba4cc8
Update versions of dependent actions to latest versions
ehennestad Nov 1, 2024
51f68be
Merge branch 'master' into convert-ci-to-actions
ehennestad Nov 1, 2024
3a5c9ea
Update test badge in README.md
ehennestad Nov 1, 2024
7ad1642
Merge branch 'convert-ci-to-actions' of https://github.com/NeurodataW…
ehennestad Nov 1, 2024
80e3247
Restore/reset files related to converting ci to github actions
ehennestad Nov 2, 2024
07b746d
Delete requirements.txt
ehennestad Nov 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion +tests/+sanity/GenerationTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function dynamicTableMethodsTest(testCase)
'VariableNames', {'id', 'start_time', 'stop_time', 'randomvalues', 'stringdata'});
% verify error is thrown when addRow input is MATLAB table
testCase.verifyError(@() TimeIntervals.addRow(t), ...
"NWB:DynamicTable" ...
'NWB:DynamicTable:AddRow:TableIsUnsupported' ...
);

retrievalIndex = round(1 + 4 .* rand(10, 1));
Expand Down
38 changes: 16 additions & 22 deletions +tests/+system/DynamicTableTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -105,25 +105,22 @@ function addContainerUndefinedIDs(~, file)
c = file.intervals_trials.vectordata.get('randomvalues');
end

function appendContainer(testCase, file)
container = testCase.getContainer(file);
function appendContainer(testCase, File)
container = testCase.getContainer(File);
container.data = rand(size(container.data)); % new random values.
file.intervals_trials.vectordata.get('stringdata').data = repmat({'FALSE'}, 20, 1);
%test adding new column with argument
file.intervals_trials.addColumn( ...
'newcolumn', types.hdmf_common.VectorData( ...
'description', 'newly added column', ...
'data', (20:-1:1) .' ...
) ...
);
File.intervals_trials.vectordata.get('stringdata').data = repmat({'FALSE'}, 20, 1);
% test adding new column with argument

Column = types.hdmf_common.VectorData( ...
'description', 'newly added column' ...
, 'data', (20:-1:1) .');
File.intervals_trials.addColumn('newcolumn', Column);
% verify error is thrown when addRow input is MATLAB table
t = table( ...
(1:2:40)', (1:4:80)' , ...
'VariableNames', {'newcolumn2', 'newcolumn3'} ...
);
testCase.verifyError(@() file.intervals_trials.addColumn(t), ...
"NWB:DynamicTable" ...
);
Table = table( ...
(1:2:40)', (1:4:80)' ...
, 'VariableNames', {'newcolumn2', 'newcolumn3'});
testCase.verifyError(@()File.intervals_trials.addColumn(Table) ...
, 'NWB:DynamicTable:AddColumn:InvalidArgument');
end

function appendRaggedContainer(~, file)
Expand All @@ -138,12 +135,9 @@ function appendRaggedContainer(~, file)
startInd = endInd+1;
end
% get corresponding VectorData and VectorIndex
[rag_col, rag_col_index] = util.create_indexed_column(dataArray);
[Column, Index] = util.create_indexed_column(dataArray);
% append ragged column
file.intervals_trials.addColumn( ...
'newraggedcolumn',rag_col, ...
'newraggedcolumn_index',rag_col_index ...
)
file.intervals_trials.addColumn('newraggedcolumn', Column, Index);
end
end

Expand Down
40 changes: 19 additions & 21 deletions +tests/+unit/dataPipeTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,61 +64,59 @@ function testInit(testCase)
function testIndex(testCase)
filename = 'testIndexing.h5';
name = '/test_data';

data = rand(100, 100, 100);
Pipe = types.untyped.DataPipe('data', data);

testCase.verifyEqual(Pipe(:), data(:));
testCase.verifyEqual(Pipe(:,:,1), data(:,:,1));

fid = H5F.create(filename);
Pipe.export(fid, name, {}); % bind the pipe.
H5F.close(fid);

testCase.verifyEqual(Pipe(:), data(:));
testCase.verifyEqual(Pipe(:,:,1), data(:,:,1));
end

function testAppend(testCase)
filename = 'testIterativeWrite.h5';

Pipe = types.untyped.DataPipe(...
'maxSize', [10 13 15],...
'axis', 3,...
'chunkSize', [10 13 1],...
'dataType', 'uint8',...
'compressionLevel', 5);

OneDimensionPipe = types.untyped.DataPipe('maxSize', Inf, 'data', [7, 8, 9]);

%% create test file
fid = H5F.create(filename);

initialData = createData(Pipe.dataType, [10 13 10]);
Pipe.internal.data = initialData;
Pipe.export(fid, '/test_data', {}); % bind
OneDimensionPipe.export(fid, '/test_one_dim_data', {});

H5F.close(fid);

%% append data
totalLength = 3;
appendData = zeros([10 13 totalLength], Pipe.dataType);
for i = 1:totalLength
appendData(:,:,i) = createData(Pipe.dataType, Pipe.chunkSize);
Pipe.append(appendData(:,:,i));
end

for i = 1:totalLength
OneDimensionPipe.append(rand());
end

%% verify data
Pipe = types.untyped.DataPipe('filename', filename, 'path', '/test_data');
readData = Pipe.load();
testCase.verifyEqual(readData(:,:,1:10), initialData);
testCase.verifyEqual(readData(:,:,11:end), appendData);

OneDimensionPipe = types.untyped.DataPipe('filename', filename, 'path', '/test_one_dim_data');
readData = OneDimensionPipe.load();
testCase.verifyTrue(isvector(readData));
Expand All @@ -130,30 +128,30 @@ function testExternalFilters(testCase)
import types.untyped.datapipe.dynamic.Filter;
import types.untyped.datapipe.properties.DynamicFilter;
import types.untyped.datapipe.properties.Shuffle;

testCase.assumeTrue(logical(H5Z.filter_avail(uint32(Filter.LZ4))));

filename = 'testExternalWrite.h5';

Pipe = types.untyped.DataPipe(...
'maxSize', [10 13 15],...
'axis', 3,...
'chunkSize', [10 13 1],...
'dataType', 'uint8',...
'filters', [Shuffle() DynamicFilter(Filter.LZ4)]);

OneDimensionPipe = types.untyped.DataPipe('maxSize', Inf, 'data', [7, 8, 9]);

%% create test file
fid = H5F.create(filename);

initialData = createData(Pipe.dataType, [10 13 10]);
Pipe.internal.data = initialData;
Pipe.export(fid, '/test_data', {}); % bind
OneDimensionPipe.export(fid, '/test_one_dim_data', {});

H5F.close(fid);

%% append data
totalLength = 3;
appendData = zeros([10 13 totalLength], Pipe.dataType);
Expand Down
66 changes: 34 additions & 32 deletions +types/+util/+dynamictable/addColumn.m
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
function addColumn(DynamicTable, varargin)
% ADDCOLUMN Given a dynamic table and a set of keyword arguments for one or
% more columns, add one or more columns to the dynamic table by providing
% either keywords or a MATLAB table
%
% ADDCOLUMN(DT,TABLE) append the columns of the MATLAB Table TABLE to the
% DynamicTable
%
% ADDCOLUMN(DT,col_name1,col_vector1,...,col_namen,col_vectorn)
% append specified column names and VectorData to table
%
% This function asserts the following:
% 1) DynamicTable is a valid dynamic table and has the correct
% properties.
% 2) The height of the columns to be appended matches the height of the
% existing columns
% ADDCOLUMN Given a dynamic table and a keyword argument, add a column to the dynamic table.
%
% ADDCOLUMN(DT,NM,VD)
% append specified column name NM and non-ragged VectorData VD to DynamicTable DT
%
% ADDCOLUMN(DT,NM, VD, VI) append specified column by col_name NM represented
% by multiple VectorIndex references VI ordered in such a way where VI(n) references V(n-1) and
% VI(1) references VectorData VD.
%
% This function asserts the following:
% 1) DynamicTable is a valid dynamic table and has the correct
% properties.
% 2) The height of the columns to be appended matches the height of the
% existing columns

validateattributes(DynamicTable,...
{'types.core.DynamicTable', 'types.hdmf_common.DynamicTable'},...
{'scalar'});
validateattributes(DynamicTable ...
, {'types.core.DynamicTable', 'types.hdmf_common.DynamicTable'} ...
, {'scalar'});

assert(nargin > 1, 'NWB:DynamicTable:AddColumn:NoData', 'Not enough arguments');
assert(nargin > 1, 'NWB:DynamicTable:AddColumn:NoData', 'Not enough arguments');

if isempty(DynamicTable.id)
if 8 == exist('types.hdmf_common.ElementIdentifiers', 'class')
DynamicTable.id = types.hdmf_common.ElementIdentifiers();
else % legacy Element Identifiers
DynamicTable.id = types.core.ElementIdentifiers();
if isempty(DynamicTable.id)
if 8 == exist('types.hdmf_common.ElementIdentifiers', 'class')
DynamicTable.id = types.hdmf_common.ElementIdentifiers();
else % legacy Element Identifiers
DynamicTable.id = types.core.ElementIdentifiers();

Check warning on line 27 in +types/+util/+dynamictable/addColumn.m

View check run for this annotation

Codecov / codecov/patch

+types/+util/+dynamictable/addColumn.m#L27

Added line #L27 was not covered by tests
end
end
end

assert(~isa(DynamicTable.id.data, 'types.untyped.DataStub'),...
'NWB:DynamicTable:AddColumn:Uneditable',...
['Cannot write to on-file Dynamic Tables without enabling data pipes. '...
'If this was produced with pynwb, please enable chunking for this table.']);
assert(~isa(DynamicTable.id.data, 'types.untyped.DataStub') ...
, 'NWB:DynamicTable:AddColumn:Uneditable' ...
, [ ...
'Cannot write to on-file Dynamic Tables without enabling data pipes. '...
'If this was produced with pynwb, please enable chunking for this table.']);

if istable(varargin{1})
types.util.dynamictable.addTableColumn(DynamicTable, varargin{:});
else
assert(~istable(varargin{1}) ...
, 'NWB:DynamicTable:AddColumn:InvalidArgument' ...
, [ ...
'Using MATLAB tables as input to the addColumn DynamicTable method has been deprecated. ' ...
'Please, use key-value pairs instead.']);
types.util.dynamictable.addVarargColumn(DynamicTable, varargin{:});
end
56 changes: 14 additions & 42 deletions +types/+util/+dynamictable/addRawData.m
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
function addRawData(DynamicTable, column, data)
function addRawData(DynamicTable, columnName, data)
%ADDRAWDATA Internal method for adding data to DynamicTable given column
% name and data. Indices are determined based on data format and available
% indices.
validateattributes(column, {'char'}, {'scalartext'});
validateattributes(columnName, {'char'}, {'scalartext'});

if (isprop(DynamicTable, column) && isempty(DynamicTable.(column))) ...
|| (~isprop(DynamicTable, column) && ~isKey(DynamicTable.vectordata, column))
if (isprop(DynamicTable, columnName) && isempty(DynamicTable.(columnName))) ...
|| (~isprop(DynamicTable, columnName) && ~isKey(DynamicTable.vectordata, columnName))
% No vecdata found anywhere. Initialize.
initVecData(DynamicTable, column, class(data));
initVecData(DynamicTable, columnName, class(data));
end

if isprop(DynamicTable, column)
Vector = DynamicTable.(column);
elseif isprop(DynamicTable, 'vectorindex') && DynamicTable.vectorindex.isKey(column)
Vector = DynamicTable.vectorindex.get(column);
if isprop(DynamicTable, columnName)
Vector = DynamicTable.(columnName);
elseif isprop(DynamicTable, 'vectorindex') && DynamicTable.vectorindex.isKey(columnName)
Vector = DynamicTable.vectorindex.get(columnName);

Check warning on line 16 in +types/+util/+dynamictable/addRawData.m

View check run for this annotation

Codecov / codecov/patch

+types/+util/+dynamictable/addRawData.m#L16

Added line #L16 was not covered by tests
else
Vector = DynamicTable.vectordata.get(column);
Vector = DynamicTable.vectordata.get(columnName);
end

% grab all available indices for column.
indexChain = {column};
indexChain = {columnName};
while true
index = types.util.dynamictable.getIndex(DynamicTable, indexChain{end});
if isempty(index)
Expand All @@ -35,10 +35,11 @@

% find true nesting depth of column data.
if isa(Vector.data, 'types.untyped.DataPipe')
depth = getNestedDataDepth(data, 'dataPipeDimension', Vector.data.axis);
depthRequestOptions = {'dataPipeDimension', Vector.data.axis};
else
depth = getNestedDataDepth(data);
depthRequestOptions = {};
end
depth = types.util.dynamictable.getDataDepth(data, depthRequestOptions{:});

% add indices until it matches depth.
for iVec = (length(indexChain)+1):depth
Expand Down Expand Up @@ -99,35 +100,6 @@
end
end

function depth = getNestedDataDepth(data, varargin)
p = inputParser;
p.addParameter('dataPipeDimension', [], @(x)isnumeric(x) && (isempty(x) || isscalar(x)));
p.parse(varargin{:});

depth = 1;
subData = data;
while iscell(subData) && ~iscellstr(subData)
depth = depth + 1;
subData = subData{1};
end

% special case where the final data is in fact multiple rows to begin
% with.
if isempty(p.Results.dataPipeDimension)
if ischar(subData)
isMultiRow = 1 < size(subData, 1);
else
isMultiRow = (ismatrix(subData) && 1 < size(subData, 2)) ...
|| (isvector(subData) && 1 < length(subData));
end
else
isMultiRow = 1 < size(subData, p.Results.dataPipeDimension);
end
if isMultiRow
depth = depth + 1;
end
end

function numRows = nestedAdd(DynamicTable, indChain, data)
name = indChain{1};

Expand Down
Loading