From 09d464c8d0d2fdacc5e9df8008e2f61b0bc65387 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 12:32:52 +0100 Subject: [PATCH 01/30] Update main function docstrings to work better with sphinx/rst formatting Add "sections" Specify input and output arguments Reformat examples --- NwbFile.m | 39 ++++++++++++++++----------- generateCore.m | 59 ++++++++++++++++++++++++---------------- generateExtension.m | 51 ++++++++++++++++++++++------------- nwbClearGenerated.m | 30 +++++++++++++++++++-- nwbExport.m | 52 ++++++++++++++++++++++++----------- nwbRead.m | 66 +++++++++++++++++++++++++++++++++------------ 6 files changed, 207 insertions(+), 90 deletions(-) diff --git a/NwbFile.m b/NwbFile.m index e7dcddbc..71bf9478 100644 --- a/NwbFile.m +++ b/NwbFile.m @@ -1,18 +1,23 @@ classdef NwbFile < types.core.NWBFile - % NWBFILE Root object representing data read from an NWB file. - % - % Requires that core and extension NWB types have been generated - % and reside in a 'types' package on the matlab path. - % - % Example. Construct an object from scratch for export: - % nwb = NwbFile; - % nwb.epochs = types.core.Epochs; - % nwbExport(nwb, 'epoch.nwb'); - % - % See also NWBREAD, GENERATECORE, GENERATEEXTENSION +% NWBFILE - Root object representing an NWB file. +% +% Requires that core and extension NWB types have been generated +% and reside in a ``+types`` namespace on the MATLAB search path. +% +% Usage: +% Example 1 - Construct a simple NwbFile object for export:: +% +% nwb = NwbFile; +% nwb.epochs = types.core.Epochs; +% nwbExport(nwb, 'epoch.nwb'); +% +% See also: +% nwbRead, generateCore, generateExtension methods function obj = NwbFile(propValues) + % NWBFILE - Create an NWB File object + arguments propValues.?types.core.NWBFile propValues.nwb_version @@ -26,7 +31,7 @@ end function export(obj, filename, mode) - % export - Export NWB file object + % EXPORT - Export NWB file object arguments obj (1,1) NwbFile @@ -98,10 +103,14 @@ function export(obj, filename, mode) end function objectMap = searchFor(obj, typename, varargin) - % Searches this NwbFile object for a given typename + % searchFor - Search for for a given typename within the NwbFile object + % % Including the full namespace is optional. - % WARNING: The returned paths are resolvable but do not necessarily - % indicate a real HDF5 path. Their only function is to be resolvable. + % + % .. warning:: + % The returned paths are resolvable but do not necessarily + % indicate a real HDF5 path. Their only function is to be resolvable. + objectMap = searchProperties(... containers.Map,... obj,... diff --git a/generateCore.m b/generateCore.m index 7349b8e1..c2b8fe6e 100644 --- a/generateCore.m +++ b/generateCore.m @@ -1,27 +1,40 @@ function generateCore(version, options) - % GENERATECORE Generate Matlab classes from NWB core schema files - % GENERATECORE() Generate classes (Matlab m-files) from the - % NWB core namespace file. By default, generates off of the most recent nwb-schema - % release. - % - % GENERATECORE(version) Generate classes for the - % core namespace of the listed version. - % - % A cache of schema data is generated in the 'namespaces' subdirectory in - % the current working directory. This is for allowing cross-referencing - % classes between multiple namespaces. - % - % Output files are generated placed in a '+types' subdirectory in the - % current working directory. - % - % GENERATECORE(__, 'savedir', saveDirectory) Generates the core class - % files in the specified directory. - % - % Example: - % generateCore(); - % generateCore('2.2.3'); - % - % See also GENERATEEXTENSION +% GENERATECORE - Generate Matlab classes from NWB core schema files +% +% Syntax: +% GENERATECORE() Generate classes (Matlab m-files) from the +% NWB core namespace file. By default, generates off of the most recent +% nwb-schema release. +% +% GENERATECORE(version) Generate classes for the +% core namespace of the listed version. +% +% GENERATECORE(__, Name, Value) Generate classes based on optional +% name-value pairs controlling the output . +% +% A cache of schema data is generated in the ``namespaces`` subdirectory in +% the matnwb root directory. This is for allowing cross-referencing +% classes between multiple namespaces. +% +% Output files are placed in a ``+types`` subdirectory in the +% matnwb root directory directory. +% +% Usage: +% Example 1 - Generate core schemas for the latest version of NWB:: +% +% generateCore(); +% +% Example 2 - Generate core schemas for an older version of NWB:: +% +% generateCore('2.2.3'); +% +% Example 3 - Generate and save classes in a custom location:: +% +% % Generates the core class files in the specified directory. +% generateCore('savedir', saveDirectory) +% +% See also: +% generateExtension arguments version (1,1) string {matnwb.common.mustBeValidSchemaVersion} = "latest" diff --git a/generateExtension.m b/generateExtension.m index f4184350..7bf14c93 100644 --- a/generateExtension.m +++ b/generateExtension.m @@ -1,22 +1,37 @@ function generateExtension(namespaceFilePath, options) - % GENERATEEXTENSION Generate Matlab classes from NWB extension schema file - % GENERATEEXTENSION(extension_path...) Generate classes - % (Matlab m-files) from one or more NWB schema extension namespace - % files. A registry of already generated core types is used to resolve - % dependent types. - % - % A cache of schema data is generated in the 'namespaces' subdirectory in - % the current working directory. This is for allowing cross-referencing - % classes between multiple namespaces. - % - % Output files are generated placed in a '+types' subdirectory in the - % current working directory. - % - % Example: - % generateExtension('schema\myext\myextension.namespace.yaml', 'schema\myext2\myext2.namespace.yaml'); - % - % See also GENERATECORE - +% GENERATEEXTENSION - Generate Matlab classes from NWB extension schema file +% +% Syntax: +% GENERATEEXTENSION(extension_path...) Generate classes (Matlab m-files) +% from one or more NWB schema extension namespace files. A registry of +% already generated core types is used to resolve dependent types. +% +% A cache of schema data is generated in the ``namespaces`` subdirectory in +% the matnwb root directory. This is for allowing cross-referencing +% classes between multiple namespaces. +% +% Output files are placed in a ``+types`` subdirectory in the +% matnwb root directory directory. +% +% Input Arguments: +% - namespaceFilePath (string) - +% Filepath pointing to a schema extension namespace file. This is a +% repeating argument, so multiple filepaths can be provided +% +% - options (name-value pairs) - +% Optional name-value pairs. Available options: +% +% - savedir (string) - +% A folder to save generated classes for NWB/extension types. +% +% Usage: +% Example 1 - Generate classes for custom schema extensions:: +% +% generateExtension('schema\myext\myextension.namespace.yaml', 'schema\myext2\myext2.namespace.yaml'); +% +% See also: +% generateCore + arguments (Repeating) namespaceFilePath (1,1) string {mustBeYamlFile} end diff --git a/nwbClearGenerated.m b/nwbClearGenerated.m index 8e0c6cfd..3e8cbc9c 100644 --- a/nwbClearGenerated.m +++ b/nwbClearGenerated.m @@ -1,9 +1,35 @@ function clearedNamespaceNames = nwbClearGenerated(targetFolder, options) - %% NWBCLEARGENERATED clears generated class files. +% NWBCLEARGENERATED - Clear generated class files. +% +% Syntax: +% NWBCLEARGENERATED() Clear generated class files from the ``+types`` +% folder in the matnwb root directory. +% +% Input Arguments: +% - targetFolder (string) - +% Path name for folder containing generated classes in a ``+types`` +% namespace folder. Default value is the matnwb root directory +% +% - options (name-value pairs) - +% Optional name-value pairs. Available options: +% +% - ClearCache (logical) - +% Whether to clear the cached schema data in the ``namespaces`` folder. +% Default is ``false`` +% +% Usage: +% Example 1 - Clear all generated classes in the matnwb root directory:: +% +% nwbClearGenerated(); +% +% See also: +% generateCore, generateExtension + arguments targetFolder (1,1) string {mustBeFolder} = misc.getMatnwbDir() options.ClearCache (1,1) logical = false end + typesPath = fullfile(targetFolder, '+types'); listing = dir(typesPath); moduleNames = setdiff({listing.name}, {'+untyped', '+util', '.', '..'}); @@ -26,4 +52,4 @@ clearedNamespaceNames = strrep(clearedNamespaceNames, '+', ''); clearedNamespaceNames = string(clearedNamespaceNames); end -end \ No newline at end of file +end diff --git a/nwbExport.m b/nwbExport.m index 655d891d..a481cec7 100644 --- a/nwbExport.m +++ b/nwbExport.m @@ -1,19 +1,41 @@ function nwbExport(nwbFileObjects, filePaths, mode) - %NWBEXPORT Writes an NWB file. - % nwbRead(nwb, filename) Writes the nwb object to a file at filename. - % - % Example: - % % Generate Matlab code for the NWB objects from the core schema. - % % This only needs to be done once. - % generateCore('schema\core\nwb.namespace.yaml'); - % % Create some fake fata and write - % nwb = NwbFile; - % nwb.session_start_time = datetime('now'); - % nwb.identifier = 'EMPTY'; - % nwb.session_description = 'empty test file'; - % nwbExport(nwb, 'empty.nwb'); - % - % See also GENERATECORE, GENERATEEXTENSION, NWBFILE, NWBREAD +%NWBEXPORT - Writes an NWB file. +% +% Syntax: +% NWBEXPORT(nwb, filename) Writes the nwb object to a file at filename. +% +% Input Arguments: +% - nwb (NwbFile) - Nwb file object +% - filename (string) - Filepath pointing to an NWB file. +% +% Usage: +% Example 1 - Export an NWB file:: +% +% % Create an NWB object with some properties: +% nwb = NwbFile; +% nwb.session_start_time = datetime('now'); +% nwb.identifier = 'EMPTY'; +% nwb.session_description = 'empty test file'; +% +% % Write the nwb object to a file: +% nwbExport(nwb, 'empty.nwb'); +% +% Example 2 - Export an NWB file using an older schema version:: +% +% % Generate classes for an older version of NWB schemas: +% generateCore('2.5.0') +% +% % Create an NWB object with some properties: +% nwb = NwbFile; +% nwb.session_start_time = datetime('now'); +% nwb.identifier = 'EMPTY'; +% nwb.session_description = 'empty test file'; +% +% % Write the nwb object to a file: +% nwbExport(nwb, 'empty.nwb'); +% +% See also: +% generateCore, generateExtension, NwbFile, nwbRead arguments nwbFileObjects (1,:) NwbFile {mustBeNonempty} diff --git a/nwbRead.m b/nwbRead.m index 70ec5c6a..03b54791 100644 --- a/nwbRead.m +++ b/nwbRead.m @@ -1,21 +1,53 @@ function nwb = nwbRead(filename, flags, options) - %NWBREAD Reads an NWB file. - % nwb = NWBREAD(filename) Reads the nwb file at filename and returns an - % NWBFile object representing its contents. - % nwb = nwbRead(filename, 'ignorecache') Reads the nwb file without generating classes - % off of the cached schema if one exists. - % - % nwb = NWBREAD(filename, options) - % - % Requires that core and extension NWB types have been generated - % and reside in a 'types' package on the matlab path. - % - % Example: - % nwb = nwbRead('data.nwb'); - % nwb = nwbRead('data.nwb', 'ignorecache'); - % nwb = nwbRead('data.nwb', 'savedir', '.'); - % - % See also GENERATECORE, GENERATEEXTENSION, NWBFILE, NWBEXPORT +% NWBREAD - Read an NWB file. +% +% Syntax: +% nwb = NWBREAD(filename) Reads the nwb file at filename and returns an +% NWBFile object representing its contents. +% +% nwb = NWBREAD(filename, flags) Reads the nwb file using optional +% flags controlling the mode for how to read the file. See input +% arguments for a list of available flags. +% +% nwb = NWBREAD(filename, Name, Value) Reads the nwb file using optional +% name-value pairs controlling options for how to read the file. +% +% Input Arguments: +% - filename (string) - +% Filepath pointing to an NWB file. +% +% - flags (string) - +% Flag for setting the mode for the NWBREAD operation. Available options are: +% 'ignorecache'. If the 'ignorecache' flag is used, classes for NWB data types +% are not re-generated based on the embedded schemas in the file. +% +% - options (name-value pairs) - +% Optional name-value pairs. Available options: +% +% - savedir (string) - +% A folder to save generated classes for NWB types. +% +% Output Arguments: +% - nwb (NwbFile) - Nwb file object +% +% Usage: +% Example 1 - Read an NWB file:: +% +% nwb = nwbRead('data.nwb'); +% +% Example 2 - Read an NWB file without re-generating classes for NWB types:: +% +% nwb = nwbRead('data.nwb', 'ignorecache'); +% +% Note: This is a good option to use if you are reading several files +% which are created of the same version of the NWB schemas. +% +% Example 3 - Read an NWB file and generate classes for NWB types in the current working directory:: +% +% nwb = nwbRead('data.nwb', 'savedir', '.'); +% +% See also: +% generateCore, generateExtension, NwbFile, nwbExport arguments filename (1,1) string {matnwb.common.mustBeNwbFile} From 37187b6f49adde6b0e1dd56e2e8d8d0a722d8660 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 14:18:18 +0100 Subject: [PATCH 02/30] Add tools for creating rst pages for functions, classes and tutorials --- .../_rst_templates/class.rst.template | 7 +++ .../_rst_templates/function.rst.template | 5 ++ .../index_core_types.rst.template | 10 ++++ .../index_functions.rst.template | 10 ++++ .../index_tutorials.rst.template | 8 +++ .../_rst_templates/tutorial.rst.template | 6 +++ .../matnwb_generateRstFilesFromCode.m | 5 ++ tools/documentation/private/filewrite.m | 5 ++ tools/documentation/private/fillTemplate.m | 9 ++++ .../private/generateRstForNwbFunctions.m | 52 +++++++++++++++++++ .../private/generateRstForNwbTypeClasses.m | 43 +++++++++++++++ .../private/generateRstForTutorials.m | 38 ++++++++++++++ .../private/getRstTemplateFile.m | 16 ++++++ 13 files changed, 214 insertions(+) create mode 100644 tools/documentation/_rst_templates/class.rst.template create mode 100644 tools/documentation/_rst_templates/function.rst.template create mode 100644 tools/documentation/_rst_templates/index_core_types.rst.template create mode 100644 tools/documentation/_rst_templates/index_functions.rst.template create mode 100644 tools/documentation/_rst_templates/index_tutorials.rst.template create mode 100644 tools/documentation/_rst_templates/tutorial.rst.template create mode 100644 tools/documentation/matnwb_generateRstFilesFromCode.m create mode 100644 tools/documentation/private/filewrite.m create mode 100644 tools/documentation/private/fillTemplate.m create mode 100644 tools/documentation/private/generateRstForNwbFunctions.m create mode 100644 tools/documentation/private/generateRstForNwbTypeClasses.m create mode 100644 tools/documentation/private/generateRstForTutorials.m create mode 100644 tools/documentation/private/getRstTemplateFile.m diff --git a/tools/documentation/_rst_templates/class.rst.template b/tools/documentation/_rst_templates/class.rst.template new file mode 100644 index 00000000..76a025ee --- /dev/null +++ b/tools/documentation/_rst_templates/class.rst.template @@ -0,0 +1,7 @@ +{{ function_name }} +{{ function_header_underline }} + +.. mat:module:: {{ module_name }} +.. autoclass:: {{ full_function_name }} + :members: + :show-inheritance: diff --git a/tools/documentation/_rst_templates/function.rst.template b/tools/documentation/_rst_templates/function.rst.template new file mode 100644 index 00000000..ef77bf98 --- /dev/null +++ b/tools/documentation/_rst_templates/function.rst.template @@ -0,0 +1,5 @@ +{{ function_name }} +{{ function_header_underline }} + +.. mat:module:: . +.. autofunction:: {{ full_function_name }} diff --git a/tools/documentation/_rst_templates/index_core_types.rst.template b/tools/documentation/_rst_templates/index_core_types.rst.template new file mode 100644 index 00000000..ecf43f5e --- /dev/null +++ b/tools/documentation/_rst_templates/index_core_types.rst.template @@ -0,0 +1,10 @@ +Neurodata Types +=============== + +These are the MatNWB neurodata types from the core schema specification. + +.. toctree:: + :maxdepth: 2 + :caption: Functions + +{{ function_list }} \ No newline at end of file diff --git a/tools/documentation/_rst_templates/index_functions.rst.template b/tools/documentation/_rst_templates/index_functions.rst.template new file mode 100644 index 00000000..9412c5da --- /dev/null +++ b/tools/documentation/_rst_templates/index_functions.rst.template @@ -0,0 +1,10 @@ +MatNWB Functions +================ + +These are the main functions of the MatNWB API + +.. toctree:: + :maxdepth: 2 + :caption: Functions + +{{ function_list }} \ No newline at end of file diff --git a/tools/documentation/_rst_templates/index_tutorials.rst.template b/tools/documentation/_rst_templates/index_tutorials.rst.template new file mode 100644 index 00000000..ca293976 --- /dev/null +++ b/tools/documentation/_rst_templates/index_tutorials.rst.template @@ -0,0 +1,8 @@ +Tutorials +========= + +.. toctree:: + :maxdepth: 1 + :caption: Tutorials + +{{ file_list }} \ No newline at end of file diff --git a/tools/documentation/_rst_templates/tutorial.rst.template b/tools/documentation/_rst_templates/tutorial.rst.template new file mode 100644 index 00000000..5c1b30fe --- /dev/null +++ b/tools/documentation/_rst_templates/tutorial.rst.template @@ -0,0 +1,6 @@ +{{tutorial_name}} +=============================== + +.. raw:: html + + diff --git a/tools/documentation/matnwb_generateRstFilesFromCode.m b/tools/documentation/matnwb_generateRstFilesFromCode.m new file mode 100644 index 00000000..bb5492b7 --- /dev/null +++ b/tools/documentation/matnwb_generateRstFilesFromCode.m @@ -0,0 +1,5 @@ +function matnwb_generateRstFilesFromCode() + generateRstForTutorials() + generateRstForNwbFunctions() + generateRstForNwbTypeClasses() +end \ No newline at end of file diff --git a/tools/documentation/private/filewrite.m b/tools/documentation/private/filewrite.m new file mode 100644 index 00000000..ab8868b1 --- /dev/null +++ b/tools/documentation/private/filewrite.m @@ -0,0 +1,5 @@ +function filewrite(filePath, text) + fid = fopen(filePath, 'wt'); + fwrite(fid, text); + fclose(fid); +end \ No newline at end of file diff --git a/tools/documentation/private/fillTemplate.m b/tools/documentation/private/fillTemplate.m new file mode 100644 index 00000000..71d72104 --- /dev/null +++ b/tools/documentation/private/fillTemplate.m @@ -0,0 +1,9 @@ +function template = fillTemplate(template, data) + fields = fieldnames(data); + for i = 1:numel(fields) + if ~isstruct(data.(fields{i})) && ~iscell(data.(fields{i})) + placeholder = sprintf('{{ %s }}', fields{i}); + template = strrep(template, placeholder, string(data.(fields{i}))); + end + end +end diff --git a/tools/documentation/private/generateRstForNwbFunctions.m b/tools/documentation/private/generateRstForNwbFunctions.m new file mode 100644 index 00000000..d6f1dd68 --- /dev/null +++ b/tools/documentation/private/generateRstForNwbFunctions.m @@ -0,0 +1,52 @@ +function generateRstForNwbFunctions() +% generateRstForNwbFunctions + + % List, filter and sort files to generate. Todo: simplify + rootDir = misc.getMatnwbDir(); + rootFiles = dir(rootDir); + rootFileNames = {rootFiles.name}; + rootWhitelist = {'nwbRead.m', 'NwbFile.m', 'nwbExport.m', 'generateCore.m', 'generateExtension.m', 'nwbClearGenerated.m'};%, 'nwbInstallExtension.m'}; + isWhitelisted = ismember(rootFileNames, rootWhitelist); + + rootFiles(~isWhitelisted) = []; + + [~, ~, iC] = intersect(rootWhitelist, {rootFiles.name}, 'stable'); + rootFiles = rootFiles(iC); + + docsSourceRootDir = fullfile(misc.getMatnwbDir, 'docs', 'source'); + exportDir = fullfile(docsSourceRootDir, 'pages', 'functions'); + if ~isfolder(exportDir); mkdir(exportDir); end + + functionTemplate = fileread( getRstTemplateFile('function') ); + classTemplate = fileread( getRstTemplateFile('class') ); + + for i = 1:numel(rootFiles) + iFile = fullfile(rootFiles(i).folder, rootFiles(i).name); + [~, functionName] = fileparts(iFile); + + mc = meta.class.fromName(functionName); + if isempty(mc) + currentTemplate = functionTemplate; + else + currentTemplate = classTemplate; + end + + data.function_name = functionName; + data.module_name = '.'; + data.function_header_underline = repmat('=', 1, numel(functionName)); + data.full_function_name = functionName; + + thisRst = fillTemplate(currentTemplate, data); + rstFilePath = fullfile(exportDir, [functionName, '.rst']); + filewrite(rstFilePath, thisRst); + end + + % Create index + indexTemplate = fileread( getRstTemplateFile('index_functions') ); + [~, functionNames] = fileparts(string({rootFiles.name})); + data.function_list = strjoin(" "+functionNames, newline); + + thisRst = fillTemplate(indexTemplate, data); + rstFilePath = fullfile(exportDir, ['index', '.rst']); + filewrite(rstFilePath, thisRst); +end diff --git a/tools/documentation/private/generateRstForNwbTypeClasses.m b/tools/documentation/private/generateRstForNwbTypeClasses.m new file mode 100644 index 00000000..cb224225 --- /dev/null +++ b/tools/documentation/private/generateRstForNwbTypeClasses.m @@ -0,0 +1,43 @@ +function generateRstForNwbTypeClasses() +% generateRstForNwbTypeClasses Generate rst files for each Neurodata matnwb class + + rootDir = misc.getMatnwbDir(); + coreTypeFiles = dir(fullfile(rootDir, '+types', '+core', '*.m')); + + docsSourceRootDir = fullfile(misc.getMatnwbDir, 'docs', 'source'); + exportDir = fullfile(docsSourceRootDir, 'pages', 'neurodata_types', 'core'); + if ~isfolder(exportDir); mkdir(exportDir); end + + functionTemplate = fileread( getRstTemplateFile('function') ); + classTemplate = fileread( getRstTemplateFile('class') ); + + for i = 1:numel(coreTypeFiles) + iFile = fullfile(coreTypeFiles(i).folder, coreTypeFiles(i).name); + [~, functionName] = fileparts(iFile); + + data.function_name = functionName; + data.module_name = 'types.core'; + data.function_header_underline = repmat('=', 1, numel(functionName)); + data.full_function_name = sprintf('types.core.%s', functionName); + + mc = meta.class.fromName(data.full_function_name); + if isempty(mc) + currentTemplate = functionTemplate; + else + currentTemplate = classTemplate; + end + + thisRst = fillTemplate(currentTemplate, data); + rstFilePath = fullfile(exportDir, [functionName, '.rst']); + filewrite(rstFilePath, thisRst); + end + + % Create index + indexTemplate = fileread( getRstTemplateFile('index_core_types') ); + [~, functionNames] = fileparts(string({coreTypeFiles.name})); + data.function_list = strjoin(" "+functionNames, newline); + + thisRst = fillTemplate(indexTemplate, data); + rstFilePath = fullfile(exportDir, ['index', '.rst']); + filewrite(rstFilePath, thisRst); +end diff --git a/tools/documentation/private/generateRstForTutorials.m b/tools/documentation/private/generateRstForTutorials.m new file mode 100644 index 00000000..977d9a60 --- /dev/null +++ b/tools/documentation/private/generateRstForTutorials.m @@ -0,0 +1,38 @@ +function generateRstForTutorials() +% generateRstForTutorials - Generate rst files for all the tutorial HTML files + + docsSourceRootDir = fullfile(misc.getMatnwbDir, 'docs', 'source'); + + tutorialHtmlSourceDir = fullfile(docsSourceRootDir, '_static', 'html', 'tutorials'); + tutorialRstTargetDir = fullfile(docsSourceRootDir, 'pages', 'tutorials'); + if ~isfolder(tutorialRstTargetDir); mkdir(tutorialRstTargetDir); end + + rstTemplate = fileread( getRstTemplateFile('tutorial') ); + + % List all html files in source dir + L = dir(fullfile(tutorialHtmlSourceDir, '*.html')); + + for i = 1:numel(L) + thisFilePath = fullfile(L(i).folder, L(i).name); + relPath = strrep(thisFilePath, docsSourceRootDir, '../..'); + + [~, name] = fileparts(relPath); + + rstOutput = replace(rstTemplate, '{{static_html_path}}', relPath); + rstOutput = replace(rstOutput, '{{tutorial_name}}', name); + + rstOutputFile = fullfile(tutorialRstTargetDir, [name, '.rst']); + fid = fopen(rstOutputFile, 'wt'); + fwrite(fid, rstOutput); + fclose(fid); + end + + % Create index + indexTemplate = fileread( getRstTemplateFile('index_tutorials') ); + [~, fileNames] = fileparts(string({L.name})); + data.file_list = strjoin(" "+fileNames, newline); + + thisRst = fillTemplate(indexTemplate, data); + rstFilePath = fullfile(tutorialRstTargetDir, ['index', '.rst']); + filewrite(rstFilePath, thisRst); +end \ No newline at end of file diff --git a/tools/documentation/private/getRstTemplateFile.m b/tools/documentation/private/getRstTemplateFile.m new file mode 100644 index 00000000..9db04ca0 --- /dev/null +++ b/tools/documentation/private/getRstTemplateFile.m @@ -0,0 +1,16 @@ +function templateFilePath = getRstTemplateFile(templateName) + arguments + templateName (1,1) string + end + fileName = templateName + ".rst.template"; + + templateFilePath = fullfile(... + misc.getMatnwbDir, ... + "tools", ... + "documentation", ... + "_rst_templates", ... + fileName ); + + assert(isfile(templateFilePath), ... + 'Template filepath not found for template with name "%s"', templateName) +end \ No newline at end of file From a231307e829946023178b9a02cd28866fedfbada Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 14:18:42 +0100 Subject: [PATCH 03/30] Add docs source --- docs/source/_static/css/custom.css | 74 + .../_static/html/tutorials/UnitTimes.png | Bin 0 -> 71834 bytes .../_static/html/tutorials/basicUsage.html | 431 ++++++ .../_static/html/tutorials/behavior.html | 369 +++++ .../_static/html/tutorials/convertTrials.html | 1119 ++++++++++++++ .../_static/html/tutorials/dataPipe.html | 441 ++++++ .../tutorials/dimensionMapNoDataPipes.html | 92 ++ .../tutorials/dimensionMapWithDataPipes.html | 110 ++ .../html/tutorials/dynamic_tables.html | 577 +++++++ .../tutorials/dynamically_loaded_filters.html | 141 ++ .../_static/html/tutorials/ecephys.html | 1356 +++++++++++++++++ .../source/_static/html/tutorials/ecephys.png | Bin 0 -> 1670 bytes .../_static/html/tutorials/ecephys_01.png | Bin 0 -> 10206 bytes .../html/tutorials/ecephys_data_deps.png | Bin 0 -> 102809 bytes .../_static/html/tutorials/icephys.html | 509 +++++++ .../source/_static/html/tutorials/images.html | 371 +++++ docs/source/_static/html/tutorials/intro.html | 307 ++++ docs/source/_static/html/tutorials/ogen.html | 201 +++ docs/source/_static/html/tutorials/ophys.html | 479 ++++++ .../tutorials/ophys_tutorial_schematic.png | Bin 0 -> 77678 bytes .../_static/html/tutorials/read_demo.html | 356 +++++ .../_static/html/tutorials/remote_read.html | 58 + .../_static/html/tutorials/scratch.html | 164 ++ docs/source/conf.py | 69 + docs/source/index.rst | 28 + .../documentation/formatting_docstrings.rst | 176 +++ docs/source/pages/developers.rst | 4 + docs/source/pages/functions/NwbFile.rst | 7 + docs/source/pages/functions/generateCore.rst | 5 + .../pages/functions/generateExtension.rst | 5 + docs/source/pages/functions/index.rst | 15 + .../pages/functions/nwbClearGenerated.rst | 5 + docs/source/pages/functions/nwbExport.rst | 5 + docs/source/pages/functions/nwbRead.rst | 5 + docs/source/pages/install_users.rst | 4 + .../core/AbstractFeatureSeries.rst | 7 + .../neurodata_types/core/AnnotationSeries.rst | 7 + .../neurodata_types/core/BehavioralEpochs.rst | 7 + .../neurodata_types/core/BehavioralEvents.rst | 7 + .../core/BehavioralTimeSeries.rst | 7 + .../neurodata_types/core/ClusterWaveforms.rst | 7 + .../pages/neurodata_types/core/Clustering.rst | 7 + .../neurodata_types/core/CompassDirection.rst | 7 + .../core/CorrectedImageStack.rst | 7 + .../core/CurrentClampSeries.rst | 7 + .../core/CurrentClampStimulusSeries.rst | 7 + .../core/DecompositionSeries.rst | 7 + .../pages/neurodata_types/core/Device.rst | 7 + .../pages/neurodata_types/core/DfOverF.rst | 7 + .../neurodata_types/core/ElectricalSeries.rst | 7 + .../neurodata_types/core/ElectrodeGroup.rst | 7 + .../neurodata_types/core/EventDetection.rst | 7 + .../neurodata_types/core/EventWaveform.rst | 7 + .../core/ExperimentalConditionsTable.rst | 7 + .../neurodata_types/core/EyeTracking.rst | 7 + .../core/FeatureExtraction.rst | 7 + .../neurodata_types/core/FilteredEphys.rst | 7 + .../neurodata_types/core/Fluorescence.rst | 7 + .../neurodata_types/core/GrayscaleImage.rst | 7 + .../neurodata_types/core/IZeroClampSeries.rst | 7 + .../pages/neurodata_types/core/Image.rst | 7 + .../neurodata_types/core/ImageMaskSeries.rst | 7 + .../neurodata_types/core/ImageReferences.rst | 7 + .../core/ImageSegmentation.rst | 7 + .../neurodata_types/core/ImageSeries.rst | 7 + .../pages/neurodata_types/core/Images.rst | 7 + .../neurodata_types/core/ImagingPlane.rst | 7 + .../core/ImagingRetinotopy.rst | 7 + .../neurodata_types/core/IndexSeries.rst | 7 + .../neurodata_types/core/IntervalSeries.rst | 7 + .../core/IntracellularElectrode.rst | 7 + .../core/IntracellularElectrodesTable.rst | 7 + .../core/IntracellularRecordingsTable.rst | 7 + .../core/IntracellularResponsesTable.rst | 7 + .../core/IntracellularStimuliTable.rst | 7 + .../source/pages/neurodata_types/core/LFP.rst | 7 + .../neurodata_types/core/LabMetaData.rst | 7 + .../neurodata_types/core/MotionCorrection.rst | 7 + .../neurodata_types/core/NWBContainer.rst | 7 + .../pages/neurodata_types/core/NWBData.rst | 7 + .../neurodata_types/core/NWBDataInterface.rst | 7 + .../pages/neurodata_types/core/NWBFile.rst | 7 + .../neurodata_types/core/OnePhotonSeries.rst | 7 + .../neurodata_types/core/OpticalChannel.rst | 7 + .../neurodata_types/core/OpticalSeries.rst | 7 + .../core/OptogeneticSeries.rst | 7 + .../core/OptogeneticStimulusSite.rst | 7 + .../neurodata_types/core/PatchClampSeries.rst | 7 + .../core/PlaneSegmentation.rst | 7 + .../pages/neurodata_types/core/Position.rst | 7 + .../neurodata_types/core/ProcessingModule.rst | 7 + .../neurodata_types/core/PupilTracking.rst | 7 + .../pages/neurodata_types/core/RGBAImage.rst | 7 + .../pages/neurodata_types/core/RGBImage.rst | 7 + .../neurodata_types/core/RepetitionsTable.rst | 7 + .../core/RoiResponseSeries.rst | 7 + .../neurodata_types/core/ScratchData.rst | 7 + .../core/SequentialRecordingsTable.rst | 7 + .../core/SimultaneousRecordingsTable.rst | 7 + .../neurodata_types/core/SpatialSeries.rst | 7 + .../neurodata_types/core/SpikeEventSeries.rst | 7 + .../pages/neurodata_types/core/Subject.rst | 7 + .../pages/neurodata_types/core/SweepTable.rst | 7 + .../neurodata_types/core/TimeIntervals.rst | 7 + .../pages/neurodata_types/core/TimeSeries.rst | 7 + .../core/TimeSeriesReferenceVectorData.rst | 7 + .../neurodata_types/core/TwoPhotonSeries.rst | 7 + .../pages/neurodata_types/core/Units.rst | 7 + .../core/VoltageClampSeries.rst | 7 + .../core/VoltageClampStimulusSeries.rst | 7 + .../pages/neurodata_types/core/index.rst | 84 + docs/source/pages/overview_citing.rst | 30 + docs/source/pages/tutorials/basicUsage.rst | 6 + docs/source/pages/tutorials/behavior.rst | 6 + docs/source/pages/tutorials/convertTrials.rst | 6 + docs/source/pages/tutorials/dataPipe.rst | 6 + .../tutorials/dimensionMapNoDataPipes.rst | 6 + .../tutorials/dimensionMapWithDataPipes.rst | 6 + .../source/pages/tutorials/dynamic_tables.rst | 6 + .../tutorials/dynamically_loaded_filters.rst | 6 + docs/source/pages/tutorials/ecephys.rst | 6 + docs/source/pages/tutorials/icephys.rst | 6 + docs/source/pages/tutorials/images.rst | 6 + docs/source/pages/tutorials/index.rst | 24 + docs/source/pages/tutorials/intro.rst | 6 + docs/source/pages/tutorials/ogen.rst | 6 + docs/source/pages/tutorials/ophys.rst | 6 + docs/source/pages/tutorials/read_demo.rst | 6 + docs/source/pages/tutorials/remote_read.rst | 6 + docs/source/pages/tutorials/scratch.rst | 6 + .../docstring_processors.cpython-311.pyc | Bin 0 -> 6843 bytes .../sphinx_extensions/docstring_processors.py | 149 ++ 132 files changed, 8397 insertions(+) create mode 100644 docs/source/_static/css/custom.css create mode 100644 docs/source/_static/html/tutorials/UnitTimes.png create mode 100644 docs/source/_static/html/tutorials/basicUsage.html create mode 100644 docs/source/_static/html/tutorials/behavior.html create mode 100644 docs/source/_static/html/tutorials/convertTrials.html create mode 100644 docs/source/_static/html/tutorials/dataPipe.html create mode 100644 docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html create mode 100644 docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html create mode 100644 docs/source/_static/html/tutorials/dynamic_tables.html create mode 100644 docs/source/_static/html/tutorials/dynamically_loaded_filters.html create mode 100644 docs/source/_static/html/tutorials/ecephys.html create mode 100644 docs/source/_static/html/tutorials/ecephys.png create mode 100644 docs/source/_static/html/tutorials/ecephys_01.png create mode 100644 docs/source/_static/html/tutorials/ecephys_data_deps.png create mode 100644 docs/source/_static/html/tutorials/icephys.html create mode 100644 docs/source/_static/html/tutorials/images.html create mode 100644 docs/source/_static/html/tutorials/intro.html create mode 100644 docs/source/_static/html/tutorials/ogen.html create mode 100644 docs/source/_static/html/tutorials/ophys.html create mode 100644 docs/source/_static/html/tutorials/ophys_tutorial_schematic.png create mode 100644 docs/source/_static/html/tutorials/read_demo.html create mode 100644 docs/source/_static/html/tutorials/remote_read.html create mode 100644 docs/source/_static/html/tutorials/scratch.html create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst create mode 100644 docs/source/pages/developer/documentation/formatting_docstrings.rst create mode 100644 docs/source/pages/developers.rst create mode 100644 docs/source/pages/functions/NwbFile.rst create mode 100644 docs/source/pages/functions/generateCore.rst create mode 100644 docs/source/pages/functions/generateExtension.rst create mode 100644 docs/source/pages/functions/index.rst create mode 100644 docs/source/pages/functions/nwbClearGenerated.rst create mode 100644 docs/source/pages/functions/nwbExport.rst create mode 100644 docs/source/pages/functions/nwbRead.rst create mode 100644 docs/source/pages/install_users.rst create mode 100644 docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/AnnotationSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/BehavioralEpochs.rst create mode 100644 docs/source/pages/neurodata_types/core/BehavioralEvents.rst create mode 100644 docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/ClusterWaveforms.rst create mode 100644 docs/source/pages/neurodata_types/core/Clustering.rst create mode 100644 docs/source/pages/neurodata_types/core/CompassDirection.rst create mode 100644 docs/source/pages/neurodata_types/core/CorrectedImageStack.rst create mode 100644 docs/source/pages/neurodata_types/core/CurrentClampSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/DecompositionSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/Device.rst create mode 100644 docs/source/pages/neurodata_types/core/DfOverF.rst create mode 100644 docs/source/pages/neurodata_types/core/ElectricalSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/ElectrodeGroup.rst create mode 100644 docs/source/pages/neurodata_types/core/EventDetection.rst create mode 100644 docs/source/pages/neurodata_types/core/EventWaveform.rst create mode 100644 docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst create mode 100644 docs/source/pages/neurodata_types/core/EyeTracking.rst create mode 100644 docs/source/pages/neurodata_types/core/FeatureExtraction.rst create mode 100644 docs/source/pages/neurodata_types/core/FilteredEphys.rst create mode 100644 docs/source/pages/neurodata_types/core/Fluorescence.rst create mode 100644 docs/source/pages/neurodata_types/core/GrayscaleImage.rst create mode 100644 docs/source/pages/neurodata_types/core/IZeroClampSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/Image.rst create mode 100644 docs/source/pages/neurodata_types/core/ImageMaskSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/ImageReferences.rst create mode 100644 docs/source/pages/neurodata_types/core/ImageSegmentation.rst create mode 100644 docs/source/pages/neurodata_types/core/ImageSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/Images.rst create mode 100644 docs/source/pages/neurodata_types/core/ImagingPlane.rst create mode 100644 docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst create mode 100644 docs/source/pages/neurodata_types/core/IndexSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/IntervalSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/IntracellularElectrode.rst create mode 100644 docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst create mode 100644 docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst create mode 100644 docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst create mode 100644 docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst create mode 100644 docs/source/pages/neurodata_types/core/LFP.rst create mode 100644 docs/source/pages/neurodata_types/core/LabMetaData.rst create mode 100644 docs/source/pages/neurodata_types/core/MotionCorrection.rst create mode 100644 docs/source/pages/neurodata_types/core/NWBContainer.rst create mode 100644 docs/source/pages/neurodata_types/core/NWBData.rst create mode 100644 docs/source/pages/neurodata_types/core/NWBDataInterface.rst create mode 100644 docs/source/pages/neurodata_types/core/NWBFile.rst create mode 100644 docs/source/pages/neurodata_types/core/OnePhotonSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/OpticalChannel.rst create mode 100644 docs/source/pages/neurodata_types/core/OpticalSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/OptogeneticSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst create mode 100644 docs/source/pages/neurodata_types/core/PatchClampSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/PlaneSegmentation.rst create mode 100644 docs/source/pages/neurodata_types/core/Position.rst create mode 100644 docs/source/pages/neurodata_types/core/ProcessingModule.rst create mode 100644 docs/source/pages/neurodata_types/core/PupilTracking.rst create mode 100644 docs/source/pages/neurodata_types/core/RGBAImage.rst create mode 100644 docs/source/pages/neurodata_types/core/RGBImage.rst create mode 100644 docs/source/pages/neurodata_types/core/RepetitionsTable.rst create mode 100644 docs/source/pages/neurodata_types/core/RoiResponseSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/ScratchData.rst create mode 100644 docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst create mode 100644 docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst create mode 100644 docs/source/pages/neurodata_types/core/SpatialSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/SpikeEventSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/Subject.rst create mode 100644 docs/source/pages/neurodata_types/core/SweepTable.rst create mode 100644 docs/source/pages/neurodata_types/core/TimeIntervals.rst create mode 100644 docs/source/pages/neurodata_types/core/TimeSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst create mode 100644 docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/Units.rst create mode 100644 docs/source/pages/neurodata_types/core/VoltageClampSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst create mode 100644 docs/source/pages/neurodata_types/core/index.rst create mode 100644 docs/source/pages/overview_citing.rst create mode 100644 docs/source/pages/tutorials/basicUsage.rst create mode 100644 docs/source/pages/tutorials/behavior.rst create mode 100644 docs/source/pages/tutorials/convertTrials.rst create mode 100644 docs/source/pages/tutorials/dataPipe.rst create mode 100644 docs/source/pages/tutorials/dimensionMapNoDataPipes.rst create mode 100644 docs/source/pages/tutorials/dimensionMapWithDataPipes.rst create mode 100644 docs/source/pages/tutorials/dynamic_tables.rst create mode 100644 docs/source/pages/tutorials/dynamically_loaded_filters.rst create mode 100644 docs/source/pages/tutorials/ecephys.rst create mode 100644 docs/source/pages/tutorials/icephys.rst create mode 100644 docs/source/pages/tutorials/images.rst create mode 100644 docs/source/pages/tutorials/index.rst create mode 100644 docs/source/pages/tutorials/intro.rst create mode 100644 docs/source/pages/tutorials/ogen.rst create mode 100644 docs/source/pages/tutorials/ophys.rst create mode 100644 docs/source/pages/tutorials/read_demo.rst create mode 100644 docs/source/pages/tutorials/remote_read.rst create mode 100644 docs/source/pages/tutorials/scratch.rst create mode 100644 docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc create mode 100644 docs/source/sphinx_extensions/docstring_processors.py diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css new file mode 100644 index 00000000..680bd1af --- /dev/null +++ b/docs/source/_static/css/custom.css @@ -0,0 +1,74 @@ +/* arg formatting by line, taken from https://github.com/sphinx-doc/sphinx/issues/1514#issuecomment-742703082 */ + +/* For general themes */ +div.body, .wy-nav-content { + max-width: 1000px; /* Set the content width */ + margin: 0; /* Remove auto-centering */ + padding-left: 30; /* Optional: Adjust padding */ +} + +/* For Read the Docs theme specifically */ +.wy-nav-content { + margin: 0; /* Remove centering (auto) */ + padding-left: 30px; /* Align content to the left */ +} + + +/*Newlines (\a) and spaces (\20) before each parameter*/ +dl.class em:not([class])::before { + content: "\a\20\20\20\20\20\20\20\20\20\20\20\20\20\20\20\20"; + white-space: pre; +} + +/*Newline after the last parameter (so the closing bracket is on a new line)*/ +dl.class em:not([class]):last-of-type::after { + content: "\a"; + white-space: pre; +} + +/*To have blue background of width of the block (instead of width of content)*/ +dl.class > dt:first-of-type { + display: block !important; +} + +.rst-content code.literal, .rst-content tt.literal { + color: #2b417e; /* Replace with your desired color */ +} +.rst-content div[class^=highlight], .rst-content pre.literal-block { + margin: 1px 0 14px +} + +.rst-content .section ol li>*, .rst-content .section ul li>*, .rst-content .toctree-wrapper ol li>*, .rst-content .toctree-wrapper ul li>*, .rst-content section ol li>*, .rst-content section ul li>* { + margin-top: 0px; +} + +/* Ensure there is 10px spacing between nested list items at different levels*/ +.rst-content li > dl > dt { + margin-bottom: 10px; +} +.rst-content dd > ul > li { + margin-bottom: 10px; +} +.rst-content .section ol.simple li>*, .rst-content .section ol.simple li ol, .rst-content .section ol.simple li ul, .rst-content .section ul.simple li>*, .rst-content .section ul.simple li ol, .rst-content .section ul.simple li ul, .rst-content .toctree-wrapper ol.simple li>*, .rst-content .toctree-wrapper ol.simple li ol, .rst-content .toctree-wrapper ol.simple li ul, .rst-content .toctree-wrapper ul.simple li>*, .rst-content .toctree-wrapper ul.simple li ol, .rst-content .toctree-wrapper ul.simple li ul, .rst-content section ol.simple li>*, .rst-content section ol.simple li ol, .rst-content section ol.simple li ul, .rst-content section ul.simple li>*, .rst-content section ul.simple li ol, .rst-content section ul.simple li ul{ + margin-bottom: 10px; +} + +/* Improve padding and margins for function docstring section titles */ +.rst-content dd > dl > dt { + padding-left: 5px; + margin-top: 20px; + margin-bottom: 10px; +} +html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt, html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{ + margin-top: 28px; + margin-bottom: 10px; +} + +button.copybtn { + height:25px; + width:25px; + opacity: 0.5; + padding: 0; + border: none; + background: none; +} diff --git a/docs/source/_static/html/tutorials/UnitTimes.png b/docs/source/_static/html/tutorials/UnitTimes.png new file mode 100644 index 0000000000000000000000000000000000000000..d31f8b5502ec3d96c69a0fe13db67dc5169d6c54 GIT binary patch literal 71834 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 +Using NWB Data \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/behavior.html b/docs/source/_static/html/tutorials/behavior.html new file mode 100644 index 00000000..4f70f592 --- /dev/null +++ b/docs/source/_static/html/tutorials/behavior.html @@ -0,0 +1,369 @@ + +Behavior Data

Behavior Data

This tutorial will guide you in writing behavioral data to NWB.

Creating an NWB File

Create an NWBFile object with the required fields (session_description, identifier, and session_start_time) and additional metadata.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'My Name', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', 'DOI:10.1016/j.neuron.2016.12.011'); % optional
nwb
nwb =
NwbFile with properties: + + nwb_version: '2.7.0' + file_create_date: [] + identifier: 'Mouse5_Day3' + session_description: 'mouse in open exploration' + session_start_time: {[2018-04-25T02:30:03.000000+02:00]} + timestamps_reference_time: [] + acquisition: [0×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: '' + general_devices: [0×1 types.untyped.Set] + general_experiment_description: '' + general_experimenter: 'My Name' + general_extracellular_ephys: [0×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [] + general_institution: 'University of My Institution' + general_intracellular_ephys: [0×1 types.untyped.Set] + general_intracellular_ephys_experimental_conditions: [] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_intracellular_recordings: [] + general_intracellular_ephys_repetitions: [] + general_intracellular_ephys_sequential_recordings: [] + general_intracellular_ephys_simultaneous_recordings: [] + general_intracellular_ephys_sweep_table: [] + general_keywords: '' + general_lab: '' + general_notes: '' + general_optogenetics: [0×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' + general_session_id: 'session_1234' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [0×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [] + +Warning: The following required properties are missing for instance for type "NwbFile": + timestamps_reference_time

SpatialSeries: Storing continuous spatial data

SpatialSeries is a subclass of TimeSeries that represents data in space, such as the spatial direction e.g., of gaze or travel or position of an animal over time.
Create data that corresponds to x, y position over time.
position_data = [linspace(0, 10, 50); linspace(0, 8, 50)]; % 2 x nT array
In SpatialSeries data, the first dimension is always time (in seconds), the second dimension represents the x, y position. However, as described in the dimensionMapNoDataPipes tutorial, when a MATLAB array is exported to HDF5, the array is transposed. Therefore, in order to correctly export the data, in MATLAB the last dimension of an array should be time. SpatialSeries data should be stored as one continuous stream as it is acquired, not by trials as is often reshaped for analysis. Data can be trial-aligned on-the-fly using the trials table. See the trials tutorial for further information.
For position data reference_frame indicates the zero-position, e.g. the 0,0 point might be the bottom-left corner of an enclosure, as viewed from the tracking camera.
timestamps = linspace(0, 50, 50)/ 200;
position_spatial_series = types.core.SpatialSeries( ...
'description', 'Postion (x, y) in an open field.', ...
'data', position_data, ...
'timestamps', timestamps, ...
'reference_frame', '(0,0) is the bottom left corner.' ...
)
position_spatial_series =
SpatialSeries with properties: + + reference_frame: '(0,0) is the bottom left corner.' + starting_time_unit: 'seconds' + timestamps_interval: 1 + timestamps_unit: 'seconds' + data: [2×50 double] + comments: 'no comments' + control: [] + control_description: '' + data_continuity: '' + data_conversion: 1 + data_offset: 0 + data_resolution: -1 + data_unit: 'meters' + description: 'Postion (x, y) in an open field.' + starting_time: [] + starting_time_rate: [] + timestamps: [0 0.0051 0.0102 0.0153 0.0204 0.0255 0.0306 0.0357 0.0408 0.0459 0.0510 0.0561 0.0612 0.0663 0.0714 0.0765 0.0816 0.0867 0.0918 0.0969 0.1020 0.1071 0.1122 0.1173 0.1224 0.1276 0.1327 0.1378 0.1429 0.1480 0.1531 … ] (1×50 double) +

Position: Storing position measured over time

To help data analysis and visualization tools know that this SpatialSeries object represents the position of the subject, store the SpatialSeries object inside a Position object, which can hold one or more SpatialSeries objects.
position = types.core.Position();
position.spatialseries.set('SpatialSeries', position_spatial_series);

Create a Behavior Processing Module

Create a processing module called "behavior" for storing behavioral data in the NWBFile, then add the Position object to the processing module.
behavior_processing_module = types.core.ProcessingModule('description', 'stores behavioral data.');
behavior_processing_module.nwbdatainterface.set("Position", position);
nwb.processing.set("behavior", behavior_processing_module);

CompassDirection: Storing view angle measured over time

Analogous to how position can be stored, we can create a SpatialSeries object for representing the view angle of the subject.
For direction data reference_frame indicates the zero direction, for instance in this case "straight ahead" is 0 radians.
view_angle_data = linspace(0, 4, 50);
direction_spatial_series = types.core.SpatialSeries( ...
'description', 'View angle of the subject measured in radians.', ...
'data', view_angle_data, ...
'timestamps', timestamps, ...
'reference_frame', 'straight ahead', ...
'data_unit', 'radians' ...
);
direction = types.core.CompassDirection();
direction.spatialseries.set('spatial_series', direction_spatial_series);
We can add a CompassDirection object to the behavior processing module the same way we have added the position data.
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('CompassDirection', direction);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehaviorTimeSeries: Storing continuous behavior data

BehavioralTimeSeries is an interface for storing continuous behavior data, such as the speed of a subject.
speed_data = linspace(0, 0.4, 50);
 
speed_time_series = types.core.TimeSeries( ...
'data', speed_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 10.0, ... % Hz
'description', 'he speed of the subject measured over time.', ...
'data_unit', 'm/s' ...
);
 
behavioral_time_series = types.core.BehavioralTimeSeries();
behavioral_time_series.timeseries.set('speed', speed_time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralTimeSeries', behavioral_time_series);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehavioralEvents: Storing behavioral events

BehavioralEvents is an interface for storing behavioral events. We can use it for storing the timing and amount of rewards (e.g. water amount) or lever press times.
reward_amount = [1.0, 1.5, 1.0, 1.5];
event_timestamps = [1.0, 2.0, 5.0, 6.0];
 
time_series = types.core.TimeSeries( ...
'data', reward_amount, ...
'timestamps', event_timestamps, ...
'description', 'The water amount the subject received as a reward.', ...
'data_unit', 'ml' ...
);
 
behavioral_events = types.core.BehavioralEvents();
behavioral_events.timeseries.set('lever_presses', time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it
Storing only the timestamps of the events is possible with the ndx-events NWB extension. You can also add labels associated with the events with this extension. You can find information about installation and example usage here.

BehavioralEpochs: Storing intervals of behavior data

BehavioralEpochs is for storing intervals of behavior data. BehavioralEpochs uses IntervalSeries to represent the time intervals. Create an IntervalSeries object that represents the time intervals when the animal was running. IntervalSeries uses 1 to indicate the beginning of an interval and -1 to indicate the end.
run_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was running.', ...
'data', [1, -1, 1, -1, 1, -1], ...
'timestamps', [0.5, 1.5, 3.5, 4.0, 7.0, 7.3] ...
);
 
behavioral_epochs = types.core.BehavioralEpochs();
behavioral_epochs.intervalseries.set('running', run_intervals);
You can add more than one IntervalSeries to a BehavioralEpochs object.
sleep_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was sleeping', ...
'data', [1, -1, 1, -1], ...
'timestamps', [15.0, 30.0, 60.0, 95.0] ...
);
behavioral_epochs.intervalseries.set('sleeping', sleep_intervals);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
% behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
% nwb.processing.set('behavior', behavior_processing_module);

Another approach: TimeIntervals

Using TimeIntervals to represent time intervals is often preferred over BehavioralEpochs and IntervalSeries. TimeIntervals is a subclass of DynamicTable, which offers flexibility for tabular data by allowing the addition of optional columns which are not defined in the standard DynamicTable class.
sleep_intervals = types.core.TimeIntervals( ...
'description', 'Intervals when the animal was sleeping.', ...
'colnames', {'start_time', 'stop_time', 'stage'} ...
);
 
sleep_intervals.addRow('start_time', 0.3, 'stop_time', 0.35, 'stage', 1);
sleep_intervals.addRow('start_time', 0.7, 'stop_time', 0.9, 'stage', 2);
sleep_intervals.addRow('start_time', 1.3, 'stop_time', 3.0, 'stage', 3);
 
nwb.intervals.set('sleep_intervals', sleep_intervals);

EyeTracking: Storing continuous eye-tracking data of gaze direction

EyeTracking is for storing eye-tracking data which represents direction of gaze as measured by an eye tracking algorithm. An EyeTracking object holds one or more SpatialSeries objects that represent the gaze direction over time extracted from a video.
eye_position_data = [linspace(-20, 30, 50); linspace(30, -20, 50)];
 
right_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
left_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
eye_tracking = types.core.EyeTracking();
eye_tracking.spatialseries.set('right_eye_position', right_eye_position);
eye_tracking.spatialseries.set('left_eye_position', left_eye_position);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('EyeTracking', eye_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

PupilTracking: Storing continuous eye-tracking data of pupil size

PupilTracking is for storing eye-tracking data which represents pupil size. PupilTracking holds one or more TimeSeries objects that can represent different features such as the dilation of the pupil measured over time by a pupil tracking algorithm.
pupil_diameter = types.core.TimeSeries( ...
'description', 'Pupil diameter extracted from the video of the right eye.', ...
'data', linspace(0.001, 0.002, 50), ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 20.0, ... % Hz
'data_unit', 'meters' ...
);
 
pupil_tracking = types.core.PupilTracking();
pupil_tracking.timeseries.set('pupil_diameter', pupil_diameter);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('PupilTracking', pupil_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

Writing the behavior data to an NWB file

All of the above commands build an NWBFile object in-memory. To write this file, use nwbExport.
% Save to tutorials/tutorial_nwb_files folder
nwbFilePath = misc.getTutorialNwbFilePath('behavior_tutorial.nwb');
nwbExport(nwb, nwbFilePath);
fprintf('Exported NWB file to "%s"\n', 'behavior_tutorial.nwb')
Exported NWB file to "behavior_tutorial.nwb"
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/convertTrials.html b/docs/source/_static/html/tutorials/convertTrials.html new file mode 100644 index 00000000..1df73a43 --- /dev/null +++ b/docs/source/_static/html/tutorials/convertTrials.html @@ -0,0 +1,1119 @@ + + + + + +NWB File Conversion Tutorial + + + + + + + +
+

NWB File Conversion Tutorial

+ +

How to convert trial-based experimental data to the Neurodata Without Borders file format using MatNWB. This example uses the CRCNS ALM-3 data set. Information on how to download the data can be found on the CRCNS Download Page. One should first familiarize themselves with the file format, which can be found on the ALM-3 About Page under the Documentation files.

+
author: Lawrence Niu
+contact: lawrence@vidriotech.com
+last updated: Sep 14, 2024
+ +

Contents

+ +

Script Configuration

+

The following section describes configuration parameters specific to the publishing script, and can be skipped when implementing your own conversion. The parameters can be changed to fit any of the available sessions.

+
animal = 'ANM255201';
+session = '20141124';
+
+identifier = [animal '_' session];
+
+% Specify the local path for the downloaded data:
+data_root_path = 'data';
+
+metadata_loc = fullfile(data_root_path, 'metadata', ['meta_data_' identifier '.mat']);
+datastructure_loc = fullfile(data_root_path, 'data_structure_files',...
+    ['data_structure_' identifier '.mat']);
+rawdata_loc = fullfile(data_root_path, 'RawVoltageTraces', [identifier '.tar']);
+
+

The animal and session specifier can be changed with the animal and session variable name respectively. metadata_loc, datastructure_loc, and rawdata_loc should refer to the metadata .mat file, the data structure .mat file, and the raw .tar file.

+
output_directory = 'out';
+
+if ~isfolder(output_directory)
+    mkdir(output_directory);
+end
+
+source_file = [mfilename() '.m'];
+[~, source_script, ~] = fileparts(source_file);
+
+

The NWB file will be saved in the output directory indicated by output_directory +

+

General Information

+
nwb = NwbFile();
+nwb.identifier = identifier;
+nwb.general_source_script = source_script;
+nwb.general_source_script_file_name = source_file;
+nwb.general_lab = 'Svoboda';
+nwb.general_keywords = {'Network models', 'Premotor cortex', 'Short-term memory'};
+nwb.general_institution = ['Janelia Research Campus,'...
+    ' Howard Huges Medical Institute, Ashburn, Virginia 20147, USA'];
+nwb.general_related_publications = ...
+    ['Li N, Daie K, Svoboda K, Druckmann S (2016).',...
+    ' Robust neuronal dynamics in premotor cortex during motor planning.',...
+    ' Nature. 7600:459-64. doi: 10.1038/nature17643'];
+nwb.general_stimulus = 'photostim';
+nwb.general_protocol = 'IACUC';
+nwb.general_surgery = ['Mice were prepared for photoinhibition and ',...
+    'electrophysiology with a clear-skull cap and a headpost. ',...
+    'The scalp and periosteum over the dorsal surface of the skull were removed. ',...
+    'A layer of cyanoacrylate adhesive (Krazy glue, Elmer''s Products Inc.) ',...
+    'was directly applied to the intact skull. A custom made headpost ',...
+    'was placed on the skull with its anterior edge aligned with the suture lambda ',...
+    '(approximately over cerebellum) and cemented in place ',...
+    'with clear dental acrylic (Lang Dental Jet Repair Acrylic; 1223-clear). ',...
+    'A thin layer of clear dental acrylic was applied over the cyanoacrylate adhesive ',...
+    'covering the entire exposed skull, ',...
+    'followed by a thin layer of clear nail polish (Electron Microscopy Sciences, 72180).'];
+nwb.session_description = sprintf('Animal `%s` on Session `%s`', animal, session);
+
+

All properties with the prefix general contain context for the entire experiment such as lab, institution, and experimentors. For session-delimited data from the same experiment, these fields will all be the same. Note that most of this information was pulled from the publishing paper and not from any of the downloadable data.

+

The only required property is the identifier, which distinguishes one session from another within an experiment. In our case, the ALM-3 data uses a combination of session date and animal ID.

+

The ALM-3 File Structure

+

Each ALM-3 session has three files: a metadata .mat file describing the experiment, a data structures .mat file containing analyzed data, and a raw .tar archive containing multiple raw electrophysiology data separated by trials as .mat files. All files will be merged into a single NWB file.

+

Metadata

+

ALM-3 Metadata contains information about the reference times, experimental context, methodology, as well as details of the electrophysiology, optophysiology, and behavioral portions of the experiment. A vast majority of these details are placed in general prefixed properties in NWB.

+
fprintf('Processing Meta Data from `%s`\n', metadata_loc);
+loaded = load(metadata_loc, 'meta_data');
+meta = loaded.meta_data;
+
+% Experiment-specific treatment for animals with the ReaChR gene modification
+isreachr = any(cell2mat(strfind(meta.animalGeneModification, 'ReaChR')));
+
+% Sessions are separated by date of experiment.
+nwb.general_session_id = meta.dateOfExperiment;
+
+% ALM-3 data start time is equivalent to the reference time.
+nwb.session_start_time = datetime([meta.dateOfExperiment meta.timeOfExperiment],...
+    'InputFormat', 'yyyyMMddHHmmss', 'TimeZone', 'America/New_York'); % Eastern Daylight Time
+nwb.timestamps_reference_time = nwb.session_start_time;
+
+nwb.general_experimenter = strjoin(meta.experimenters, ', ');
+
+
Processing Meta Data from `data/metadata/meta_data_ANM255201_20141124.mat`
+
+
nwb.general_subject = types.core.Subject(...
+    'species', meta.species{1}, ...
+    'subject_id', meta.animalID{1}(1,:), ... %weird case with duplicate Animal ID
+    'sex', meta.sex, ...
+    'age', meta.dateOfBirth, ...
+    'description', [...
+        'Whisker Config: ' strjoin(meta.whiskerConfig, ', ') newline...
+        'Animal Source: ' strjoin(meta.animalSource, ', ')]);
+
+

Ideally, if a raw data field does not correspond directly to a NWB field, one would create their own using a custom NWB extension class. However, since these fields are mostly experimental annotations, we instead pack the extra values into the description field as a string.

+
+% The formatStruct function simply prints the field and values given the struct.
+% An optional cell array of field names specifies whitelist of fields to print.
+% This function is provided with this script in the tutorials directory.
+nwb.general_subject.genotype = formatStruct(...
+    meta, ...
+    {'animalStrain'; 'animalGeneModification'; 'animalGeneCopy';...
+    'animalGeneticBackground'});
+
+weight = {};
+if ~isempty(meta.weightBefore)
+    weight{end+1} = 'weightBefore';
+end
+if ~isempty(meta.weightAfter)
+    weight{end+1} = 'weightAfter';
+end
+weight = weight(~cellfun('isempty', weight));
+if ~isempty(weight)
+    nwb.general_subject.weight = formatStruct(meta, weight);
+end
+
+% general/experiment_description
+nwb.general_experiment_description = [...
+    formatStruct(meta, {'experimentType'; 'referenceAtlas'}), ...
+    newline, ...
+    formatStruct(meta.behavior, {'task_keyword'})];
+
+% Miscellaneous collection information from ALM-3 that didn't quite fit any NWB
+% properties are stored in general/data_collection.
+nwb.general_data_collection = formatStruct(meta.extracellular,...
+    {'extracellularDataType';'cellType';'identificationMethod';'amplifierRolloff';...
+    'spikeSorting';'ADunit'});
+
+% Device objects are essentially just a list of device names.  We store the probe
+% and laser hardware names here.
+probetype = meta.extracellular.probeType{1};
+probeSource = meta.extracellular.probeSource{1};
+deviceName = [probetype ' (' probeSource ')'];
+nwb.general_devices.set(deviceName, types.core.Device());
+
+if isreachr
+    laserName = 'laser-594nm (Cobolt Inc., Cobolt Mambo 100)';
+else
+    laserName = 'laser-473nm (Laser Quantum, Gem 473)';
+end
+nwb.general_devices.set(laserName, types.core.Device());
+
+
structDesc = {'recordingCoordinates';'recordingMarker';'recordingType';'penetrationN';...
+    'groundCoordinates'};
+if ~isempty(meta.extracellular.referenceCoordinates)
+    structDesc{end+1} = 'referenceCoordinates';
+end
+recordingLocation = meta.extracellular.recordingLocation{1};
+egroup = types.core.ElectrodeGroup(...
+    'description', formatStruct(meta.extracellular, structDesc),...
+    'location', recordingLocation,...
+    'device', types.untyped.SoftLink(['/general/devices/' deviceName]));
+nwb.general_extracellular_ephys.set(deviceName, egroup);
+
+

The NWB ElectrodeGroup object stores experimental information regarding a group of probes. Doing so requires a SoftLink to the probe specified under general_devices. SoftLink objects are direct maps to HDF5 Soft Links on export, and thus, require a true HDF5 path.

+
+% You can specify column names and values as key-value arguments in the DynamicTable
+% constructor.
+dtColNames = {'x', 'y', 'z', 'imp', 'location', 'filtering','group', 'group_name'};
+dynTable = types.hdmf_common.DynamicTable(...
+    'colnames', dtColNames,...
+    'description', 'Electrodes',...
+    'x', types.hdmf_common.VectorData('description', 'x coordinate of the channel location in the brain (+x is posterior).'),...
+    'y', types.hdmf_common.VectorData('description', 'y coordinate of the channel location in the brain (+y is inferior).'),...
+    'z', types.hdmf_common.VectorData('description', 'z coordinate of the channel location in the brain (+z is right).'),...
+    'imp', types.hdmf_common.VectorData('description', 'Impedance of the channel.'),...
+    'location', types.hdmf_common.VectorData('description', ['Location of the electrode (channel). '...
+    'Specify the area, layer, comments on estimation of area/layer, stereotaxic coordinates if '...
+    'in vivo, etc. Use standard atlas names for anatomical regions when possible.']),...
+    'filtering', types.hdmf_common.VectorData('description', 'Description of hardware filtering.'),...
+    'group', types.hdmf_common.VectorData('description', 'Reference to the ElectrodeGroup this electrode is a part of.'),...
+    'group_name', types.hdmf_common.VectorData('description', 'Name of the ElectrodeGroup this electrode is a part of.'));
+
+% Raw HDF5 path to the above electrode group. Referenced by
+% the general/extracellular_ephys Dynamic Table
+egroupPath = ['/general/extracellular_ephys/' deviceName];
+eGroupReference = types.untyped.ObjectView(egroupPath);
+for i = 1:length(meta.extracellular.siteLocations)
+    location = meta.extracellular.siteLocations{i};
+    % Add each row in the dynamic table. The `id` column is populated
+    % dynamically.
+    dynTable.addRow(...
+        'x', location(1), 'y', location(2), 'z', location(3),...
+        'imp', 0,...
+        'location', recordingLocation,...
+        'filtering', '',...
+        'group', eGroupReference,...
+        'group_name', probetype);
+end
+
+

The group column in the Dynamic Table contains an ObjectView to the previously created ElectrodeGroup. An ObjectView can be best thought of as a direct pointer to another typed object. It also directly maps to a HDF5 Object Reference, thus the HDF5 path requirement. ObjectViews are slightly different from SoftLinks in that they can be stored in datasets (data columns, tables, and data fields in NWBData objects).

+
nwb.general_extracellular_ephys_electrodes = dynTable;
+
+

The electrodes property in extracellular_ephys is a special keyword in NWB that must be paired with a Dynamic Table. These are tables which can have an unbounded number of columns and rows, each as their own dataset. With the exception of the id column, all other columns must be VectorData or VectorIndex objects. The id column, meanwhile, must be an ElementIdentifiers object. The names of all used columns are specified in the in the colnames property as a cell array of strings.

+
+% general/optogenetics/photostim
+nwb.general_optogenetics.set('photostim', ...
+    types.core.OptogeneticStimulusSite(...
+    'excitation_lambda', meta.photostim.photostimWavelength{1}, ...
+    'location', meta.photostim.photostimLocation{1}, ...
+    'device', types.untyped.SoftLink(['/general/devices/' laserName]), ...
+    'description', formatStruct(meta.photostim, {...
+    'stimulationMethod';'photostimCoordinates';'identificationMethod'})));
+
+

Analysis Data Structure

+

The ALM-3 data structures .mat file contains analyzed spike data, trial-specific parameters, and behavioral analysis data.

+

Hashes

+

ALM-3 stores its data structures in the form of hashes which are essentially the same as python's dictionaries or MATLAB's maps but where the keys and values are stored under separate struct fields. Getting a hashed value from a key involves retrieving the array index that the key is in and applying it to the parallel array in the values field.

+

You can find more information about hashes and how they're used on the ALM-3 about page.

+
fprintf('Processing Data Structure `%s`\n', datastructure_loc);
+loaded = load(datastructure_loc, 'obj');
+data = loaded.obj;
+
+% wherein each cell is one trial. We must populate this way because trials
+% may not be in trial order.
+% Trial timeseries will be a compound type under intervals/trials.
+trial_timeseries = cell(size(data.trialIds));
+
+
Processing Data Structure `data/data_structure_files/data_structure_ANM255201_20141124.mat`
+
+

NWB comes with default support for trial-based data. These must be TimeIntervals that are placed in the intervals property. Note that trials is a special keyword that is required for PyNWB compatibility.

+
ephus = data.timeSeriesArrayHash.value{1};
+ephusUnit = data.timeUnitNames{data.timeUnitIds(ephus.timeUnit)};
+
+% Lick direction and timestamps trace
+tsIdx = strcmp(ephus.idStr, 'lick_trace');
+bts = types.core.BehavioralTimeSeries();
+
+bts.timeseries.set('lick_trace_ts', ...
+    types.core.TimeSeries(...
+    'data', ephus.valueMatrix(:,tsIdx),...
+    'data_unit', ephusUnit,...
+    'description', ephus.idStrDetailed{tsIdx}, ...
+    'timestamps', ephus.time, ...
+    'timestamps_unit', ephusUnit));
+nwb.acquisition.set('lick_trace', bts);
+bts_ref = types.untyped.ObjectView('/acquisition/lick_trace/lick_trace_ts');
+
+% Acousto-optic modulator input trace
+tsIdx = strcmp(ephus.idStr, 'aom_input_trace');
+ts = types.core.TimeSeries(...
+    'data', ephus.valueMatrix(:,tsIdx), ...
+    'data_unit', 'Volts', ...
+    'description', ephus.idStrDetailed{tsIdx}, ...
+    'timestamps', ephus.time, ...
+    'timestamps_unit', ephusUnit);
+nwb.stimulus_presentation.set('aom_input_trace', ts);
+ts_ref = types.untyped.ObjectView('/stimulus/presentation/aom_input_trace');
+
+% Laser power
+tsIdx = strcmp(ephus.idStr, 'laser_power');
+ots = types.core.OptogeneticSeries(...
+    'data', ephus.valueMatrix(:, tsIdx), ...
+    'data_conversion', 1e-3, ... % data is stored in mW, data unit for OptogeneticSeries is watts
+    'description', ephus.idStrDetailed{tsIdx}, ...
+    'timestamps', ephus.time, ...
+    'timestamps_unit', ephusUnit, ...
+    'site', types.untyped.SoftLink('/general/optogenetics/photostim'));
+nwb.stimulus_presentation.set('laser_power', ots);
+ots_ref = types.untyped.ObjectView('/stimulus/presentation/laser_power');
+
+% Append trials timeseries references in order
+[ephus_trials, ~, trials_to_data] = unique(ephus.trial);
+for i=1:length(ephus_trials)
+    i_loc = i == trials_to_data;
+    t_start = find(i_loc, 1);
+    t_count = sum(i_loc);
+    trial = ephus_trials(i);
+
+    trial_timeseries{trial}(end+(1:3), :) = {...
+        bts_ref int64(t_start) int64(t_count);...
+        ts_ref  int64(t_start) int64(t_count);...
+        ots_ref int64(t_start) int64(t_count)};
+end
+
+

The timeseries property of the TimeIntervals object is an example of a compound data type. These types are essentially tables of data in HDF5 and can be represented by a MATLAB table, an array of structs, or a struct of arrays. Beware: validation of column lengths here is not guaranteed by the type checker until export.

+

+VectorIndex objects index into a larger VectorData column. The object that is being referenced is indicated by the target property, which uses an ObjectView. Each element in the VectorIndex marks the last element in the corresponding vector data object for the VectorIndex row. Thus, the starting index for this row would be the previous index + 1. Note that these indices must be 0-indexed for compatibility with pynwb. You can see this in effect with the timeseries property which is indexed by the timeseries_index property.

+

Though TimeIntervals is a subclass of the DynamicTable type, we opt for populating the Dynamic Table data by column instead of using `addRow` here because of how the data is formatted. DynamicTable is flexible enough to accomodate both styles of data conversion.

+
trials_epoch = types.core.TimeIntervals(...
+    'colnames', {'start_time'}, ...
+    'description', 'trial data and properties', ...
+    'start_time', types.hdmf_common.VectorData(...
+        'data', data.trialStartTimes', ...
+        'description', 'Start time of epoch, in seconds.'), ...
+    'id', types.hdmf_common.ElementIdentifiers(...
+        'data', data.trialIds' ) );
+
+% Add columns for the trial types
+for i=1:length(data.trialTypeStr)
+    columnName = data.trialTypeStr{i};
+    columnData = types.hdmf_common.VectorData(...
+         'data', data.trialTypeMat(i,:)', ... % transpose for column vector
+         'description', data.trialTypeStr{i});
+    trials_epoch.addColumn( columnName, columnData )
+end
+
+% Add columns for the trial properties
+for i=1:length(data.trialPropertiesHash.keyNames)
+    columnName = data.trialPropertiesHash.keyNames{i};
+    descr = data.trialPropertiesHash.descr{i};
+    if iscellstr(descr)
+        descr = strjoin(descr, newline);
+    end
+    columnData = types.hdmf_common.VectorData(...
+         'data', data.trialPropertiesHash.value{i},...
+         'description', data.trialTypeStr{i});
+    trials_epoch.addColumn( columnName, columnData )
+end
+
+nwb.intervals_trials = trials_epoch;
+
+

Ephus spike data is separated into units which directly maps to the NWB property of the same name. Each such unit contains a group of analysed waveforms and spike times, all linked to a different subset of trials IDs.

+

The waveforms are placed in the analysis Set and are paired with their unit name ('unitx' where 'x' is some unit ID).

+

Trial IDs, wherever they are used, are placed in a relevent control property in the data object and will indicate what data is associated with what trial as defined in trials's id column.

+
nwb.units = types.core.Units('colnames',...
+    {'spike_times', 'trials', 'waveforms'},...
+    'description', 'Analysed Spike Events');
+esHash = data.eventSeriesHash;
+ids = regexp(esHash.keyNames, '^unit(\d+)$', 'once', 'tokens');
+ids = str2double([ids{:}]);
+nwb.units.spike_times = types.hdmf_common.VectorData(...
+    'description', 'timestamps of spikes');
+
+for i=1:length(ids)
+    esData = esHash.value{i};
+    % Add trials ID reference
+
+    good_trials_mask = ismember(esData.eventTrials, nwb.intervals_trials.id.data);
+    eventTrials = esData.eventTrials(good_trials_mask);
+    eventTimes = esData.eventTimes(good_trials_mask);
+    waveforms = esData.waveforms(good_trials_mask,:);
+    channel = esData.channel(good_trials_mask);
+
+    % Add waveform data to "unitx" and associate with "waveform" column as ObjectView.
+    ses = types.core.SpikeEventSeries(...
+        'control', ids(i),...
+        'control_description', 'Units Table ID',...
+        'data', waveforms .', ...
+        'description', esHash.descr{i}, ...
+        'timestamps', eventTimes, ...
+        'timestamps_unit', data.timeUnitNames{data.timeUnitIds(esData.timeUnit)},...
+        'electrodes', types.hdmf_common.DynamicTableRegion(...
+            'description', 'Electrodes involved with these spike events',...
+            'table', types.untyped.ObjectView('/general/extracellular_ephys/electrodes'),...
+            'data', channel - 1));
+    ses_name = esHash.keyNames{i};
+    ses_ref = types.untyped.ObjectView(['/analysis/', ses_name]);
+    if ~isempty(esData.cellType)
+        ses.comments = ['cellType: ' esData.cellType{1}];
+    end
+    nwb.analysis.set(ses_name, ses);
+    nwb.units.addRow(...
+        'id', ids(i), 'trials', eventTrials, 'spike_times', eventTimes, 'waveforms', ses_ref);
+
+    % Add this timeseries into the trials table as well.
+    [s_trials, ~, trials_to_data] = unique(eventTrials);
+    for j=1:length(s_trials)
+        trial = s_trials(j);
+        j_loc = j == trials_to_data;
+        t_start = find(j_loc, 1);
+        t_count = sum(j_loc);
+
+        trial_timeseries{trial}(end+1, :) = {ses_ref int64(t_start) int64(t_count)};
+    end
+end
+
+

To better understand how spike_times_index and spike_times map to each other, refer to this diagram from the Extracellular Electrophysiology Tutorial.

+

Raw Acquisition Data

+

Each ALM-3 session is associated with a large number of raw voltage data grouped by trial ID. To map this data to NWB, each trial is created as its own ElectricalSeries object under the name 'trial n' where 'n' is the trial ID. The trials are then linked to the trials dynamic table for easy referencing.

+
fprintf('Processing Raw Acquisition Data from `%s` (will take a while)\n', rawdata_loc);
+untarLoc = strrep(rawdata_loc, '.tar', '');
+if ~isfolder(untarLoc)
+    untar(rawdata_loc, fileparts(rawdata_loc));
+end
+
+rawfiles = dir(untarLoc);
+rawfiles = fullfile(untarLoc, {rawfiles(~[rawfiles.isdir]).name});
+
+nrows = length(nwb.general_extracellular_ephys_electrodes.id.data);
+tablereg = types.hdmf_common.DynamicTableRegion(...
+    'description','Relevent Electrodes for this Electrical Series',...
+    'table',types.untyped.ObjectView('/general/extracellular_ephys/electrodes'),...
+    'data',(1:nrows) - 1);
+objrefs = cell(size(rawfiles));
+
+endTimestamps = trials_epoch.start_time.data;
+for i=1:length(rawfiles)
+    tnumstr = regexp(rawfiles{i}, '_trial_(\d+)\.mat$', 'tokens', 'once');
+    tnumstr = tnumstr{1};
+    rawdata = load(rawfiles{i}, 'ch_MUA', 'TimeStamps');
+    tnum = str2double(tnumstr);
+
+    if tnum > length(endTimestamps)
+        continue; % sometimes there are extra trials without an associated start time.
+    end
+
+    es = types.core.ElectricalSeries(...
+        'data', rawdata.ch_MUA,...
+        'description', ['Raw Voltage Acquisition for trial ' tnumstr],...
+        'electrodes', tablereg,...
+        'timestamps', rawdata.TimeStamps);
+    tname = ['trial ' tnumstr];
+    nwb.acquisition.set(tname, es);
+
+    endTimestamps(tnum) = endTimestamps(tnum) + rawdata.TimeStamps(end);
+    objrefs{tnum} = types.untyped.ObjectView(['/acquisition/' tname]);
+end
+
+% Link to the raw data by adding the acquisition column with ObjectViews
+% to the data
+emptyrefs = cellfun('isempty', objrefs);
+objrefs(emptyrefs) = {types.untyped.ObjectView('')};
+
+trials_epoch.addColumn('acquisition', types.hdmf_common.VectorData(...
+    'description', 'soft link to acquisition data for this trial',...
+    'data', [objrefs{:}]'));
+
+trials_epoch.stop_time = types.hdmf_common.VectorData(...
+     'data', endTimestamps',...
+     'description', 'the end time of each trial');
+trials_epoch.colnames{end+1} = 'stop_time';
+
+
Processing Raw Acquisition Data from `data/RawVoltageTraces/ANM255201_20141124.tar` (will take a while)
+
+

Add timeseries to trials_epoch

+

First, we'll format and store trial_timeseries into intervals_trials. note that timeseries_index data is 0-indexed.

+
ts_len = cellfun('size', trial_timeseries, 1);
+nwb.intervals_trials.timeseries_index = types.hdmf_common.VectorIndex(...
+    'description', 'Index into Timeseries VectorData', ...
+    'data', cumsum(ts_len)', ...
+    'target', types.untyped.ObjectView('/intervals/trials/timeseries') );
+
+% Intervals/trials/timeseries is a compound type so we use cell2table to
+% convert this 2-d cell array into a compatible table.
+is_len_nonzero = ts_len > 0;
+trial_timeseries_table = cell2table(vertcat(trial_timeseries{is_len_nonzero}),...
+    'VariableNames', {'timeseries', 'idx_start', 'count'});
+trial_timeseries_table = movevars(trial_timeseries_table, 'timeseries', 'After', 'count');
+
+interval_trials_timeseries = types.core.TimeSeriesReferenceVectorData(...
+    'description', 'Index into TimeSeries data', ...
+    'data', trial_timeseries_table);
+nwb.intervals_trials.timeseries = interval_trials_timeseries;
+nwb.intervals_trials.colnames{end+1} = 'timeseries';
+
+

Export

+
nwbFilePath = fullfile(output_directory, [identifier '.nwb']);
+if isfile(nwbFilePath)
+    delete(nwbFilePath);
+end
+nwbExport(nwb, nwbFilePath);
+
+ +
+ + + diff --git a/docs/source/_static/html/tutorials/dataPipe.html b/docs/source/_static/html/tutorials/dataPipe.html new file mode 100644 index 00000000..1d404b4f --- /dev/null +++ b/docs/source/_static/html/tutorials/dataPipe.html @@ -0,0 +1,441 @@ + + + + + +Neurodata Without Borders (NWB) advanced write using DataPipe + + + + + + + +
+

Neurodata Without Borders (NWB) advanced write using DataPipe

+ +

How to utilize HDF5 compression using dataPipe

+
authors: Ivan Smalianchuk and Ben Dichter
+contact: smalianchuk.ivan@gmail.com, ben.dichter@catalystneuro.com
+last edited: Jan 04, 2021
+ +

Contents

+ +

Neurophysiology data can be quite large, often in the 10s of GB per session and sometimes much larger. Here, we demonstrate methods in MatNWB that allow you to deal with large datasets. These methods are compression and iterative write. Both of these techniques use the types.untyped.DataPipe object, which sends specific instructions to the HDF5 backend about how to store data.

+

Compression - basic implementation

+

To compress experimental data (in this case a 3D matrix with dimensions [250 250 70]) one must assign it as a DataPipe type:

+
DataToCompress = randi(100, 250, 250, 70);
+DataPipe = types.untyped.DataPipe('data', DataToCompress);
+
+

This is the most basic way to acheive compression, and all of the optimization decisions are automatically determined by MatNWB.

+

Background

+

HDF5 has built-in ability to compress and decompress individual datasets. If applied intelligently, this can dramatically reduce the amount of space used on the hard drive to represent the data. The end user does not need to worry about the compression status of the dataset- HDF5 will automatically decompress the dataset on read.

+

The above example uses default chunk size and compression level (3). To optimize compression, compressionLevel and chunkSize must be considered. compressionLevel ranges from 0 - 9 where 9 is the highest level of compression and 0 is the lowest. chunkSize is less intuitive to adjust; to implement compression, chunk size must be less than data size.

+

+DataPipe Arguments

+

+ + + + + + + +
maxSizeSets the maximum size of the HDF5 Dataset. Unless using iterative writing, this should match the size of Data. To append data later, use the maxSize for the full dataset. You can use Inf for a value of a dimension if you do not know its final size.
dataThe data to compress. Must be numerical data.
axisSet which axis to increment when appending more data.
dataTypeSets the type of the experimental data. This must be a numeric data type. Useful to include when using iterative write to append data as the appended data must be the same data type. If data is provided and dataType is not, the dataType is inferred from the provided data.
chunkSizeSets chunk size for the compression. Must be less than maxSize.
compressionLevelLevel of compression ranging from 0-9 where 9 is the highest level of compression. The default is level 3.
offsetAxis offset of dataset to append. May be used to overwrite data.
+

+

Chunking

+

HDF5 Datasets can be either stored in continuous or chunked mode. Continuous means that all of the data is written to one continuous block on the hard drive, and chunked means that the dataset is automatically split into chunks that are distributed across the hard drive. The user does not need to know the mode used- HDF5 handles the gathering of chunks automatically. However, it is worth understanding these chunks because they can have a big impact on space used and read and write speed. When using compression, the dataset MUST be chunked. HDF5 is not able to apply compression to continuous datasets.

+

If chunkSize is not explicitly specified, dataPipe will determine an appropriate chunk size. However, you can optimize the performance of the compression by manually specifying the chunk size using chunkSize argument.

+

We can demonstrate the benefit of chunking by exploring the following scenario. The following code utilizes DataPipe's default chunk size:

+
fData = randi(250, 100, 1000); % Create fake data
+
+% create an nwb structure with required fields
+nwb = NwbFile( ...
+    'session_start_time', datetime('2020-01-01 00:00:00', 'TimeZone', 'local'), ...
+    'identifier', 'ident1', ...
+    'session_description', 'DataPipeTutorial');
+
+fData_compressed = types.untyped.DataPipe('data', fData);
+
+fdataNWB=types.core.TimeSeries( ...
+    'data', fData_compressed, ...
+    'data_unit', 'mV', ...
+    'starting_time', 0.0, ...
+    'starting_time_rate', 30.0);
+
+nwb.acquisition.set('data', fdataNWB);
+
+nwbExport(nwb, 'DefaultChunks.nwb');
+
+

This results in a file size of 47MB (too large), and the process takes 11 seconds (far too long). Setting the chunk size manually as in the example code below resolves these issues:

+
fData_compressed = types.untyped.DataPipe( ...
+    'data', fData, ...
+    'chunkSize', [1, 1000], ...
+    'axis', 1);
+
+

This change results in the operation completing in 0.7 seconds and resulting file size of 1.1MB. The chunk size was chosen such that it spans each individual row of the matrix.

+

Use the combination of arugments that fit your need. When dealing with large datasets, you may want to use iterative write to ensure that you stay within the bounds of your system memory and use chunking and compression to optimize storage, read and write of the data.

+

Iterative Writing

+

If experimental data is close to, or exceeds the available system memory, performance issues may arise. To combat this effect of large data, DataPipe can utilize iterative writing, where only a portion of the data is first compressed and saved, and then additional portions are appended.

+

To demonstrate, we can create a nwb file with a compressed time series data:

+
dataPart1 = randi(250, 1, 1000); % "load" 1/4 of the entire dataset
+fullDataSize = [1 40000]; % this is the size of the TOTAL dataset
+
+% create an nwb structure with required fields
+nwb=NwbFile( ...
+    'session_start_time', datetime('2020-01-01 00:00:00', 'TimeZone', 'local'), ...
+    'identifier', 'ident1', ...
+    'session_description', 'DataPipeTutorial');
+
+% compress the data
+fData_use = types.untyped.DataPipe( ...
+    'data', dataPart1, ...
+    'maxSize', fullDataSize, ...
+    'axis', 2);
+
+%Set the compressed data as a time series
+fdataNWB = types.core.TimeSeries( ...
+    'data', fData_use, ...
+    'data_unit', 'mV', ...
+    'starting_time', 0.0, ...
+    'starting_time_rate', 30.0);
+
+nwb.acquisition.set('time_series', fdataNWB);
+
+nwbExport(nwb, 'DataPipeTutorial_iterate.nwb');
+
+

To append the rest of the data, simply load the NWB file and use the append method:

+
nwb = nwbRead('DataPipeTutorial_iterate.nwb', 'ignorecache'); %load the nwb file with partial data
+
+% "load" each of the remaining 1/4ths of the large dataset
+for i = 2:4 % iterating through parts of data
+    dataPart_i=randi(250, 1, 10000); % faked data chunk as if it was loaded
+    nwb.acquisition.get('time_series').data.append(dataPart_i); % append the loaded data
+end
+
+

The axis property defines the dimension in which additional data will be appended. In the above example, the resulting dataset will be 4000x1. However, if we set axis to 2 (and change fullDataSize appropriately), then the resulting dataset will be 1000x4.

+

Timeseries example

+

Following is an example of how to compress and add a timeseries to an NWB file:

+
fData=randi(250, 1, 10000); % create fake data;
+
+%assign data without compression
+nwb=NwbFile(...
+    'session_start_time', datetime(2020, 1, 1, 0, 0, 0, 'TimeZone', 'local'), ...
+    'identifier','ident1', ...
+    'session_description', 'DataPipeTutorial');
+
+ephys_module = types.core.ProcessingModule( ...
+    'description', 'holds processed ephys data');
+
+nwb.processing.set('ephys', ephys_module);
+
+% compress the data
+fData_compressed=types.untyped.DataPipe( ...
+    'data', fData, ...
+    'compressionLevel', 3, ...
+    'chunkSize', [100 1], ...
+    'axis', 1);
+
+% Assign the data to appropriate module and write the NWB file
+fdataNWB=types.core.TimeSeries( ...
+    'data', fData_compressed, ...
+    'data_unit', 'mV', ...
+    'starting_time', 0.0, ...
+    'starting_time_rate', 30.0);
+
+ephys_module.nwbdatainterface.set('data', fdataNWB);
+nwb.processing.set('ephys', ephys_module);
+
+% write the file
+nwbExport(nwb, 'Compressed.nwb');
+
+ +
+ + + diff --git a/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html b/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html new file mode 100644 index 00000000..34e8d692 --- /dev/null +++ b/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html @@ -0,0 +1,92 @@ + +MatNWB <-> HDF5 Dimension Mapping

MatNWB <-> HDF5 Dimension Mapping

This tutorial demonstrates how the dimensions of a MATLAB array maps onto a dataset in HDF5. There are two main differences between the way MATLAB and HDF5 represents dimensions:
  1. C-ordering vs F-ordering: HDF5 is C-ordered, which means it stores data in a rows-first pattern, whereas MATLAB is F-ordered, storing data in the reverse pattern, with the last dimension of the array stored consecutively. The result is that the data in HDF5 is effectively the transpose of the array in MATLAB.
  2. 1D data (i.e vectors): HDF5 can store 1-D arrays, but in MATLAB the lowest dimensionality of an array is 2-D.
Due to differences in how MATLAB and HDF5 represent data, the dimensions of datasets are flipped when writing to/from file in MatNWB. Additionally, MATLAB represents 1D vectors in a 2D format, either as row vectors or column vectors, whereas HDF5 treats vectors as truly 1D. Consequently, when a 1D dataset from HDF5 is loaded into MATLAB, it is always represented as a column vector. To avoid unintentional changes in data dimensions, it is therefore recommended to avoid writing row vectors into an NWB file for 1D datasets.
Contrast this tutorial with the dimensionMapWithDataPipes tutorial that illustrates how vectors are represented differently when using DataPipe objects within VectorData objects.

Create Table

First, create a TimeIntervals table of height 10.
% Define VectorData objects for each column
% 1D column
start_col = types.hdmf_common.VectorData( ...
'description', 'start_times column', ...
'data', (1:10)' ... # maps onto HDF5 dataset of size (10,)
);
% 1D column
stop_col = types.hdmf_common.VectorData( ...
'description', 'stop_times column', ...
'data', (2:11)' ... # maps onto HDF5 dataset of size (10,)
);
% 4D column
randomval_col = types.hdmf_common.VectorData( ...
'description', 'randomvalues column', ...
'data', rand(5,2,3,10) ... # maps onto HDF5 dataset of size (10, 3, 2, 5)
);
 
% 1D column
id_col = types.hdmf_common.ElementIdentifiers('data', int64(0:9)'); % maps onto HDF5 dataset of size (10,)
 
% Create table
trials_table = types.core.TimeIntervals(...
'description', 'test dynamic table column',...
'colnames', {'start_time','stop_time','randomvalues'}, ...
'start_time', start_col, ...
'stop_time', stop_col, ...
'randomvalues', randomval_col, ...
'id', id_col ...
);

Export Table

Create NWB file with TimeIntervals table and export.
% Create NwbFile object with required arguments
file = NwbFile( ...
'session_start_time', datetime('2022-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test file' ...
);
% Assign to intervals_trials
file.intervals_trials = trials_table;
% Export
nwbExport(file, 'testFileNoDataPipes.nwb');
You can examine the dimensions of the datasets on file using HDFView. Screenshots for this file are below.
Screen Shot 2022-01-07 at 11.07.25 AM.png
Screen Shot 2022-01-07 at 11.07.19 AM.png
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html b/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html new file mode 100644 index 00000000..fb17536d --- /dev/null +++ b/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html @@ -0,0 +1,110 @@ + +MatNWB <-> HDF5 Dimension Mapping

MatNWB <-> HDF5 Dimension Mapping

This tutorial is easier to follow if you have already looked at the dimensionMapNoDataPipes tutorial or if you compare these side by side.
The key difference when using DataPipe instead of VectorData is that 1D data can be represented in HDF5 as 2D, thus allowing you to write either row or column vectors. This is made possible because of the maxSize property of the DataPipe class, which lets you specify a max size for each dimension. By setting the maxSize to [1, N] or [N, 1], vectors in HDF5 are represented as 2D arrays, just like in MATLAB. The flipping of the dimension order still applies, so a row vector in MATLAB becomes a column vector in HDF5 and vice versa.
Please note: The following tutorial mixes row and column vectors and does not produce a valid dynamic table. The tutorial is only meant to showcase how data maps onto HDF5 datasets when using DataPipe objects.

Create Table

First, create an expandable TimeIntervals table of height 10.
% 1D column
start_col = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', 1:10, ... # maps onto HDF5 dataset of size (10, )
'maxSize', Inf ...
) ...
);
% 1D column
stop_col = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', 1:10, ... # maps onto HDF5 dataset of size (10, 1)
'maxSize', [1 Inf], ...
'axis', 2 ...
) ...
);
% 1D column
cond_col = types.hdmf_common.VectorData( ...
'description', 'condition column', ...
'data', types.untyped.DataPipe( ...
'data', randi(2,10,1), ... # maps onto HDF5 dataset of size (1, 10)
'maxSize', [Inf, 1], ...
'axis', 1 ...
) ...
);
% 4D column
randomval_col = types.hdmf_common.VectorData( ...
'description', 'randomvalues column', ...
'data', types.untyped.DataPipe( ...
'data', rand(5,2,3,10), ... # maps onto HDF5 dataset of size (10, 3, 2, 5)
'maxSize', [5, 2, 3, Inf], ...
'axis', 4 ...
) ...
);
% 1D column
ids_col = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int64(0:9), ... # maps onto HDF5 dataset of size (10, )
'maxSize', Inf ...
) ...
);
% Create table
trials_table = types.core.TimeIntervals(...
'description', 'test dynamic table column',...
'colnames', {'start_time', 'stop_time', 'randomvalues', 'conditions'}, ...
'start_time', start_col, ...
'stop_time', stop_col, ...
'conditions', cond_col, ...
'randomvalues', randomval_col, ...
'id', ids_col ...
);

Export Table

Create NWB file with expandable TimeIntervals table and export.
% Create NwbFile object with required arguments
file = NwbFile( ...
'session_start_time', datetime('2022-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test file' ...
);
% Assign to intervals_trials
file.intervals_trials = trials_table;
% Export
nwbExport(file, 'testFileWithDataPipes.nwb');
You can examine the dimensions of the datasets on file using HDFView. Screenshots for this file are below.
Screen Shot 2022-01-12 at 1.12.42 PM.png
Screen Shot 2022-01-12 at 1.12.47 PM.png
Screen Shot 2022-01-07 at 4.26.21 PM.png
Screen Shot 2022-01-07 at 4.26.12 PM.png
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/dynamic_tables.html b/docs/source/_static/html/tutorials/dynamic_tables.html new file mode 100644 index 00000000..af3f6c88 --- /dev/null +++ b/docs/source/_static/html/tutorials/dynamic_tables.html @@ -0,0 +1,577 @@ + +DynamicTables Tutorial

DynamicTables Tutorial

This is a user guide to interacting with DynamicTable objects in MatNWB.

MatNWB Setup

Start by setting up your MATLAB workspace. The code below adds the directory containing the MatNWB package to the MATLAB search path. MatNWB works by automatically creating API classes based on a defined schema.
%{
path_to_matnwb = '~/Repositories/matnwb'; % change to your own path location
addpath(genpath(pwd));
%}

Constructing a table with initialized columns

The DynamicTable class represents a column-based table to which you can add custom columns. It consists of a description, a list of columns , and a list of row IDs. You can create a DynamicTable by first defining the VectorData objects that will make up the columns of the table. Each VectorData object must contain the same number of rows. A list of rows IDs may be passed to the DynamicTable using the id argument. Row IDs are a useful way to access row information independent of row location index. The list of row IDs must be cast as an ElementIdentifiers object before being passed to the DynamicTable object. If no value is passed to id, an ElementIdentifiers object with 0-indexed row IDs will be created for you automatically.
MATLAB Syntax Note: Using column vectors is crucial to properly build vectors and tables. When defining individual values, make sure to use semi-colon (;) instead of instead of comma (,) when defining the data fields of these.
col1 = types.hdmf_common.VectorData( ...
'description', 'column #1', ...
'data', [1;2] ...
);
 
col2 = types.hdmf_common.VectorData( ...
'description', 'column #2', ...
'data', {'a';'b'} ...
);
 
my_table = types.hdmf_common.DynamicTable( ...
'description', 'an example table', ...
'colnames', {'col1', 'col2'}, ...
'col1', col1, ...
'col2', col2, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0;1]) ... % 0-indexed, for compatibility with Python
);
my_table
my_table =
DynamicTable with properties: + + id: [1×1 types.hdmf_common.ElementIdentifiers] + colnames: {'col1' 'col2'} + description: 'an example table' + vectordata: [2×1 types.untyped.Set] +

Adding rows

You can add rows to an existing DynamicTable using the object's addRow method. One way of using this method is to pass in the names of columns as parameter names followed by the elements to append. The class of the elements of the column must match the elements to append.
my_table.addRow('col1', 3, 'col2', {'c'}, 'id', 2);

Adding columns

You can add new columns to an existing DynamicTable object using the addColumn method. One way of using this method is to pass in the names of each new column followed by the corresponding values for each new column. The height of the new columns must match the height of the table.
col3 = types.hdmf_common.VectorData('description', 'column #3', ...
'data', [100; 200; 300]);
col4 = types.hdmf_common.VectorData('description', 'column #4', ...
'data', {'a1'; 'b2'; 'c3'});
 
my_table.addColumn('col3', col3,'col4', col4);

Create MATLAB table and convert to dynamic table

As an alternative to building a dynamic table using the DynamicTable and VectorData data types, it is also possible to create a MATLAB table and convert it to a dynamic table. Lets create the same table as before, but using MATLAB's table class:
% Create a table with two variables (columns):
T = table([1;2], {'a';'b'}, 'VariableNames', {'col1', 'col2'});
T.Properties.VariableDescriptions = {'column #1', 'column #2'};

Adding rows

T(end+1, :) = {3, 'c'};

Adding variables (columns)

T = addvars(T, [100;200;300], 'NewVariableNames',{'col3'});
T.Properties.VariableDescriptions{3} = 'column #3';
 
% Alternatively, a new variable can be added directly using dot syntax.
T.col4 = {'a1'; 'b2'; 'c3'};
T.Properties.VariableDescriptions{4} = 'column #4';
T
T = 3×4 table
 col1col2col3col4
11'a'100'a1'
22'b'200'b2'
33'c'300'c3'

Convert to dynamic table

dynamic_table = util.table2nwb(T, 'A MATLAB table that was converted to a dynamic table')
dynamic_table =
DynamicTable with properties: + + id: [1×1 types.hdmf_common.ElementIdentifiers] + colnames: {'col1' 'col2' 'col3' 'col4'} + description: 'A MATLAB table that was converted to a dynamic table' + vectordata: [4×1 types.untyped.Set] +

Enumerated (categorical) data

EnumData is a special type of column for storing an enumerated data type. This way each unique value is stored once, and the data references those values by index. Using this method is more efficient than storing a single value many times, and has the advantage of communicating to downstream tools that the data is categorical in nature.

Warning Regarding EnumData

EnumData is currently an experimental feature and as such should not be used in a production environment.
CellTypeElements = types.hdmf_common.VectorData(...
'description', 'fixed set of elements referenced by cell_type' ...
, 'data', {'aa', 'bb', 'cc'} ... % the enumerated elements
);
CellType = types.hdmf_experimental.EnumData( ...
'description', 'this column holds categorical variables' ... % properties derived from VectorData
, 'data', [0, 1, 2, 1, 0] ... % zero-indexed offset to elements.
, 'elements', types.untyped.ObjectView(CellTypeElements) ...
);
 
MyTable = types.hdmf_common.DynamicTable('description', 'an example table');
MyTable.vectordata.set('cell_type_elements', CellTypeElements); % the *_elements format is required for compatibility with pynwb
MyTable.addColumn('cell_type', CellType);

Ragged array columns

A table column with a different number of elements for each row is called a "ragged array column." To define a table with a ragged array column, pass both the VectorData and the corresponding VectorIndex as columns of the DynamicTable object. The VectorData columns will contain the data values. The VectorIndex column serves to indicate how to arrange the data across rows. By convention the VectorIndex object corresponding to a particular column must have have the same name with the addition of the '_index' suffix.
Below, the VectorIndex values indicate to place the 1st to 3rd (inclusive) elements of the VectorData into the first row and 4th element into the second row. The resulting table will have the cell {'1a'; '1b'; '1c'} in the first row and the cell {'2a'} in the second row.
 
col1 = types.hdmf_common.VectorData( ...
'description', 'column #1', ...
'data', {'1a'; '1b'; '1c'; '2a'} ...
);
 
col1_index = types.hdmf_common.VectorIndex( ...
'description', 'column #1 index', ...
'target',types.untyped.ObjectView(col1), ... % object view of target column
'data', [3; 4] ...
);
 
table_ragged_col = types.hdmf_common.DynamicTable( ...
'description', 'an example table', ...
'colnames', {'col1'}, ...
'col1', col1, ...
'col1_index', col1_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1]) ... % 0-indexed, for compatibility with Python
);

Adding ragged array rows

You can add a new row to the ragged array column. Under the hood, the addRow method will add the appropriate value to the VectorIndex column to maintain proper formatting.
table_ragged_col.addRow('col1', {'3a'; '3b'; '3c'}, 'id', 2);

Accessing row elements

You can access data from entire rows of a DynamicTable object by calling the getRow method for the corresponding object. You can supply either an individual row number or a list of row numbers.
my_table.getRow(1)
ans = 1×4 table
 col1col2col3col4
11'a'100'a1'
If you want to access values for just a subset of columns you can pass in the 'columns' argument along with a cell array with the desired column names
my_table.getRow(1:3, 'columns', {'col1'})
ans = 3×1 table
 col1
11
22
33
You can also access specific rows by their corresponding row ID's, if they have been defined, by supplying a 'true' Boolean to the 'useId' parameter
my_table.getRow(1, 'useId', true)
ans = 1×4 table
 col1col2col3col4
12'b'200'b2'
For a ragged array columns, the getRow method will return a cell with different number of elements for each row
table_ragged_col.getRow(1:2)
ans = 2×1 table
 col1
1[{'1a'};{'1b'};{'1c'}]
21×1 cell

Accessing column elements

To access all rows from a particular column use the .get method on the vectordata field of the DynamicTable object
 
my_table.vectordata.get('col2').data
ans = 3×1 cell
'a'
'b'
'c'

Referencing rows of other tables

You can create a column that references rows of other tables by adding a DynamicTableRegion object as a column of a DynamicTable. This is analogous to a foreign key in a relational database. The DynamicTableRegion class takes in an ObjectView object as argument. ObjectView objects create links from one object type referencing another.
dtr_col = types.hdmf_common.DynamicTableRegion( ...
'description', 'references multiple rows of earlier table', ...
'data', [0; 1; 1; 0], ... # 0-indexed
'table',types.untyped.ObjectView(my_table) ... % object view of target table
);
 
data_col = types.hdmf_common.VectorData( ...
'description', 'data column', ...
'data', {'a'; 'b'; 'c'; 'd'} ...
);
 
dtr_table = types.hdmf_common.DynamicTable( ...
'description', 'test table with DynamicTableRegion', ...
'colnames', {'data_col', 'dtr_col'}, ...
'dtr_col', dtr_col, ...
'data_col',data_col, ...
'id',types.hdmf_common.ElementIdentifiers('data', [0; 1; 2; 3]) ...
);

Converting a DynamicTable to a MATLAB table

You can convert a DynamicTable object to a MATLAB table by making use of the object's toTable method. This is a useful way to view the whole table in a human-readable format.
my_table.toTable()
ans = 3×5 table
 idcol1col2col3col4
101'a'100'a1'
212'b'200'b2'
323'c'300'c3'
When the DynamicTable object contains a column that references other tables, you can pass in a Boolean to indicate whether to include just the row indices of the referenced table. Passing in false will result in inclusion of the referenced rows as nested tables.
dtr_table.toTable(false)
ans = 4×3 table
 iddata_coldtr_col
10'a'1×4 table
21'b'1×4 table
32'c'1×4 table
43'd'1×4 table

Creating an expandable table

When using the default HDF5 backend, each column of these tables is an HDF5 Dataset, which by default are set to an unchangeable size. This means that once a file is written, it is not possible to add a new row. If you want to be able to save this file, load it, and add more rows to the table, you will need to set this up when you create the VectorData and ElementIdentifiers columns of a DynamicTable. Specifically, you must wrap the column data with a DataPipe object. The DataPipe class takes in maxSize and axis as arguments to indicate the maximum desired size for each axis and the axis to which to append to, respectively. For example, creating a DataPipe object with a maxSize value equal to [Inf, 1] indicates that the number of rows may increase indifinetely. In contrast, setting maxSize equal to [8, 1] would allow the column to grow to a maximum height of 8.
% create NwbFile object with required fields
file= NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'ExpandableTableTutorial' ...
);
 
% create VectorData objects with DataPipe objects
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', [1, 2], ... # data must be numerical
'maxSize', Inf ...
) ...
);
 
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', [2, 3], ... #data must be numerical
'maxSize', Inf ...
) ...
);
 
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(5, 2), ... #data must be numerical
'maxSize', [5, Inf], ...
'axis', 2 ...
) ...
);
 
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int32([0; 1]), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'id', ids_exp ...
);
% export file
nwbExport(file, 'expandableTableTestFile.nwb');
Now, you can read in the file, add more rows, and save again to file
readFile = nwbRead('expandableTableTestFile.nwb', 'ignorecache');
readFile.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 4, ...
'randomvalues', rand(5,1), ...
'id', 2 ...
)
nwbExport(readFile, 'expandableTableTestFile.nwb');
Note: DataPipe objects change how the dimension of the datasets for each column map onto the shape of HDF5 datasets. See README for more details.

Multidimensional Columns

The order of dimensions of multidimensional columns in MatNWB is reversed relative to the Python HDMF package (see README for detailed explanation). Therefore, the height of a multidimensional column belonging to a DynamicTable object is defined by the shape of its last dimension. A valid DynamicTable must have matched height across columns.

Constructing multidimensional columns

% Define 1D column
simple_col = types.hdmf_common.VectorData( ...
'description', '1D column',...
'data', rand(10,1) ...
);
% Define ND column
multi_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional column',...
'data', rand(3,2,10) ...
);
% construct table
multi_dim_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'simple','multi'}, ...
'simple', simple_col, ...
'multi', multi_col, ...
'id', types.hdmf_common.ElementIdentifiers('data', (0:9)') ... % 0-indexed, for compatibility with Python
);
 

Multidimensional ragged array columns

DynamicTable objects with multidimensional ragged array columns can be constructed by passing in the corresponding VectorIndex column
% Define column with data
multi_ragged_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional ragged array column',...
'data', rand(2,3,5) ...
);
% Define column with VectorIndex
multi_ragged_index = types.hdmf_common.VectorIndex( ...
'description', 'index to multi_ragged_col', ...
'target', types.untyped.ObjectView(multi_ragged_col),'data', [2; 3; 5] ...
);
 
multi_ragged_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'multi_ragged'}, ...
'multi_ragged', multi_ragged_col, ...
'multi_ragged_index', multi_ragged_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1; 2]) ... % 0-indexed, for compatibility with Python
);

Adding rows to multidimensional array columns

DynamicTable objects with multidimensional array columns can also be constructed by adding a single row at a time. This method makes use of DataPipe objects due to the fact that MATLAB doesn't support singleton dimensions for arrays with more than 2 dimensions. The code block below demonstrates how to build a DynamicTable object with a mutidimensional raaged array column in this manner.
% Create file
file = NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test_file' ...
);
 
% Define Vector Data Objects with first row of table
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', 1, ...
'maxSize', Inf ...
) ...
);
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', 10, ...
'maxSize', Inf ...
) ...
);
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(3,2,5), ... #random data
'maxSize', [3, 2, Inf], ...
'axis', 3 ...
) ...
);
random_exp_index = types.hdmf_common.VectorIndex( ...
'description', 'index to random data column', ...
'target',types.untyped.ObjectView(random_exp), ...
'data', types.untyped.DataPipe( ...
'data', uint64(5), ...
'maxSize', Inf ...
) ...
);
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int64(0), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% Create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'randomvalues_index', random_exp_index, ...
'id', ids_exp ...
);
% Export file
nwbExport(file, 'multiRaggedExpandableTableTest.nwb');
% Read in file
read_file = nwbRead('multiRaggedExpandableTableTest.nwb', 'ignorecache');
% add individual rows
read_file.intervals_trials.addRow( ...
'start_time', 2, ...
'stop_time', 20, ...
'randomvalues', rand(3,2,6), ...
'id', 1 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 30, ...
'randomvalues', rand(3,2,3), ...
'id', 2 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 4, ...
'stop_time', 40, ...
'randomvalues', rand(3,2,8), ...
'id', 3 ...
);
 

Learn More!

Python Tutorial

+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/dynamically_loaded_filters.html b/docs/source/_static/html/tutorials/dynamically_loaded_filters.html new file mode 100644 index 00000000..7250e315 --- /dev/null +++ b/docs/source/_static/html/tutorials/dynamically_loaded_filters.html @@ -0,0 +1,141 @@ + +Using Dynamically Loaded Filters in MatMWB

Using Dynamically Loaded Filters in MatMWB

Installing Dynamically Loaded Filters

HDF5 can use various filters to compress data when writing datasets. GZIP is the default filter, and it can be read with any HDF5 installation without any setup, but many users find that other filters, such as Zstd, offer better performance. If you want to read an HDF5 Dataset that was compressed using another filter in MATLAB, such as Zstd, you will need to configure MATLAB to read using dynamically loaded filters.
The easiest way we have found to set up dynamically loaded filters is to use the Python package hdf5plugin. This library has a sophisticated installation process that compiles several of the most popular dynamically loaded filters and works across popular operating systems. Installing this Python package is a trick that allows us to offload the tricky parts of installing dynamically loaded filters in MATLAB.

Linux or Mac

1. In your Terminal window, install hdf5plugin:
pip install hdf5plugin
2. In that same Terminal window, set the environment variable HDF5_PLUGIN_PATH:
export HDF5_PLUGIN_PATH=$(python -c "import hdf5plugin; print(hdf5plugin.PLUGINS_PATH)");
3. From that same Terminal window, launch MATLAB:
/Applications/MATLAB_R2021b.app/bin/matlab
The path above is an example of a common location for OSX. The exact path of MATLAB may vary on your computer.

Windows

1. Install hdf5plugin in the Command Prompt:
pip install hdf5plugin
2. Determine the path of the plugin installation. In the Command Prompt, run:
python -c "import hdf5plugin; print(hdf5plugin.PLUGINS_PATH)
2. Set the environment variable HDF5_PLUGIN_PATH to point to the local installation of the plugins (from hdf5plugin.PLUGINS_PATH) through System Properties > Advanced > Environment Variables:
path-screenshot.png
3. Restart MATLAB.
That's it! Now you can read datasets that use the following filters:
  • Bitshuffle
  • Blosc
  • FciDecomp
  • LZ4
  • Zfp
  • Zstd
The beauty of HDF5 is that it handles the rest under the hood. When you read a dataset that uses any of these filters, HDF5 will identify the correct decompression algorithm and decompress the data on-the-fly as you access it from the dataset.
For more information about installing filter plugins, see the MATLAB documentation.

Writing with Dynamically Loaded Filters

To write with dynamically loaded filters, first follow the installation steps above. This feature requires MATLAB version ≥ 2022a.
DataPipe objects can be used to write using Dynamically loaded filters. This tutorial will be using the Zstd dynamic filter as an example.
The DynamicFilter property takes in an enumerated type Filter which is a hard-coded list of all listed registered filter plugins in HDF5.
import types.untyped.datapipe.properties.DynamicFilter
import types.untyped.datapipe.dynamic.Filter
import types.untyped.datapipe.properties.Shuffle
 
zstdProperty = DynamicFilter(Filter.ZStandard);

Parameters

Some filter plugins allow for setting special configuration parameters to modify the filter's behavior. The DynamicFilter property type contains a modifiable parameters field which can be used to set your parameters. This is equivalent to setting the cd_values argument in HDF5. In the case of the Zstandard HDF5 plugin, the first (and only) array argument value indicates the compression level.
zstdProperty.parameters = 4; % compression level.

Multiple Filters

You can use multiple dynamic filters by concatenating multiple DynamicFilter properties together. They will be applied in order of the inserted array.
ShuffleProperty = Shuffle();
dynamicProperties = [ShuffleProperty zstdProperty];

Writing

The DataPipe class takes in a keyword argument called filters which is an array of DynamicFilter objects. Supplying a 'filters' argument will deactivate the default GZIP compression.
% We're already compressing using zstd so we should disable
% compressionLevel (gzip).
dataPipe = types.untyped.DataPipe('data', rand(1, 10000), 'filters', dynamicProperties);
 
timeseries = types.core.TimeSeries(...
'data', dataPipe, ...
'data_unit', 'data-unit', ...
'starting_time_rate', 1.0, ...
'starting_time', 0.0);
 
nwbFile = NwbFile(...
'identifier', 'dynamically_loaded_filters_tutorial', ...
'session_description', 'test_datapipe_filters', ...
'session_start_time', datetime("now", 'TimeZone', 'local') );
nwbFile.acquisition.set('ts', timeseries);
 
nwbExport(nwbFile, 'test.nwb');
 
The data is now compressed using Zstandard compression using a compression level of 4 and Shuffled
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ecephys.html b/docs/source/_static/html/tutorials/ecephys.html new file mode 100644 index 00000000..8d82796e --- /dev/null +++ b/docs/source/_static/html/tutorials/ecephys.html @@ -0,0 +1,1356 @@ + +Neurodata Without Borders Extracellular Electrophysiology Tutorial

Neurodata Without Borders Extracellular Electrophysiology Tutorial

This tutorial

Create fake data for a hypothetical extracellular electrophysiology experiment. The types of data we will convert are:
  • Voltage recording
  • Local field potential (LFP)
  • Spike times
It is recommended to first work through the Introduction to MatNWB tutorial, which demonstrates installing MatNWB and creating an NWB file with subject information, animal position, and trials, as well as writing and reading NWB files in MATLAB.

Setting up the NWB File

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata. For all MatNWB functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'Last Name, First Name', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: + + nwb_version: '2.7.0' + file_create_date: [] + identifier: 'Mouse5_Day3' + session_description: 'mouse in open exploration' + session_start_time: {[2018-04-25T02:30:03.000000+02:00]} + timestamps_reference_time: {[2018-04-25T03:00:45.000000+02:00]} + acquisition: [0×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: '' + general_devices: [0×1 types.untyped.Set] + general_experiment_description: '' + general_experimenter: 'Last Name, First Name' + general_extracellular_ephys: [0×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [] + general_institution: 'University of My Institution' + general_intracellular_ephys: [0×1 types.untyped.Set] + general_intracellular_ephys_experimental_conditions: [] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_intracellular_recordings: [] + general_intracellular_ephys_repetitions: [] + general_intracellular_ephys_sequential_recordings: [] + general_intracellular_ephys_simultaneous_recordings: [] + general_intracellular_ephys_sweep_table: [] + general_keywords: '' + general_lab: '' + general_notes: '' + general_optogenetics: [0×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: {'DOI:10.1016/j.neuron.2016.12.011'} + general_session_id: 'session_1234' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [0×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [] +

Extracellular Electrophysiology

In order to store extracellular electrophysiology data, you first must create an electrodes table describing the electrodes that generated this data. Extracellular electrodes are stored in an electrodes table, which is also a DynamicTable. electrodes has several required fields: x, y, z, impedance, location, filtering, and electrode_group.

Electrodes Table

Since this is a DynamicTable, we can add additional metadata fields. We will be adding a "label" column to the table.
numShanks = 4;
numChannelsPerShank = 3;
 
ElectrodesDynamicTable = types.hdmf_common.DynamicTable(...
'colnames', {'location', 'group', 'group_name', 'label'}, ...
'description', 'all electrodes');
 
Device = types.core.Device(...
'description', 'the best array', ...
'manufacturer', 'Probe Company 9000' ...
);
nwb.general_devices.set('array', Device);
for iShank = 1:numShanks
shankGroupName = sprintf('shank%d', iShank);
EGroup = types.core.ElectrodeGroup( ...
'description', sprintf('electrode group for %s', shankGroupName), ...
'location', 'brain area', ...
'device', types.untyped.SoftLink(Device) ...
);
nwb.general_extracellular_ephys.set(shankGroupName, EGroup);
for iElectrode = 1:numChannelsPerShank
ElectrodesDynamicTable.addRow( ...
'location', 'unknown', ...
'group', types.untyped.ObjectView(EGroup), ...
'group_name', shankGroupName, ...
'label', sprintf('%s-electrode%d', shankGroupName, iElectrode));
end
end
ElectrodesDynamicTable.toTable() % Display the table
ans = 12×5 table
 idlocationgroupgroup_namelabel
10'unknown'1×1 ObjectView'shank1''shank1-electrode1'
21'unknown'1×1 ObjectView'shank1''shank1-electrode2'
32'unknown'1×1 ObjectView'shank1''shank1-electrode3'
43'unknown'1×1 ObjectView'shank2''shank2-electrode1'
54'unknown'1×1 ObjectView'shank2''shank2-electrode2'
65'unknown'1×1 ObjectView'shank2''shank2-electrode3'
76'unknown'1×1 ObjectView'shank3''shank3-electrode1'
87'unknown'1×1 ObjectView'shank3''shank3-electrode2'
98'unknown'1×1 ObjectView'shank3''shank3-electrode3'
109'unknown'1×1 ObjectView'shank4''shank4-electrode1'
1110'unknown'1×1 ObjectView'shank4''shank4-electrode2'
1211'unknown'1×1 ObjectView'shank4''shank4-electrode3'
 
nwb.general_extracellular_ephys_electrodes = ElectrodesDynamicTable;

Links

In the above loop, we create ElectrodeGroup objects. The electrodes table then uses an ObjectView in each row to link to the corresponding ElectrodeGroup object. An ObjectView is an object that allow you to create a link from one neurodata type referencing another.

ElectricalSeries

Voltage data are stored in ElectricalSeries objects. ElectricalSeries is a subclass of TimeSeries specialized for voltage data. In order to create our ElectricalSeries object, we will need to reference a set of rows in the electrodes table to indicate which electrodes were recorded. We will do this by creating a DynamicTableRegion, which is a type of link that allows you to reference specific rows of a DynamicTable, such as the electrodes table, by row indices.
Create a DynamicTableRegion that references all rows of the electrodes table.
electrode_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(ElectrodesDynamicTable), ...
'description', 'all electrodes', ...
'data', (0:length(ElectrodesDynamicTable.id.data)-1)');
Now create an ElectricalSeries object to hold acquisition data collected during the experiment.
electrical_series = types.core.ElectricalSeries( ...
'starting_time', 0.0, ... % seconds
'starting_time_rate', 30000., ... % Hz
'data', randn(12, 3000), ...
'electrodes', electrode_table_region, ...
'data_unit', 'volts');
This is the voltage data recorded directly from our electrodes, so it goes in the acquisition group.
nwb.acquisition.set('ElectricalSeries', electrical_series);

LFP

Local field potential (LFP) refers in this case to data that has been downsampled and/or filtered from the original acquisition data and is used to analyze signals in the lower frequency range. Filtered and downsampled LFP data would also be stored in an ElectricalSeries. To help data analysis and visualization tools know that this ElectricalSeries object represents LFP data, store it inside an LFP object, then place the LFP object in a ProcessingModule named 'ecephys'. This is analogous to how we stored the SpatialSeries object inside of a Position object and stored the Position object in a ProcessingModule named 'behavior' earlier.
electrical_series = types.core.ElectricalSeries( ...
'starting_time', 0.0, ... % seconds
'starting_time_rate', 1000., ... % Hz
'data', randn(12, 100), ...
'electrodes', electrode_table_region, ...
'data_unit', 'volts');
 
lfp = types.core.LFP('ElectricalSeries', electrical_series);
 
ecephys_module = types.core.ProcessingModule(...
'description', 'extracellular electrophysiology');
 
ecephys_module.nwbdatainterface.set('LFP', lfp);
nwb.processing.set('ecephys', ecephys_module);

Sorted Spike Times

Ragged Arrays

Spike times are stored in another DynamicTable of subtype Units. The default Units table is at /units in the HDF5 file. You can add columns to the Units table just like you did for electrodes and trials. Here, we generate some random spike data and populate the table.
num_cells = 10;
firing_rate = 20;
spikes = cell(1, num_cells);
for iShank = 1:num_cells
spikes{iShank} = rand(1, randi([16, 28]));
end
spikes
spikes = 1×10 cell
 12345678910
11×21 double1×24 double1×18 double1×28 double1×25 double1×18 double1×21 double1×28 double1×16 double1×19 double
Spike times are an example of a ragged array- it's like a matrix, but each row has a different number of elements. We can represent this type of data as an indexed column of the units DynamicTable. These indexed columns have two components, the vector data object that holds the data and the vector index object that holds the indices in the vector that indicate the row breaks. You can use the convenience function util.create_indexed_column to create these objects.
[spike_times_vector, spike_times_index] = util.create_indexed_column(spikes);
 
nwb.units = types.core.Units( ...
'colnames', {'spike_times'}, ...
'description', 'units table', ...
'spike_times', spike_times_vector, ...
'spike_times_index', spike_times_index ...
);
 
nwb.units.toTable
ans = 10×2 table
 idspike_times
1121×1 double
2224×1 double
3318×1 double
4428×1 double
5525×1 double
6618×1 double
7721×1 double
8828×1 double
9916×1 double
101019×1 double

Unsorted Spike Times

In MATLAB, while the Units table is used to store spike times and waveform data for spike-sorted, single-unit activity, you may also want to store spike times and waveform snippets of unsorted spiking activity. This is useful for recording multi-unit activity detected via threshold crossings during data acquisition. Such information can be stored using SpikeEventSeries objects.
% In the SpikeEventSeries the dimensions should be ordered as
% [num_events, num_channels, num_samples].
% Define spike snippets: 20 events, 3 channels, 40 samples per event.
spike_snippets = rand(20, 3, 40);
% Permute spike snippets (See dimensionMapNoDataPipes tutorial)
spike_snippets = permute(spike_snippets, [3,2,1])
spike_snippets =
spike_snippets(:,:,1) = + + 0.2857 0.6608 0.4367 + 0.3600 0.1626 0.8400 + 0.5475 0.0764 0.6130 + 0.1149 0.6369 0.1302 + 0.0839 0.7551 0.8632 + 0.5529 0.3187 0.0518 + 0.0574 0.7624 0.8898 + 0.6274 0.6888 0.5444 + 0.0840 0.9389 0.8369 + 0.2149 0.7280 0.1379 + 0.9564 0.0906 0.6076 + 0.3179 0.7600 0.8335 + 0.1967 0.6754 0.0739 + 0.8329 0.0264 0.5930 + 0.5987 0.1428 0.5505 + 0.8641 0.5217 0.8748 + 0.9503 0.9436 0.6849 + 0.0988 0.6326 0.2984 + 0.8497 0.6740 0.0928 + 0.9513 0.1322 0.6433 + 0.7060 0.3816 0.9528 + 0.9193 0.2140 0.7737 + 0.6863 0.3327 0.0457 + 0.1618 0.8800 0.0862 + 0.0080 0.8270 0.6271 + 0.8749 0.5075 0.5516 + 0.5649 0.8422 0.1858 + 0.8480 0.8885 0.5991 + 0.4204 0.1576 0.2430 + 0.5801 0.3614 0.8714 + 0.2796 0.5922 0.5711 + 0.7843 0.8961 0.5321 + 0.3181 0.6451 0.6615 + 0.1203 0.4240 0.7307 + 0.5491 0.9083 0.7756 + 0.1166 0.2054 0.1056 + 0.2286 0.7850 0.2984 + 0.7974 0.3422 0.4096 + 0.6017 0.6665 0.8854 + 0.6872 0.0761 0.4004 + + +spike_snippets(:,:,2) = + + 0.3537 0.5763 0.0558 + 0.3264 0.0852 0.3904 + 0.6326 0.9991 0.9243 + 0.0142 0.3299 0.1727 + 0.7906 0.1044 0.6286 + 0.5325 0.6482 0.3741 + 0.1618 0.3340 0.6112 + 0.1891 0.3239 0.9190 + 0.8035 0.1656 0.9514 + 0.8351 0.5910 0.6103 + 0.4504 0.8050 0.4005 + 0.6363 0.7321 0.7352 + 0.3229 0.4718 0.3551 + 0.5228 0.9125 0.7943 + 0.5838 0.8591 0.1924 + 0.1646 0.9811 0.3952 + 0.5876 0.7013 0.5506 + 0.6731 0.0672 0.7196 + 0.4278 0.2166 0.7300 + 0.3082 0.0349 0.0998 + 0.6421 0.0494 0.0303 + 0.8604 0.1456 0.3563 + 0.8400 0.7252 0.2455 + 0.1522 0.9904 0.1299 + 0.1487 0.3543 0.3340 + 0.7294 0.4209 0.4987 + 0.7297 0.4765 0.9868 + 0.5830 0.8183 0.6142 + 0.0503 0.9344 0.5153 + 0.1720 0.1192 0.9391 + 0.9381 0.5673 0.6301 + 0.2820 0.4955 0.2350 + 0.8668 0.6107 0.3925 + 0.6064 0.8543 0.9834 + 0.6108 0.4038 0.0298 + 0.3266 0.5498 0.5972 + 0.1845 0.5921 0.9274 + 0.6278 0.8075 0.1228 + 0.1886 0.3756 0.3849 + 0.0585 0.6472 0.9450 + + +spike_snippets(:,:,3) = + + 0.0790 0.9798 0.3705 + 0.2565 0.3337 0.3708 + 0.4972 0.1644 0.8170 + 0.8341 0.4508 0.3991 + 0.4084 0.5430 0.4807 + 0.3439 0.6642 0.5703 + 0.9957 0.1053 0.1126 + 0.1819 0.7859 0.5963 + 0.1255 0.1200 0.8429 + 0.8847 0.4127 0.9843 + 0.8943 0.6678 0.0982 + 0.0207 0.9872 0.9731 + 0.2515 0.3483 0.4459 + 0.6889 0.5565 0.2033 + 0.3814 0.8721 0.6273 + 0.5217 0.2188 0.8595 + 0.9447 0.0148 0.1967 + 0.8213 0.0100 0.3596 + 0.5204 0.0630 0.8961 + 0.3929 0.7670 0.6272 + 0.0254 0.7624 0.0719 + 0.3342 0.8155 0.2478 + 0.5622 0.2468 0.7245 + 0.3886 0.1705 0.6961 + 0.6668 0.7717 0.4452 + 0.0322 0.9974 0.1323 + 0.6823 0.3939 0.0021 + 0.3614 0.2507 0.6621 + 0.7852 0.7651 0.5013 + 0.3042 0.7980 0.0191 + 0.0733 0.7833 0.2535 + 0.2063 0.2281 0.8499 + 0.4524 0.5881 0.5972 + 0.7459 0.2085 0.7855 + 0.7223 0.9363 0.3330 + 0.3031 0.0221 0.9362 + 0.0528 0.5472 0.0018 + 0.8966 0.7395 0.3072 + 0.0647 0.9329 0.9689 + 0.7266 0.1662 0.9855 + + +spike_snippets(:,:,4) = + + 0.2545 0.1178 0.0623 + 0.1434 0.7919 0.9073 + 0.4296 0.1924 0.6309 + 0.5161 0.1803 0.0288 + 0.3179 0.7982 0.2427 + 0.2748 0.3133 0.0101 + 0.2930 0.1957 0.6955 + 0.6836 0.9551 0.2006 + 0.1638 0.1767 0.6102 + 0.9307 0.7772 0.1972 + 0.9277 0.5743 0.1019 + 0.7270 0.7339 0.1749 + 0.4948 0.2646 0.5360 + 0.8644 0.7049 0.4960 + 0.5880 0.8958 0.5753 + 0.0046 0.9635 0.3048 + 0.2872 0.7341 0.1214 + 0.0006 0.4956 0.6200 + 0.8845 0.5147 0.8833 + 0.1212 0.1808 0.5548 + 0.5224 0.7684 0.0541 + 0.6122 0.9229 0.9004 + 0.2470 0.2454 0.3503 + 0.4826 0.1955 0.2739 + 0.9240 0.7621 0.3529 + 0.1734 0.0407 0.7469 + 0.6788 0.3651 0.0434 + 0.1148 0.2578 0.8098 + 0.0898 0.4526 0.1006 + 0.2722 0.9148 0.1465 + 0.3516 0.7715 0.5164 + 0.6509 0.2735 0.4134 + 0.6057 0.5503 0.6118 + 0.7246 0.3308 0.9303 + 0.4009 0.2966 0.9465 + 0.8563 0.4760 0.5895 + 0.3833 0.8435 0.3921 + 0.1216 0.6948 0.9617 + 0.0278 0.8568 0.0843 + 0.2423 0.5999 0.6319 + + +spike_snippets(:,:,5) = + + 0.5522 0.6140 0.5597 + 0.4872 0.7144 0.3502 + 0.4616 0.4691 0.4971 + 0.3555 0.4293 0.6880 + 0.0012 0.4328 0.0814 + 0.6816 0.5303 0.6519 + 0.5734 0.7206 0.2286 + 0.6116 0.0223 0.9631 + 0.0033 0.3719 0.2236 + 0.9431 0.1396 0.0793 + 0.4107 0.9010 0.0474 + 0.8080 0.4957 0.3896 + 0.0888 0.6888 0.4446 + 0.6716 0.0954 0.6248 + 0.9566 0.5706 0.0673 + 0.8721 0.6358 0.3166 + 0.2042 0.8066 0.7187 + 0.2414 0.9353 0.2232 + 0.5845 0.9186 0.1913 + 0.7381 0.7520 0.3618 + 0.9733 0.9983 0.0858 + 0.7678 0.0200 0.8217 + 0.0691 0.5319 0.3943 + 0.0594 0.5005 0.8020 + 0.2298 0.7428 0.3926 + 0.3677 0.9118 0.1565 + 0.6005 0.3140 0.8609 + 0.0459 0.6408 0.3229 + 0.2813 0.0560 0.4407 + 0.0798 0.9051 0.9379 + 0.5298 0.4222 0.8412 + 0.8171 0.5180 0.8489 + 0.9351 0.5550 0.4569 + 0.2314 0.2319 0.0156 + 0.9743 0.4728 0.9320 + 0.5814 0.4281 0.6112 + 0.7730 0.4871 0.8687 + 0.7774 0.5609 0.8990 + 0.6401 0.7684 0.9780 + 0.6937 0.3855 0.2773 + + +spike_snippets(:,:,6) = + + 0.7563 0.7273 0.0133 + 0.3592 0.8510 0.3783 + 0.2910 0.2957 0.0741 + 0.2222 0.6956 0.2550 + 0.7024 0.7359 0.8882 + 0.7378 0.5554 0.4322 + 0.3820 0.1226 0.2407 + 0.7241 0.7887 0.3233 + 0.5451 0.4922 0.9290 + 0.8519 0.6864 0.1900 + 0.8484 0.4515 0.9817 + 0.7977 0.2750 0.3051 + 0.0746 0.6490 0.7667 + 0.0516 0.7495 0.5505 + 0.6045 0.0482 0.7680 + 0.1531 0.1959 0.0531 + 0.3139 0.2795 0.4414 + 0.0599 0.3964 0.1188 + 0.7404 0.0559 0.8768 + 0.9584 0.2563 0.1812 + 0.5370 0.5704 0.2613 + 0.3878 0.6127 0.9154 + 0.3381 0.5848 0.7191 + 0.6035 0.1744 0.6816 + 0.8049 0.4899 0.3228 + 0.4373 0.2797 0.0940 + 0.8263 0.6326 0.0613 + 0.6774 0.9938 0.4658 + 0.0273 0.3114 0.2264 + 0.5349 0.2503 0.0345 + 0.2270 0.1829 0.8232 + 0.1635 0.3015 0.7972 + 0.3778 0.3966 0.0747 + 0.7063 0.9485 0.0910 + 0.3828 0.1401 0.8424 + 0.3238 0.2967 0.7296 + 0.4243 0.0952 0.8124 + 0.4995 0.4657 0.7694 + 0.1477 0.7937 0.8987 + 0.4048 0.1036 0.0002 + + +spike_snippets(:,:,7) = + + 0.4580 0.9207 0.2481 + 0.1836 0.6374 0.5111 + 0.0742 0.8433 0.9611 + 0.0159 0.8911 0.7889 + 0.2008 0.8055 0.7266 + 0.5994 0.8005 0.9522 + 0.4349 0.8104 0.8747 + 0.3428 0.7336 0.5881 + 0.1481 0.0946 0.9118 + 0.0831 0.5271 0.7814 + 0.0389 0.8958 0.8084 + 0.3233 0.3729 0.3681 + 0.4450 0.6990 0.3603 + 0.6181 0.9658 0.1873 + 0.5898 0.2502 0.0125 + 0.2308 0.2501 0.7988 + 0.8013 0.1657 0.0417 + 0.9670 0.8803 0.1257 + 0.0179 0.6742 0.8754 + 0.3485 0.9857 0.9407 + 0.2647 0.2288 0.6305 + 0.0033 0.4910 0.1960 + 0.9030 0.8703 0.3570 + 0.5235 0.2891 0.1002 + 0.2324 0.0010 0.2280 + 0.2612 0.3101 0.0855 + 0.5968 0.3174 0.4167 + 0.9640 0.7214 0.4913 + 0.1577 0.9385 0.1025 + 0.1977 0.5359 0.2251 + 0.3496 0.7907 0.7362 + 0.0281 0.6693 0.0673 + 0.2872 0.3925 0.5873 + 0.3650 0.4479 0.9278 + 0.8428 0.9876 0.6636 + 0.8083 0.6426 0.9633 + 0.5935 0.5707 0.7156 + 0.6495 0.7981 0.8620 + 0.4248 0.5334 0.1100 + 0.0895 0.6389 0.0086 + + +spike_snippets(:,:,8) = + + 0.3173 0.7239 0.5842 + 0.9806 0.3725 0.7392 + 0.2726 0.0862 0.7087 + 0.7864 0.9975 0.8527 + 0.1554 0.4242 0.5882 + 0.7534 0.3430 0.0112 + 0.3874 0.5027 0.4471 + 0.3280 0.3424 0.3045 + 0.1938 0.7505 0.7932 + 0.2593 0.2949 0.4888 + 0.5396 0.1103 0.9481 + 0.1231 0.6017 0.7679 + 0.0303 0.7178 0.3082 + 0.6338 0.5286 0.1721 + 0.5765 0.0735 0.1011 + 0.4303 0.6201 0.7509 + 0.4182 0.1388 0.1476 + 0.2248 0.5659 0.0135 + 0.4919 0.7920 0.0609 + 0.2389 0.4693 0.8358 + 0.7148 0.3369 0.5812 + 0.1331 0.7267 0.6000 + 0.7245 0.4390 0.8241 + 0.5550 0.9808 0.7514 + 0.9064 0.5307 0.9582 + 0.8849 0.6957 0.7481 + 0.2192 0.9088 0.5844 + 0.2364 0.5855 0.7902 + 0.7584 0.0846 0.1370 + 0.7727 0.2945 0.2277 + 0.8112 0.0527 0.8338 + 0.3527 0.3030 0.9882 + 0.0002 0.2667 0.9848 + 0.2195 0.0832 0.4510 + 0.8938 0.0728 0.4832 + 0.8841 0.6265 0.6150 + 0.8769 0.5066 0.4031 + 0.6807 0.3424 0.9336 + 0.2345 0.0089 0.7260 + 0.2382 0.3721 0.6684 + + +spike_snippets(:,:,9) = + + 0.7887 0.1178 0.4502 + 0.8269 0.3962 0.5211 + 0.9859 0.7808 0.8825 + 0.7219 0.9935 0.1152 + 0.0173 0.1858 0.4448 + 0.0044 0.2231 0.0820 + 0.8686 0.5382 0.2548 + 0.4520 0.9463 0.1562 + 0.5869 0.8375 0.2189 + 0.7871 0.7757 0.4243 + 0.1588 0.5478 0.6302 + 0.4440 0.3944 0.8391 + 0.3337 0.1684 0.5720 + 0.0759 0.9428 0.7316 + 0.2520 0.8726 0.6824 + 0.5137 0.2414 0.0452 + 0.8611 0.8641 0.2069 + 0.5499 0.9632 0.1246 + 0.3078 0.4369 0.9144 + 0.7565 0.7739 0.5479 + 0.3609 0.3100 0.5928 + 0.3456 0.5537 0.2476 + 0.6165 0.3551 0.7373 + 0.5038 0.6011 0.9023 + 0.9612 0.7047 0.1553 + 0.3012 0.6818 0.3364 + 0.7665 0.1590 0.6036 + 0.7387 0.1920 0.7323 + 0.6689 0.6163 0.1122 + 0.0597 0.1126 0.9075 + 0.4382 0.9988 0.5944 + 0.5352 0.5993 0.9150 + 0.0732 0.8298 0.4190 + 0.1000 0.1752 0.8498 + 0.2800 0.1332 0.4710 + 0.8509 0.2318 0.1789 + 0.0231 0.6613 0.9997 + 0.8469 0.7678 0.8919 + 0.7672 0.0896 0.8968 + 0.3195 0.3421 0.0274 + + +spike_snippets(:,:,10) = + + 0.0246 0.1593 0.0619 + 0.1265 0.4231 0.4204 + 0.8991 0.6043 0.3761 + 0.9254 0.1290 0.2395 + 0.3477 0.0261 0.3631 + 0.4255 0.5196 0.4987 + 0.4745 0.6889 0.5786 + 0.6804 0.9587 0.7136 + 0.2899 0.7803 0.5361 + 0.4230 0.3868 0.5688 + 0.3492 0.5184 0.7157 + 0.3322 0.2954 0.5267 + 0.3834 0.0758 0.3822 + 0.3095 0.9617 0.0138 + 0.1127 0.2855 0.8902 + 0.4317 0.8442 0.9792 + 0.7420 0.0902 0.0456 + 0.7662 0.1624 0.1115 + 0.6543 0.3400 0.6864 + 0.6296 0.1255 0.5455 + 0.9029 0.0879 0.5006 + 0.6050 0.8464 0.5591 + 0.3601 0.3826 0.1305 + 0.8042 0.7910 0.4607 + 0.5606 0.6963 0.7884 + 0.3205 0.2410 0.7519 + 0.0024 0.6577 0.6181 + 0.1318 0.1590 0.2453 + 0.2791 0.1981 0.6390 + 0.2292 0.9181 0.1980 + 0.5690 0.9134 0.5699 + 0.0218 0.6615 0.1191 + 0.7396 0.8588 0.8290 + 0.4791 0.2304 0.7855 + 0.6231 0.5621 0.5530 + 0.2377 0.7269 0.4795 + 0.9498 0.7593 0.3992 + 0.4043 0.5552 0.6333 + 0.3988 0.1177 0.9016 + 0.6999 0.0590 0.5461 + + +spike_snippets(:,:,11) = + + 0.2220 0.6161 0.5482 + 0.4123 0.1179 0.7582 + 0.7245 0.9598 0.6356 + 0.7210 0.6697 0.9592 + 0.5535 0.0940 0.7996 + 0.4417 0.4261 0.0502 + 0.4423 0.5592 0.2896 + 0.9676 0.3315 0.7111 + 0.6885 0.1419 0.4361 + 0.0655 0.0035 0.1327 + 0.3826 0.6864 0.3620 + 0.0971 0.4998 0.1317 + 0.6550 0.8911 0.7389 + 0.7726 0.3506 0.4817 + 0.9044 0.0072 0.4791 + 0.9699 0.5444 0.4785 + 0.0592 0.5717 0.1154 + 0.6694 0.8065 0.3114 + 0.0451 0.4908 0.5335 + 0.6083 0.8033 0.7573 + 0.4028 0.8962 0.7841 + 0.6808 0.6181 0.2081 + 0.7436 0.6448 0.3955 + 0.0222 0.5468 0.5692 + 0.5026 0.1176 0.5409 + 0.3683 0.6528 0.1630 + 0.3198 0.7608 0.0527 + 0.0307 0.6699 0.7209 + 0.9927 0.2533 0.7853 + 0.5352 0.7892 0.0157 + 0.4994 0.9594 0.8645 + 0.2182 0.8810 0.4853 + 0.0327 0.1107 0.5328 + 0.4987 0.8243 0.0942 + 0.3071 0.6421 0.3681 + 0.0825 0.2762 0.5065 + 0.7225 0.3783 0.5774 + 0.1505 0.5689 0.6437 + 0.2730 0.3795 0.4207 + 0.3812 0.3843 0.7570 + + +spike_snippets(:,:,12) = + + 0.4536 0.0348 0.1280 + 0.0937 0.3153 0.8244 + 0.0254 0.7321 0.1776 + 0.5266 0.1378 0.7937 + 0.8184 0.4512 0.3127 + 0.2168 0.1070 0.4184 + 0.4308 0.8058 0.1924 + 0.2676 0.6272 0.4703 + 0.7924 0.5767 0.2068 + 0.6820 0.1100 0.2608 + 0.8025 0.4762 0.5039 + 0.5449 0.9441 0.8725 + 0.6045 0.0750 0.7539 + 0.3219 0.0809 0.9189 + 0.6374 0.1076 0.6818 + 0.6043 0.9807 0.2184 + 0.4737 0.8263 0.0113 + 0.4661 0.1748 0.3905 + 0.1515 0.5849 0.7402 + 0.0386 0.0491 0.0699 + 0.0451 0.8471 0.2438 + 0.9840 0.0645 0.1164 + 0.4521 0.4423 0.6804 + 0.3990 0.5811 0.2845 + 0.9404 0.8687 0.0697 + 0.7555 0.0850 0.2861 + 0.4284 0.9813 0.1606 + 0.1728 0.0667 0.2492 + 0.5687 0.0777 0.2709 + 0.2264 0.5504 0.8100 + 0.0541 0.0368 0.5259 + 0.4999 0.3839 0.5377 + 0.8043 0.5455 0.7418 + 0.2652 0.8362 0.7342 + 0.4792 0.8744 0.6765 + 0.3494 0.9809 0.9374 + 0.4120 0.9923 0.1729 + 0.8693 0.4478 0.9683 + 0.2047 0.0203 0.7611 + 0.2584 0.7700 0.7948 + + +spike_snippets(:,:,13) = + + 0.0157 0.0647 0.6416 + 0.7638 0.1440 0.4338 + 0.3404 0.7033 0.9323 + 0.3327 0.2419 0.0158 + 0.1027 0.5447 0.9455 + 0.4155 0.5504 0.4124 + 0.4389 0.7090 0.1019 + 0.1135 0.1518 0.7802 + 0.1862 0.0991 0.9385 + 0.8108 0.3330 0.3703 + 0.8882 0.0429 0.0777 + 0.6938 0.3064 0.6495 + 0.3660 0.9739 0.9794 + 0.2094 0.1125 0.6123 + 0.9374 0.2039 0.4439 + 0.3380 0.0786 0.5313 + 0.6468 0.8286 0.6347 + 0.8093 0.9563 0.2979 + 0.2439 0.3000 0.7636 + 0.9876 0.4089 0.9489 + 0.0655 0.6382 0.4527 + 0.5187 0.1400 0.3417 + 0.4703 0.5862 0.9807 + 0.2048 0.6405 0.4133 + 0.7057 0.7886 0.9320 + 0.2141 0.2059 0.3750 + 0.5749 0.4097 0.0935 + 0.1075 0.7629 0.3086 + 0.7586 0.6562 0.2721 + 0.8715 0.4178 0.0318 + 0.7900 0.4142 0.5397 + 0.5306 0.9462 0.6588 + 0.4306 0.8370 0.4302 + 0.5899 0.5945 0.0762 + 0.8496 0.3275 0.7257 + 0.2751 0.2624 0.5545 + 0.1538 0.0924 0.3398 + 0.7160 0.0858 0.0901 + 0.9813 0.8219 0.6498 + 0.8121 0.5394 0.8237 + + +spike_snippets(:,:,14) = + + 0.0221 0.8614 0.7634 + 0.3983 0.6727 0.3550 + 0.9539 0.1561 0.6813 + 0.5583 0.5885 0.9967 + 0.4075 0.0813 0.4429 + 0.2310 0.0248 0.7509 + 0.6427 0.7612 0.3196 + 0.7487 0.7756 0.8189 + 0.0629 0.4779 0.6550 + 0.8839 0.6752 0.3173 + 0.6077 0.3845 0.6331 + 0.3133 0.1429 0.8957 + 0.4345 0.7169 0.9520 + 0.0100 0.3340 0.6187 + 0.5361 0.1743 0.7057 + 0.7226 0.3522 0.6633 + 0.2896 0.4041 0.8869 + 0.7655 0.6235 0.4579 + 0.8103 0.0950 0.5008 + 0.8029 0.9529 0.8503 + 0.3489 0.9096 0.4176 + 0.6609 0.9922 0.3921 + 0.8915 0.3578 0.2378 + 0.3911 0.2561 0.2023 + 0.1938 0.1357 0.0510 + 0.5737 0.0948 0.8470 + 0.0540 0.6226 0.4725 + 0.2451 0.7750 0.8025 + 0.8169 0.3337 0.6998 + 0.2011 0.9325 0.9863 + 0.8228 0.7149 0.2157 + 0.9203 0.8141 0.3663 + 0.7566 0.7477 0.5670 + 0.8637 0.9622 0.7179 + 0.8148 0.6600 0.2051 + 0.4581 0.3688 0.0015 + 0.0350 0.1912 0.5752 + 0.5222 0.8343 0.5959 + 0.7374 0.5795 0.6809 + 0.9218 0.9206 0.8737 + + +spike_snippets(:,:,15) = + + 0.7865 0.7816 0.6155 + 0.5372 0.8114 0.4325 + 0.3727 0.8911 0.7185 + 0.0353 0.0340 0.4635 + 0.5840 0.0862 0.1968 + 0.9519 0.5324 0.2445 + 0.3134 0.7084 0.1353 + 0.1848 0.7557 0.4126 + 0.0106 0.8252 0.7487 + 0.1224 0.7105 0.7313 + 0.8718 0.5462 0.9643 + 0.2917 0.9847 0.3488 + 0.5548 0.8527 0.6744 + 0.0908 0.7675 0.8040 + 0.1565 0.1608 0.3398 + 0.7158 0.9291 0.1652 + 0.6822 0.5056 0.6049 + 0.6157 0.1270 0.7319 + 0.8646 0.4195 0.5200 + 0.0489 0.9945 0.8455 + 0.8568 0.1741 0.1825 + 0.5738 0.0524 0.1239 + 0.8604 0.1834 0.0426 + 0.3432 0.0838 0.5858 + 0.1225 0.9384 0.2049 + 0.8457 0.1780 0.2640 + 0.0039 0.7270 0.9247 + 0.2992 0.3269 0.1270 + 0.9960 0.6671 0.7288 + 0.3351 0.2429 0.6398 + 0.5583 0.6861 0.2227 + 0.3737 0.3230 0.4208 + 0.0274 0.5645 0.2406 + 0.5473 0.4634 0.8339 + 0.6710 0.1899 0.1739 + 0.9775 0.4120 0.5454 + 0.3883 0.3817 0.5621 + 0.4033 0.6777 0.4225 + 0.7234 0.2555 0.9704 + 0.1591 0.4925 0.9903 + + +spike_snippets(:,:,16) = + + 0.9601 0.4261 0.8380 + 0.4578 0.3814 0.9816 + 0.7650 0.7522 0.4094 + 0.0475 0.7618 0.2626 + 0.0517 0.4061 0.9587 + 0.3187 0.5696 0.0575 + 0.9259 0.4462 0.2033 + 0.5448 0.1725 0.5606 + 0.1203 0.7607 0.0725 + 0.8443 0.4323 0.7786 + 0.5015 0.6739 0.2669 + 0.0986 0.3635 0.6841 + 0.4879 0.6765 0.0829 + 0.9984 0.5124 0.1379 + 0.2017 0.6917 0.2215 + 0.2086 0.6594 0.5955 + 0.1700 0.9997 0.9314 + 0.2926 0.9844 0.1808 + 0.7029 0.1544 0.3578 + 0.1826 0.5482 0.9631 + 0.5764 0.0881 0.1198 + 0.2232 0.6394 0.1269 + 0.0194 0.4497 0.3601 + 0.7354 0.7041 0.9677 + 0.8830 0.4438 0.7886 + 0.5622 0.1308 0.8845 + 0.7777 0.6478 0.4099 + 0.9439 0.9918 0.3069 + 0.1511 0.8404 0.6417 + 0.3328 0.5915 0.7386 + 0.7354 0.3478 0.1711 + 0.5607 0.9615 0.8547 + 0.8202 0.1768 0.7434 + 0.9688 0.3658 0.0483 + 0.3226 0.4188 0.7442 + 0.6305 0.0570 0.8543 + 0.0882 0.8940 0.9014 + 0.2526 0.0621 0.0940 + 0.8808 0.3889 0.5883 + 0.6945 0.2075 0.9826 + + +spike_snippets(:,:,17) = + + 0.2518 0.5818 0.5687 + 0.1692 0.2601 0.8900 + 0.9416 0.9621 0.9741 + 0.6602 0.8987 0.2570 + 0.1918 0.9138 0.1570 + 0.4492 0.2385 0.0065 + 0.2470 0.6987 0.7600 + 0.4756 0.2300 0.5099 + 0.7998 0.4087 0.1418 + 0.3431 0.9322 0.6007 + 0.6657 0.9061 0.4275 + 0.8680 0.5521 0.2827 + 0.2041 0.2542 0.1747 + 0.8590 0.0126 0.7438 + 0.9619 0.0740 0.6684 + 0.0479 0.9238 0.3660 + 0.0550 0.0913 0.3853 + 0.6281 0.2848 0.6150 + 0.7952 0.2270 0.6275 + 0.4146 0.8272 0.9834 + 0.7049 0.8764 0.7300 + 0.8926 0.7747 0.2170 + 0.0096 0.2669 0.7916 + 0.4492 0.9704 0.6413 + 0.2610 0.9420 0.6689 + 0.5655 0.8651 0.2311 + 0.7637 0.2866 0.1800 + 0.2147 0.0454 0.4293 + 0.2542 0.5383 0.2259 + 0.9819 0.8289 0.4630 + 0.6742 0.0134 0.3185 + 0.1221 0.7918 0.9145 + 0.1994 0.2983 0.8845 + 0.1645 0.5156 0.6800 + 0.0033 0.2655 0.0551 + 0.5693 0.5705 0.0326 + 0.2717 0.6311 0.1091 + 0.9801 0.3286 0.5908 + 0.5141 0.1275 0.6234 + 0.3310 0.6454 0.7261 + + +spike_snippets(:,:,18) = + + 0.6792 0.1536 0.7686 + 0.4197 0.6222 0.3823 + 0.3714 0.4234 0.2509 + 0.7312 0.3902 0.0159 + 0.7945 0.9858 0.1467 + 0.5954 0.0416 0.6832 + 0.6992 0.2205 0.4558 + 0.5526 0.3969 0.6680 + 0.0647 0.0462 0.5156 + 0.0087 0.1158 0.5595 + 0.3502 0.2373 0.1293 + 0.3708 0.9807 0.5301 + 0.6378 0.8133 0.2024 + 0.3669 0.0297 0.6785 + 0.2471 0.4422 0.2760 + 0.2854 0.5070 0.9157 + 0.4780 0.8939 0.2641 + 0.0007 0.4187 0.1611 + 0.2343 0.2207 0.5019 + 0.3406 0.6748 0.8346 + 0.1625 0.8566 0.0020 + 0.5023 0.6093 0.5408 + 0.5373 0.5792 0.9837 + 0.4145 0.5383 0.3824 + 0.6758 0.0357 0.1393 + 0.2909 0.0263 0.2084 + 0.9296 0.4293 0.0568 + 0.1195 0.8615 0.3890 + 0.6217 0.4449 0.8288 + 0.2219 0.2328 0.2420 + 0.9573 0.7066 0.0879 + 0.3173 0.7592 0.4618 + 0.0770 0.2486 0.6727 + 0.3387 0.2439 0.4620 + 0.6189 0.2457 0.6979 + 0.4550 0.4702 0.6991 + 0.3922 0.4539 0.3660 + 0.1967 0.6131 0.1070 + 0.9190 0.3731 0.9881 + 0.6727 0.0177 0.8368 + + +spike_snippets(:,:,19) = + + 0.5723 0.4162 0.5737 + 0.7365 0.7400 0.0192 + 0.3486 0.1283 0.5239 + 0.5596 0.0741 0.9627 + 0.7251 0.3255 0.9218 + 0.4888 0.2237 0.8870 + 0.2031 0.5954 0.6067 + 0.3191 0.4081 0.8511 + 0.4727 0.8630 0.4413 + 0.0459 0.9529 0.9355 + 0.0706 0.7296 0.7513 + 0.1063 0.2827 0.0582 + 0.9681 0.3477 0.4857 + 0.0197 0.6786 0.2709 + 0.5342 0.7845 0.3807 + 0.4403 0.9275 0.8690 + 0.1449 0.7715 0.0911 + 0.4528 0.3883 0.4938 + 0.2482 0.6608 0.2387 + 0.2375 0.4085 0.6854 + 0.4636 0.7216 0.3191 + 0.4088 0.6269 0.4242 + 0.6251 0.8265 0.3873 + 0.7480 0.9342 0.8437 + 0.7078 0.5361 0.7469 + 0.6856 0.5139 0.4963 + 0.9682 0.1418 0.5922 + 0.3966 0.6482 0.8709 + 0.6875 0.3060 0.4249 + 0.3703 0.8274 0.2291 + 0.7073 0.3335 0.8946 + 0.5328 0.5704 0.4358 + 0.3135 0.3453 0.4677 + 0.4190 0.8187 0.4792 + 0.4847 0.5276 0.9708 + 0.2647 0.7839 0.6610 + 0.4281 0.1247 0.1745 + 0.4473 0.1502 0.9363 + 0.5404 0.8297 0.3259 + 0.8203 0.1586 0.0542 + + +spike_snippets(:,:,20) = + + 0.1943 0.5619 0.1293 + 0.9273 0.8478 0.7970 + 0.1930 0.3151 0.4309 + 0.7377 0.5235 0.5462 + 0.2804 0.0120 0.1150 + 0.1102 0.7948 0.4490 + 0.3471 0.2344 0.6634 + 0.9368 0.7492 0.8331 + 0.0338 0.8952 0.0004 + 0.3556 0.8802 0.2122 + 0.3900 0.4580 0.4061 + 0.3526 0.3209 0.9486 + 0.1045 0.3968 0.7268 + 0.3758 0.6132 0.2621 + 0.2642 0.8817 0.2963 + 0.5723 0.1138 0.3768 + 0.9351 0.0277 0.3637 + 0.1406 0.2203 0.2729 + 0.5997 0.5051 0.3468 + 0.2357 0.7267 0.0786 + 0.9923 0.6103 0.2772 + 0.1016 0.6222 0.8030 + 0.4693 0.7362 0.1926 + 0.7770 0.7613 0.1670 + 0.8043 0.4398 0.5129 + 0.5659 0.0116 0.4863 + 0.2755 0.4490 0.3219 + 0.9902 0.0969 0.6059 + 0.5231 0.5545 0.6418 + 0.6858 0.0013 0.3912 + 0.7352 0.1463 0.8434 + 0.3129 0.7567 0.2621 + 0.8429 0.9725 0.3036 + 0.0225 0.0850 0.6518 + 0.8738 0.2763 0.6083 + 0.9140 0.8860 0.5986 + 0.1661 0.6797 0.4139 + 0.0239 0.4074 0.7700 + 0.3080 0.5475 0.4765 + 0.7355 0.0164 0.3026 +
 
% Create electrode table region referencing electrodes 0, 1, and 2
shank0_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(ElectrodesDynamicTable), ...
'description', 'shank0', ...
'data', (0:2)');
 
% Define spike event series for unsorted spike times
spike_events = types.core.SpikeEventSeries( ...
'data', spike_snippets, ...
'timestamps', (0:19)', ... % Timestamps for each event
'description', 'events detected with 100uV threshold', ...
'electrodes', shank0_table_region ...
);
 
% Add spike event series to NWB file acquisition
nwb.acquisition.set('SpikeEvents_Shank0', spike_events);

Designating Electrophysiology Data

As mentioned above, ElectricalSeries objects are meant for storing specific types of extracellular recordings. In addition to this TimeSeries class, NWB provides some Processing Modules for designating the type of data you are storing. We will briefly discuss them here, and refer the reader to the API documentation and Intro to NWB for more details on using these objects.
For storing unsorted spiking data, there are two options. Which one you choose depends on what data you have available. If you need to store complete and/or continuous raw voltage traces, you should store the traces with ElectricalSeries objects as acquisition data, and use the EventDetection class for identifying the spike events in your raw traces. If you do not want to store the raw voltage traces and only the waveform ‘snippets’ surrounding spike events, you should use SpikeEventSeries objects.
The results of spike sorting (or clustering) should be stored in the top-level Units table. The Units table can hold just the spike times of sorted units or, optionally, include additional waveform information. You can use the optional predefined columns waveform_mean, waveform_sd, and waveforms in the Units table to store individual and mean waveform data.
For local field potential data, there are two options. Again, which one you choose depends on what data you have available. With both options, you should store your traces with ElectricalSeries objects. If you are storing unfiltered local field potential data, you should store the ElectricalSeries objects in LFP data interface object(s). If you have filtered LFP data, you should store the ElectricalSeries objects in FilteredEphys data interface object(s).

Writing the NWB File

nwbExport(nwb, 'ecephys_tutorial.nwb')

Reading NWB Data

Data arrays are read passively from the file. Calling TimeSeries.data does not read the data values, but presents an HDF5 object that can be indexed to read data. This allows you to conveniently work with datasets that are too large to fit in RAM all at once. load with no input arguments reads the entire dataset:
nwb2 = nwbRead('ecephys_tutorial.nwb', 'ignorecache');
nwb2.processing.get('ecephys'). ...
nwbdatainterface.get('LFP'). ...
electricalseries.get('ElectricalSeries'). ...
data.load;

Accessing Data Regions

If all you need is a data region, you can index a DataStub object like you would any normal array in MATLAB, as shown below. When indexing the dataset this way, only the selected region is read from disk into RAM. This allows you to handle very large datasets that would not fit entirely into RAM.
% read section of LFP
nwb2.processing.get('ecephys'). ...
nwbdatainterface.get('LFP'). ...
electricalseries.get('ElectricalSeries'). ...
data(1:5, 1:10)
ans = 5×10
-1.0039 -0.5621 -1.1019 0.1768 0.2032 0.1612 -0.5518 0.8552 -1.3040 -0.5646 + -0.3458 -1.2921 -0.1967 1.7260 -1.5245 1.3653 -0.6380 0.8438 -0.7094 0.7466 + 0.2758 -0.3401 0.3549 0.4890 -0.2288 0.1290 2.1648 0.1316 -0.2172 0.3036 + -0.8548 -1.5282 -0.0919 -0.1388 1.7996 -0.2845 -1.1904 0.5773 -0.3059 -0.9745 + 0.1536 -0.2051 2.4873 1.0999 0.6398 -0.1086 -0.2511 -0.0993 1.3019 0.0095 +
 
% You can use the getRow method of the table to load spike times of a specific unit.
% To get the values, unpack from the returned table.
nwb.units.getRow(1).spike_times{1}
ans = 21×1
0.8383 + 0.6321 + 0.2418 + 0.2965 + 0.9865 + 0.2779 + 0.9945 + 0.5980 + 0.1216 + 0.1694 +

Learn more!

See the API documentation to learn what data types are available.

MATLAB tutorials

Python tutorials

See our tutorials for more details about your data type:
Check out other tutorials that teach advanced NWB topics:

+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ecephys.png b/docs/source/_static/html/tutorials/ecephys.png new file mode 100644 index 0000000000000000000000000000000000000000..90f0770ffaaea6675f89328c07f8b13c108d4784 GIT binary patch literal 1670 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 literal 0 HcmV?d00001 diff --git a/docs/source/_static/html/tutorials/ecephys_data_deps.png b/docs/source/_static/html/tutorials/ecephys_data_deps.png new file mode 100644 index 0000000000000000000000000000000000000000..8854d00098bec86e777bddea5ed99f2d8485dbcc GIT binary patch literal 102809 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 literal 0 HcmV?d00001 diff --git a/docs/source/_static/html/tutorials/icephys.html b/docs/source/_static/html/tutorials/icephys.html new file mode 100644 index 00000000..5b095148 --- /dev/null +++ b/docs/source/_static/html/tutorials/icephys.html @@ -0,0 +1,509 @@ + +Intracellular electrophysiology \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/images.html b/docs/source/_static/html/tutorials/images.html new file mode 100644 index 00000000..bbdaecdf --- /dev/null +++ b/docs/source/_static/html/tutorials/images.html @@ -0,0 +1,371 @@ + +Storing Image Data in NWB

Storing Image Data in NWB

Image data can be a collection of individual images or movie segments (as a movie is simply a series of images), about the subject, the environment, the presented stimuli, or other parts related to the experiment. This tutorial focuses in particular on the usage of:

Create an NWB File

nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'LastName, FirstName', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', 'DOI:10.1016/j.neuron.2016.12.011' ... % optional
);
nwb
nwb =
NwbFile with properties: + + nwb_version: '2.7.0' + file_create_date: [] + identifier: 'Mouse5_Day3' + session_description: 'mouse in open exploration' + session_start_time: {[2018-04-25T02:30:03.000000+02:00]} + timestamps_reference_time: {[2018-04-25T03:00:45.000000+02:00]} + acquisition: [0×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: '' + general_devices: [0×1 types.untyped.Set] + general_experiment_description: '' + general_experimenter: 'LastName, FirstName' + general_extracellular_ephys: [0×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [] + general_institution: 'University of My Institution' + general_intracellular_ephys: [0×1 types.untyped.Set] + general_intracellular_ephys_experimental_conditions: [] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_intracellular_recordings: [] + general_intracellular_ephys_repetitions: [] + general_intracellular_ephys_sequential_recordings: [] + general_intracellular_ephys_simultaneous_recordings: [] + general_intracellular_ephys_sweep_table: [] + general_keywords: '' + general_lab: '' + general_notes: '' + general_optogenetics: [0×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' + general_session_id: 'session_1234' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [0×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [] +

OpticalSeries: Storing series of images as stimuli

OpticalSeries is for time series of images that were presented to the subject as stimuli. We will create an OpticalSeries object with the name "StimulusPresentation" representing what images were shown to the subject and at what times.
Image data can be stored either in the HDF5 file or as an external image file. For this tutorial, we will use fake image data with shape of ('time', 'x', 'y', 'RGB') = (200, 50, 50, 3). As in all TimeSeries, the first dimension is time. The second and third dimensions represent x and y. The fourth dimension represents the RGB value (length of 3) for color images. Please note: As described in the dimensionMapNoDataPipes tutorial, when a MATLAB array is exported to HDF5, the array is transposed. Therefore, in order to correctly export the data, we will need to create a transposed array, where the dimensions are in reverse order compared to the type specification.
NWB differentiates between acquired data and data that was presented as stimulus. We can add it to the NWBFile object as stimulus data.
If the sampling rate is constant, use rate and starting_time to specify time. For irregularly sampled recordings, use timestamps to specify time for each sample image.
image_data = randi(255, [3, 50, 50, 200]); % NB: Array is transposed
optical_series = types.core.OpticalSeries( ...
'distance', 0.7, ... % required
'field_of_view', [0.2, 0.3, 0.7], ... % required
'orientation', 'lower left', ... % required
'data', image_data, ...
'data_unit', 'n.a.', ...
'starting_time_rate', 1.0, ...
'starting_time', 0.0, ...
'description', 'The images presented to the subject as stimuli' ...
);
 
nwb.stimulus_presentation.set('StimulusPresentation', optical_series);

AbstractFeatureSeries: Storing features of visual stimuli

While it is usually recommended to store the entire image data as an OpticalSeries, sometimes it is useful to store features of the visual stimuli instead of or in addition to the raw image data. For example, you may want to store the mean luminance of the image, the contrast, or the spatial frequency. This can be done using an instance of AbstractFeatureSeries. This class is a general container for storing time series of features that are derived from the raw image data.
% Create some fake feature data
feature_data = rand(3, 200); % 200 time points, 3 features
 
% Create an AbstractFeatureSeries object
abstract_feature_series = types.core.AbstractFeatureSeries( ...
'data', feature_data, ...
'timestamps', linspace(0, 1, 200), ...
'description', 'Features of the visual stimuli', ...
'features', {'luminance', 'contrast', 'spatial frequency'}, ...
'feature_units', {'n.a.', 'n.a.', 'cycles/degree'} ...
);
% Add the AbstractFeatureSeries to the NWBFile
nwb.stimulus_presentation.set('StimulusFeatures', abstract_feature_series);

ImageSeries: Storing series of images as acquisition

ImageSeries is a general container for time series of images acquired during the experiment. Image data can be stored either in the HDF5 file or as an external image file. When color images are stored in the HDF5 file the color channel order is expected to be RGB.
image_data = randi(255, [3, 50, 50, 200]);
behavior_images = types.core.ImageSeries( ...
'data', image_data, ...
'description', 'Image data of an animal in environment', ...
'data_unit', 'n.a.', ...
'starting_time_rate', 1.0, ...
'starting_time', 0.0 ...
);
 
nwb.acquisition.set('ImageSeries', behavior_images);

External Files

External files (e.g. video files of the behaving animal) can be added to the NWBFile by creating an ImageSeries object using the external_file attribute that specifies the path to the external file(s) on disk. The file(s) path must be relative to the path of the NWB file. Either external_file or data must be specified, but not both. external_file can be a cell array of multiple video files.
The starting_frame attribute serves as an index to indicate the starting frame of each external file, allowing you to skip the beginning of videos.
external_files = {'video1.pmp4', 'video2.pmp4'};
 
timestamps = [0.0, 0.04, 0.07, 0.1, 0.14, 0.16, 0.21];
behavior_external_file = types.core.ImageSeries( ...
'description', 'Behavior video of animal moving in environment', ...
'data_unit', 'n.a.', ...
'external_file', external_files, ...
'format', 'external', ...
'external_file_starting_frame', [0, 2, 4], ...
'timestamps', timestamps ...
);
 
nwb.acquisition.set('ExternalVideos', behavior_external_file);

Static Images

Static images can be stored in an NWBFile object by creating an RGBAImage, RGBImage or GrayscaleImage object with the image data. All of these image types provide an optional description parameter to include text description about the image and the resolution parameter to specify the pixels/cm resolution of the image.

RGBAImage: for color images with transparency

RGBAImage is for storing data of color image with transparency. data must be 3D where the first and second dimensions represent x and y. The third dimension has length 4 and represents the RGBA value.
image_data = randi(255, [4, 200, 200]);
 
rgba_image = types.core.RGBAImage( ...
'data', image_data, ... % required
'resolution', 70.0, ...
'description', 'RGBA image' ...
);

RGBImage: for color images

RGBImage is for storing data of RGB color image. data must be 3D where the first and second dimensions represent x and y. The third dimension has length 3 and represents the RGB value.
image_data = randi(255, [3, 200, 200]);
 
rgb_image = types.core.RGBImage( ...
'data', image_data, ... % required
'resolution', 70.0, ...
'description', 'RGB image' ...
);

GrayscaleImage: for grayscale images

GrayscaleImage is for storing grayscale image data. data must be 2D where the first and second dimensions represent x and y.
image_data = randi(255, [200, 200]);
 
grayscale_image = types.core.GrayscaleImage( ...
'data', image_data, ... % required
'resolution', 70.0, ...
'description', 'Grayscale image' ...
);

Images: a container for images

Add the images to an Images container that accepts any of these image types.
image_collection = types.core.Images( ...
'description', 'A collection of logo images presented to the subject.'...
);
 
image_collection.image.set('rgba_image', rgba_image);
image_collection.image.set('rgb_image', rgb_image);
image_collection.image.set('grayscale_image', grayscale_image);
 
nwb.acquisition.set('image_collection', image_collection);

Index Series for Repeated Images

You may want to set up a time series of images where some images are repeated many times. You could create an ImageSeries that repeats the data each time the image is shown, but that would be inefficient, because it would store the same data multiple times. A better solution would be to store the unique images once and reference those images. This is how IndexSeries works. First, create an Images container with the order of images defined using an ImageReferences. Then create an IndexSeries that indexes into the Images.
rgbImage = imread('street2.jpg');
grayImage = uint8(sum(double(rgbImage), 3) ./ double(max(max(max(rgbImage)))));
GsStreet = types.core.GrayscaleImage(...
'data', grayImage, ...
'description', 'grayscale image of a street.', ...
'resolution', 28 ...
);
 
RgbStreet = types.core.RGBImage( ...
'data', rgbImage, ...
'resolution', 28, ...
'description', 'RGB Street' ...
);
 
ImageOrder = types.core.ImageReferences(...
'data', [types.untyped.ObjectView(RgbStreet), types.untyped.ObjectView(GsStreet)] ...
);
Images = types.core.Images( ...
'gs_face', GsStreet, ...
'rgb_face', RgbStreet, ...
'description', 'A collection of streets.', ...
'order_of_images', ImageOrder ...
);
 
types.core.IndexSeries(...
'data', [0, 1, 0, 1], ... % NOTE: 0-indexed
'indexed_images', Images, ...
'timestamps', [0.1, 0.2, 0.3, 0.4] ...
)
ans =
IndexSeries with properties: + + indexed_images: [1×1 types.core.Images] + indexed_timeseries: [] + starting_time_unit: 'seconds' + timestamps_interval: 1 + timestamps_unit: 'seconds' + data: [0 1 0 1] + comments: 'no comments' + control: [] + control_description: '' + data_continuity: '' + data_conversion: [] + data_offset: [] + data_resolution: [] + data_unit: 'N/A' + description: 'no description' + starting_time: [] + starting_time_rate: [] + timestamps: [0.1000 0.2000 0.3000 0.4000] +
Here data contains the (0-indexed) index of the displayed image as they are ordered in the ImageReference.

Writing the images to an NWB File

Now use nwbExport to write the file.
nwbExport(nwb, "images_test.nwb");
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/intro.html b/docs/source/_static/html/tutorials/intro.html new file mode 100644 index 00000000..63ebd0ce --- /dev/null +++ b/docs/source/_static/html/tutorials/intro.html @@ -0,0 +1,307 @@ + +Introduction to MatNWB

Introduction to MatNWB

Installing MatNWB

Use the code below within the brackets to install MatNWB from source. MatNWB works by automatically creating API classes based on the schema.
%{
!git clone https://github.com/NeurodataWithoutBorders/matnwb.git
addpath(genpath(pwd));
%}

Set up the NWB File

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata using the NwbFile command. For all MatNWB classes and functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value. Ellipses are used for clarity.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'Last, First', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb

Subject Information

You can also provide information about your subject in the NWB file. Create a Subject object to store information such as age, species, genotype, sex, and a freeform description. Then set nwb.general_subject to the Subject object.
Each of these fields is free-form, so any values will be valid, but here are our recommendations:
  • For age, we recommend using the ISO 8601 Duration format
  • For species, we recommend using the formal latin binomal name (e.g. mouse -> Mus musculus, human -> Homo sapiens)
  • For sex, we recommend using F (female), M (male), U (unknown), and O (other)
subject = types.core.Subject( ...
'subject_id', '001', ...
'age', 'P90D', ...
'description', 'mouse 5', ...
'species', 'Mus musculus', ...
'sex', 'M' ...
);
nwb.general_subject = subject;
 
subject
Note: the DANDI archive requires all NWB files to have a subject object with subject_id specified, and strongly encourages specifying the other fields.

Time Series Data

TimeSeries is a common base class for measurements sampled over time, and provides fields for data and timestamps (regularly or irregularly sampled). You will also need to supply the name and unit of measurement (SI unit).
For instance, we can store a TimeSeries data where recording started 0.0 seconds after start_time and sampled every second (1 Hz):
time_series_with_rate = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0);
For irregularly sampled recordings, we need to provide the timestamps for the data:
time_series_with_timestamps = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'timestamps', linspace(0, 1, 10));
The TimeSeries class serves as the foundation for all other time series types in the NWB format. Several specialized subclasses extend the functionality of TimeSeries, each tailored to handle specific kinds of data. In the next section, we’ll explore one of these specialized types. For a full overview, please check out the type hierarchy in the NWB schema documentation.

Other Types of Time Series

As mentioned previously, there are many subtypes of TimeSeries in MatNWB that are used to store different kinds of data. One example is AnnotationSeries, a subclass of TimeSeries that stores text-based records about the experiment. Similar to our TimeSeries example above, we can create an AnnotationSeries object with text information about a stimulus and add it to the stimulus_presentation group in the NWBFile. Below is an example where we create an AnnotationSeries object with annotations for airpuff stimuli and add it to the NWBFile.
% Create an AnnotationSeries object with annotations for airpuff stimuli
annotations = types.core.AnnotationSeries( ...
'description', 'Airpuff events delivered to the animal', ...
'data', {'Left Airpuff', 'Right Airpuff', 'Right Airpuff'}, ...
'timestamps', [1.0, 3.0, 8.0] ...
);
 
% Add the AnnotationSeries to the NWBFile's stimulus group
nwb.stimulus_presentation.set('Airpuffs', annotations)

Behavior

SpatialSeries and Position

Many types of data have special data types in NWB. To store the spatial position of a subject, we will use the SpatialSeries and Position classes.
Note: These diagrams follow a standard convention called "UML class diagram" to express the object-oriented relationships between NWB classes. For our purposes, all you need to know is that an open triangle means "extends" and an open diamond means "is contained within." Learn more about class diagrams on the wikipedia page.
SpatialSeries is a subclass of TimeSeries, a common base class for measurements sampled over time, and provides fields for data and time (regularly or irregularly sampled). Here, we put a SpatialSeries object called 'SpatialSeries' in a Position object. If the data is sampled at a regular interval, it is recommended to specify the starting_time and the sampling rate (starting_time_rate), although it is still possible to specify timestamps as in the time_series_with_timestamps example above.
% create SpatialSeries object
spatial_series_ts = types.core.SpatialSeries( ...
'data', [linspace(0,10,100); linspace(0,8,100)], ...
'reference_frame', '(0,0) is bottom left corner', ...
'starting_time', 0, ...
'starting_time_rate', 200 ...
);
 
% create Position object and add SpatialSeries
Position = types.core.Position('SpatialSeries', spatial_series_ts);
 
% create processing module
behavior_mod = types.core.ProcessingModule('description', 'contains behavioral data');
 
% add the Position object (that holds the SpatialSeries object)
behavior_mod.nwbdatainterface.set('Position', Position);
NWB differentiates between raw, acquired data, which should never change, and processed data, which are the results of preprocessing algorithms and could change. Let's assume that the animal's position was computed from a video tracking algorithm, so it would be classified as processed data. Since processed data can be very diverse, NWB allows us to create processing modules, which are like folders, to store related processed data or data that comes from a single algorithm.
Create a processing module called "behavior" for storing behavioral data in the NWBFile and add the Position object to the module.
% create processing module
behavior_mod = types.core.ProcessingModule('description', 'contains behavioral data');
 
% add the Position object (that holds the SpatialSeries object) to the
% module and name the Position object "Position"
behavior_mod.nwbdatainterface.set('Position', Position);
 
% add the processing module to the NWBFile object, and name the processing module "behavior"
nwb.processing.set('behavior', behavior_mod);

Trials

Trials are stored in a TimeIntervals object which is a subclass of DynamicTable. DynamicTable objects are used to store tabular metadata throughout NWB, including for trials, electrodes, and sorted units. They offer flexibility for tabular data by allowing required columns, optional columns, and custom columns.
The trials DynamicTable can be thought of as a table with this structure:
Trials are stored in a TimeIntervals object which subclasses DynamicTable. Here, we are adding 'correct', which will be a logical array.
trials = types.core.TimeIntervals( ...
'colnames', {'start_time', 'stop_time', 'correct'}, ...
'description', 'trial data and properties');
 
trials.addRow('start_time', 0.1, 'stop_time', 1.0, 'correct', false)
trials.addRow('start_time', 1.5, 'stop_time', 2.0, 'correct', true)
trials.addRow('start_time', 2.5, 'stop_time', 3.0, 'correct', false)
 
trials.toTable() % visualize the table
nwb.intervals_trials = trials;
 
% If you have multiple trials tables, you will need to use custom names for
% each one:
nwb.intervals.set('custom_intervals_table_name', trials);

Write

Now, to write the NWB file that we have built so far:
nwbExport(nwb, 'intro_tutorial.nwb')
We can use the HDFView application to inspect the resulting NWB file.

Read

We can then read the file back in using MatNWB and inspect its contents.
read_nwbfile = nwbRead('intro_tutorial.nwb', 'ignorecache')
We can print the SpatialSeries data traversing the hierarchy of objects. The processing module called 'behavior' contains our Position object named 'Position'. The Position object contains our SpatialSeries object named 'SpatialSeries'.
read_spatial_series = read_nwbfile.processing.get('behavior'). ...
nwbdatainterface.get('Position').spatialseries.get('SpatialSeries')

Reading Data

Counter to normal MATLAB workflow, data arrays are read passively from the file. Calling read_spatial_series.data does not read the data values, but presents a DataStub object that can be indexed to read data.
read_spatial_series.data
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access all the data in the matrix using the load method with no arguments.
read_spatial_series.data.load
If you only need a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_spatial_series.data(:, 1:10)

Next Steps

This concludes the introductory tutorial. Please proceed to one of the specialized tutorials, which are designed to follow this one.
See the API documentation to learn what data types are available.
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ogen.html b/docs/source/_static/html/tutorials/ogen.html new file mode 100644 index 00000000..4e19478b --- /dev/null +++ b/docs/source/_static/html/tutorials/ogen.html @@ -0,0 +1,201 @@ + +Optogenetics

Optogenetics

This tutorial will demonstrate how to write optogenetics data.

Creating an NWBFile object

When creating a NWB file, the first step is to create the NWBFile object using NwbFile.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', char(java.util.UUID.randomUUID), ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'Last, First M.', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', 'DOI:10.1016/j.neuron.2016.12.011'); % optional
nwb
nwb =
NwbFile with properties: + + nwb_version: '2.6.0' + file_create_date: [] + identifier: 'b843652e-3404-48c7-8686-4904e786ea4c' + session_description: 'mouse in open exploration' + session_start_time: {[2018-04-25T02:30:03.000000+02:00]} + timestamps_reference_time: [] + acquisition: [0×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: '' + general_devices: [0×1 types.untyped.Set] + general_experiment_description: '' + general_experimenter: 'Last, First M.' + general_extracellular_ephys: [0×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [] + general_institution: 'University of My Institution' + general_intracellular_ephys: [0×1 types.untyped.Set] + general_intracellular_ephys_experimental_conditions: [] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_intracellular_recordings: [] + general_intracellular_ephys_repetitions: [] + general_intracellular_ephys_sequential_recordings: [] + general_intracellular_ephys_simultaneous_recordings: [] + general_intracellular_ephys_sweep_table: [] + general_keywords: '' + general_lab: '' + general_notes: '' + general_optogenetics: [0×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' + general_session_id: 'session_1234' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [0×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [] +

Adding optogenetic data

The ogen module contains two data types that you will need to write optogenetics data, OptogeneticStimulusSite, which contains metadata about the stimulus site, and OptogeneticSeries, which contains the values of the time series.
First, you need to create a Device object linked to the NWBFile:
device = types.core.Device();
nwb.general_devices.set('Device', device);
Now, you can create and add an OptogeneticStimulusSite.
ogen_stim_site = types.core.OptogeneticStimulusSite( ...
'device', types.untyped.SoftLink(device), ...
'description', 'This is an example optogenetic site.', ...
'excitation_lambda', 600.0, ...
'location', 'VISrl');
 
nwb.general_optogenetics.set('OptogeneticStimulusSite', ogen_stim_site);
With the OptogeneticStimulusSite added, you can now create and add a OptogeneticSeries. Here, we will generate some random data and specify the timing using rate. If you have samples at irregular intervals, you should use timestamps instead.
ogen_series = types.core.OptogeneticSeries( ...
'data', randn(20, 1), ...
'site', types.untyped.SoftLink(ogen_stim_site), ...
'starting_time', 0.0, ...
'starting_time_rate', 30.0); % Hz
nwb.stimulus_presentation.set('OptogeneticSeries', ogen_series);
 
nwb
nwb =
NwbFile with properties: + + nwb_version: '2.6.0' + file_create_date: [] + identifier: 'b843652e-3404-48c7-8686-4904e786ea4c' + session_description: 'mouse in open exploration' + session_start_time: {[2018-04-25T02:30:03.000000+02:00]} + timestamps_reference_time: [] + acquisition: [0×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: '' + general_devices: [1×1 types.untyped.Set] + general_experiment_description: '' + general_experimenter: 'Last, First M.' + general_extracellular_ephys: [0×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [] + general_institution: 'University of My Institution' + general_intracellular_ephys: [0×1 types.untyped.Set] + general_intracellular_ephys_experimental_conditions: [] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_intracellular_recordings: [] + general_intracellular_ephys_repetitions: [] + general_intracellular_ephys_sequential_recordings: [] + general_intracellular_ephys_simultaneous_recordings: [] + general_intracellular_ephys_sweep_table: [] + general_keywords: '' + general_lab: '' + general_notes: '' + general_optogenetics: [1×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' + general_session_id: 'session_1234' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [1×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [] +
Now you can write the NWB file.
nwbExport(nwb, 'ogen_tutorial.nwb');
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ophys.html b/docs/source/_static/html/tutorials/ophys.html new file mode 100644 index 00000000..c9cab142 --- /dev/null +++ b/docs/source/_static/html/tutorials/ophys.html @@ -0,0 +1,479 @@ + +MatNWB Optical Physiology Tutorial

MatNWB Optical Physiology Tutorial

Introduction

In this tutorial, we will create fake data for a hypothetical optical physiology experiment with a freely moving animal. The types of data we will convert are:
  • Acquired two-photon images
  • Image segmentation (ROIs)
  • Fluorescence and dF/F response
It is recommended to first work through the Introduction to MatNWB tutorial, which demonstrates installing MatNWB and creating an NWB file with subject information, animal position, and trials, as well as writing and reading NWB files in MATLAB.

Set up the NWB file

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata. For all MatNWB functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'LastName, FirstName', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: + + nwb_version: '2.6.0' + file_create_date: [] + identifier: 'Mouse5_Day3' + session_description: 'mouse in open exploration' + session_start_time: {[2018-04-25T02:30:03.000000+02:00]} + timestamps_reference_time: {[2018-04-25T03:00:45.000000+02:00]} + acquisition: [0×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: '' + general_devices: [0×1 types.untyped.Set] + general_experiment_description: '' + general_experimenter: 'LastName, FirstName' + general_extracellular_ephys: [0×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [] + general_institution: 'University of My Institution' + general_intracellular_ephys: [0×1 types.untyped.Set] + general_intracellular_ephys_experimental_conditions: [] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_intracellular_recordings: [] + general_intracellular_ephys_repetitions: [] + general_intracellular_ephys_sequential_recordings: [] + general_intracellular_ephys_simultaneous_recordings: [] + general_intracellular_ephys_sweep_table: [] + general_keywords: '' + general_lab: '' + general_notes: '' + general_optogenetics: [0×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: {'DOI:10.1016/j.neuron.2016.12.011'} + general_session_id: 'session_1234' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [0×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [] +

Optical Physiology

Optical physiology results are written in four steps:
  1. Create imaging plane
  2. Acquired two-photon images
  3. Image segmentation
  4. Fluorescence and dF/F responses

Imaging Plane

First, you must create an ImagingPlane object, which will hold information about the area and method used to collect the optical imaging data. This requires creation of a Device object for the microscope and an OpticalChannel object. Then you can create an ImagingPlane.
optical_channel = types.core.OpticalChannel( ...
'description', 'description', ...
'emission_lambda', 500.);
 
device = types.core.Device();
nwb.general_devices.set('Device', device);
 
imaging_plane_name = 'imaging_plane';
imaging_plane = types.core.ImagingPlane( ...
'optical_channel', optical_channel, ...
'description', 'a very interesting part of the brain', ...
'device', types.untyped.SoftLink(device), ...
'excitation_lambda', 600., ...
'imaging_rate', 5., ...
'indicator', 'GFP', ...
'location', 'my favorite brain location');
 
nwb.general_optophysiology.set(imaging_plane_name, imaging_plane);

Storing Two-Photon Data

You can create a TwoPhotonSeries class representing two photon imaging data. TwoPhotonSeries, like SpatialSeries, inherits from TimeSeries and is similar in behavior to OnePhotonSeries.
InternalTwoPhoton = types.core.TwoPhotonSeries( ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0.0, ...
'starting_time_rate', 3.0, ...
'data', ones(200, 100, 1000), ...
'data_unit', 'lumens');
 
nwb.acquisition.set('2pInternal', InternalTwoPhoton);

Storing One-Photon Data

Now that we have our ImagingPlane, we can create a OnePhotonSeries object to store raw one-photon imaging data.
% using internal data. this data will be stored inside the NWB file
InternalOnePhoton = types.core.OnePhotonSeries( ...
'data', ones(100, 100, 1000), ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0., ...
'starting_time_rate', 1.0, ...
'data_unit', 'normalized amplitude' ...
);
nwb.acquisition.set('1pInternal', InternalOnePhoton);

Plane Segmentation

Image segmentation stores the detected regions of interest in the TwoPhotonSeries data. ImageSegmentation allows you to have more than one segmentation by creating more PlaneSegmentation objects.

Regions of interest (ROIs)

ROIs can be added to a PlaneSegmentation either as an image_mask or as a pixel_mask. An image mask is an array that is the same size as a single frame of the TwoPhotonSeries, and indicates where a single region of interest is. This image mask may be boolean or continuous between 0 and 1. A pixel_mask, on the other hand, is a list of indices (i.e coordinates) and weights for the ROI. The pixel_mask is represented as a compound data type using a ragged array and below is an example demonstrating how to create either an image_mask or a pixel_mask. Changing the dropdown selection will update the PlaneSegmentation object accordingly.
selection = "Create Image Mask"; % "Create Image Mask" or "Create Pixel Mask"
 
% 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);
center = randi(90,2,n_rois);
for i = 1:n_rois
image_mask(center(1,i):center(1,i)+10, center(2,i):center(2,i)+10, i) = 1;
end
 
if selection == "Create Pixel Mask"
ind = find(image_mask);
[y_ind, x_ind, roi_ind] = ind2sub(size(image_mask), ind);
 
pixel_mask_struct = struct();
pixel_mask_struct.x = uint32(x_ind); % Add x coordinates to struct field x
pixel_mask_struct.y = uint32(y_ind); % Add y coordinates to struct field y
pixel_mask_struct.weight = single(ones(size(x_ind)));
% Create pixel mask vector data
pixel_mask = types.hdmf_common.VectorData(...
'data', struct2table(pixel_mask_struct), ...
'description', 'pixel masks');
 
% When creating a pixel mask, it is also necessary to specify a
% pixel_mask_index vector. See the documentation for ragged arrays linked
% above to learn more.
num_pixels_per_roi = zeros(n_rois, 1); % Column vector
for i_roi = 1:n_rois
num_pixels_per_roi(i_roi) = sum(roi_ind == i_roi);
end
 
pixel_mask_index = uint16(cumsum(num_pixels_per_roi)); % Note: Use an integer
% type that can accommodate the maximum value of the cumulative sum
 
% Create pixel_mask_index vector
pixel_mask_index = types.hdmf_common.VectorIndex(...
'description', 'Index into pixel_mask VectorData', ...
'data', pixel_mask_index, ...
'target', types.untyped.ObjectView(pixel_mask) );
 
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'pixel_mask'}, ...
'description', 'roi pixel position (x,y) and pixel weight', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'pixel_mask_index', pixel_mask_index, ...
'pixel_mask', pixel_mask ...
);
 
else % selection == "Create Image Mask"
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'image_mask'}, ...
'description', 'output from segmenting my favorite imaging plane', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'image_mask', types.hdmf_common.VectorData(...
'data', image_mask, ...
'description', 'image masks') ...
);
end

Adding ROIs to NWB file

Now create an ImageSegmentation object and put the plane_segmentation object inside of it, naming it PlaneSegmentation.
img_seg = types.core.ImageSegmentation();
img_seg.planesegmentation.set('PlaneSegmentation', plane_segmentation);
Now create a ProcessingModule called "ophys" and put our img_seg object in it, calling it "ImageSegmentation", and add the ProcessingModule to nwb.
ophys_module = types.core.ProcessingModule( ...
'description', 'contains optical physiology data')
ophys_module =
ProcessingModule with properties: + + description: 'contains optical physiology data' + dynamictable: [0×1 types.untyped.Set] + nwbdatainterface: [0×1 types.untyped.Set] +
ophys_module.nwbdatainterface.set('ImageSegmentation', img_seg);
nwb.processing.set('ophys', ophys_module);

Storing fluorescence of ROIs over time

Now that ROIs are stored, you can store fluorescence dF/F data for these regions of interest. This type of data is stored using the RoiResponseSeries class. You will not need to instantiate this class directly to create objects of this type, but it is worth noting that this is the class you will work with after you read data back in.
First, create a data interface to store this data in
roi_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(plane_segmentation), ...
'description', 'all_rois', ...
'data', (0:n_rois-1)');
 
roi_response_series = types.core.RoiResponseSeries( ...
'rois', roi_table_region, ...
'data', NaN(n_rois, 100), ...
'data_unit', 'lumens', ...
'starting_time_rate', 3.0, ...
'starting_time', 0.0);
 
fluorescence = types.core.Fluorescence();
fluorescence.roiresponseseries.set('RoiResponseSeries', roi_response_series);
 
ophys_module.nwbdatainterface.set('Fluorescence', fluorescence);
Finally, the ophys ProcessingModule is added to the NwbFile.
nwb.processing.set('ophys', ophys_module);

Writing the NWB file

nwb_file_name = 'ophys_tutorial.nwb';
if isfile(nwb_file_name); delete(nwb_file_name); end
nwbExport(nwb, nwb_file_name);

Reading the NWB file

read_nwb = nwbRead(nwb_file_name, 'ignorecache');
Data arrays are read passively from the file. Calling TimeSeries.data does not read the data values, but presents an HDF5 object that can be indexed to read data.
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence')...
.roiresponseseries.get('RoiResponseSeries').data
ans =
DataStub with properties: + + filename: 'ophys_tutorial.nwb' + path: '/processing/ophys/Fluorescence/RoiResponseSeries/data' + dims: [20 100] + ndims: 2 + dataType: 'double' +
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access the data in the matrix using the load method.
load with no input arguments reads the entire dataset:
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries').data.load
ans = 20×100
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN +
If all you need is a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries'). ...
data(1:5, 1:10)
ans = 5×10
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN +
% read back the image/pixel masks and display the first roi
plane_segmentation = read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('ImageSegmentation'). ...
planesegmentation.get('PlaneSegmentation');
 
if ~isempty(plane_segmentation.image_mask)
roi_mask = plane_segmentation.image_mask.data(:,:,1);
elseif ~isempty(plane_segmentation.pixel_mask)
row = plane_segmentation.getRow(1, 'columns', {'pixel_mask'});
pixel_mask = row.pixel_mask{1};
roi_mask = zeros(imaging_shape);
ind = sub2ind(imaging_shape, pixel_mask.y, pixel_mask.x);
roi_mask(ind) = pixel_mask.weight;
end
imshow(roi_mask)

Learn more!

See the API documentation to learn what data types are available.

Other MatNWB tutorials

Python tutorials

See our tutorials for more details about your data type:
Check out other tutorials that teach advanced NWB topics:

+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ophys_tutorial_schematic.png b/docs/source/_static/html/tutorials/ophys_tutorial_schematic.png new file mode 100644 index 0000000000000000000000000000000000000000..7e8a94e4f2fac07ac11577c339e2b90afba900e0 GIT binary patch literal 77678 zcmdqJ1y>x;6R5qgz@m%0L$IL1-95pBy9EgD8r(f-0>M2H+}+*XS=}~9w)EtaW&Be^@P0Uqg#6R%y@d5z!-%X8-RArgy2aU0ej0VRT8IYaaRl~!h zRgL^cJBK?#!J()=5kr0Ou`n>gsG@*8m97SB)M$#wYL-E!PoAy*j5Kw;gtBiiTPHh# zxmXAY{iqbWO|AKW-ZPkLsf6mT6L<G8BMV#fOE8`udTOK6+evz&p3Y|kuF&s8KLP?U$N{t>^7`xqm7LIyLvoPSb_M`gsQ=kv zJPJhIpxvl#^;yeBOW_m0sl6?W@jpGt;$iCm%?$tudhkOJZOvVbDLia#?40>MgsA?N zgCBbQkC>H;;$K-@tc9qw6qG3>?48UhxL7z@*rSX2M zVr6ef@lUzNCibo_LR3`$RP?{?-|cj<`ttwPWas=JwV(>J{__j#2NpKg|CJ5BRPY}u zzmk)cIke_~$`}41_^+J*m+U|F2(tcD`Twsm|JL+hQmCrJ$bzi@>o#HJPu+Cd0Dve! zPEzc%2h52+$Vk0!I`djl77-}`fYf&Pn<^Zq@AYJNX}w0cr9-$yE2~&3tBvObnOl;H zo`M1o?yxNs3o(9u)aBhB>?|7k>rpd z3W^`w{@d3+?)m;4sMXVvkr)Wkme~(y0}nGVTn?U2D^;ZRU1{AGv#|CXkRN%gjv-wvk_j# zXQM(acN$U{ViZ#Or??oyILce88xJ^Y_tPWi03i<}O-l^o)d-JkSx``LJ*R0n<+`9R zW0B#C_w9DcqStwsw$x7HB|ASqxVol>IGOu;W)4*dB{;+19{?pS$z3ocyM91r6 zY4)6leUb`sY;`sJa%!?V@s9zQ0_TBPQkQKsp6OQ4D>%h%p)Z#ggB2aGX_>x{R>Kn$ zBE`+wXFmiVIna-mC8aUvlhwF7UtXX7US|!wO5huKP`+%FzhO|;e4YCZbP5kdJ!eiS zo$kMJI6XJ?xq#1bTekZA`LkqCC|cgb)xMgx)e_99b1~KLV-N;^oX{;nUtizU7MXVyT_vOyeER}HP>+_Woj5@lg z>%+-KBsCpfSf=+Y-*TmK&^zl^(s}YTF`mZ<=7!ZR)!>_g6&B*7^o0#b&`XSwVGcei z8tHCcoN(^dgh*Di`(GB#(F3TiUX<8DB@$uaTqD5KcASWOk>Fu|B0cX9ZS=g}%{qZ> zoya%;;Gk%SfuadrZu^{{BIEhkN`l{Y8thJUZz!VpDAH4$MPX$q&|q5m7L`eTW5O=I zUv3r-uV<8sEHXV)(zv2>E-#&sk&%C^eFsV8rJR-k5cdf=+IMf!a~-$)<<@Y?tT1{J zvoXbHcDf$68OAeXy*nuuJxlKzbN@()i~r957|Q=V`?>wvx*wB>=%{-q0#9>GY_&19 z;%0bc1lvaWdM@P1QiWuhwY$6fw*=+I8RsmV9{FP0`E1IbLBp4^G&ww_b%ws(2=17b z7I#*KbnX}gbTV-v@7wc5eCzH!t5=5`=Br+E-hR%nGTIGx8T1`~uUA=|?q_<GqMLp$^jLJDEVcK2}k*dlfy=93W}Aw)eA6Iq+bk(J?P6 znA0AvFK>6*@#qt&Rhj{LYE_2s82YnLVO!bis@@~CNXvuCoLo2GuzqwC;JF-3-0m%-tt+#q3YQH_X9nJqq&sL}WGO&vz;$LU$ zh)y6tvXz9e8U$9a_bC53`Rvw8RP=D8T@kLSgJBWpK9M&ur(YNMS@Shn(?D)zx?u`J z83Z442}aLco>7d8l`2rGaML+CpPR})&ots1ThU}5$(Weaei}xZ+$ zm+pAkb8v2%(I+Nz_b~M3#@iID1@j~GW6Vonj0@fAeVxh;Y5d8SXS3eQm*Ln?m=Frn zPbGJzTlZBePddiQRroYmR9F5rZH-lcE%@M{r!rEiG2Nb$o}0<85(7!rN$!BltlxySO41=cw26+A0_pukD?D2aH{`iOx_~9@;Wt7EOrQHbc76!#Wpl^0& zaTuL1eJiVBjMSq4{8VgwW+sc)q~j#!)K3>TFwOlK&&K4&>n+d4(%a2~q07}6&rCt8 zfpnbUWw;B4S^t;Vbo*xQ;C|?EW9@bry)d;LNns8oK4uvNQ3hvckdU532V>y?x!)?+ zF+3Up;?wPEo~Ge9_0`M|A3m7GbSFm%5)u*y6JGnhJsEa|)iE@>ur@+qr*9M*-3H>w zm7MoRF$o}j18|!g#gc20^=-ay0bS%-d27-QkskCgbv77REjsh# zgXzI;s$q%~uE_Af9%8MwjZK+Wviq7V{vW+2RR_x~%-*_Z%NNmonp8b;sSv5)W)n1$ z8-d*&wSyR*bq5AMcNb;Z7Pr$}UHdk5h!XE6oE1l-uNdniYV}#`m11`x`JEh$ zB@;7afp*Q8DX2<=XXBaOBQuIZ3d^dV0TCml?yHowxn&Cm-Z6HKn+Vz#Zx8!f1s>6+ zHsP=&JAlWP#VXVMi6DdBFu+q+oJufgO4iaWIF?jk7*`xC2qy@4SY_Ha@dF~bjMC&w z2z0P5tH~G$d{R_&>tx5%N@Z=m4@6`~l%X%QTWg7M5~F0)D2rF%rSBnQYY8ELYuNC0 zM*A`efN{K;(~L4nV>1r4i6#*8s%>-*Y%+eq(s&PRX|>TVWsd)gxDA4#MyZn{DcpSbfz(hYF@#i-YMoGL4SL?qPaSRfrs*@B`f50jUlGH2ACE@ zrwn_1J~n*ADt#JZ?Z{8JYmOlEfWREBweT=&Rf-P6_^7I4K@ArbF=Lp%fq|wY{G-~y zWV^Opn{(_)q;6!6dj_?_$)YUZ_ov8|&%M}6s-8c4U}c17$vH*kT#shuzVxXTDHc#l zgodB{2Wn{Y28UeY!#3%+xFyhi_%Jnx&UOK##5N)=s=GyY6SSYBlqEc!vVRr4n`T{} z^Y*+i0-O5tCqv^Tde@VE$BV{gHn|wYxVrE~gB>z3FQe~h$J2?jEBLTBr0TX(ZyQ2a zVpXC#uBIeJBRFI8TX_;R*)SQ_*ZJzxv;O&&i9yiDv8Kz2@qox*Lq z9R5j^(5>C~?$o_o;*;_RtL}H-AlU(dz?N`Kwj3onx$mzL7S|KEC8q`CC4C@IU>{0I z_y=LrepG&_5mYcCat{e&pQ@Tc5fmofznq7c^_qMPr4W zcc5*g+p1w+Wi(6w(O4^>@%-FIWm@}C&P&QXq>prMtIN6y_IDt0qsN}jMRpaSB7j+} z>+a=l!#YXNGhU0yNY7D_4L;Vry?N_86gMNks`EQN83gYY!ykAf?Tcp9r&T8UE;FRI zkK>b^2|R;lrkMOPbnYn7-f4E;SBwH>y7u8}YS}P+ma7rT_JvRm>o+>&2o349dDZpS z6qr@bZOWU9v0N|(>p5oH)fzDQ!)GQNc)FMKp{%X^xJr~ zNAWln8z5ms*Q~vzuE*1N92{tgh3EN;;?TE^ASm-~%H4If#wn3^y-x=|rHue<0{f$8 zYdwz=5sl Qg)@R;^HAeVE{R+-2k+rvTHwGP&tO9X#)kWf_D|PgnFr9dNzW*@9q0^^U1CQX|9X`&95-z=)6?kVufk2S3u9VzcO;1Zv0^mr#8T5znIq{v z9LyU4^u=6InNxmMXi+}aRen?$GakGMedwmbF{$;=2TKpVq_h#`vkYjaAMzZ%q&SZN zsq2+FJ-zBk$M}sdXmDrJaKCnIKw99tt3Av{BZ#V0<@TI+W44xMqyX}naxYNi)yT^D z3))Ax^dFpy#~_cF8JD*1OV~^+L#0IeNyOJ6`_6!$Y^w=~yru|RaJD>JbPk%ZB9JOy zI20_H`Cx>6hOy-@;)yl-j&a!wmyu3C>!`sR?*r>;n1+$)6ykY3h>b#ybmAm0(I2q_ zFVzo|2fd!pgXNh3ld=4rxpQv8yA@&=9oN$zx8x-RAyfE9xRk}E=<|;oZ?EnbSj7Tp zhYlCr-2mTf(pmhQAobE40Le1kands0Fh{FZ4ssRTCT%JQ6!xsK^rR$(8ZPhZEa$K)-tr8*4;K|27Mcgzhh7 zK=#W)5EG+WI);eG&MZHG`&y{+hBC|{32uY~F&p0?ibcP9u;$f2tTUif-mv^$ML56$ zQO5!jU;(J^B@=y8ks+l~i5yT%U%a{>Zc_(Fd|JP>nY&O>;=(I#EeWwRz~6(O z4J|{!Jw>% zJ8amaLUE}PH+z{oi=jag&nC}YoJ#JZ`_&b!N*z#Rk;*!`-!ml(Ut=YP%Owpu=pv8B z#cb};IEVliC+XJkPu3;yVp+QRP~5mGG^EbCzWaA0uiwj@*j2Vz5S&PofI(=d#vgjc zYy6gv2L3i8!zi_wx}{W=$-a~2*QTcgXEJ5DM^h+v?Lb&sBgEUP2!1O*f;Yrv_qxpH zpNeY{k)T9aG-Y|Znva5SB@h~PAW7|yk!GU!|ID^3XFYtZbQORSd7>bDUMWh|&FP%ge zIG-W(zXYP`ez_4KD$+Y?IlX5(J^W!Mao_VECazAflYZj#{A)&ul#$X*^UAj18jzJq zuM9nxd?{!iWEJCL_Xc?qi4EuymB`RC3A zkNu2>2z^%*S4#t}9+8QcpP3IE944McmqZ&J6M{X*s&q{_f4YxwIgOxhfxnfLY%y;7 zQz9Rl05A8>!9bZ{yS=H$W(;2Y+3g(OO4=w>P$ln&&7f-CDxZ)tLK9nJ>np@~;)9X> zO!xA^s5LY9!yL^0C6yu zlMRGRD^u$ofGCST^!09ejS7GnIZo^CP4lfirR9w6xGoSn2Srr4tZT{Q=`9I=O%IcLwntQ7!ecvwnH2;|qYzJ*Nyn zu&N5;>A{kR1KNqc!Cc?2*hi%g!;k!gPFaB~*yNw94n}p^7kynI-f4iv&fjfPON;LS zQOJRYBQvO$hFVk(NKHjKzGq?~fp%_{MJ)^Hff6BvkV!?vY=#tgQaJwMhO4#pqHLzk z!XSV_gD)u!4Q<2am zJjT)QZ*Z=pj-!Y~-C-?%+xP9fE<*78Zjhs7^~>E>8$k*L-VPjic(|%&pUrSFAi=ny z3ikZVE7HA}0Iv3!*aDpDwz_^&BXvirdXXvNrW$a#OWZJ1H)1;jgPDd@YkMaB{= zT!r5Ml3e!v3=_oC2hv*IE{-T%sSdx18sl309;slR4evS-N_A3P-#~r77$nHyi)!nt zZ+eBz4F>}{dc#(->_3mS5*jw0_&ah3lIJxnoCjIWDE=nJd`AWLW7ehBIE=>lS{c16 zpk+G;%QC8GtWx3-qAclu19X>^J@*wul>*?CA>bteFH3W~+Jr2_)YDFG5M9S}@6Ogb z?1~;fEFNRCDYFe$DHg-Rn;(v1wssjRh^^UTup{1jZ4$O<3ibDbB40 zN+7EE2d52diW&TFSY2>ZfzRuRI{1+3M=>kP{DOj}gXD1j*W!ekIwg|6t|}5ZeI+3I zd6*Y0na|&iijI@cULq0$?4tSKQK{p?U`q4`8n>;Z9VFf)-&#WZ)dW*1t_!4LBI6oo zG>lz9+$GtWCfrH6WG6q@Jnbf0w;7B;#-`iAxS`gej=k;n=geZGFdn@Q2NmJ#Nr~&( z8Kt4p+u98HsugrMO4A46u0%OSwjI<4Z~eC77NOQ|v?}JP?{!$<WgfkPC-V&F0E z9jc7|tt>*>JB>6cty!C!V}T#htuVL|n~TAeAVFS^;5tOeygwMc0ODu-`!(6}DZ9^q z=5n@s@&>h~({73?GsMG!FdKtNR-%2WwQlXE8_=L_ipq$c)VK9<7`_$kW5Xog>4@G7 zLyq&oh+U zu}%C(X>Mm4)a(G#!ts{pIyRcDw7q!xW&R!~hjbd+imd?2!0RQ9*Ll9mOvC7Fla!4l zV`rFu=7(O8HMcqgfMzqOu>_rMQ8&yhRA^;GjIuIaWD?6}Ekwh75BT5JxmJ+dz5hwgu+^T|1EQ|5E#0ZGXJ=R6k zmk%Qkei6Td#d`snOK2GB<)8+e%8)Q>?sf?B`iW9+ZAMCV1i2QbTZc6 z?_xkvg3e)OPHVNEiTOaDiWsfMv>0!fr`WJuLy_#{k}gm`L5<$ADtg~*-FrD2 zI2NKiGMUHx90-T9vT-WIb+4DbW13nB89>(!?Dm5Ud`N@3Yz9?d^IkArN(Gegn1typ za`Y!B9&Zn~b0q)|pl@IqJFUZ!3%HmD!(=}}tMAV`B6yF}srPGtuqK9;s~JSD8(|-b zVbAGV3|JEdd7W~thOy59H$CLBdldAFw;r;BkF<{{ulIfE>JBgl18}jd6aXHjO1%Ls z2fdwbCE@zQPZ=iqzDv6au1^7=ZXI=j)DxeT0dof-VY6p zec%3|L;i$2P*m$nuQt->NVp!o+a2bsr8enP_Cwz#hb8_4y=iv@Hj^+lgVk*wuMNyc zl8%GUEd}f822U!JDmuxnQyD5PtC2>y7aKX9I0diK>20lIk*x>Rx@#_yiA~ZxJf$D+ z*4O*F=6W=zvzru(=|HvEm#Bl{AR|V}@vaP2gkoL#jQ+K84D9UGAnG3Fi9}D8JC-=P%aNYZ9f&3aD zPk|yI<=)?!s5hX(JX^Z}=Myiuf@tl005wljW7AA>qeEF;IFRNZNn8wq}!6>Be zyK#=3NFdw6aclaFylvKdk4Xe&rUO$%{**c;QEvY?2vR@Q_>_rH0D>U(;!{ZG%S`+4 z8+=i8k+ktMFBI(0ZYnx9G{bNLtgM=k@5W1iGr=N9i~RcDD~ae*=^iuz`^IF7ekpS} z?PY4>kM!OX*K@ z2}`1K7E1rdA*-P ztCj|OQ!KY`GEd7-3Ei-0{n1Yz4Lz{Zo0#_Y&M~0qTX%ttkzsHWivP{7vg@XQ5hCqu z4DhIEzaN*pqx=H}&lZMDo7i72g9Qw@!3UsU>*LPpg;&LH7k&lGQ{;p}y?|im>2cU< zLm zb_2bM8JQeb;sPz(PFmrkyQ$Y>i7ya~TfRkG`XScau40Rd+<%Cygoz0T8~?=-HIH+a z`BNG0PTqS~nVHGeu{UR-R2g1VWKHHSW*4SL;4S>rR(Py9b2R*if$ScK?%e8uNK?%~ zp{D1(!Yyxzz+x$BYKF8@>o#E}TyNAbojkrV1PWB#EE)LfAA@*9>h?8%x&8_2UU8&1fQSlmSGK;) zuQ(}-B=QtI$Hb``cLU)g(6kmm1uF2Dl%3$9Umlek{)!5AQrQizT@IWE_}n&o4Hs3) zs(Jj;R5(Yro~XD>o5yH^l$=NqTHxfQHvY-TEk8Iyu^;ee^E^%{;{LHbeabn%yH0%H z8jwe;W8D)+jZez&=FoNn-}rSTZ+8w?aq~t;w|#jUS4qEoF(8^6k>xl!2F89<6DE=) z!2=WC4cN0h?V)tdReKV;2U3{Zob*^9l(1?YA#MrJxvorS4|w2>-T*l7>$|IN2*+yv zfx%D0IEq>r-lSQ(x1$Z*N(keBo#j#b2C)wO!|*3IpNCHiz!PYyv%Zs00E-HGjsx<7 zv^9?Ixz{!+FjjQ|TLBBku5S+)bxkuO%hw%7SMwQe<3dbG_5n*f>LWgUAmlY{REaCp=bj9M5OXA-^Yh zM=lS(WtPs;u*+X26V@G|+NT;iR5yaBw!H;Gowy?-PW?^m=H_i>tzPOy$EKcT;}a{c zHGxVjg@4g#MopW0l8`BvuJS^Xx#S&wQ$!@Xt+Ao0cOQkge?0#R>*V4n6WApH9X^lmF zZ%5JRtdTPcvg0I{H=XV5;_W&3H(WFAr`{(r+v{A|$R4u4V2goL7msFnVFg~v8)cmW zt&);TT=41IMU%p}rve1t*+IJmg#riJe#l@&(Y zv=?z|QXBeuE2UI)s9_TiK9bN|;B!C2g(M~R2>8%Imf(5j=^CfY?S?wPXj(cUZV6fUOP8+sj$gY1DUVy|aeS>er;MyL6 zYoXFTFuVQ|epLvj3%>eKl`>Mrh+{Ww5dWWpBnUAch^0o4Ba~+K48K3{W~VzG1+CC` zKRS%q8^v&5r59duiN}Dt7Ibh{f&uewLfjD;Q-SQ8sM?&K#c9OcYK} zt1hj3(Gv-&N4YnWw*Pth`t{?_@y;FWE>e zhAk$bKk_0;72eI$hZ&`|x0Wq8V7s6`=zC6-FB{qVXto3neHt3zrIlN(y6;9|Ro)-Z zI1KwKBD4l8`Y*FFpR!a zqRnUYZ$sVJzqe^dJu00?w}MF1s#FHeLOgUpfNOJoSE zY!ltQw?Jd>fl(q+hF4cP^B|sx6M3bVM|0YqRZ~o6dhbW(bs+W#1yv4?t!89#EgS6C z4ho6wa~syZqI}<8+ygeM%~ABBmk}t=KvY4Ax1e(@~Tt z59m@^{Q{$BAaWda*deE@@tJpdc5ZO5-=Px-^2nvFg5IziF8g)CrUhPf2<`9?O!Ci) z_Q&7R&h=+yP~L6un}b7qpPe#(#rn59{fK`k62@`g9%SX|il#lH2;aX#pl2vQ$udzW zbh7&Uh3wgDKf|4g`_otWey>zU%RKH-s$hQ$6nUb@L?z*>`a=6;@61E38jFegMGLzs zl<6()c-Il;23ev@J@3;xyr)5$m~Th(P&9(cO4;A<`68?@CBKj5W0ZyJd;-l!CWu6P z2nxQ=NwdqQn2Z$v^f>=9(~$F$dFs#=bXK>w-DuZ;U6il$x;taO@O!yQy@3V{7q+y2 zLPHQNQOx`(oY{JipWL=-ld;~Z_ZVZH#A}ZuQM92d2GkPUdb#$wLjFv0z z4tS*Rme?Z-X;IY&N`#{3^hM#-w9;MpQTsx{2~L|u2S--}s4@xjMM~sekN^)Y#H$04*4g-{YhUGICdRz`e0#K{?!j76!Q&5T z4_+jwn}@B)8_;+axq|D^*_A?4mHRHUFTxvF*gMmlLR+uip_FRXp$E-{!w)thnK|Rz z_|i3$V8zyryB;?KG3m8Ku^xOP`?ebmlLC6yAfcD*8S_m&{p!HcbP)m#C|x|83@q%J zO;%HfXdMh+LBmNBP`;*Run0<$>a3sVr~k#-ixoJF*FPq>;#qS^Y=sG-X&j^;{bhk=f9hGd9EChlz^KI=9cxjPq4ZReVJhwL02MZK<^i!if9TsIFLx}BL#qMYP zr8W~4`Q^y|>4OowX~y!#z(s!QOd2`C`MXNrf%#+`EwOjp#d;o_A-QGe(Og|lMIB@l z2f-)Hy?<7zNi)!oJsWS2=Au%d;fm`La_x0oCaM?88OoD8TYXblo&-`@Q(Vz&Ho9lK*JjKNg;$gQz(IZOe~F ziFPqEV{)Jw`Ykd&D(&Ep9n5CME&`r&l_(MHX7L=4Mzw7Cu>cIp(Q;Hj2+*VFP~njRWrulA5LaOs%aDpLYT^I^X_H;Y@YAX2^$5TScK$il;lMN z7=jl#&3n+vvWqd@PF6RU8IZMXQX(Q_bJ<=cB|CgyBo7Z+w7sLt-0z7AIuWH<$h1s?pUT9_ zrYJkP0Ol9ZZ<(nUHrVrE{~BLqH`cc6gnZ*dF7siWt+Dq>KMx%ws5YS!N-9UdfoK~3 zFqW_vT-ln!54NtkB~fgnDXYyyzw0R(_%r=iH(bjA*DwIVX=|&tv}Mdzdqa;bYyZjN96s~s=&)hVu{M27vePPLPbRaS~I@1 zUv7+mLeINS$(MGDj*jJPl^S|^{sTIM<9NPMX8sz=FHaY%ZQuyb)9;20s$F>d%rwTJCF}se_!2vQQTE-FF3mGGVsnTWK4) z+UO^D_{@O8wuV@x%IWI+?aWHKGUx4TuX#~RSc z(Q^c2?!(-yQj#a&Kc^aal`yhiZtGE2kZMGj7JR+nFYJvW;TtA`vV^P0o?=NbY5wHA zjaMVM=9UkFT04TypSv2Ts)G>3B% z(X87D=V0aFXIcWxvsOweqKq{Jr!TS0A0p!7;u=0jh$0mysD)%d+z=wqVv}@?%kop& zi+^{y^WjyF^Px9%-28n(1fnS!{%8E3~sP0zJZs6NL9~7Vq z--~ibbefm2$TYHCM6YF^=a|Y^q5j2~L;IlHP*_9ErLmgr!KDq2ZJh{ix?kBA4HnbO zZ}~V#ZNV>jSQskC_PjKiS|HdPM9{X1a6&(7(9hQfw>EjUvf#ffUs`R|Tk^psB$S%^ zFjf7XhT1gMnfjxDCr(XET`ip5ES6&)EiEmd;Xo+9n~Vy8)d&9^6Lk_!$J8`^#9MNc zeQmTtw)#D3b0V{zwy8^_g(qd`+kjJ>a#1zAfdJ&$1f9E>tFkjl1TM)sa8p!>&)+Uj zzc^Dn&dgzuA5Onn#b9y!#$e_|(_2{Jm(V)c{;X{zKw3SR0{7TaD{LmUf6IKPxx7$4 zKy|f-Ck{5STiZuQSh~Rl8ajd*@syn9Fg=~@m8+o-BhgS@cbhna3@xX!>{c@&=wI^ zuthfs&e2W+dnsi&@rMeq7YBs+`w)U_o^y8dt94w%#)71>{^r+i_i11opn*w{Tgm$SuGoG;_F0e23qnn$@1c& zW${enO~<_8rc*a6vdv5Duc~lc(}uLa*{XiiD7S1CUT~}1cVDm>v>qs`Jac#^At8}H zUGANZ56CF_SX8h29w+zx!15s5VN55?o!i%7DpdI%7{hIUnHcPM7AxIanHL&;> zhsuFDHdRT=cRBbf>5A0!&7R08GQwY{EX}=I!0i#}&_ID4pRLw-xmFQXptqJQR`G3q z>B$9hsl%g97z|$TieKrnAJ{U?#?{DSRiM&D+x)I#Dd}~Y)9b8y+-1{qK+Qp!!k(H( zAMndM!N~rAAIpCTWqA~CV+YV%@>6eqm0KMr2RS>xckdVD_mBhrFGkLIE};GDvXX;N zh-E$8Rq=856@A@61P=DXJmoKD=kQwE_Bk$Qe)edSJkPPY0r=1IBVRa+)!6kPFXhSp zh)mJoM{m9lJP3*(bTy9+xIzZadWFSvIcru_)~bBB^yd0X=^NP^Ni{pZ+GJ||Rm2i8 z`Q*n0v6#ojANC&`PL*#l^k{!ddq~q{RFL%Pn|d6o;F#$}=X%kYJ9qYPt9K?Av;!0r zdXLla;||2!Tbq8w5=|%b*QC#)>TKBY*=CvC3YM+4M!B61Q9jT!@3FE!1QME50A+@s z3+3}kJAW#%JGXLl>^N@KS|k%@Vk2nTES(>CxJmyuQhUhNgz^O>{u7~-1}qrVa*oT6 z2W@7qBdneTq7^J!7QrdTBeWEkC(*O)$-Zr{ldgYz_oI72J=nUvstdL6kkL`Nx8;9$ zLE_S>Tw1Tn6`TZxJVqD<1+rvbp31TnR&`K@!L~(B7kWeFXFSkx-oAt@7;6CK- z#5v8aHXRv84{hR;_`?|$=&Q&vZzUewi7Z4=U878+%TU=m)ixjTnb`rD^_nAuJ^zB_ z7hB`8VBIfYzy4K7DP>&gU5EqX;(Ur?OgMb04RE;)*|v1p;LJl6s;rEpa>*Acq}_i$ zC0M?`Kjpl*wczt&soR`5yXaS{2=?!^F{z->y0Vt;qQs0&l*t+oZ&eKx+cG49q!wIB z7E67GJ00!hQ?4;C99phOaV$=O#5rqD;@=6%_>s59>s;^I4#9MNLFCcS^JH1HlDTwP zmU?o+n`+@N8EF zu{G^C{zKB{!^S>AU+Ut@9V0XUtn=+k3XUIX7b{E^q;)?sJx@sGZQTW2Z8oOqC3IKF zvJt=2mdmF|Q&Nh#3?l^{G-9?LRz-jNtQFi))WX>Ihz^U`tSqc{f2!m+odhqHltQ9V zAIH8p$k8A}1X}f8_^#pSSNyj@ zJ*SjzGw`!Zj;4K0dyb#nWeoXTiYP|0nxBht(Mb&x)I|)#l0qTOs-dAFmQx1T7P3HG zJBJI>!o=D+5I4t-O>}V`J7#qN^E7oRTOZiyfwKod%9tH-Z1f`Ofx2EwWSlH(2rW^- z)nH1d3yEr{r36-7Df(`F;0q!;*L(5Zn97KsyUe~NTi9ZyA+uLRWpsVt(czYpT=*(D zJgNPKlk%>&gn**@8g^B>;pl(%ld%0-Lv{KPu9j1Rq92n=pFcm}3>_ok-Dm&$_!Am- zqg!n5z0mx055GJ8=nBKb76t^dxqP*9Dm%~f^R!4J1%JAV+9?JmBBe+imiN>JNN8$C~N-U(S$rjK748uI|@0$1=Kj1NzZ%)|WI z?{G9#zj6qt_S|#uzK)t=(KB&|3um@P=}3Z*q5%t=pdB&e00WhJ*FiOG!~tkFqKeAexhsq@6^9FOF_7qXy2U`jcN9SU9mPFTy&KgVwa`auRPQ42vZn=T{e` zx+PKk$57Q`B$t1DdEYSYYOx&$Zu(VqX#+u zqt1&;)x2)tVIQ%#a@g%y2rF^J-sHzD8wUoxS>0r+14&;2gT^vyaiA3`;|!zXr$P=o zmqI_03y|%Kt~e%T0DIGU5;x)g%NI4oM&uBF?{ONJK z0(H4wD6ZKhyyzyq+G;ZaRj2wZy6q^FW1BksuqhAYAK3c6Y}t!n6L*9*DqDoGfj z8vVMc@rn2rLWA%dKOWhh3ad_Ie(bK1#=vV%`?F8c(np zHWBl4X&`7WRDwmhlTT~0L}y;plij3rK;bXz%<|okO2jk6{#F~~*6xCu!Iq1*c5Bn@ zBLt(dwRGBZSe>{sl|l;7X)8u6RdoZ`2ZUe!z!1$svXMsB9b`3>X7jjN!6a|8aD+`q zqcU=j=$jWQ#cI&!>5eze&XP)g_^c9>ohpr9YPHbvyR8TA@ z_&$S564>?iD2YJc5izlXejH~gK3O;Ih1X@eD_zK67CWYn5uoKJ0tR~@+MnB`1@ zap5$lG*gdu*mk;vv@gTs3M6n)^L8vwN0us0TyHkyMSe!7q%Sq0xFPqlI338y&d>gX zwida--YvDtgdQKCDdeIMM3_rBV&Ih%&`OJbbuXyWrr==-$=bk!N{Y52fo%E~F0&nw{*M*Vvx(EmNN zTrAj~yYuOkAX>k>g#LAE3Y|W_Bdy(-MJ<|#({V?o(8DkN zMV~Fnql+h`(5PW;_=Cb_6dfH(nR|=b8V;rFF6l!zKKv{FxuuZqz95ZG7}=gyuir;~ zx<}G+!&B(K=N8kF^dfrl+V0djHIBA!&!yAG#Z$(<5?ZpcfMU3P)E#}K4J=b%!vVtq z|KWhsx#d3uE2ZV^#)B&lRvSV&-@t`8e{&ISWF^wCM@w3=ubc*TZN{&L3!>JoVq_CZ zN8UV=oD@xSf8R;x_m8Gi$91Hye%L_0{%;|5zWz6Mx$dTOPVP)`tjOSk!~{8tt+}w! z0GjfTG)hg0rFjc?5+z`B3|=}ANN-MAOWiJBK%alNo`&{sMeW%gna5VzWoLDxb(^xN z!;QaEpa1)lKK^QrD8n(mn$c0W|3w+BxR$QUq)t~Ypx0;aqPs3grM9h`(XEgDK?Cpm zi!Q(aH+FA^(ce#QPn&m?i7OV*GiGn4u9q*Mm)~DbJ-Q_D*BhdQ2hV@7hONXBdhE8L z^ya_&Q0w?mcGs3t_vCOoo$EPy%4!lb6xJKEF`P*x91^&p)9Z@mzog^=U=#e!i9l z_xpuzeR45vXV-KDThvAgO0)Ra&tYZ8*JR#$O7j#~pXO_5{`!<GJ##0 zJctt`0_e}>nWF3lwG5#FJ>uCK+{;R>lKOUxroJ7b*}WJ@U;UCvkka0(MPqVso$)lFW6n#H9nZ%juXgh zlUeleynS@$V0H^`%n|DpmQcU;krc<4*NmUH(YT}#x~NAaee-K36%>|H-!8F4OlKf3 z3WAnltwY$I8bCeSx?H<)FTJs*fL*oGv@nC+xoh`QYFmb9Izo9POZv_{dhWYjbl#w7 zTFvWtCH(e(tYyM|3D@SO6Mv_7rmPY->7?6+P`B0*)U{odZ20NjEuQB6K7_uX+D|OM z1a{35mkYcZnFaLRm+3UDZI~Pp0e@i)!|mCx3%?VVt>a<+5_zq3D1G-rALbEHC|X6+ zqCMrp->WX{$-Lx-G}5{7Ho1jB(t&jFsi2J#nk0{DCrzYY>#x*_}pK-6)cL_B{7 zRh}h|g$6$d;43@${wjYIJRpd_iK+qcg3A>lFoFwFlCF6ixTHL@FrW#(1TLe1;jobfub~^nM=FYV-4aR-xL^sv zB?(mk1sul81`mM8Gj@>@!%GCia25(@#erR~rM$7FH(R6FK^o6iXqkRt9^;zh@Zs7t zUJt}NDK^PyK>1;;m|OEs)K_P0r@!C*i&(T5u{BwSvKSPfX2O~#z6`&NHx+&T;}(i< z5u{{LLX_h?q~gRCJ5)otj`~@r61EaC0a(0zH%*(lg-&Q6N|WANMKkAap;7EEefG}5 z)QK(7LS9=0PfhSM4)}GOaX)9JR-QMDDhoD|#bdNQcRp3-tstv3i^oqnRTk}~%G}vh zS;*-nyBVG*euYb@Z0{^i580y0Hx_OwqU~%sO&Hxy zth@Qkve`n6WH(|9QBF%)2~ad!P`eA+^%zd=T87b^e`NE9nhLs<*UO+RR&Fh%o@~_t zclxeETCl!=IpuREMT2&+Epw{q;vZix_4S81;+%^UB|>z z48N{!JzH`Vg_$N?x)v>1TS8IE!IYg_Oew8nXjxtb#qfrhWjPgW4K`yJY5{NUtdu&V z3`cp6;}fVe;^HG68+3lyP(&SKg6PY&MSPT`L|nI9G73dOPyHdCp1pS$(P*9#XI0WM z{UfLg(}DaJ|GAEKa9zM(#D}xe%h*#aO8Sj2Hq(1=@{8Ad^B~Wuq+xx+DYZo?t;;N> zGag^VGl_My=Yvsn+UVAF)$?m;c-wGsU%ML0+!zitCI_I@@@7->qo1XO;U_y3SsC7s z96*_Kr%-9eYDye`E#+_ci;_m3NjYm4(e^jTQ?p_BP~zx|C@4Ha#&p)AS+sBVqpUpF zQPP=T@`p7-DC6S^6d2Ku!rG3cr1Afx9aA5n(rq76@&z-fWbaN2jc-NGI`^ZBq5|4E z{Y5I;FqHx$(|8b>UDS=)JH)+ga?ePP246cgNeFq9Ur*hA-@(uFo{ z&E}&9#ng7`UOIJbN3pP;nY@Zl?+`)XFWg6`kIkXGuI)!XzTL>{WMOo|s8srH&Ssj& zXXyrZj*$&CSTAeN?oQywA$wSOyV>f)IYu0)XCqD&2Q0mCUQdkR`1$Gi^xNW{bk>9} z^f|-i^X}Bs#&w|OYcgpzE9U-u0Dsq>B3^S#qN#Uxp{46`=%a5o)5Et6rlpex@;YP& zy)rmi6!o){`O6>dK82MkXD7MdP*ga!fZeE2hHVqV=)oKN(>mS^vvo&4z0x(FzwZ}G zZ%$o9Gg&#V;>|*j-ZF@W_1?%Eek!PMw-&T~-9EbH)y-0eFnqKVy^49q`cEuR#Kkk6 z-!96cbJDZvg?oq6_+cBx((J?e=Q52rW^vDzJ=j9bl(ksYf5VnMYQqYaS!II1aR6^%_bM$?a*+A2X?B z_hyRdd@-dmOwQURtiWETnC>H}&4f#-vb>Bk-@B3`+jI~`XhENbl~7Pr0>z|uWy3Ru z6(!4_#J1FO*a@`v&)Ky7ix(()!au3q`FGR0Cw}39giLU{nStQz-N|niAzXHm>^j`d zn?Z1XFo)0Rg|f>J2g`AkU=AMv=-DNX{#=$xEm(=IPA_B^={B+U*6u8#cyd#i!s&!eSgcI2s z!`VZ?{r=ZZ%3@juCGkP~$?NE!*(H1yk++8OVEBd4BEIz5W{Tyb3XyDSz5V$Hy8Le) zsV{HrShD<;|Jq-4(s6BREbk=6PSb~9TqHX%$0UZ(hOK#`@IedC92W5T$3K>E zd3j~DdV3LlJ#!NcfOP zOLGVFe^+LqY!X7=&t|NrkLT;I0Uz!*LQoWGko z-|!+U*b^y#=|l>O9?5x(RoDnk?p2h2tO(h=$O3!)&SJXe>E*m5G+5Tz;NFA9wkx}g zE_moKYQ+{!BCqK|hu!w_YI$8)3ZJcmWrv-Od3+Y`ilAnYK;bl&$z&AgnUba^3${NLnc4;U#!_@cm7kW0P2^ z#hkBOOT^+cQ~|V=56oXSX{jtffX+Ux!{~dqzTWtG4<+zA;ch+$*@@TuVp-9m?5}^_ zL;0*^l3CHCJRGjS;hB~4O1YL?$Csz?poc!&Mv0QQk~%Vt3DH>hV_^P&~2=@%ml-M#^iv-$|f+nE9I@4O^G8;qU3*k zO2z5xD6a3(w0F^LPID-&%F<$Xk*=VSlu=ZXHJifX6RCLLZVKT2F+s82D5&LXD&Cz= zvE7p;j~H{Nhf!D9-8w=6GR;kj`o%8EG#*d@{3Fn6*?!}fT((&@{ zQdY!JXi2Q-KwB!$azVc;6J34~1(^qhGP+TDeghmU#%928- zSJVmTfHuEA%7G?PgyPdhak{@#(-VAkxPb!- z#-5HbB{*66c?wX7JRYkA&5t_Pq<{{f&4~zQ!uoY=#o3SAowft=?+2|?2V1nc^+yiq zVDU5B;i8l^yQDN8@IjQjauHv`s4R088*;I%z<%X;tk-dXPm^b&*ch6(b`iBX;UbD2 zavK#adV(T){DYeJ7(#m&{^%J0S-W{%aX7n8M{$;1N*sGJL38F6x=k~ibT_83et z!*8Yh-~UUk|2ad-DO^8?H`nxM9PT3)1eoA!9B2|nxSt7rI_maUz}y)Q99|s2+|dt* zrMN~TTT|}RAE@P!G1T&yak7>d7JnL*7vS zF20`%$6i7ai7Ax7c@^!RelLY}8Ay9)zfG~-22tnRrcgym5tU}`;+>)StXv~pxGD;B zsr709qJYz{XG=7RKQ{gmmF@YGf@6{QG9d3Lz^*LA$ zIE?s%&292Elmj|A8;a);=MP}>tg>(iC7p03?OXmEZM@}J3hBV!Nq%Ku&q-MnJMery z5^)7>ewVGp>`h`N!Z#Wa97pM|52lE2|E92(X|y|Y6BVwSL;(>)MA?;REvEI)T|&{_ z##8CO?NqdSlI%<^&)FgqhCn`Ak-L5wWqf`IH6MB<6>j{4inqQ-LCr?85`@3iPZ@fM z+pnpMYZ65WHyd1X;k-Q`8%Rtr8~_Kf>lBCeaZJU8CgOmfT7=*hJf9Y?`9-(yN-eMa zFS|+$s9En(;=U~0@H;EEu2i`0Q$Fj?3)}+CZcE+(6Bs>;iq^hOh0AvF8ebys{u~8F zyvSb4&q?f7-9_1RZ{YhjoHmlr2u|T+6dgG)D_VAw21hrivKgAc0MCQ*b+X`9yZ{7Iy>vgt#Wqo$ebk;4Ia27>uOXhv>6wQHL!702-yyrstk z!-2YRKwP6S{b>6q_fq1CH&ena{1fw47Lu!^m? z6jp-6YF1FJ;J7P#4%|1Zto4Z^<*(2*JBl)<+{ijFi0_Z#x31}qQqO2itnYH5i4-AE zv{(X0MTXNYS06|HuKk)$?c0l*$KaJ_jvZZQM&-Mm#ygZb#7-ce>nJWOr}t+rq3Mqf zqP8iC(l>On`WZPeTWaS^K(%2@qEt~puFY9%N1i@ z1x6-uIm|PZVS?Xsph*-VxCKE6W!SfOM_Tm41e)~xEc$dw7`2Ov<~N*}L#Te+qv}C8 z+@E_2i>Oy*E`9g-5E^&Gh}D(30gFD1G}rDrXBpe57JD$Pb{M9L6*5?eX=wccMAq zqWF=Z_;7EJ$1{S`;CQMT-i{0K?)Wraw+Ezdp9{;Y7$IPrh;8X!-2S|z4Z@1AM=f#Nui6lsa(dWblHCekLYrbxECcoJJX zTs7HEbQ5tvnw8rP?YBReh6FE~R1tyzRff^gk#zL1-iR{c2;=~~1I7~c-GQB*{T$Xhe+94+S)iV&n2Vd(2a$@}z)gU0Tq@qQ5oV|_VLhxV8- zZU8;><*}4j@;!gTD~xx0^Sj23X;@!QA42#5emTBHMWv-QEo%(jGj`x1EZaYd@)r`Eda=#*v zdmNq-kYCgA>>iIeg|Xk;K=A14#vp*O`xStp1&TznRz%EHW za04$nfQ$5)L7gf7#s8y^KN>*6D;HAB$_xs^kp&ZchywxqlKx=I3~oi`{Jzv%UmQ(6 zI=7bgGcT*C+Dt|fR^^9zG8`}*sD%Tt4517mRPVd{LfV)^0DV>nQ}+Os#0KL6@hTCi#f6$k8Q%MdT$hKx{cs#nG6upq0Noz=l0 zkK)pMT@vK*^#C3XipS0y z#b<}Hrz74SMqzPPoVd5aS%DN-8ApBF_M|gU9ZqSf-v4-ajf&8&d5en5sHmj03Vk5g zUxRz|_-j4TcOv+cDiPr!QU!aP`E8vH;Z4T@yDaiRg;;{D1UGKlMRzYory9Wr~?Q1(^2J>v~|}4y6D~o^u)g|qoam(mA4yt>ujV)MW{*wg{)q; zoo0OhC;hf~4eiM)kitQ^O<&icA_m0U98j4Pz(Of8E}Di9>_#V!8$_KtwDNXQ2H%my z0Ui7v>WoPi+*H}wg>>H&A5h1mex`(kP%7q6F_oFujC$ahiSiZ)xZ6?N6rxsVtfE_9 zc$405c{6qF*vf0l(4!)h*F+%8r6?v;p22Ue!;KSGB80$GS((>9*6$8bgt`bmYwj{ug8!i|9lqc@FdpA=Dz4 zUwB;c3Y|DMi{}0M03AKFn^A_o*jT;8RxMeUKiX(-%t6Ox^yJ|yhRg}!G#oe>4zOPGqHL?82$h`eJNEKzI)B+Xi~^}xqs`UFIS?3F=9sV04%(HO<>S;E z^8A|vW`gD4?PsXBbD)YM)CKS2Qmh3Q^2`seHS@7eJHveYo@YriRj;Jdl5z*BX6EkO zP3CLV8U5yKEn?_71RSWM2(>(wo$QI#D0B4_4mjG5+f8RRenPh)(?>XP1WdFTB*0?oM_< zKoG+RxktO6fh#be%Kkq|)A*Y5x{x%|4d|**UF%CmW8$ukmY{%8X^*Dis~0;eSFrkC zT3zkE7#|rKNpW#F|7L=BIe-aF<42}Va#flQj6k@Mof)qFc8FyVn zen~phmnU@AHBa5)EUA0lma&*u%mc@Sp2qv$(o)_t)FV&W45)Km%c%{GaqKZ{cagk}% zJhB}tqWmff07|R8qKKkG6DTnT=Ur>whX93>SGtK#8*nd;?(r|mD_JYT9D=ygDwF$C zE~8EJfz+qXIUHB&EYG6sqUF>)qCNFWK7&%459Yj^9SW%i%d)nXbST|?#n)RmOpom&|2_;xuwwDTcV!+b`Cc`Ex>PQrDlvd=@ z$$cN9n@;?c&Kv$DW2w0f>xjC^z`WTab*=;y2$bOH(WB{#E3V-8$dyw_NC83glht;@Wf&0bCCETOcdbP1Gu` z9VN!5P)WrKiVTUT5uId&ziHSm_Q+yOJq%&JlW135w&CAy-iU)N!mlm$AYM7hHf31a2-HF!rEn4xF%j5uo%O?qARn92m}Z zET!1+HgxIehnS{5Z1wJQ(V#dKKWK%O-X&=q^>2GVRq%uX^a@^9&iiSvN{<5%co8aP z!m{qlIEME)k^b5^hgPO9ppc*_Cy!7k@Ih%7!Z|KT;`XPhtY`j7yF2nYc3v*9UV=tz4jWlYuAo8ZrtcbUd@ldWj{+&)3ZIzevcCD z&-VN3N{7-eEiI*^k3L%J^6|$X^C8b5{tOM?zV8bGoy^(Mw7>ZRS%wDR=Rkw|i^CcW z3yG0&RalZoLpq&Ei_>4F=ztUv__;;%XngN?CB3w)gd#&@WgHilW{5Qx8IsH*-(rPR z!U}XP3;OOXNMqTGEaNd&PQ|<-x||ij_<OHSgO0(1HyQMGC%;jMe91umh zC9CP8qh6bHh}BqLS;*~Wv2xkMv<7o}dx{BbN1Jj#r=cA#q~YC9r0-T=NwHxAC^{^Sa@j&H zt6WE+K~U^F7`C4%h|0<`dVa>gLkvnYf!$gDKv7}M)nV~fu-mzU=`Uma2vH$j zxNfazUDoGx`ha^Vv3Ux8zWfI2(0n9MS`rzqka7#=GQBBG=VpfO&-INHdf;vjWTjhN zmd@$%lvVUQ4UV{gU$(NFCjWje#fKBcN1npg_e@susazVY!(gVli`+x`6_@R!g0e-d z^ja}p+1#cBMFCb;R?yypd7Q5Wx9=V=1kr5fRj;O&f8^;%&TK|rtxO)b&>vI;J+GmQ4l^<3-PGP|<*lpUKj_P_h{kZ&1swmjXX@RUf zDyS_hy|aei0Ubpr4Y-m<7N0|3EPRyG5{6Q{gl;tLuX`vpemwO`9ZJ)FdxI8i{x>DZ zoK5=*H`4nH{>}=jCo7OJY8Kv_&KUF)#Y8seaa}Ce&FZZB}1Mzc}~rCZ0ZpdI@*(u`#f(=lwJc55@3>ljLfCHeHz z^2wB$zea}N(Ou4?^gXL-!KRyOe2>YjSb}+NEsq9u8pGEDnz{5H%Exx*gfxs#vBGP;B5YkBRy(g3Mf9Jk= zFvI`>LUk@oX5M|boO|AT=ewue``GqAEIaTI1iBBR>3JbMU3>|IwODgt9#RU|Qv-Cw z`0h_5qc9HL!-w%LBThJ&u?d4ZT>%%P2VT7TAP%H$!tV5+aMPg0@OBF%sM+Ii&VH;o z_yMn@CnqzxXy-E=!@9oB@i`;EBoiwS%%*uZPie=~27CcO&k%JDHpl;reOU_$#K8nZ z^AfOg@Fbm6AQa&r92^W0{(t$)Ulgdk`|i7Hn7eoHR^I}WdXloaxw$AWFIOMQFDNKb zr`xx0uQbTw;$s(%OYn{4iH4b$mUaZlq>_NGm@x$u6A}_sJMu?>Px1l-166qeH9ZM9 z3wWiZr07{kzQ6e53k8CCd3h=?E-p@e3%z<0z!iYj>Cy8ozh!bD8fM$2wly2H9bMLz z{wN#})1-sFBU17YqD$yN^pBc^d0Usj!1u}xiMo@|p&j<6ufm|tZs@vO|zIHBET~oy`x7UJwFlq2_iC4xMt8p_-NT3 zD6L7rWu2eE@R+NxYVSgrIEKr5O;Z5!>4wWO%=J!;>Nyp;#Tm#bNTL=+e<-y;HQyx= z*3u~D?;eVN9WO(MYJW5OMvrFu_v7uKSCDU61jZ@_)_EoADCKr}20J(0Jp3h8IJB=l{9prd32=ZkpuP%X;qc;BBvJ~_Pn*=|P5bR6c z09T_IhLQfNx(b5q-RR(VH*OmKBvNzZu{&c8dbc0R^5d~+&y9FwLL33r4J-D}yxiq=z8^;R0`xp-#IDG-X5rTf!{4I2gl4l#$08Z`=Jh)S7B2JUQug^5pMono)}go%ZH7c*3TMCd};^)9q6Q#_#_+ zkxd)0_`qH~<8n}GI!h07|N6#y)Up1&4gbXc+ z9KPH%4sJ9fdOCH&drMkm{$<(HvvB$cnlqb2N1M^@$rT$ z;OPL2?eZC%9o_KO&r`^+tyo77)yvoBqF?(-_;T}yiceXEDR}R<-ZXdiBUo>vkJkt8 z&J*zY#xd}69EzOMKrGyI8_m*GR=62inBM_TF1jOoTP1Gme4Wye|8IF8YKURjd^nsF z9ajvDd=0_A?eO-ZTaa1y9hQ*qFHcFvz>Zg8-RY&)1Ur?2@@tL zun-MSKycP4vyhXUqX2dA;KBIryScdGh8wVM-8#Jb>Z`c_{`;|qvr7T0d+)thX@LTw zbLY-g8s^@;dsV=W6x&poHwtF5htv?*!UtXa6~s;jVe?OFxW!-fr2KsaK= z2vuLe)7RHm0jP{2BO^nB+%wNSqv-wTKmUoDug}EYcil~(Ua!iI88Zf3wro)*UFk!g zAjt1FO=_yxrY%|}k5Rw{<2k9JRKH zzlvTz6?junn$7;}5a>39H5!pwn1q;!!K91NBekrGnlhSG;}Pk3J?n6Z0{xi=2D-TN zypsd1OQbE(^-AWsvi}V940(`3d?LZljzCPmC;!>mN`DgMBGlR{kQTbisL9sUXCu<% z8q#M{d=T1voMYff^J9<5!T3i=B5O)tF3LaZXdlDz7^npoAia1KBE6<7-}rqDec!Qv!evCc+sEdXm2Eq_6TzuuKbN+&Xhjy9rG32w`X3x0!B8< zF={QR=EadO?cwXvoqaGEiq}=fA4F|i=&o%j#VrFC5&%1?{z3?FGTCwDweoWHLqTZ{ zwb}^yy9`tOD5}~GU-vN8#!a!WQ2xEfeogT82%v_YLpr$8^*K!E3<%|+@aOw8!~`i| zCH^yALla+|Bmtx=ue?(I7Lb@UX_5j$DJ$C3h7B7OVCrRyii?zAPLA8PYlnUN_9?AQ zfbf=EZo#fyyD)UZLTpkt0W{b7Iz% z)W3g!1-PP_N)lj}R9#iAv_<(X=1>7#8NqwwkrZK{48Y7TN}{l)Kh3=h z;&Q&{QjQ)Jv^CTQ4=XKEl>2^QllB%vS0c91XTDkbd}`1y{!v1xhmz$5-^iK zHQh`4)Q)OsstxlTjB8`>M|{R^>`qyUK+h<|cD+Jth;=NJ)UqYu*wRm_EvFEWl+nw0 zWI2cp_8i`TgxoDO;d&DUYpG%8kPeZPgi`{o1HreRbP2uM9043j5fP01 zqGl?lS2-`iI&xzOxQPZS%OS+)_vM#gDs!dO5zRF`EF1}m2@1Rq9Xcf0khjC@nw*$6 zC1B^*Uw>8Sp>x%ZN-ONX-T7OtMaPf1%EOd925wGT%s!nmTk6u#6>L_3pc(SgxdQh?f1=+^W3Fc+s#m9heA zqOFLQQ(V1R2~n0%lTjCkp*WDUL`5}THQ*j!BxLPEh+jJf z@!X5#qE8q=Gg1liO#0o#Un!cX3iuJ&lg`<;$O%buBmpH321f#D2@0xyK|B9ms?MkD zhT^9~&naRBNX0KGHMF`K?L3gyn9mm)%Q(h3eK4OW_4mGlg@ zq&~yIojr=D8V_VR0{=p9GnMcpyzaues8?{6ynm|3VGU@Nw%zj1FZEgk7dpw)p=R_kEAupyw0Ru53icfF; z{P}8JvKCKIIxxtw)X|T%n+U260Yn`%^nAU(d|NJI(9cWx+?-rhOd?Dr`cyzrOtMl& zOrD}CN)n%<_zy)J?a_l5tY=TmoHKAyZHbmFzDb={y{`4!%7KfQ1DcSO=s^*}E)KDb1pE zzB4}CdAB2gYZR*)>A7^zNAPrvkgio*La!wGtF>*TOsC0t+cC7;m3V*)$`iA8V?+F0 z29%7)lamfBAQSU1iBLc-pe1z$7$xXOz};Sfq$V2#zC@PDi;wMW!mw=D99%PG241-) zS(!Mct(@Xie7Nc})-$jz3$e}iY?@>ZptgmZXnbOtReUuAm+(dSCXnmQWi1KW+c@?C zc>1bTYOp)-zg<(%JtCHC5nsfpTnX;6Q|8RY+de@~1^y5mTY7G81eirgdUP7RE7}oAzgvERRgzX7e z;+nztqHoj~rRB?5SMB>AJ5uLR8xH3^Vp4sa?LQVAJ$qfrO(_5&U?9FWi3FWJd-f5N zplC!AIVnE3|NFoHQ-tV&DRLl75d=^~S5u5^Uk3RaSI%oMu@ z1q&8n`0(MXn$V;Jon%WZ;w%PeG-9AqF1&3n{n7H)c9J-fW+32x3b_hsRSP5-6eFo7Cb6f#S<;nU^MqI=jV z6qaW(B!2WK5tl3f z}bIPF{wn>*(STjkxUfC}aexBsa%MEI;rzvdQ~){@sz7vtVbc6}36|c-ejE6LB>oL_M+Q;5-~ISdC!!Yw^Dw&*EU_L&YO%HiQ#jar zASr((DX<{`h$W_h09MH|j94@TDqwTu0`A0iUiK7c~qgC@71 zSk3^WowysCXg)2((*4u~2__znoe566D7@FH_N0I5M|^IZQ$D5u=Z-5}l<(pY!L~fr zb7@aaX7l0yQ1I^|aCW6Yk7iq%8B;XQtgT6$IiHgNUyj9zTJU!SXm_EcEL_Wm4&>=Rmn ztrmQc1@0x)Y4~~HqXdTxMVpI5IC@^A#3zWcjW<=dXBvF8`bpAQXR|0HH)=3Rryg)z@l_5;!AC z08YT_?YG}nKqQOirL34FB?&NzDOJErQe#esG@W^>tsv%LNVwS9~sXpSH6m3%chXPCo4<5uj@4O?$m6j=yu2Nn! z!aMK0Q{{_!Q(y8R(XR6s%vZFkll&t-PhE?RiHX5nMzy~F&)3za0k_?Dn_5fx%{Sj* z+qP|LjiUqwS(B9m=amC(G-(px5KTc_6fJ(F1`0S|4vk_{`OOH46ynXxh>-R*jA3~R z=4hZ6qM65}z5u2Ghvs(^6QF@Rn)FkkCMGli3PPglQ~FoFMS`-s1;EK3sG7e^xXh?i zIcMx(ZsX`VO{JKo|KPea&>jX;>C;A$1-0Q^~+j6ec`{H^Y!)P1#3Ez=j7z9G}D`>->j+%jUFDJ zc<{jo)$x=mQxtgrw){7x8A_%AwScIMPf|mJc9W@t)BYn;tc@huY13e>cu{jeBv>2W zLe2dTR_NyhcNbo_JD&QZYB zDjuapJ3f|H(Igv{CaiZOfS{+NW0!IQFqX+ohecNNwzN|k2sNim&K;$#rS>h)Yb_~y zjwM2YQZvF-;iI%`b>(l%{NZ>l#(cE2`C1+4I37j3tL*0Zo-+Yr>vt=V9JVMd#C}!1WH>YCA$^MuH zm#v+);jLC&Z%F?>StF-&UOH|~`>ic^IyAIJowGw9FENFl%*{CeH(#T+pw`-kl>=4| zv@HkDIuMGsS6Nx9f?;%%qL@Bq8#56Qk~VJHhdcjzJCb%SR>!-{+pVbs{wXXeQXlI@){3#lu3s8ZCu-+JNM}K z`Eg6_wUl?FJ}k-$D_z}O5FQnZ0X^Hn-PNe>cYX3_QS_qvsRw@@|7NQryV`5+-!kjYsT9lS{6tI+BmcOM*OP1); zTrZ=4AL;rOf9P%N?KZF8Qu}%xDJ%8NWh^xUv6&sA+oFtA(aRt6`*{86byZ>h;W5Fm zp=L8odx|;X&?0m7v#t4V-c$4A*0dpIPPLw8dHF3#Mt8hqS-zz*Cp&Lh=TypC)@$jv z))qI1HW`rkV=#X79g3Upo{Ry#+F$r!cwB~SCmbCOTn^xZMlOi6qp?B8YE4!SoI4Jf z6{tpMI2j$O{(&^QXj!GCkUvFRkwk)TY(etD^SKcJ_pIX!T zM^k6bfZ$5z7z8#c*_<}W)How2j?2|d1OkQ1G9r=o)cE~K|K9d1l zuWJ(jr+yQkKVDmV{$p@}Ml{BgG{MV0lF|3t<|S=mWjO{2HOD>|NV>|}I0lD+@6zKM z7!@_!hJ3c+f?H35V3yAy+}f=JZU|WpgH0u>8)Q9^Ib!TgpDoM!`fvSPA4~INJ^y6i z$E$a$`Ih~(rmkK_|JLg_KbCySGY22(#jM}wo&z-|dj?Y3VMgp0eDuydbb9xG_ZcMBwTvS+K3=M$&B&@O=7YS!@1bIA8-8mnN)R?vB5W z8Nw9+veAm{+uDLAaEd%R1_*VnBr=>+J#-`}?Pf>VSc4$M>iZd5j7DxBRRYBP> z+UBFC!Jq!Eqv6JK~7kHgdnvi6Oja^}zPIUTcT zu>#=TW5RLYgq!fe&mZFQF=aGSaX-+8vur^+rz5p?cA;^=)>hU()??**d&CA_i?L&R zC=%NO1f`*4fKWHrbc_zhy^oB+2XB6l4pB`=NcJJ>ax0`-P4FabSWjCyaItZ~)~1oWhuCrZsXVOPpM=Ko{n}&}dp`J} zo1w0s8Vgq!Cw%hbV%P^9HG!4y9XoA!y_7Dybu5=62jt!sE49&JLVaZg7lHGRmp8Oth^ZB1>DufS z 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

({u2Vdx-ORb7k! z%>D%r&(A@>z(9Dr^4)MUsXa;k7MiqXSvk-$2h8$EZpVtGT0Ho_EqHX`E4Jz zQm%+q;siHq%dNJUdjSBUd9*E(r)Anu5{e-9>3132JLf3Pu;k!&m_yUtzv$<)LFt~A)YR@r zr|-8SfB9#qNu5hj657odi9@upGtkXgiGr0MQmgGx^V~Aj6y?L<6`8>YKkCd%0|ha4NCjT;+1phc?S$MCl;?}lw|)q75$C$Gdg#iMvQSr z;o7hHW&rvj>BB4CIuHJ_6W|y^|D}Ty!Gywp+jlT_xg0fz=Abn3P8hv|2vkSARDz9? z%|>P)`VDvFauEornL9^O3*|Vy23-T+kvG#k$+r+tTXqVHpxQ2{7?8v^luwlY)<*i} z8f&R#ia(YbqpdNJbhA+wuyqOJwc8K(F8xuJorcPt|KbE@3hFWzqGa!Sc=s7aaAKSE z>(w#1rf}I4yq1^4l)aRiZM@RHrB7Q2N0g@=K;@42chNCJaMyZ6(9LY~S=3*3 z7d-o1f%26fz$0d`(f}I@!TV(!{05GL&DtfrPM^S(KOgq)1afgUauQ(c98Q|mphyI1 z<~44pF6aHoz#2mciZfX9PsgQ zLWfXatVzm6@r&Q%tygb?yPLeky=}no`~%?;!FmE_Gr+^f-X2AJ)}vq^LDZL+?H zo#!x-!Ad46LB(JP(Y$QzeK}$03qwt;M_`e2BFn|sBcNdGDCo(G>e!7x?J49s&<90< zMlfoUR|Uv_em7C@Wv`e^%^;PLpg-X7moAJHq`&C)e;{weVC4PyXZoK$MGAdVej~3( z)IDz^XJssMXWyh~U`OV}2HB;Zt3S<&$^7lk^8ymgq-?FG`PR4pXc+#nUHQ>U6ZfHV z|5x-avbtzuB8c6()1Nnok&9Qswco4o>^T}0=?SPy+6~(w60ONT$SYe%7mk&PM%pFv zQ6g65U0wn_0?0O;I0!GKYjA@D;gK4*0For;%MB)y-p1aY=2Dthm6?+#q>i3g#MZzl zOergTt7jA_L9l4|I+X2O1zV4f?1$GwG)-xf=cOMNl`3Nra8GP2kBvcDC7&@>=L8j-wPQWBa^O@sz^NAM>P+a` zE&xmRR$=7g4Y>J+F{j$hamy>c<+x|h$%zv^K)O_+P^aLNAd>2MnhUFP86Y#70#->% zQmF|>0WC^qYATGDq-Nq2%%BTK)|he^P*6(rUkGXnGC0#;p+@Ey7=^l&Jv>kI909Va z?l6NptLgVC!r@!4MEW~pkoxi%WdC;>&4Aeydfn(FdmUmL%~A`uvZP zGIIb+aeS0A0!kumWhp>i`XZDi9iTt)Hn@aEp=k5Zu%(uy31a~bUyd(XX(^6DU8zlN zL|xiCYJw7>>VVqp-Q;5-?3`U0cM^%()L)dJ)zLdZ(LSE>Bl*mp~ z310<766IczJ_TzP{{*Pym|!WqD%rM#fjKS+n))1SOA2Xn{)%ANnRAG}D42gY_TCtZ z>bxxYU-l>Z0W*1@k@%Jp*&zPOhT0nPjFSuvrCTa_@#N``TniLS#Gc%X%T~u>Of-2^ zD-#=ee&u6LRt}t94v2CiCi%fp!T51y8p_Hm)%_>p=uFdbp71km_7v)$@EOuh%EM>p zXjJAtx|1kWoha}sD2S=;kO`vY*z&vxJ!NiVnf!GN&~Ew@hh^<9!pT=4lDV;A&CA+0iwzzY8oB7s{j>Qk|3ImXh=#Rn@y|=JtTt~n$T=V zGo_g2s&mp1H0}nP=@MDQ4n9LJN8Yke85}}wj{qj-SxI6#E!y%E+TZebnh#S^vF8J7 ziX1w%E-^{k8vRjo_!Ig+@20ty%PbfKBU)$R=z%*hC{cD-X zTk}5xu_N5t$B@Rw)M%+q(T}M>)1e3Iau0CW^>Fa-z{pERaT-I__0#Vb(H`wQdZBpz z*YIVZ?H`RtA$@wT9s9$*T^FRzeTzOimKKvL`-%ATD_qtvlpq-cM=xJwE_g?o%7r$i z9m)ejFqiw|2>+b=(iizLYVz-Zch6zSS-p__h^No73qo$(imD7o`j+Gqi2dm=++g&v# zr8z4NrSuun7OFmWE+bK~{b{6p^M;C2^cg&fzMKji{^U`H=dWQP%TQ!~dmjq7t)h=; zkn(5cum6!kx{7Abmyz=An~b9LB6yC66W8z65Try9RHyD$(UpEfC!@Z)4C&vzq}B~q zrS4M!6MnH*QNU)f?Z2z&)~d9F$_!`ArH1uov~BO-LT$B3p_z?=>kJR=Kvn7i279m> zMpG8;UXQ|!3mF}{R4pY4yY6vi>Z>bC*8+oT%eeeuAbpcRqHyOL2rtGI?6v=gx3?iEQhMH}8_J~E^s(Hv+F^>_QnXvVw#+H+${I6<4QeTO~|9ucu znT*V3w5VsdK}xHx%S+(3p_ZvYU#^ok&Dry)ffaC>MK9%F{GFzyuBQhz+TV~o>nUpf zmdR$tLYVt*1Y*rq1k_IpMQ=$uhz^u=C9UVxD+@ z>P>C;yxCCOIxn}gsi6t4zx*l2m%RlSS08F1y2-F*E;m$gc24lHHHJ{)2m{PB040=4 zC|LJVNDemVb4U!vkVW9KZKZ6IBJ0TQJh~A``G#ZjjwtoTY*YM8Y)&n>cN@wd4E5(i za|r~ob@JqQi2@^g?~&9b*`8>6)XL<%oo9E}^QHN0C55ckhAii>^Q0M)JEJsKB~XZF z(l0oSeQ=R_#dgxzUn7X*%T6ne)h7D;UO?1s&*RV=x1(zJY!wL`_Sj-&E{&f#m>MvH zOe%Mh0DrGku*(5+sw#YvlTYnDBRrM$PYc|#$;vxCX4g^>l zig#+j!?uLy_FU^{!$tk7ak(m-%$V|*BjCFE3cM0t4JD|P-|AYjz>y@^jtlf_vdM>d zjn}+(5;ZJX=+8X_ZY1z~)86~hvJWpwC>n2f# zoV`=n+Idi5D~(aKNvUtc(EiI*n@0jgM1r$DTUXX|lX@n8iyx8-c>^^qmk|`MY)9I2 zm-g5ewFNu(krWbAUi?JneCgjgnsqxUT5Rl%%C9NrKP5gWK}1m7I&+4dyaZ*5lhOXJ zw`lJC8zV+tmB}q*(SKMso4C-9fIHeeJ`p`OT3d=A+IU3cvNw`P(!L-Y{ci?8ZG19Z z8%B}H7*rzt!n0$?Pu?Po{YiTadU5Uw8JE>Z3JSaMJYN|k32`Sr)NJDt^JgND6x!qeIAv2UL~&y+~$diR#L7VcZOj!sU4Rp zsB7SEP`qstOMr|;Y;^o@?>-QXM*0cp7uN0#;jIAT5UnY>o-8Qz*?Wyv^fhv4Acx>9 z;MtjLLsxU-0}hFcEjUW9NAzJC{$pe8L-<7mZZWYG>v?n=j1p>^4aHlj9g^2Ne_O0` z31zC=ffqn{WldHNTml@p0D!P{50eHU!f|;3n$0zxJjN~ z#$2FTJ*tZ;NqY?ZFt?+BQ(`K?t+#O`U+6p{;Q5=vwYgwb#{!@O<^2C zvMW&)zm!_vQVR9qW&z7KjwH*r%=zTOvAdca9iLRw>C>7Jdblo7A}xP+PI7pjV03I9 z$!C)B9@Cfkm>RF1)C564(7%ZsqDeJYXVOIdpaLZuV|VtW^H(n@-_k$nQ|m8;cD)QC z%cTz&xUWHac>(!EZJlNHaj1GyU4gm$U^|l7x5j%pCNPrcY4hGgRU#M1dko?^ZGvEV zeOes5YD|XDU?Q&}$X@g|HN!+sN(LWk!(8MN|1$IS5+9{?cz+xYTmV4$2Y;v(KyX31 zQw+EG`Y%nE`AU9^iPF|}B!#-B)m##{Bmq>+rS-)JIYwEUL3^jJZMJHF)YKR)(@>U# zO4(D^sa>mT9W5mF9qBL5Ma9m)Yi3~5ak|NrBb$SJax9n7J>06Hh{k-nT>E3xvolB7 z%1SJ2y6`!00RZ9oWfC9h^GksAE?`K#N6e8&B#dZ8O0zkAL4CFdCY(F9=bSYJ4CT!d zq9JqK3iZu@Th|t^n4L4twp^pwqBeQ5Q41J4z()MRO2d~sK)8S6)KUrY56iVXRW6*k zr}KPP7}2Un;$$2<&*M0c-OD7QsG=SQdmFeqay_$^Rp;?)w65#(4}|C8CTVPZ@*Yi(QPCqAgkNHg#pd3rS-fr_U(*7}ZB!fvWZ+TAg z_2Uzjk#mytvU*!5$&=y=rdy&>>PoU~^T=*aL%%-{4J6Sx^!#Kb*y)VF~8sU?Bj3p}-oKq+Kq%mqH2 zb4{LrU`<^khIjWsIp1P(AiWAs4s!1tpAxBaqVvbEX+6_c95}B)*vLEJ1ouZfV?hBM_+YwGLw1=kDO)xqdcBq252 zQp|-DDKi+hL|O}Zay3QRHPK{dO_wGI7<9|~#1_egCfq!^1JX0g@cr8dFgVPJI!4Lc z3F0v|@J5h(K?lIP`X)F~dz52+X{NL#n@UT$2CGVW)^C{e1cXhnXY94Brzh94Il`UV z84YIqcU17S^~kCJ@kXp8@Y!H{)==!K^Fxud4NScGlB+LBc9qVOP1@j-N zr?;i&>&KSm^>R{1|E+)Pc`em%UZ$lso0pMxbQ<*EdfW1CSzgcA^WNf}9cJI(88XSKhU{p$I8-pT6g<&LSx&Wv?72&<{ZK<{LPdc;w% z@hvC1W~diF&bLg!1plh)9N_5guI~MoxqX}ltl1Yl2SlC~S2n=J0JM4mE+UwGiKRjO zg|#(}D5Qo+oP-}E&m9dmI9yPNEBkxn`ibrF&>Oo@V`_w#Gp!E1S4!Acd2J)yXj=7h z;nsjwFg&aGz5P5VkwTGfCsF9W)?W1T=(#CCH>VW|m;S=A`1Zp5&zZV2{sLS}vqHXJO2rxW)j zmK(>K&I$+43lQoTbZGyMcrIuRdKJxqk0P9WbOH-yvF7g9#(T z;o;_h%-jm>FEHV`X%V=5L<92m-2KqC_$YB4 zvhwQTYOIBmVg{4zCHw%bph(06*Jc{_pA9{lzUn3>8$)Zd>B6#cRzd` z?|H7JBlz>`p*#5+u+wj6i`{B=x zI=1i4$AdGsqQgz=@yWOQF{n=vItLr6C@^Hp%J;K!L!1|lP(~t-U=awNqk{1F_wUE7 zPdmbu`)5WN55w8Ock@|JeDsUY@+!%6TK_GECMisfPuZNLfI6pDlEv8e+!}0sy+1m) zPe!fDnSJvHJ*ES#S?gab2TsHRF{8@_%IH|hjlj#Wc1H(va9Krj7hl|~yq9u+YQN+b zts-!y)I_2@W;mXEWCEfh{M0?)mT%TR5xu8%P8-5Ws+ym}la#vDPm8}Vqr3tAVtkNW zP>D~LW@A)@5r6(76~p=m(GO^c-fs5zY(*F>%tYs!2Ke676mIX+kOegX^B|j;psjXT3hicE3 zd#RS!7Gr$hFkCZfaI3~H`?X#aAgD{`6OO7?g-+PQrE=1dU?#pi4>y7vmAb^-T4ZqI z?d)VA*fE2cb?p^V*cVrT;ZN@YlIjt+VkqnlR3}Y>ZsY{-jY2NKUz|~km&W?j6dH`@ z-rS1UzRtxBV>~eX79ZJtTvAO0@oGxP4w7#ZV^`A_#mpr83|2jpiR7uW~*IpeOO9$4^ES9EIct%0Pg zEpiKM;p*y$o5lv?>ES*q+Em6D)4?52+~|+z{@PV(oUObp7Yb`GIy%m~Mrg?~HDhkm z$)?tr2%$D@){{I9jt0Dl39Kb;@1g_?>x0tj`&kq|cP?w#mRNyLzdL|QmqlVm zq!+d@%CoSfh7<65`10*Hv*yO(Pm`nZ;(fi4oL-8J+p|zy!Z)q^*kfCKIaaJo#brao z5fNH~zrC{$-IgWcve+;T8xVs1@r8_Qpe;)lrgTI=K-xTLT{9+cJ$sI}!z&c2JfguGWZkXqrK6A;JdH z41+xl%B>?n)hU241?LaZ-ed)Zr?WkGhoMW52T@#DFHfsxHrLn~?&Y8~J|lx|q<(}q z*9*!I{_hs3wIzL6)7j&I6$o3!TZ&wHZ{P*=rU9Xco5t~hsU>Z8oPE+cf8;%Uts2yM zE+jk^Q%~!jaCv#INCx?*rv|=--quE5oNP^P#(`D=T&oJT8A%sILzWBOP24?3Pa05a z$6`Jg4YGOC^K`SPek|oI+iG6Vdfav#5b0t~C*gp+k-oORh(cQnjyOs2vwT{7Q?`6S zDo|XrivTFmYE>w#+H?#^oMj@;uz8vJ)>9}{vYpllRSFP}QGib`FMw#2cL7=e;~5fV ztu7q+y_~l4rHwdXWo;WV@+>rC1;P_BRRIo#wU28w{yO?&^)15NPTprnFw!(JM`|Ua zSZe?!z|>qz5pBlK#zBFWcJ5f=FL_c=Dt|JKgxOT*yqZZ)a z%Rj_@KjcB#FcQ@z^%1%VBl^wrus5en%2R^FqA?%6xF5zl-2$QqkSK^Iz+0n zBAn|Rt5IHefZwTbwClkg<)e=DD(6dU4x+YUAJ4@ot}|u~ zZtO(|I^WRVREcW`Jx1WM#ez-m5>Ud>F{Cf+_@S~kmkV=U6)okpD_F(>?vA6m`LC-Q zivVAhDGwEOYuHvKfq^f{DUU$VO0ojz|xp->mA!HXF#z*VMqn+n?6ji2S z*^aM~U!DvHTN#5~t6IJRU>m>Fv}@qVYoG*n6Dn#`Nn0Ray_HGe+`>MG@z`JSTc11} zxhU2jC*gplf%+sxt*5OVI7=L8OAXr!geSR3qD6?NRo_6f9EEBfwUNFNw;`i=AADUS zxCh`1h|k`Rt*K9-bHL-cBJv636d%TpwC@N=9;{bQ00_m<4!3bP5yzIiaC9O-J9DvjD%$ysMvw3(*}nsw&ali&!Q@%#H*Fe(jjJ&dVjEnh&uY$?L`o#Yb5Ir!d=lkS@4YnlDCot!;oI77D zlucT?;IeMtvtLK-OJ9w{g)3G00Jm-o5(wt2W;S7R@6TwyZNRp}KXB)a6m$!C6rF2JVg9p8hOs~@ja6Rz(yo4h}W zyz)4LTPd%HXy}d#_&tna7}}A1aPmS%K|FS*&n4KqDV*yUaVz3;*PwIIbVT~ShC^AK zaVYl}PHz0v^*zaLU_H%iFUgLfkn%=?f0V1G8jIGWVcMSbz3246|1}K@DpUipp~_so!6z0c9e!cnLMSuGAWx2$b;{ z5_K=e^qPj0+&F@b8{2pdbJxF#y;*PKvM%pqWcO?Mx|jXv;unoTuQ2RS-G(&@>F5wN z1O1{$qo^W}dmq-KZ}dpC3y5LZwhIMclLFrkA-(Y5<#%&W!~#aZU54voA3|P92DRf_ zOds(ymhEbXU-sXDev#8KB4!Ff?poB;R>IFS1l__1;LXJo5a=0=0Iy)aipw6ILV6>k zFcEP%9=K_k@TnRV)uoF5_U!wM^|vD`um>C+oZ#;4jjrK?xMf~00Wb{L4ZWZHIKD_g zTZm}iiMV0dlLYq?6qEk(egDM%2II?h4-o9UFg#{5V!K?)>sQN&y+-uyFcJ$kx^cqt z6}8bYUPr$1-kM~t(V~K^9s!W80QlMJhw$>E4#+G#tibTct^bGTevHBP_>~mc z{b}CgCcBL$EZF=uUR&G|GZys6!c8BbeNb0wQ0@rx9896o17EHA2WI{{94q&JkCKW4 zEZKiEwWi0=Cwe$OU-<%_`e_Iro*PcEsl)KjHxukOVd3WYs4>>!-U)Bxwqaj$oe|BV z)X>~$4iunUzH2TXUf2)!eAkKEQ8Gq!n?|!^1wni~b|$UGtG^7vXR97TSyeGM#s80W zXW*9l;uXy-c=B{eHBzW1#J>D{<* zUN_t~zYjLWEk$hSNd#o>rcqxFC!;I2B`m|MKX=3DtDc9Kn?L$R{00BpdpFlsEhU&% z;_amauxQsa)G*yJe-ruiQ%BrKKF#0oJ|Y7;bA44${AbNb6qIHYy!VifE(Fb`)JmKc zSj){;UB4F-`ur88m4%r3QzZVqVgPBKi4Gw>(KGZ~@@p?A9ro<63LpJ;GybutBhvE| z(LZ_|&B2MB;8-66;iF2;Z+JbQ-xBg~$CoIM8QuX)lQQ|H7%m0SU#V_U&id2JfwRE@ zi7=B>26UEir;BD-QvdUs)+8oBA zyf+Z-bs5ci$>9&m;>-L}r2+Z4hJpL=G5{u+ zuk&%H?+pm^=|=yZ9R_!tjA31_SD@kQfipa|2Of((h@bXnU|{D^ zZa8gH_q4jnjqUL(%Y5amU)(C^>dbGCC@VH zbtSKPIlV3YTgvFa^>52O`E8jj%SgWEvHX@~*{5ZmWm&D{iT~<#E!))FX#Oo_TFN_K z4|-YsynZa-&6DMLnxB(ny{_dssvexH8Xb(t$Spx9=X_i{d8}oDGyZJ_!jrJD`DIg= ziDp6vdn1LY4+X2d<(|9FQ{yn&_hq0*1&_scO(D%Ser}E-xOLR)G$EOgmLISAR(RH) zKDFYiM80FJ0u%e)K~3r^__zmA3;TxkdRkaGR(KIjFf0K_ld8mPzEyrnM@QOeFJaVR@AuTUnd;U2N z`O(-^uFA`Aa+<8Qe88IM2Xt0r7VX>K)>YN9WjXOn>cU%mT1JDsf{Ot@jU{Sq&9ALC zHtXNxaX{EE*UiDffTtgx48war;?>prF)|_;u1*p)Oy8lo^08M)6K}_BSI^h$>c`sE z*N?RC6SXV#o1d3jmU(il_hXqa_lFeJ^I9y^LR->a^Q70+^QFy}^7QlOuJuDzR;NQU z^}3dM&GYp#dS$(=p06M4^(>Diucf5dZRwnzXW5RFw>;L%Sbkf!sh8FBhBzm0M{Foxp3)@IrQC?GJ>)c+cRE7pxZ>n z1qDtVm#pzqZo^ zh&<7FBw$9?v0b@|OKY|=(l(2l;O9zPer|4OzPct2H*|fPZ-jr6<)qJiRaWX}RO~^d zohRGS_%6#JimP`jGijt>H~g@7BQ@=f^v(X66AOQuYZp_4jMT1?nDNXKPx_b7$sb)7 z`6DH{I44L7?8rk;4_CbK zXp9(2OPh<6y4U0tdu^4hKzI^PiU7BzSJ&mfIa>YOv96%%qdFev0kTp#-w13U^asMA@9me~17%|D3jk_2V`oYwzyjIGRJppM2T8`2;=FD%bN zw}}3@XXs*T#oIWMC?GA)q(zo+9LCo8-!P{4H2ke;4QWZoz)qKQtmzDfnM94?sH|8{COcVgbQiTHZm-!ZDo?T8BMPSa`w18MU4-m)|lRV88dzINym z+F$w7a*H#t>cE$bCXIua69aO#Jx}1chY_FM2qF!tkEGn~)F!V~TG#pmOOTkomG6Kw z(*Jl3uIzs|f%gqYZzhl%)U~+QP)waARoU2;w2rivk`GPiG9L&N9SA2rYhyOW%Rz`#&JK3b5N?f68X|okS4!P}e9WZy(LhMFf9eMcavP1M9ip zb3lZXBmqB}nL9YpWMNHK4xB{}$h{`FT}y9zIn!=!XnMM2r*SeeVO&{R4IZBPZ$$K2 zP9aDwfI_+TNg_n+sDT`$d9n-5l}4^#OCd10&{yXxGL~!D^5~DMW*B}1n{6UsRMJc+ z5uEMWzdeO^I>Yu;c&?s;IGTdPNX=~x?jE^~OD96`;=&;2`|}fw$FKSohthZ9`)z-r zfFDS+=Q>_^M@4f(V;VtWf&!(o+C;Xwhrc7(cPiH^jwg7@G6w>WTBLt~qZD758-0%H z{0>5|kh?H_zQe5Zc+`{!qoHz8cupUZyqBCXm?xQfxFTiK5RrXS*4Nma7(A!=d2G{ust zRF+vmItEZv-NcE)cz(LE?jrgMvsgCK)TKWv8|d4O{d9s|N~^i!e(fsKJ(T9)0pzRr zl*+zR^YlqzYt?Kko?ymJh#4VUQniNHGDzPPmI-Ab(mJE<$?qp^y@caZUAL0QJve5S zhzuG_PWTw!mC{Td;M>`>En2i2O&$H9&c+ALe;(@VNgD&3HgB!Lf4=#iFku2g&bihe`{UB=azM}T%P+r-GtM{zAtCe;{YY|w@JE=l z_m;Cwn6s%hg#$Gv0iNSa(@R>lI9`D1tVI(L$#>=Kjj0(7xA$rgl*Z)9dlfZ#=n;7Y zW{$l{SW3+%AJJT1u)8=5Z!Wr*LYf;&(~PKk_6l0=o?z52{>1^`v?FV~eHuF}$ocWS= zdfBt5)_{vzvVC)C)W+nshA#q16R=|T2i;2(->lY1r)ODBHpEv?K8cTt@|3BTBVhQU zn#6bYRSW4YKXl!*KGmb(nMS%f2HFkB;gNVEV~E2ym2GZvfv~xpa`U+yIJg`*5Fm6- zqs+vZ6Uv)OQr~9T{!*vJP9F66r1=n#0nWOny7-?`M3$Gw`3qBt9R>&wt>3qRqK?5DQRaI~_Gc8# z)ePncbxd`x9oU4tm1APg?`bVwPE|+MnRuA`j z4=e0&LGW;YT;2ZdhXXDU?uRLDaY?==n;gHDJ==TTUTNuX`SWBJR7;ND+4^m^bRQ00 zeVV|CUox*c+~WoE(Q@bfT_wvL8tI<;-> zlg->{QdSunu6y#sUb5vS8>OQF06+jqL_t*Tx%qBre82r@sLq!2<%R$`yS*^KXfx8& zv-vz}t)#vC3%^-WTwFMY58*4m2J|9Nq=B{_7~1yK3$=S7v+7^ibGNw5ffnI_W1n$; zH_d&1I{*+m0(3HWzxdx5yCVLi-P{F33%qq*f7>Qm5Ui{7!KS4Hv1@x00)x3785bhk zB?E0kiQCFA<$%C4EErguU4!qo*P0!1JOTHY)nAX@6}hP3rs~&BkHOWKPBYt?wG|jD z+5UYS=byfJnVtc?nigjk%>5E*-Xq`@GK^e z=W4cEt|6=AtfBe*{e?LhsKRyItG*qI;=)nr(lg3vEzKFEU)Zo)ycIYgFpT0WVPYa% zN)@z3)}yy~6fFnc@#1$G*vXd*KXUK62;+MI+X4)OgM%@9_H5+t&P8Qqr8`Np!{UCo zI}Yggl9ran?b)1{p#NA04(kQ=w&N07GF??w*WkGq=kbNPAE4i$G``P@n`F0vRw!-I z&%X}oo0E{b@c=zR!oC=Ej*mlawS0u4u%!j5?X~;eemNkMB(=_vKzy=#J03VQ3Rhe_ ztxbTintO(3Wo2=qSnGmd`+b01%E?cx(07CJ=b2hzpeHs=m-H4sp zS={2w*$w6=`1G@HOXJkucmmf#$?ubQmDipvpWdZA|OG zbm*g3!MAdeAZ+JD>`e*j=i7E-QvOaS z=%}4D?q`rMULUNqS0g-F6u9lW95{SA-~wSg7+VQoD}#0HPvEnFR93wQTXXh(0d8w_ z$p9{+Y^!VQQNxcPkJ}R8-)sX4GlNzZqHGEG`FJ4jdo{ z4nQ;U5(rjQ z*CRQ%8lA#@5gq1>O$6Vn8f^x>Pu4^7L^KM(i^;p@B;_w*UfVx`pf7>HZ?`~b#Z6m- zp;+A3Z8nEbZXTBdhYJTf`)E(yw#1qqjdO=~YPTkCJHCvKY_0Y*}UL9^;RQ5MQKZR+xNhUrl)O zN}8@s!+VFIw4x6AB{j$_t;LKnF}UH9;kf^adHjy@^FmUrAK%5OO$31L zdDZAlZ7#F82CGWzFgn5)T_b&skd|l_Sf%W)#-_48h-H}}o%|3M=#AvvHF)TTA?Oqn zjEA3EjR1ddOdZ<=F%f>qAW#Pfcq6^A2I~sz5W+f!#P}gR$j1nNfmRyxtSYHP57NV| z2IvOH`6IuKbdxTBg2tEBFb4;DV*szK4~x>P5X9?Aq#=;B2uK8$1uU~JuNK-EZ!l?( zV}G*v zS`-sd;{&~r80&8U__cLq*nZ0htlqdAr`@&zr%ejQ`cwk{3#*W_s{*0qiN;1+uRT97Sh6rz>tGgt};fed# z<-jlHfD44bl!L8YUi1GtO3#9#D%}4~9y$jUpuDmU*IzsgmkbQ1zfMAtS_dU%C6Bwu z1mN-+@#xwq1bKy(cxO&JvI$atzTUXz)b99hWj+Oa9ES9d#2q6rof(76Q1 zD^Ki(p#!2(P+W<(zQ{mkK{dLD`Jj83FnqH(0}s8Ljaf&B;luAYBA#Fp5$t1tZszz{ zJTfwld#cvqlW#MyVp|#F2n4&>_W4ue@V6nI*oR7#SJV=`>+$4=X_$UQ6t|r7LPl;C ze*ZswHC1*Q9)EWy5(r2y-;ziTw;pfKPQxzJ5J6zgU|o}v!g1%N-Hayp?Xqlqv7!LM z1lX|y!Vni7fYr&xc;~`?oH*2(zHi=DiTh8E!;!<{5E|l#+5ek_47T&qoGgqR7>3^* z)tRq33c!x^61*{ImocHfdUYQ}M+PCOZxlZK^I$Amy_@^eRw6bkfIw7Yw7eW@!XtVG z<2S!bWWD~_l3Iw@=46^lhqTP=X7s?kWxH|Kl&XsNt`e#0s0gYb07D*7?K!4?3 zzv_+Sk50g{)tQKi3c}sjjlyHkufY@Z^YGLaiI_S*9*e%u#DK&oBy^9!{3YqwR#c1O zNzv5OdLSdS1SMtFIB8064CoU@ntV{o^Ikn7NJk{De`p0V3u|%a#7?;M>_OPDdAHF5 zZ@P3Cy2Nh5#m{fYMJM&ZDaZCkR&E&z3AjTB#9|P^e%9?v2#|q%n|uH@UT^g59>$48 zEqc;adhM?U5u7VfR9uadS+;k4IIe$mEkQrT@Gy)3*Q-Yumaor4LYF{XG%E?4QkUY3 zHAOg&v|M}P5bVq-#_oa&oPI(gMhuF@O@CdDZc+a1OD`NZA)XV8Dx=0};^O1I7H@u= zgKnHa9Eh2exN62*qn(+Vh4t&#VeZ_y*s^5{UU}seT{c>xG(=ZRH0*HyxEwej4(y#? z9#Anib6au15rW{$Qho8Oq!8RRy$dd!IRKlt=HZo%We5-`k{J2QWU_mL=F`4cEy6Kl zm*Kkmmm)4U1jG7-AgjcA?Ne@EInKQ2dkndB3Dy%(CXelgc{HV-FftM&26x6y4=uy- zlUAYkDf3ZHq@6jnJ4$G({O4=QShp#c!ha~uQRZnfj-p?0#bVBtt}XF<`TU9 zX)=-s#9uAS!0?Nf;;+xHF`DQReM6B=6Y-fRCE}|EJ8{&AMHn-35$4R_foVtgq@#}} zVQPTB)Oz3iGzBBCUXDNhbEPrmo-;8PSNFHG%+&>mG#>N^Gv;awyk?D6XN75Bbux)z*ri|-`44O%$y*EGAL#|6Ng7}9Y zzK?F*y5jibkH^CgKWsoxuT0XU$eK!JHgz}6Za#P2oGu5%0aqhD#Giv;3i)c98(1$mTP_@60&~Jc0}vGvh%lNSrNLbBr`4EoWE3u* z)gO0UGm2)=<;FxQ4et2y-LPtXH)9&@LbI$iJONYz&9l9FgaStdU@XBiILO!d6s5@e z)7O}hRYDD`7&DFyMSfuwiV0R6E^`$cHF?2C?o5Lxao&LwuB0UPVoMbk9IUkT36jk%}oj{n5Kq02Xg9 zHQ+g*cQ^{><(SFIVwyt-MA45)`gfDIE^)yK=FCQQH9vWwqM^Vkr<{s+-gyUSoNv12iA{5V8}N0@uokJ$Wn+?*~4IxYuXAUwp!zLeTnfWHqmZ_CHk zkE~>Hh#!|OP_toBNg##4i5z4fjG(6Q{@ZUx7yw+kE|(_MYSj02q^|x-5eX(ZN=lb8 zu%z+r8&u4E2wJra!%W^@Xh4NBA1OPFv1)T6dNRmEOEm^{rNG}n&$p z;WXQ-yy-oGUi{HV*D9!q_U{^qKin`9c?Ff&kep|$hsni1y045PJ(x7e_gcm=lpoi*r~3z%LP&HyHGXezAIzFH3n?inxaz8_Fn;`a zTzKJy$jHdRk|j&Abjg+@>;|2 z#1rXX8cIM?vTr1ifX%1LZSt@P^ht=sdB6P@um2^N??3E}Nn_$01+enVgeqArL9KIB zN3a*tGRx=}^T8+I<=~aq3m8U8XCxhxCnN?Sp60?xE*E%XRWZT|Hroh_63hzX*_2jp z>e`r6jN7l-0Zj5IA_3!j2Vi2KU}G+P<%7-i+f^gCuo{0^ScsXuseLtohD?q8Z=geh z+tO%2jMsJUOK@DrpqHxZS`>0Q!}&8e0F%js%6c3#BnZ-)Ri9Iq(WO*?#glr1kO2dB zR_~q^(0;g!K0AhqzVqw;)JT0W;OfP|mu2*6_CtSa-HQ0s(h9Y&Iu|UjV1#UlbI$A9 z%wQA!q6!KckradLpGyNGt8vG~Xv7le*KaFC9D!YYR^ODIx&+3o*IfA-e;NvEveLVE zZ@l%^TPP|jG8J|2-rWG?v}w~YZ{9rIb=O^(IddkCKKf{L-_;I(2EW}R9f|`k5FX;g zO(n}{KhioPDBvYjwa{DZ)Fh~-Q3cY37f$or*7R})epKO}s|MrT;|np0k(z-t^+_9% zfDa6$36Wr@a&pfJ2xQhAM{u1@bL6y1h4}X$#^QpLGmK`Y;E{J`Z%1-k8ScDh1g@mt zEIYRx2|dD1q~!Z^)3A!5uyFY<+;aH{j2;$iw8Fj#5m>o?H_m+~1vg|A;fiyH&;(j+ z0PX7YYVq1f+h|tIMIhUk`BwA@xdeFfg-U}Fkg2btS*G1(jMDsa{!Yv~u^$$%2u9BC za)N#YGIPps{6ibqPlpdGXHyW(u&PslzIl5IPCB*^*8Fi4)+Og-^`-(7T(kJ$;V8Vb zno*^l4L<~QMdYs{SSO9{js?&8VeaBAmT`YUIDvmNK0IsnW5;bXMQ3G{`o zr4Lo6*=|u>^1;68h0Gc^ZH6GNUgJb%l^wGbHt*Y`OB_qq$-#_>h%g_k_32yDqoM2cWPpr*P8Yqu67mtc{)qX3EVVTh&=>*IMl z4FGLRD@7p}g?EYY!iH@{$fbWwOAeUD`0&=Jlpuuux-XYxgR2GzbYaFnmb|?P3)kc$ zi^~?WGK;w+BZyIMh02B!S?gzR0v2PQTs` zE`O-2tYp+E18)fCPk)&WIuW@>Gs2iR-zDHHf^-~0z?vYzgt&`V$#x^lL9TjSjec@zr4DQZ3?KUByvT=+AT%Mr=K%~V;0GQ%%Pt! zd3z~>{hMzl_2WX!ExAQFhs#rX_v&i2N`avTKusF#N7wWT4h}XZQXd~5JoeaQNK8zG z{E52fnn~N{cki~+zBu5;ABX3JvwczD4ycv6P8!*&HOV;r&V`sXxG&9WS`OYkwSY1Y zSzKzMWEW)HlC0M3f0@JR%%0JHT;JBP$eRn;6=~Ry>-c2mi=od=K(n3KW9YZiB6%g5 zQ%k8D_M`@*hd@ul7A~kSrbZ+!O+hmK7>J?;@5;M{OBj@Riosm0uD}v!_Y^H4=MF4f zU`~Y!q5Pxoi@ARAhKq;e&c7|id`4a>`t<+q7>2M=f1H2Mazjf#wX67s_A5zCI>EdT zgILsW`PWkOs}awI`vIgy!75n7L)ibdIXZUT{OT%rsgv!MW#ym*8csbC7S?ThM?sZtB;1=33DtE`E09cjNxvfh)5x!Vj zRJY-~+7J)ep6XNm4Wu}<@V=5*Vaw{=vu)+KeBIv|D*yh{{(;b$SnV%=(`(ioT2)zv zd+)m!4HYKBd@<(_wB4!AzTLwxilXC<_i8f30Hwtu8ucnW_DboRakypqa# z=F@r8yb|`Fc~s7(rg`mk`)z5o=Z03+ck2@yFoJ6hyKu*wGHPv0l1t5&Y2gegk%r{i zr>1>TT}|l`uxXD`&m_=NKSiTwYFgIN*icsOHm%F@N_B`AqR%rmJvZe&Gk6?(wr$(e zZO{ICWdF&-pHt5I69WPRaR2@H*omfmZr14)VD7rH=k=fN!PAj zgHJ#G)M$j#7~SM@z~um~K`s+q4zv~rq*Y043Z{>)6SriGU?7Rr+*}a+xm-VRNR%1W zYJhj#aR=tinS;fP7aJd<3x+>;9Ncn;69-%%Y@^W>C>*#5Ya`-Xxfxk3WwvZ$xoqWD z51;Dkn)FEfoOoRS@k4>(9xkj@B@D}(V?dMWB5v{o?T5}?8NGqbWG+^nMtQkoyuyw@b%YUyTLQRgrRQPcFzG92-{!?6|(N{ z?}zhFAAwI+ZRIv%{$>rFV~&!B=zd%t?0p98a}gRGh__ay;F4Jb5girY81T{(kE~rS zdimv-;qABIHgB-75vDCszgvgPf%e1!cR_s{jh4imj9SmX^aU<|A{A$iPcqw>8B?il zy2<500|&GdkzSnq+RAOXcUmxRzj-F#B;veMt0go-%V@jc{@ZWA#kzIt%)X@36y-~F zlgojQ!~quw+i0}q8k7(d=6yDMG5-1T8Z67_o701WxgA;a_nvGca@}SQ1_$(f@IlF| zs>Gn6GF*LTKb&#;ByQNvt=o9rn%P^DG+Xa|_uYrH&N>T8NlC5NR!h?D*5z`bB{|>% zVM}uJC+f07N7?f7i?C&D8g^wBnm55qTXYq}pJ3o&eA|w{-j>n1Q#kr1_CVJzF{a0! z&rM6Sz}B6doQ&6Bf8Fd$YOl3)TW)sI&} zImh-}((iK+y5lz4!W__Zp=Y8s0dRjL&zLa-uf6t~0YU}NxXI-}N8x}Aga>J)CFEQH zJV?wx#G7sF=fY0R^xndtpdf78v+mjmsV1CALM zm^^thzWVAb(~rF`oNBk-atU`i&@2wPK-ercxtUxJ9NrwTrqH27haw{*19^FQW*LPA z$iut$Ztpre2V5ZR=ud##f0qLd9MHN#e}8`*al{e${`>FERTl`|$D<=~zy-pNFv@Np zT@EyZ12#Bk#E21mZ{b=~!fJ}mDDGx)InZu7-~wU09XgkAmjf-!0Skoj@$o1rDM4;- zu4%y43R{-XZf%Di2RdxjsD9ke4_r%`-Q;qhZ8&f+mPH&$&tx9;_4UQz!Gp1W{dyBT zqu+@I$pfj&&FgZYy>Otz0ipd->xb?BxZBHPdU(<4Ou*WHonUjgXnzy^`}fE4<;x8a z+H1`z@8)qi&>lI^p@7hWU=?3&vvq40a&ik%$4!P^ov=MVy6uZ*Es_rk3_wE9PUz7i z+7NG_$3w9vy?XV+JMX-MnwlE;`S~4+B)T-Y9B45PbQmDCLTB~b9eD1wFOXKf3IP$j z;q6n`V$AGt6*)VAwf}CnQ9o*`0#R3xfT@Eg;==QeK}2})A-8DVYJYl*eQwH8;gdB1pqu-07MHMu&Yu7FVgl)qiw-uKI2ZsZP{o5xj2xjcc$EA1u z9mkx!lEDCesHrByy`&1b$>l&>a=;6rA;6mTwHV}oIc~ji2H!fuV3b41KyTku_{k@q z7{;7@^2zQd+|7JMByW_gX5F-y-uI}n{-OI#^Z$}_(PHIaTeSB}Xg%0vJA_6kCeN9- z8VN&IBOt&J6&2u&chAA*z1x0=-WsJzvHI)?fL%ly4N@^ zhdUGW;MCOAVQtD*bm>}cG(i^x5BE_&#QR4}gcpB$;a$_PWm~2p)>G&mZ0fDBjf#pw zQBjd0$6os}+LXuhBlD`x52e0Y`la>$Sn}*g)8ij2rwz+xPX3{OYoh{fU-v`zJ?r*t z_dt26vKuyR!0g$x&3e%0wCCA&M>{^OH9}b__&lJpvKGZvC5T|~TZ84Iqm8rM-$RT8 zg2KSSN|cnAHBffw#V!X12GV*`(P&=#Ar@L~$-%$XR>*1drj-HWmU%BQ(l{2->UQ5Z@?Mc+W|Ni?ZC@3&Mpmm7z=6!|nb_@Ldu)ZR&2Q`J*|R4uyzoK@q?A1K*>;sjfHr2#7`*Vp3kK9CPo9jiW5+^#QSt;i zON+E6nbd!a|6q?c2AT zI&D7BvZ~M0KWEMy1G+cebQ6v`>L}Ak-Lvh8e$Vz*kFCqzGv{6oukAH`E9sv7wjd~9 zw^Gx3&Bca4x@X(bx6SLB=U^Nk)*9i#V5r-s%Yo){Kw`rRkLH$X%X|`=Wo2c}7FNk+H0@HhaY}uOoH0fJ0>Q^)G0xjm6c_HLt0}_PL9#EHgDccKWZrk z4H{(Lk7$Lb%(Pp#ZZ)7JppA@-H0@MYR+?oV0vYXwBaqO}IHF%5EK{V;wH!oA%RvNY zp2A#j+}OEur)gh+qx||PjU$-rGkWxBbFM$h$;rm#7#A0ZzJ2>P)+MbmBshdts7kXT zfq+cWw@MmoX_gImVSrTUGGU6(dIzL{R7tdt8a2vj)ZMyuW!pzVd|9wy0ebc7)!3e{ zNo(xWrw_Vz?P~h2zG&G?Nl7u%($b7JsIgJLEnBuAA|k@HWhVj}1My$m>Z$CIAw$gO z>!L}ti55E+qD|M8w@a5UW=~r2J1Hs2l#$jeV3Z|6W@07XQ~OHd`L=D_keQij`q86D z4|Dxs|8arvV2^;?zRQ8bgaZ#n;F zxBu>TB=kzaEw|iafKJfshwHDu9w(e|f&pChMWAN;C7^rsu}6`UolOlb0WZJ&GNg4~aKQxz+yyf4y!#Fo zEL>pfzUQ8MOr2AvOhNsgeKjU&d3WD^w*h6DE^oc{Ry_Um(*}UWOM#6A>#C|M1L`wo z%n(V2zkmJfUyX)1Y}hbEgZMgW(jb=O@8AV9tQ?z_lle*~W5-`^kmJF2Ryj7BQqyYc=y<2j~P(aWaJF z(Kvtpd>noB(WWl~S50VczWHWqvpos+zDAqXIO?U(PdxF2F`thgKOVp1xZi#EU6?+7 zdgH{%bnYPhae=T|On1x%^_=av$&&*zb67JEXS%ZWwNX;A2^;4RNMo|s$I(X3|6nsR ze|dTN&=fCz{CH|RZv8kNKXg~ZSXz`7#y^zL^SZaU(<`fO2p9w~(mEzioQTs;KOMjQ z?Qe~V&}s(5h7U6W_^r3z!nx<3YlON4tU&L=i!Q|2F=LJP_xHd5-Ds0)@1chtg3Nzc zTycfbtX_QaMVxu&nK!T^Xi~G^Kw1 z`a%9b0fLhHV6{6sE-0e(N?#o4)1bzpUz!68}0JgV~;g}qr7wH&NUkBQ%^mm zQ%s*W9hY2s2|oY)b6j)HHRhhc>BJLHL_uMJ0ryv5ebs1=%74{WSK&Kqm#R}V-FM%8 z)CPBPT)LV*Xfhz+JpTCOl^y^3*T3-k>#yU68*VTfynL)*e)*;GTM9&_9iDs6IT%Ro zPkA*dx$U;wOxcYaHzI(R0Qp?yyL|J_H_dfD3!+c`mJihO+T3j8KQ0h@Fx;C$T|&%L zul zf~rTRNqzoHC^rD1{C@&LnNjt24n>HnewhnJvjFcBYNHBJQIe1sC`(IHIR)6*NrKFr z0!o3JuFEX>%rnmz@RonEysR8PzK(_^jde9OIcc;?dS9d_8Pa&I#`@iN-x-smOtnh3 zkEtoCkk3(IFLR|dGnpo(B?<^N=@7691eNmg^Y9h@yV7C>tQw!3+#GD)wAlcmcysc} zCt>oG$tJ(XMgUns%~|(U=DFvdGxtP;OruKD#07Y=3h3EVUiC@+HbBT z$aU`fQN0UQb-Q@Q@!ojBAEEyJ5$G3*sDM5wEZ@$12WOxf$vi|3G+_`3#z&2YueU$8 z=PiK0uc8AzLF>SXttW65R&3+`uZ5Lfhe!eMp?}Q(ohQ@uU-fXNHUX`J@qv#+j8S3ZM32$50$lPq{wQ1-U#xI zN0;!C2oLIvt+`*r-#e7|e?C796At1ZX;RXh1SbCl3_n2Eo(A)uji{6We*5jWjmDuO z(hj8&oOhn>f4JN zvcOC2+j?wXm6HisJhHUQG%I>US5Q!Z(V(Sm2}A|30#5;%%Bnx|n`)fYu1vzptE4t8 z9Rf<-7vQOkzBS2^*5>5hx4)GY1T_9ry*}PPrjJPjlW@w(r=7GY5L8kr;*rcERG$1E}s1II@H!xQSc`k!wgDmj-Hdjq{#u^ccAK6%y!0Gf<+SC<6*B=SM&Gz&kq|q0uK?sTu`J~S0&fsb zA9D>}n3s1%*cI5UuvwkI^l`CLyh4BPZW`?>(Gx z#u+A{MAxPDN&AohjE#*k=15()WyOD+bj_ZtjIU-5hy`qOfqzuiMl-4o zX;`|ZByh6yDcD7zW__BvFJZ3xwr!QQY0H*mV|tV@SAdB??8zsew6_fa*!MW9ym%-r z)&}-SyK;VuFj}L)Nd0P>lqV`GrV%`(oeA9aMiBV{qobqPeuM$are$?5ntgnn{@UOq zO+qX{d6w~G6szs4u1g~p2#PKn=;N7Rc>@Cc8_Vci^$PIRzxW>UM*CLqkFINyq9i}4 zCIm7g?=N}Mu)q7Qd2R0qw`MEtpzgZ*N=zMgHvIj9kXx8RfyT8%durjA>_nIO`sjz@ zJXm{Z0`#1{7(vGe*SYVQ?xY!2*OnU4lu1>B-ho5`pz`|Cj8?`OSAeO@hvvT8`YNN{ z`1^#Ja*igWAG9y3>trITCtcJsjLD5Y43*haW$N+Ur;GzE-qh5X88xKCmo!M55*-rk zGB2ur(WpArMjg+Md6@UBYKwX7W9~WrE(=-|GR#|2wF9GiUWfqlYst3PsC^BfPoswY z@h3eMeE2I^HiR_#n|k#KsBO_Cb8{V!)pzwt?UdK%At7corX6ts(sQ?wt}28DMdJ98 z=VI~3+4yYT{fG$chw_^F7&qWKj!`C_ns+ueuyL%T4l_nRh*ABHK}K#0Dyz#dxaSy* z9XJi|E_?#(cD;vSzc|Bx(cz?qNkzlBiOw3DH0z8BJe)Ln8~%t_qFc1q?x{3mRbI0d zlad}qe5bzHk(~@bUkU|Qnv$J>WodWgA*{kK)*4x8(&hDH> zQ(SZCmGG1gNCK~rTET(^3ys-Nz9j*R1f@)dGE+(@D@elvr|vCWxRAWw`h_qmfu3arl2!mKkNkdC+f$a144dh{F&m3`Y02r zrQ`C;FE>6vnF<9s3T%;y^`w(dGI`a8O#*sN0u-n+ zmZ@5rr;-AoCQqJhOrZidt%uawN%cz_tN1o{>^P1|o{7E{Fj}qL_P4FST_9}aIGxx$ zUa|Enyu5T4I){woK-W@W)9m0i)o34}79`M<$x8qvA+CQ0JUHNWd#Vi(l-I}?QBNT~ zihxpzoRX!~N_sJ$AA!Mfz?ahu+bMJyN~^LBKowPPq1Ka#PBg9Mly0EuEt_SyXqR;s zSMNaAu+c2ypxftyUd66A4VLy%Xt**v&1 zGqORpyMr2BC*B)`+|rE%o&U*>0JBl7eAulKJqd zr=MyJv)6S6*J$ColA;|2GFq^%a`LO$`XsoYe(GtXjVP}LGWpPK(ozlyU?nXxkPlJ7 zBmw;L%P*Uc0#B4jK{uXB=RM&TBMZiVxa=KqgJ`M|_gzr=$sjG)?u*CPlJJ6V*q4K!K6gZVE8PKecT& zUDcyVTqQs5t$gpj_ZpfNRHQzOuhx&LB`+$kWO=HIMyI$=#*D4LDeussLrorOnA#Y? zQ&Scn1e8jOZ2k1pPx0Doud&`RqXB9xRJS5d-(^(o^UptL_@^Zz0{MfT4(m;~HMu13 zX}+k$uOEH_38Ur`K!PYF_ceXV3|K|sf8+Gmu`z8a-dKJbIt8D|vhoFGQ_x;!OruYH zeig^79+yph6kn}<6B{x=p`U0c&K>g{g8W1ApZS+CXrwdx(QGzte+&mMz9{N%x_5=fNneEXIh$iI}_UExfnt9LyT|7LG`qOu7x)PHI-hdn}&0bz7;Rb{XMdZ)-z88 zb`-pdn@?DU=rD%Te>?^MGvzJx?lK5fH5Di)51;?)4xByqe+hJ6`17az5g)-YcK$1^ z+Qo^;FkE}$qj>47M=+w-WYgCdzdIjM!J44tb%Ig~SaYV(?)lvQUU zsmm!ibHYt{`iol~^Qf^1?0x3yIiU&&@WQ68)tHv>JDfdhBKalXl=ypSB!R@A{`4nY zdg-OV_%%!x1XZWZlk$T}OOT+qg7?RIZObcee?AILq=x_{{SK_B7!?%XqVg3 zzw@!^89jy~)v<5uAvD?_HG$Be2m%xVRYG%5HbMgSh#FZlfUZLBH<9snrDIKXdQH`t+|buGfQ@IQSHNzVanJ_`wLQPWcWe z(+3v7;FCdJPr`usQF!W$oAIBo?nG+NTAChKRy-D&AA=DIuF3HgRVnSOQ;| zjDoUs!>8D=-e#;cacD0|JXcacg#sF^*3ewQQhEE4u+y5GmHR!tX*ks#o zdaiu7PSs(LJ@b1WtNi}XJ+Ikg&$^o)H>G2L=bD__x@@~T*JpqAdFIu9+kek~?XMjp ze;Vr5eb2gWSzDK-!`5T-idG#fd5(>x*XFn1wmw_G?UU|%_QUhsv;M|<*shYLLuFN` zl5NAB9K=8E=NH<_Op*|jX4S992nIV`hv}m(#xaA>(unWVvV^ws+2}~dgi)YNa5EZ< zZiEpD(~U;)#*$l%KwiD;K`f$H)T;}3jiBjM^_5l>;>{(uW83bpur=>V9NBj|g?lcw zpe-2E?OLSduA(;Pi-BEFKtcH~g04S?#ZNOP*2Srh6X?=0s`s(TFU~<$vC3SHl$;gD zSJgLeEa_Ou2hEpe-f!^PhAWU>^gLFj%%?!7vzpx*+H;E05IBuY2!mwUklv`D3G|V+A3-SKiiy39Q$e3YOZhuL7ryA71T54!I z^QE6|N8z*dc~;ZpT7peE)F^mgKPs72X%yRA6$^;YI%=j0o>@X|R{%S-`w7S{TS5(S z3UcTRUZ4FuHSs9~zisSG9tOspjpWSb2K*AD$I~K^Wq!bmHhzjZo3F>t{N)_NDx*b9 z)1GObm5VTK#D(OEOtSTyP!w_Ot2r5oV*84ebA-=c;tu^Iy>+5juU^K~)>P>I5cLvt zo*#{i6^yow1mD9CKWzMjx@HBUj+^H7Jh%1Qe4gj_n!auFEURljmf!Q9=kbr#W$(!n zv`WE z>)VqS&vu*Eh~OBMRu=J|uQ6*z1WZJ6`EFyb4Ds*78oX#;D>FhlJRqJx#O;g- zD3JuI(~r8@(4b}{q${dRkevG-zF9K@69%13ZE8BcS^Ey=ZoY~^J%NY{Q<+<6e&sN- z?d-yI6jfx?hpAJB^biCh1Dx%ybl(9NuF+x;N;!eV&pVb{C*MR)0bWuW!EeWoKW@M& zrmRZ%`vzir_C{)3@up700?p{ycgq_^j|MX`Q@)6Tl59i=jiN>t%+C%F%{o2Vzi@lZXwU(e$J}hnF@Zmu8j-1A8#q>2TqM36V0jMiJSaUYM+B^}b48Mma%}cOi=j*Jm zmSxgl%T%#UIm<)^oyfI!H0d^IvWkXfaI?rWN&T#(pDWOx8!Fdr=cgMg8L(4TyN+7b zF(%L3i~dC4+;a^6IEHj^uR*U6Mr;lxoi(JL;7@?6peCpQm_R>8(Q*A3gKgyJ3Jc<^ zd#YB@q}kW>O-mutip3*SHGPFzcf>VNP0R5cOmLmgc2Hm*;Q-+>} zqx=5D0Os2CF9}@QuhI*t3~>3|+==KDc_KBsTz;0b&nGhINc&St>ts|dwY$A^lTgwj zU#OlHO)><0GU;j=h_4T6t1dD=X_ap;Ni+1C8fX7YySTmS?Ejbcr6qy&m(t>vcR3&q z?A4V!Fh3-w^btTAQwQ1NfR&`Tv>)my6cAcCE?<}eCXVcT0zoPUrww}%187c_X)c2C zBpWimBN#+5od01Hlrpr-Z5WqyBDU^YLx7-pkAm6Ru1Uh2T8qz-#*f#tb6?CD@`P!( zQ^-(E?*Eioevs7VBuwn{7(pcuJ9i6|e5g%z#o8V7k@3?`xplprP{CatHia=YO}Y*-J1RVC@mM+yUZM)o}#NA$Sc1RtF~;(cnZ zvG_h^Ho~Z;O4E~2ZS=d6I-69l`sPc(i3?9eLiALO=y5J?Ju%hrZ1(D>*|z}%9cOW{ z#z%mv1^4qezGM1x<>Y0IUQJ^3YZsi5^rZ2FuHLo)I}5%T)`f^5{ zCezgXJ+;=p*qpw?1o%uzywC8zN8|)d>iaLF)oP5KXTnaE^j&ReHO~l7&GW&lIRQI%B$KM8PeuRzRWMCU1^ufXq4`vAN0w`1M*B^2O(6v`u!mcJ06 zEq@6|4>^rdh_4vUU|Z%oe6s3M!~_j6ZB$h|J2z-8U1&frEZ^}G;v#xt+NcW|$vM-2 zK~Y&gqekDRpD7Z@44Gv#nE?M_e7)vfuHj2F_qs;*#`GgDVq3FJnS#<>B|VE+2SY-&A;wY56MwZF#nH0tb1OdkFfc_LR9R&SPCDWWqixpG&!))Nw-!BOmVX5Mccund zXEa*XBTY2OFPhPvU*PQpT%R`V99(|vL)6Nop>eT2&D@`^dXSne7vmG83M$qjnG=#R z4A@yqKd3jyImAC6^EUny5h1ZSi;bg=!@!O1d0T{b|0yz&Tni(Kvhz; zm0H+vetH|%;B7A{+Wbz#!5q0@Ajt0F_d%O^gW{Li1}6?%cA5z=almT}TNKeG0pKv1rwyOxs@|rYs3m=b=fM{eEt);1Q7kQOn zXbC20HRbQ#2UliiXLlLVx`$hr$;???xs>IPV*T{h@xI1MTi(emEB~Ot$LUWx&5(ZR z8gV51>W|%}Te+rjE&JrWN=N*1KGKRKS-1SQTEnOju4nK~6+1SD001mmNkl*rT~L20FQ3q&`1|j_-)xMn-B4U0G>?G$=P>7h3xpm6EI}(lX9b(RC1IdFC9UAp zwq!mXuQ+J)p!D|TlSEeFt=J*^)! z^bpjv;9E;3w0n#bjG?+1BguBO8;ULCN3a%9sGX*LQSgjo=5vey0*z|=;yXCJ0)g&W0)o%%Zf7UA+oqa;( zKa&Ra-~44o4aosRr{4lNf1{D9oYOC5)7EZH>ycU2U%e(^6*F%gc@;`+mdhW=yQWDX zDzH>PokYFg@S}wMs4zU%t~gGg6-cG_Jh!0|4NfGhCLKzSHdw?-z(`y)^&Y(X&BNG1 z>wumQ(c_s8MIe-)Q~p8itfGyXFT3nAE#oP0oAyN}Ue9(#qqJ9ng+Rs;>`vcQx4=|` z#2ApxHStF@Y4P~%6)%}Qe$)unzVd2+O=l}OORvhRj6hatU;dg)4c7(8mY3pDQ*CPh z^*G?DDl~!67^qMC13~3cza1dgnUXU`nq>GYIL7qZ*`7*w1?KyXmw?y)boT9w4}HMj ztbLDu$weH0y~SW(5LA6O?OW1bJviUi{Q33QUz^=eM1{R}a42$Hbve*>9I(IUwrlYa zZfRd(w=bbh@0s&PA-6w~#vG0JJ?m;copn?kzxVf%1s3;WEnX;A++B+lEACbtin}|- z-HJQK-Q9}2ySuyphR^po&z|{b&)H-$$<4jV?EAi=KDFxhH2W|$7r*sw=(x0N*TObH zedSeLH(eATMN?c(WO7-+$-JSIC) zN_`6>I97-1f9heX*qp+Xi{mbvv9pIXe;`cgYBu4v#5o;N@}NN9J<~S|yyK=gDYH74 zBcM!7pWOm*CPK0v`8V?m&wy6Nd`%*S{?6=rN;}H`1MD&f;^G}l@a;-JbdL5q$yIHg zx?Rl#5qNNRp54=ez~2dV_F(+?*Sb56H;q5K8QqD+?^@Jv|J8|#XD8RM3QhMS5}|g6Z*#}I8htr3CpNl=7r9wk zXRfgB?xr$#`JBshkW$GcywcHis^;cKv+RZ8*|m7p9)hKmbm=ShkyS{r3zvr&XkQAvoE4${r*FSz`qlK1KWozUt~@0f zdk3qWToz;sc%5G14X{&R#gm5wRH&;GphQkUfqmn#eN^i?;9)pl(6R8P!@ZB5)xY)C z>YXj;qr^co`N3Y!C9123p0!W7eN6kE@?83PQT?p<%z9-%?|#IAGRr(MN%>+y&GLLJ zlD|_vCz9!`r+N;hhX0o*AVf`u4!XI7M-F49YrVJ?a<956$L&-xi?<>AX?0d7Doj6m zZT{Dz@_LA@2omL+NZ9H`-P?ND?Xy5*4SNPOlyEY!M_65dEUa5;*~1UR?+fukNlj_a zhgN$eWwE4k8ehG8XX61C5L@RgP2%y=lvcILRsZ;ii?j85#VnXNXuknKxXvc#$R~BQ z@^?Q!uo*VLBpH{@a+a0v-CB6gh^x*0F)Hi~KENh`B@#uQSLN;jOIGPK*`KO(@AeHMHwS6TS%~+|{OAVt>#H1le7d#Crd3T8 z%thBrm!blXh+JX2DN97^%dV%a5gB6D(L-5|?k}>@=GMurw6%ITZw(0T8v;S&U>M&N z7gRi~$Tzohv6kz;In~<{$!R#F?x7&WY3#vO$t)HG|BK6wKs`f}YdA5y&gy~)f&I<( zF&AxaMLawyZ1PxjY=5ofW(^SdMqiUCMJthBCt{{pWed>7F!4AzMIFdRCM+#b{Aa3s zLs;Yt$9P1c+*Z7p2L}q#u^K(`T7C$M`!?3D_+AbIy)MSYZCRjOc~dZ7Ms? z+X|S7um3H)KP%ltsTrGm1o@hpnmXSu7>8TQIjDMV_{{F@I&oSz0771fEZAY+tTwmn zV781peDP9(<%qPU(8Q7(elOR!U2axbaR;=f0w}EuV!^4A-!pYj(>)m+%;%!su z$_v1>L4Q~G%0XNBfmjgPx(v(xP2(?AxcT^2kw%L|qgG5rEMIwCW~o`4;t3Z&&fnQA z+?8;~X;jcqQ+HX`uLS*zBy`%zK^Q3C2!sKCEXdQ1Rs(Z z<}wPG-yiNFGl6ojx;^)(DMP0XF6&hrjC?I7@AAnJYtBb0+xM=kap0;$#uFso%9{Ps z{3Sr?P)bIE&M|(LBmrgqx{3=a_)-`YiCPm0)vkF-^KUG)Ty9ch|E~W)_$lSWE!Mi( zYoEZX1+rCLLW+F3BT3lpSo09&zm(mEXZBLWe}BymCjg*Hy$%;dr_Zui+@gepDeG#+ zpTKx3!(fyP*2Rdp4@73+T|Ha~yzmzB;i2cf?PvG_V{NNG8f(kHxeJ^+E4P}HA9pgA zljVI?_@snQcL>`UlzbTk3n^=EEm`tTU-*@C_L2~VIhnF6THydp z1q1`mS(c{K>C(aHg!9Qhp4e83i5!he5s(U zl5AMJ54mI=YjBuieWca7D4xSPO-_|%bR`PUmXe{5aax*E(iOAUR?H8H2}*(I5CyVO zyJ12F?xGIDE*N=7*(0|KDJ_R)#Xq0DmJ5{E^U2RK!TALRTNnLAL5^XK$*&!1)v1;X zacf!(YsxuWbyI|#>%a{AS11uB$y`+Ox4Ssww1Z#egPx))!Y&^l{1k;_FB}c~1>L#! zG=fhxMIR~8G!em%kV+FNswM>j+M<$fjcO;1j>BoT?j@o~E}2zOm&}xoLLx~-307Y^7HbgfL(hd7T|RB^ zg-b=Anbj=ynj=Q_!~1opWUfvQU>eY6;{AcJ0)+ayfc`d#-HMQjiOG7=v_KC?C=Ub` z!ytV5FBz-@)egucA=(`gY{=ifyrJHMr;0pj^$m+XA?*wWc%!Liy={!&&wdsFIXPRut;i!+M-IC8v=*j-cBgdUnZ9PTWtbg(~{eDHN1rzO2XcUOQ`wUNNQ zE|D98dO3%-Bxey56WgD&T_*c&7!LYTMDNszyVBVXo)pT%`auo4(#Yd0g-!(qcq?$* zDxzDofO0cBGC_B7g16Gb;Y0qXYN*8J3HU5fo8B~&N702nU zGa~nU>3qgm1dxi=zh1*gONLJB@)b4^&Io=mJ(y`{dffLOs*U)O-|ArsVSBRu?v`xJ zX150Pypy-%{E!a%X_TmMGU=ZBa)fIi3J-CC^k%2bzr7o8F#jOsUBc!5bc)Wgw=Lpc z!$M@`krwXrJGx;%jQl&#Uz*itau}UbDiJp6a<6trVjUnNk?#O}~a!OIZBSE-5!9j{GuuzWH3)J|M=I z;n)E6^z^Xs^_G2@|A%nbu_I9Zo}*Q*Hp7Nz*n>cEE+PiXq^yl^=cz`Zaa7j*q0f-( z)B5~MfrYLpv;Qerr7%t0j@wL}Ww(TeO+9JiP4#!2W=Qka{1Dz7)yJmX?A17m-E%Q{CCOGYomwKrE=CZm4b>F$;YE(IbRqK;(o$^Mev zXlm`noNOecar&#<)~&f%)|>GF$JPSY)Sj)`WVzM^90~jVC95zW3niSc?id>F)=vTa zsi#~+K{HME#qE&+m=M}1a-v7_6z{{{U> zJKeMLgZ`^VrzLOb%bjUPh|LR?`pmTw7}e{SuPNuSSpp~a)^@|+qD8r*}eLSV|g34F3H`K)ugR+O=HQ{sMvzn@qK&J(Txxr z8WAV0f8cmMUyWc5C%60I>p`WzbmNAd5`~m%W@qNY8L>!5d4fbA7Js@Tlm7EZYk?^Y zS(8&VBYsJqG_vRSt{L(#1Y_BBfwo2mf?w$hBRVY(FzpK}ASQ#{OkCSDxE~3vbng9{>TW@lxrT|{FoKP=6 z5^A{|Cy#`;0E0x6kfbBFZzet^mDJvFZ`%vn2-^44t7Ojc3@_43b09u(oS2rRrR_?D zEzfI4Er^`|`67YL<}J@7`rGT5uA02q^xvh|1pU1ZV$LEUAt4SmlC9i_12PDfPA2_) zAh)jJLY{W$Q@4O*x<){kboENE7X_X9(!{F9*Enx=kJcNM- zrC{VcGI5apQ`wIc?oS)Py6`yvx*i4m+`<#~MRxYMy>Uc31blvsP*jSV9>h_1BR4!x zsLl47ivIM@koBw`75H-@RkmFpe+;_Es>&B*k=+MojOfIyU|B0@uvE2Kg|2blZ`=6% z_+*uUmM&`FRH99+q!_h;J4Zzl;7l;cKPMLS4UkADF$jL4U1FaYuo)$9p7-i=u7=B_ zQd1)BWTksHYb4u>atNhV3=M|GCm({8a2H9UzPa)v+DOWma((^u?{Es0UZ(eBj(E zSBuzstr&&Kq9Av2PY!*>`PyS=WzhlOf8AB#uZvkRy{Rgm(j~4y=cgZc$U3EqfqO{i z`oCCDzvXR?|6=%TUOCwX4ev+?8I^z~=B*Z^?o+yiXgxTs-Ehp$eese)JIuj{{zg?Q zxOe+%o#g8;o-IP0)r_j9iqYy)Y$zTzlVB(20itmkWUVX7LYa3>G{(C<< zkaEAsh)5Jb0a}uR`8*6TzV1y1IB0~TiCWL4bXYEH3bYMY%YO21ayqPJuKp|fVI{{vHI9ueE;v%V*m&tvLUh&Yo@_YDXo&B> zO87}uBnu)_DVFPCWR%aLyA?Su{U?xt=P6i z2)utjsjejm>aSH4DAiY^)fChHU`91m+gE8lCvQwOjgD^kZB9=vI^?rHJn1h%_-;JtBl>-i^m>!L8B;?AcX)464SUj z6^b&tKd?sl?zEFW?&8m7R4}$xcbNTTzv79<&Ly3X@;#2fty*K(g+Sbf3Escrj46j1on#KgTM;G!{^ty4`X?pOfU6XGx16Jouu)$ z7n`3KuDk+_9PmYoR`Vd4#hWi>a4&ERw~WvtMyUIwl`4tcGMll?6c3KPhLk3hheWRU z-ufJqZ*>Eor&{s)CWn;gPP=wsVv4qm^SDsy>jet%3g^YlIZy0w5`CeKrHN%}TJK^D z=>IVwI}45v$sjz*OBweiuOuEVQr^6PWb& zw;C)$sswZ`gP#(!kYq{qw^sDfZuP8lR&zGrU(&N(B!9}CIWkE@M~&Cby)_A=q<@K> zJ?!dBOnMYwKay6`xCO6Cc!SC!1>*SpkM?vHIY?_HBI?{4aHwvRqx!U<8hV2 zv8${@BkSCwJ2(r8GKlt`7FUJdZjX36?X{?p4Z`p3v(pBehBaee z_4Ijr3X8>l=)17ek|h=yB;=_RWs5eOdsLLzw7V&#b`Ty%q*BG&Ew-Jy($~?Uo#X0! zGI^t4JEu~Amp#Q(H#4Qag2jbB!<*dHbu0v=2!l;{pV@Ay_B`VIR7C~18OJ`6D! ztMBAkRk{3@QH&wF-Rs*Ko3HZudS8zt7?JQFT6~Y{S&UJGXxxlC(Y`nZShP1=Ht28F zqefqbDg|rNl!@|9@!~sVTvO5sR((A1ysI|KP^daWN1i?BfwaRD&j)XQ@ zR9X5ZZFsgCPXF|KW}~k&pVd;L7$sBlTx~U*)8MPq)}b)|blddkT)wdQSQua!Bd|p- zKw^>l@>+a~{|JL)+xJIs~dL!IV9h=~BYI7eLfr$f6!-vV8c0 zf^B2Pi~odmk&!lzVPHvqegq~~_bK9ozgL6~6*#Jjn+ZBOtTswmK?Cpa6)28en3U8S z{mAu7GXtk}xtW*%J$JL5GwUiTVM;|c;?RqMQZ~bU0ju0{sQry9Ubz$vrx;#d=sURp zl;CH;ol=jc1T1{6;Bqt}%XzGi6YiHiI%HJMOkO)SpWH;3g2;$8GJ{FyQXv&?cF8q& z{mU6}qm(S}4f2)p7Q)p`Pox_OStUb7i9JKvKKfy3h=FsIHs`p2=v*O>={?(fnE1Km zb4smE!S_EbB3{r;fja5H99_2Wu70g(x&cAE0qrHnG05& z<=~^ddCblbdQq(P#h-R37@l>j74RAll|&b)2bwkSGP#A*L8mE{=`{w)q{mw!-VLTa z2yc&0_=SVj&xDk;e*v=$#!_qAn&2mb@)Yz zMnFLJ8{Q^Ad2}pvX=&s(S|ArA6D=@n@7!8AyO;*)oGe$;Q;ak>XMPw~5a`&xU)P5u zFWGv>P>IT=Ienv@;)!)JBcwioLf$k({s344m7DLPYaXT~1q6(R{}kmt2{9xFaG6X4 z;~(Di0bDy>yT zw#fIJX^Li!ex5zo?d=*@1(iUxe-IJ0SK3?X6LHDzd9`gkVRnl+`(FbcFbkEyC(t+l zDyOPQ+=46?QYekbu&)ZP4tMb;s~zJf6X-S#1=hYddYH(Xrp5dtvOzYbuVBI;_7SJF zRwJn)O=_k3_G-AQWBAlW+d5Vn?OBf8^767%*W5pl@pfN`bebW^XtCZpvP1@n0Vedp z=kNY0dLC5j_EWbVC)c$4^CVqsV=BFA(dmSgIkjGTN-9Y#h&Yyt9y2Ix#o31hp3ZE&BzG3a@3b!E&cxdXpTTddZMCv>NxV7wXhip z1kGhJD{cxbKXygt2?k-Or6INg;?uDP#JNdaU{L=m-RtyGi?9?S=N2`C(mw+O=+h^$ zUNuE|OT?4Ua_O)r!i?QF*(X8Kb&6lt)DJ!PWs;Cnar@~~h-@{~zx@4{a)>?Gy+nYAV#XX8l1ST|DX zI)V`HYqN~F`1pQ*(mA_7-TyP9D3B(dzm1KJ?ddXSqxFW+crV(NNEDFnPo2i?#I|&s zw9pumNFkjfuBq9QY-@|XAe_{@f$CrpSOHlQ@@{6Nf!^knDZPDtk~TJ#%QpR=V0QuV z4&B7WgzZIkcvuTxpDG72AD{N^@qz(@*;Ib`NA5tAg<>hR5dc?Vx%E)o?A`dk*`X=X z;qZY3ly5qf&xPHwRAcy7{hiQtp9H$xpg+R%=*V{V53kW$TF(h%%T~E?5Q^>Vvy02s zNblvc-54JF@pMt_RgAZ#8!w7JfzaY#B^o>iU2hii+3%4q)l2TTyqQr^r}y6lBhUlm zxM%=F4Ca9gx^O4T2((R5y#Z;^zoPZcbBc%B!%IF~@~5COWs zL-3_gMoBr_HEXrlyK4<5hbqGmQZ=>aC*^&;$NZtJOkve>5xAh!V4VeUP&!q0>sMWi z(Q9nB`jYJo&OXtUw>|zHb~X#_z)0n^%P&!_>;fB(rSpa-A-7rR{;hC698*4u-r_k_>3Q@Cp;DcEYwN;yo>$Ee_Pz2Z&U$vAK;jv z)z;>gyHz)_60tB`y;Z$|6@Y%Vi*dB;Z%yKC2p~6@o^Ll^?Ep|ES{$Qok9hJ!Idk~T z-FY|aQ?YQx5J2<@BXGRlpLpH!c)X4VtaB0!kmM@(0bE2fm&4xy=z7XN3UqrvQ(OQm z6_+QKc-XLodoWX+XtCO|uw8AC{Oa2%_+N}!$A>DzBlAz{wC%Eo75Po73tJ{h-awd0 zr(u-MZPmE*!N5ikTsnk)`Yr6t<7P&Mf`1UmLoDq0u~cOk`V1saT>^xf>s8t&eY;*_h0I~u$WwJ6+g7IH*zq#6=GDc>6b9o44{A7nn$uEP$hWxcp|WnbE^KV4;1GEGX3VZQ)+_}OOE@*J z_H!Z;`amS^)|J}RFeOBPE|UQ8)c zzApHtDGW|dyY#l34-&UrYtu^ixK$&|H$5=@VPE(U{IP_GM*p9A0NN~>2+CKLU{gm& zwh-oDqX2#!@s**IC}FEatvXCKCpF~q??1UCwgBuj?Vzj`%JLL zgH!>Nlzc8Zw%s2mLFRi%OGX4sA;Ish+xk}!i^H5MfQE>@Vag=P8z|B~kZ9&{1U>{< zhw6W{>RnS22k0|lf$>o1O_iH5FvmsJ;AL(@Ux=8l551-{WReq193n0x6`~ZMl zFzCh#RkLp$-2$+HjJ}VlyI$>$v6o4Py>yEHz7Xa%Mol2-nWPv#>ITk4#?p)fC-}(S zo~~%T=5NAQ`ZZ(2&n-Uq0g#^9N-joZC_7k2CFm{xr2gP%G+k%mi4W6oa|es`b19sG zXKhbq+s~MUo4S_JZG)XgTLc+>DQ{1bQG56TIZwuW`lYOAI7mQ%@{#TJJd%PY{; z$`p(C6RqUZO1bS$0fDqmLZaK#^xDAk|3WXA7|n66u&(cH^Si7pdvrolT!g=kKm!(| z4PluAHkW}KNhZvko>CG(FzR&R2z(X?%Lr<#hH=2+P|*r$LbFr#k)ika+S<_V(rn^R z#!@^`sbd z*Th0oD4qWW-?A8i`2e&N5doyW8?+u@esXZcRU$=&J^n0?sBb1G46Lu80UHSeOxbrg zq%w)^Z+}nJ8w8@Os`%moAkROG{v#_Ap=;te)k9>cpd-sh6DLp4HZP*3QCGR(5px8j zh0r@~X@`iBD5bHG|7MaexIqZqiI@#nj66%=k2tG+vyBzMSa-rYdd0E3+`h~PLNy3E zYq^S`g5n2<8I1nSkxJ`NHQaE%@i1N;w^Z?plI7HH!b4J~MoNC-h%=!?n$m2A(0BIJ}-8J(c(8^Y2~@l%<) zG+=gvKv4Oz^Lll=BRRE)Amk4E=r6Jyo}45gCv7QgYs;qYRy8T;Bc2keHay7d!asN( zg#(U%og)8R(g{G#fI;9a-nl%?1g>U5KjA_9nfSkXL&q*{&5;mD?I8#m_oizO78#r7 z050I}=7R=clj{^vkY(}&7xOQDiZ18BqpO}zXb&bA$es{md&rE*=6~2?Uar@}yeN~< zk++6^yF?I4Phu2eYIb12cHzP5TE{g04cImEZ-KZbNa#SQ%22qYT{;&g=Z>oww5PIM z$64^;72^*_31so%IU`$ipD(7fiYmLmX%bSG^)2TV7aphDr83eFvK0z|X$tyvyUkG`iPG}h3KXlo2{Ny#|K7p5KdZa0Z$Ho6xN zoKd#QU;hPlET~!pl3yJmBy|o0gMTv!HY+fr{P`bRrlnJySt-&H2 C=7_xj literal 0 HcmV?d00001 diff --git a/docs/source/_static/html/tutorials/read_demo.html b/docs/source/_static/html/tutorials/read_demo.html new file mode 100644 index 00000000..46c9dafb --- /dev/null +++ b/docs/source/_static/html/tutorials/read_demo.html @@ -0,0 +1,356 @@ + +Reading NWB Data in MATLAB

Reading NWB Data in MATLAB

Introduction

Download the data

First, let's download an NWB data file from the DANDI neurophysiology data archive.
A NWB file represents a single session of an experiment. It contains all the data of that session and the metadata required to understand the data.
We will use data from one session of an experiment by Chandravadia et al. (2020), where the authors recorded single neuron electrophysiological activity from the medial temporal lobes of human subjects while they performed a visual recognition memory task.
  1. Go to the DANDI page for this dataset: https://dandiarchive.org/dandiset/000004/draft
  2. Toward the top middle of the page, click the "Files" button.
demo_dandi_view_files_in_dataset.png
3. Click on the folder "sub-P11MHM" (click the folder name, not the checkbox).
demo_dandi_select_folder.png
4. Then click on the download symbol to the right of the filename "sub-P11HMH_ses-20061101_ecephys+image.nwb" to download the data file (69 MB) to your computer.
demo_dandi_download_data.png

Installing matnwb

Use the code below to install MatNWB from source using git. Ensure git is on your path before running this line.
!git clone https://github.com/NeurodataWithoutBorders/matnwb.git
Cloning into 'matnwb'... +Updating files: 95% (520/542) +Updating files: 96% (521/542) +Updating files: 97% (526/542) +Updating files: 98% (532/542) +Updating files: 99% (537/542) +Updating files: 100% (542/542) +Updating files: 100% (542/542), done.
MatNWB works by automatically creating API classes based on the schema. For most NWB files, the classes are generated automatically by calling nwbRead farther down. This particular NWB file was created before this feature was supported, so we must ensure that these classes for the correct schema versions are properly generated before attempting to read from the file.
% add the path to matnwb and generate the core classes
addpath('matnwb');
 
% Reminder: YOU DO NOT NORMALLY NEED TO CALL THIS FUNCTION. Only attempt this method if you
% encounter read errors.
generateCore(util.getSchemaVersion('sub-P11HMH_ses-20061101_ecephys+image.nwb'));

Read the NWB file

You can read any NWB file using nwbRead. You will find that the print out for this shows a summary of the data within.
% ignorecache informs the `nwbRead` call to not generate files by default. Since we have already
% done this, we can skip this automated step when reading. If you are reading the file before
% generating, you can omit this argument flag.
nwb = nwbRead('sub-P11HMH_ses-20061101_ecephys+image.nwb', 'ignorecache')
nwb =
NwbFile with properties: + + nwb_version: '2.1.0' + file_create_date: [1×1 types.untyped.DataStub] + general_devices: [1×1 types.untyped.Set] + identifier: 'H11_9' + session_description: 'New/Old recognition task for ID: 9. ' + session_start_time: 2006-11-01 + timestamps_reference_time: 2006-11-01 + acquisition: [2×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: 'learning: 80, recognition: 81' + general_experiment_description: 'The data contained within this file describes a new/old recogntion task performed in patients with intractable epilepsy implanted with depth electrodes and Behnke-Fried microwires in the human Medical Temporal Lobe (MTL).' + general_experimenter: '' + general_extracellular_ephys: [9×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [1×1 types.core.DynamicTable] + general_institution: 'Hunigton Memorial Hospital' + general_intracellular_ephys: [0×1 types.untyped.Set] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_sweep_table: [] + general_keywords: [1×1 types.untyped.DataStub] + general_lab: 'Rutishauser' + general_notes: '' + general_optogenetics: [0×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: [1×1 types.untyped.DataStub] + general_session_id: '' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [1×1 types.core.Subject] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [1×1 types.core.TimeIntervals] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [1×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [1×1 types.core.Units] +
You can also use util.nwbTree to actively explore the NWB file.
util.nwbTree(nwb);

Stimulus

Now lets take a look at the visual stimuli presented to the subject. They will be in nwb.stimulus_presentation
nwb.stimulus_presentation
ans =
Set with properties: + + StimulusPresentation: [types.core.OpticalSeries] +
This results shows us that nwb.stimulus_presentation is a Set object that contains a single data object called StimulusPresentation, which is an OpticalSeries neurodata type. Use the get method to return this OpticalSeries. Set objects store a collection of other NWB objects.
nwb.stimulus_presentation.get('StimulusPresentation')
ans =
OpticalSeries with properties: + + distance: 0.7000 + field_of_view: [1×1 types.untyped.DataStub] + orientation: 'lower left' + dimension: [1×1 types.untyped.DataStub] + external_file: '' + external_file_starting_frame: [] + format: 'raw' + starting_time_unit: 'seconds' + timestamps_interval: 1 + timestamps_unit: 'seconds' + data: [1×1 types.untyped.DataStub] + comments: 'no comments' + control: [] + control_description: '' + data_conversion: 1 + data_resolution: -1 + data_unit: 'meters' + description: 'no description' + starting_time: [] + starting_time_rate: [] + timestamps: [1×1 types.untyped.DataStub] +
OpticalSeries is a neurodata type that stores information about visual stimuli presented to subjects. This print out shows all of the attributes in the OpticalSeries object named StimulusPresentation. The images are stored in StimulusPresentation.data
StimulusImageData = nwb.stimulus_presentation.get('StimulusPresentation').data
StimulusImageData =
DataStub with properties: + + filename: 'sub-P11HMH_ses-20061101_ecephys+image.nwb' + path: '/stimulus/presentation/StimulusPresentation/data' + dims: [3 300 400 200] + ndims: 4 + dataType: 'uint8' +
When calling a data object directly, the data is not read but instead a DataStub is returned. This is because data is read "lazily" in MatNWB. Instead of reading the entire dataset into memory, this provides a "window" into the data stored on disk that allows you to read only a section of the data. In this case, the last dimension indexes over images. You can index into any DataStub as you would any MATLAB matrix.
% get the image and display it
% the dimension order is provided as follows:
% [rgb, y, x, image index]
img = StimulusImageData(1:3, 1:300, 1:400, 32);
A bit of manipulation allows us to display the image using MATLAB's imshow.
img = permute(img,[3, 2, 1]); % fix orientation
img = flip(img, 3); % reverse color order
F = figure();
imshow(img, 'InitialMagnification', 'fit');
daspect([3, 5, 5]);
To read an entire dataset, use the DataStub.load method without any input arguments. We will use this approach to read all of the image display timestamps into memory.
stimulus_times = nwb.stimulus_presentation.get('StimulusPresentation').timestamps.load();

Quick PSTH and raster

Here, I will pull out spike times of a particular unit, align them to the image display times, and finally display the results.
First, let us show the first row of the NWB Units table representing the first unit.
nwb.units.getRow(1)
ans = 1×8 table
 origClusterIDwaveform_mean_encodingwaveform_mean_recognitionIsolationDistSNRwaveform_mean_sampling_ratespike_timeselectrodes
11102256×1 double256×1 double11.29171.440798400373×1 double0
Let us specify some parameters for creating a cell array of spike times aligned to each stimulus time.
%% Align spikes by stimulus presentations
 
unit_ind =8;
before =1;
after =3;
getRow provides a convenient method for reading this data out.
unit_spikes = nwb.units.getRow(unit_ind, 'columns', {'spike_times'}).spike_times{1}
unit_spikes = 2116×1
103 ×
5.9338 + 5.9343 + 5.9346 + 5.9358 + 5.9364 + 5.9375 + 6.0772 + 6.0776 + 6.0797 + 6.0798 +
Spike times from this unit are aligned to each stimulus time and compiled in a cell array
results = cell(1, length(stimulus_times));
for itime = 1:length(stimulus_times)
stimulus_time = stimulus_times(itime);
spikes = unit_spikes - stimulus_time;
spikes = spikes(spikes > -before);
spikes = spikes(spikes < after);
results{itime} = spikes;
end

Plot results

Finally, here is a (slightly sloppy) peri-stimulus time histogram
figure();
hold on
for i = 1:length(results)
spikes = results{i};
yy = ones(length(spikes)) * i;
 
plot(spikes, yy, 'k.');
end
hold off
ylabel('trial');
xlabel('time (s)');
axis('tight')
figure();
all_spikes = cat(1, results{:});
histogram(all_spikes, 30);
ylabel('count')
xlabel('time (s)');
axis('tight')

Conclusion

This is an example of how to get started with understanding and analyzing public NWB datasets. This particular dataset was published with an extensive open analysis conducted in both MATLAB and Python, which you can find here. For more datasets, or to publish your own NWB data for free, check out the DANDI archive here. Also, make sure to check out the DANDI breakout session later in this event.
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/remote_read.html b/docs/source/_static/html/tutorials/remote_read.html new file mode 100644 index 00000000..9746120e --- /dev/null +++ b/docs/source/_static/html/tutorials/remote_read.html @@ -0,0 +1,58 @@ + +Remote read of NWB files

Remote read of NWB files

It is possible to read an NWB file (or any HDF5 file) in MATLAB directly from several different kinds of remote locations, including AWS, Azure Blob Storage and HDFS. This tutorial will walk you through specifically loading a MATLAB file from AWS S3, which is the storage used by the DANDI archive. See MATLAB documentation for more general information.
To read an NWB file file from an s3 store, first you need to figure out the s3 path of that resource. The easiest way to do this is to use the DANDI web client.
  • (skip if on DANDI Hub) Make sure you do not have a file ~/.aws/credentials. If you do, rename it to something else. On Windows this file would be somewhere like C:/Users/username/.aws/credentials.
  • Find and select a dandiset you want on the DANDI Archive, then click
  • Navigate to the NWB file of interest and click
  • Find the second entry of "contentURL"
  • In your MATLAB session, take the end of that url (the blob id) and add it to this expression: s3 = 's3://dandiarchive/blobs/<blob_id>'. In this case, you would have:
s3 = 's3://dandiarchive/blobs/7ee/415/7ee41580-9b0b-44ca-8675-6959ddd8dc33';
  • Read from that s3 path directly with:
nwbfile = nwbRead(s3);
That's it! MATLAB will automatically detect that this is an S3 path instead of a local filepath and will set up a remote read interface for that NWB file. This appoach works on any computer with a fairly recent version of MATLAB and an internet connection. It works particularly well on the DANDI Hub, which has a very fast connection to the DANDI S3 store and which provides a MATLAB environment for free provided you have a license.

Note: MATLAB vs. Python remote read

Python also allows you to remotely read a file, and has several advantages over MATLAB. Reading in Python is faster. On DANDI Hub, for MATLAB, reading the file takes about 51 seconds, while the analogous operation takes less than a second in Python. Python also allows you to create a local cache so you are not repeatedly requesting the same data, which can further speed up data access. Overall, we recommend remote reading using Python instead of MATLAB.
+
+ +
\ No newline at end of file diff --git a/docs/source/_static/html/tutorials/scratch.html b/docs/source/_static/html/tutorials/scratch.html new file mode 100644 index 00000000..fe467263 --- /dev/null +++ b/docs/source/_static/html/tutorials/scratch.html @@ -0,0 +1,164 @@ + +Scratch Data

Scratch Data

This tutorial will focus on the basics of working with a NWBFile for storing non-standardizable data. For example, you may want to store results from one-off analyses of some temporary utility. NWB provides in-file scratch space as a dedicated location where miscellaneous non-standard data may be written.

Setup

Let us first set up an environment with some "acquired data".
ContextFile = NwbFile(...
'session_description', 'demonstrate NWBFile scratch', ... % required
'identifier', 'SCRATCH-0', ... % required
'session_start_time', datetime(2019, 4, 3, 11, 0, 0, 'TimeZone', 'local'), ... % required
'file_create_date', datetime(2019, 4, 15, 12, 0, 0, 'TimeZone', 'local'), ... % optional
'general_experimenter', 'Niu, Lawrence', ...
'general_institution', 'NWB' ...
);
% simulate some data
timestamps = 0:100:1024;
data = sin(0.333 .* timestamps) ...
+ cos(0.1 .* timestamps) ...
+ randn(1, length(timestamps));
RawTs = types.core.TimeSeries(...
'data', data, ...
'data_unit', 'm', ...
'starting_time', 0., ...
'starting_time_rate', 100, ...
'description', 'simulated acquired data' ...
);
ContextFile.acquisition.set('raw_timeseries', RawTs);
 
% "analyze" the simulated data
% we provide a re-implementation of scipy.signal.correlate(..., mode='same')
% Ideally, you should use MATLAB-native code though using its equivalent function (xcorr) requires
% the Signal Processing Toolbox
correlatedData = sameCorr(RawTs.data, ones(128, 1)) ./ 128;
% If you are unsure of how HDF5 paths map to MatNWB property structures, we suggest using HDFView to
% verify. In most cases, MatNWB properties map directly to HDF5 paths.
FilteredTs = types.core.TimeSeries( ...
'data', correlatedData, ...
'data_unit', 'm', ...
'starting_time', 0, ...
'starting_time_rate', 100, ...
'description', 'cross-correlated data' ...
)
FilteredTs =
TimeSeries with properties: + + starting_time_unit: 'seconds' + timestamps_interval: 1 + timestamps_unit: 'seconds' + data: [0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461] + comments: 'no comments' + control: [] + control_description: '' + data_continuity: '' + data_conversion: 1 + data_offset: 0 + data_resolution: -1 + data_unit: 'm' + description: 'cross-correlated data' + starting_time: 0 + starting_time_rate: 100 + timestamps: [] +
ProcModule = types.core.ProcessingModule( ...
'description', 'a module to store filtering results', ...
'filtered_timeseries', FilteredTs ...
);
ContextFile.processing.set('core', ProcModule);
nwbExport(ContextFile, 'context_file.nwb');

Warning Regarding the Usage of Scratch Space

Scratch data written into the scratch space should not be intended for reuse or sharing. Standard NWB types, along with any extensions, should always be used for any data intended to be shared. Published data should not include scratch data and any reuse should not require scratch data for data processing.

Writing Data to Scratch Space

Let us first copy what we need from the processed data file.
ScratchFile = NwbFile('identifier', 'SCRATCH-1');
ContextFile = nwbRead('./context_file.nwb', 'ignorecache');
% again, copy the required metadata from the processed file.
ScratchFile.session_description = ContextFile.session_description;
ScratchFile.session_start_time = ContextFile.session_start_time;
We can now do an analysis lacking specification but that we still wish to store results for.
% ProcessingModule stores its timeseries inside of the "nwbdatainterface" property which is a Set of
% NWBDataInterface objects. This is not directly mapped to the NWB file but is used to distinguish
% it and DynamicTable objects which it stores under the "dynamictable" property.
FilteredTs = ContextFile.processing.get('core').nwbdatainterface.get('filtered_timeseries');
% note: MatNWB does not currently support complex numbers. If you wish to store the data, consider
% storing each number as a struct which will write the data to HDF5 using compound types.
dataFft = real(fft(FilteredTs.data.load()));
ScratchData = types.core.ScratchData( ...
'data', dataFft, ...
'notes', 'discrete Fourier transform from filtered data' ...
)
ScratchData =
ScratchData with properties: + + notes: 'discrete Fourier transform from filtered data' + data: [11×1 double] +
ScratchFile.scratch.set('dft_filtered', ScratchData);
nwbExport(ScratchFile, 'scratch_analysis.nwb');
The scratch_analysis.nwb file will now have scratch data stored in it:
scratch_filtered.png
function C = sameCorr(A, B)
% SAMECORR scipy.signals.correlate(..., mode="same") equivalent
for iDim = 1:ndims(B)
B = flip(B, iDim);
end
C = conv(A, conj(B), 'same');
end
+
+ +
\ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..b14ade56 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,69 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import os +import sys + +sys.path.append('sphinx_extensions') +from docstring_processors import process_matlab_docstring + +def setup(app): + app.connect("autodoc-process-docstring", process_matlab_docstring) + +project = 'MatNWB' +copyright = '2024, Neurodata Without Borders' # Todo: compute year +author = 'Neurodata Without Borders' + +release = '2.7.0' # Todo: read from Contents.m + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.mathjax", # or other extensions you may need + 'sphinxcontrib.matlab', # generate docs for matlab functions + 'sphinx.ext.autodoc', # autogenerate docs + 'sphinx.ext.napoleon', # for parsing e.g google style parameter docstring + 'sphinx.ext.viewcode', + 'sphinx_copybutton', +] + +# -- Options that are MATLAB specific ---------------------------------------- + +highlight_language = 'matlab' + +primary_domain = "mat" + +# Get the absolute path of the script's directory +script_dir = os.path.dirname(os.path.abspath(__file__)) + +# Compute the absolute path two levels up from the script's directory +matlab_src_dir = os.path.abspath(os.path.join(script_dir, '..', '..')) + +matlab_class_signature = True +matlab_auto_link = "all" +matlab_show_property_default_value = True + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "sphinx_rtd_theme" + +html_static_path = ['_static'] +html_logo = os.path.join(matlab_src_dir, 'logo', 'logo_matnwb_small.png') +html_theme_options = { + # "style_nav_header_background": "#AFD2E8" + "style_nav_header_background": "#000000" + } + # 'navigation_depth': 1, # Adjust the depth as needed + +templates_path = ['_templates'] +exclude_patterns = [] +html_css_files = [ + 'css/custom.css', +] diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..327d56ad --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,28 @@ +MatNWB documentation +==================== + +Add your content using ``reStructuredText`` syntax. See the +`reStructuredText `_ +documentation for details. + +.. toctree:: + :maxdepth: 2 + :caption: Getting Started + + pages/install_users + pages/tutorials/index + pages/overview_citing + +.. toctree:: + :maxdepth: 2 + :caption: MatNWB Documentation + + pages/functions/index + pages/neurodata_types/core/index + +.. toctree:: + :maxdepth: 2 + :caption: For Developers + + pages/developers + pages/developer/documentation/formatting_docstrings diff --git a/docs/source/pages/developer/documentation/formatting_docstrings.rst b/docs/source/pages/developer/documentation/formatting_docstrings.rst new file mode 100644 index 00000000..fe1998a9 --- /dev/null +++ b/docs/source/pages/developer/documentation/formatting_docstrings.rst @@ -0,0 +1,176 @@ +Writing a Properly Documented MATLAB Docstring +============================================== + +A well-documented MATLAB function should be structured to provide all necessary details about its purpose, usage, inputs, outputs, and examples in a clear and consistent format. This guide outlines the key sections and formatting rules to follow when documenting MATLAB functions, using ``NWBREAD`` as an example. + +1. Function Summary +------------------- + +Provide a one-line summary of the function's purpose at the very beginning of the docstring. Use uppercase function names followed by a concise description. + +**Example**:: + + % NWBREAD - Read an NWB file. + +2. Syntax Section +------------------ + +Document the different ways the function can be called (function signatures). Include all variations and briefly describe their purpose. Each syntax line should start with a code literal and describe what it does. + +**Example**:: + + % Syntax: + % nwb = NWBREAD(filename) Reads the nwb file at filename and returns an + % NWBFile object representing its contents. + % + % nwb = NWBREAD(filename, flags) Reads the nwb file using optional + % flags controlling the mode for how to read the file. See input + % arguments for a list of available flags. + % + % nwb = NWBREAD(filename, Name, Value) Reads the nwb file using optional + % name-value pairs controlling options for how to read the file. + +3. Input Arguments +------------------- + +Provide a detailed description of all input arguments. Use the following format for each input: +- Start with a ``-`` followed by the argument name. +- Add the argument type in parentheses (e.g., ``(string)``). +- Write a concise description on the same line or in an indented paragraph below. +- For optional or additional parameters, list their sub-arguments as indented items. + +**Example**:: + + % Input Arguments: + % - filename (string) - + % Filepath pointing to an NWB file. + % + % - flags (string) - + % Flag for setting the mode for the NWBREAD operation. Available options are: + % 'ignorecache'. If the 'ignorecache' flag is used, classes for NWB data types + % are not re-generated based on the embedded schemas in the file. + % + % - options (name-value pairs) - + % Optional name-value pairs. Available options: + % + % - savedir (string) - + % A folder to save generated classes for NWB types. + +4. Output Arguments +-------------------- + +Document all outputs of the function. Use a similar format as the input arguments: +- Start with a ``-`` followed by the output name. +- Add the output type in parentheses. +- Provide a brief description. + +**Example**:: + + % Output Arguments: + % - nwb (NwbFile) - Nwb file object + +5. Usage Examples +------------------ + +Provide practical examples of how to use the function. Each example should: +- Start with "Example X - Description" and be followed by a colon (``::``). +- Include MATLAB code blocks, indented with spaces. +- Add comments in the code to explain each step if necessary. + +**Example**:: + + % Usage: + % Example 1 - Read an NWB file:: + % + % nwb = nwbRead('data.nwb'); + % + % Example 2 - Read an NWB file without re-generating classes for NWB types:: + % + % nwb = nwbRead('data.nwb', 'ignorecache'); + % + % Note: This is a good option to use if you are reading several files + % which are created of the same version of the NWB schemas. + % + % Example 3 - Read an NWB file and generate classes for NWB types in the current working directory:: + % + % nwb = nwbRead('data.nwb', 'savedir', '.'); + +6. See Also +----------- + +Use the ``See also:`` section to reference related functions or objects. List each item separated by commas and include cross-references if applicable. + +**Example**:: + + % See also: + % generateCore, generateExtension, NwbFile, nwbExport + +7. Formatting Tips +------------------- + +- **Consistent Indentation**: + - Indent descriptions or additional information using two spaces. + +- **Bold Text**: + - Use ``**`` around key elements like argument names in the rendered documentation. + +- **Code Literals**: + - Use double backticks (``) for MATLAB code snippets in descriptions. + +- **Directives**: + - Use Sphinx-compatible directives for linking (``:class:``, ``:func:``, etc.) when writing in RST. + +8. Final Example +----------------- + +**Complete Example**:: + + % NWBREAD - Read an NWB file. + % + % Syntax: + % nwb = NWBREAD(filename) Reads the nwb file at filename and returns an + % NWBFile object representing its contents. + % + % nwb = NWBREAD(filename, flags) Reads the nwb file using optional + % flags controlling the mode for how to read the file. See input + % arguments for a list of available flags. + % + % nwb = NWBREAD(filename, Name, Value) Reads the nwb file using optional + % name-value pairs controlling options for how to read the file. + % + % Input Arguments: + % - filename (string) - + % Filepath pointing to an NWB file. + % + % - flags (string) - + % Flag for setting the mode for the NWBREAD operation. Available options are: + % 'ignorecache'. If the 'ignorecache' flag is used, classes for NWB data types + % are not re-generated based on the embedded schemas in the file. + % + % - options (name-value pairs) - + % Optional name-value pairs. Available options: + % + % - savedir (string) - + % A folder to save generated classes for NWB types. + % + % Output Arguments: + % - nwb (NwbFile) - Nwb file object + % + % Usage: + % Example 1 - Read an NWB file:: + % + % nwb = nwbRead('data.nwb'); + % + % Example 2 - Read an NWB file without re-generating classes for NWB types:: + % + % nwb = nwbRead('data.nwb', 'ignorecache'); + % + % Note: This is a good option to use if you are reading several files + % which are created of the same version of the NWB schemas. + % + % Example 3 - Read an NWB file and generate classes for NWB types in the current working directory:: + % + % nwb = nwbRead('data.nwb', 'savedir', '.'); + % + % See also: + % generateCore, generateExtension, NwbFile, nwbExport diff --git a/docs/source/pages/developers.rst b/docs/source/pages/developers.rst new file mode 100644 index 00000000..9515ff98 --- /dev/null +++ b/docs/source/pages/developers.rst @@ -0,0 +1,4 @@ +Developers +============= + +hello \ No newline at end of file diff --git a/docs/source/pages/functions/NwbFile.rst b/docs/source/pages/functions/NwbFile.rst new file mode 100644 index 00000000..2327759d --- /dev/null +++ b/docs/source/pages/functions/NwbFile.rst @@ -0,0 +1,7 @@ +NwbFile +======= + +.. mat:module:: . +.. autoclass:: NwbFile + :members: + :show-inheritance: diff --git a/docs/source/pages/functions/generateCore.rst b/docs/source/pages/functions/generateCore.rst new file mode 100644 index 00000000..33eb05b5 --- /dev/null +++ b/docs/source/pages/functions/generateCore.rst @@ -0,0 +1,5 @@ +generateCore +============ + +.. mat:module:: . +.. autofunction:: generateCore diff --git a/docs/source/pages/functions/generateExtension.rst b/docs/source/pages/functions/generateExtension.rst new file mode 100644 index 00000000..42a93291 --- /dev/null +++ b/docs/source/pages/functions/generateExtension.rst @@ -0,0 +1,5 @@ +generateExtension +================= + +.. mat:module:: . +.. autofunction:: generateExtension diff --git a/docs/source/pages/functions/index.rst b/docs/source/pages/functions/index.rst new file mode 100644 index 00000000..fae4ae4d --- /dev/null +++ b/docs/source/pages/functions/index.rst @@ -0,0 +1,15 @@ +MatNWB Functions +================ + +These are the main functions of the MatNWB API + +.. toctree:: + :maxdepth: 2 + :caption: Functions + + nwbRead + NwbFile + nwbExport + generateCore + generateExtension + nwbClearGenerated \ No newline at end of file diff --git a/docs/source/pages/functions/nwbClearGenerated.rst b/docs/source/pages/functions/nwbClearGenerated.rst new file mode 100644 index 00000000..166e00fb --- /dev/null +++ b/docs/source/pages/functions/nwbClearGenerated.rst @@ -0,0 +1,5 @@ +nwbClearGenerated +================= + +.. mat:module:: . +.. autofunction:: nwbClearGenerated diff --git a/docs/source/pages/functions/nwbExport.rst b/docs/source/pages/functions/nwbExport.rst new file mode 100644 index 00000000..ee4e5c0e --- /dev/null +++ b/docs/source/pages/functions/nwbExport.rst @@ -0,0 +1,5 @@ +nwbExport +========= + +.. mat:module:: . +.. autofunction:: nwbExport diff --git a/docs/source/pages/functions/nwbRead.rst b/docs/source/pages/functions/nwbRead.rst new file mode 100644 index 00000000..94d455e5 --- /dev/null +++ b/docs/source/pages/functions/nwbRead.rst @@ -0,0 +1,5 @@ +nwbRead +======= + +.. mat:module:: . +.. autofunction:: nwbRead diff --git a/docs/source/pages/install_users.rst b/docs/source/pages/install_users.rst new file mode 100644 index 00000000..20156dbf --- /dev/null +++ b/docs/source/pages/install_users.rst @@ -0,0 +1,4 @@ +Install MatNWB +============== + +Todo: installation \ No newline at end of file diff --git a/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst b/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst new file mode 100644 index 00000000..c4eebc7b --- /dev/null +++ b/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst @@ -0,0 +1,7 @@ +AbstractFeatureSeries +===================== + +.. mat:module:: types.core +.. autoclass:: types.core.AbstractFeatureSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/AnnotationSeries.rst b/docs/source/pages/neurodata_types/core/AnnotationSeries.rst new file mode 100644 index 00000000..7bd34497 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/AnnotationSeries.rst @@ -0,0 +1,7 @@ +AnnotationSeries +================ + +.. mat:module:: types.core +.. autoclass:: types.core.AnnotationSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst b/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst new file mode 100644 index 00000000..a30da28d --- /dev/null +++ b/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst @@ -0,0 +1,7 @@ +BehavioralEpochs +================ + +.. mat:module:: types.core +.. autoclass:: types.core.BehavioralEpochs + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/BehavioralEvents.rst b/docs/source/pages/neurodata_types/core/BehavioralEvents.rst new file mode 100644 index 00000000..73b76ec7 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/BehavioralEvents.rst @@ -0,0 +1,7 @@ +BehavioralEvents +================ + +.. mat:module:: types.core +.. autoclass:: types.core.BehavioralEvents + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst b/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst new file mode 100644 index 00000000..28729a31 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst @@ -0,0 +1,7 @@ +BehavioralTimeSeries +==================== + +.. mat:module:: types.core +.. autoclass:: types.core.BehavioralTimeSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst b/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst new file mode 100644 index 00000000..326078d5 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst @@ -0,0 +1,7 @@ +ClusterWaveforms +================ + +.. mat:module:: types.core +.. autoclass:: types.core.ClusterWaveforms + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Clustering.rst b/docs/source/pages/neurodata_types/core/Clustering.rst new file mode 100644 index 00000000..f4d71e48 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Clustering.rst @@ -0,0 +1,7 @@ +Clustering +========== + +.. mat:module:: types.core +.. autoclass:: types.core.Clustering + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/CompassDirection.rst b/docs/source/pages/neurodata_types/core/CompassDirection.rst new file mode 100644 index 00000000..741510f6 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/CompassDirection.rst @@ -0,0 +1,7 @@ +CompassDirection +================ + +.. mat:module:: types.core +.. autoclass:: types.core.CompassDirection + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst b/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst new file mode 100644 index 00000000..5aaa228a --- /dev/null +++ b/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst @@ -0,0 +1,7 @@ +CorrectedImageStack +=================== + +.. mat:module:: types.core +.. autoclass:: types.core.CorrectedImageStack + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst b/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst new file mode 100644 index 00000000..499c5716 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst @@ -0,0 +1,7 @@ +CurrentClampSeries +================== + +.. mat:module:: types.core +.. autoclass:: types.core.CurrentClampSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst b/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst new file mode 100644 index 00000000..5d8c5f1b --- /dev/null +++ b/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst @@ -0,0 +1,7 @@ +CurrentClampStimulusSeries +========================== + +.. mat:module:: types.core +.. autoclass:: types.core.CurrentClampStimulusSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/DecompositionSeries.rst b/docs/source/pages/neurodata_types/core/DecompositionSeries.rst new file mode 100644 index 00000000..d187f416 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/DecompositionSeries.rst @@ -0,0 +1,7 @@ +DecompositionSeries +=================== + +.. mat:module:: types.core +.. autoclass:: types.core.DecompositionSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Device.rst b/docs/source/pages/neurodata_types/core/Device.rst new file mode 100644 index 00000000..ce022bed --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Device.rst @@ -0,0 +1,7 @@ +Device +====== + +.. mat:module:: types.core +.. autoclass:: types.core.Device + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/DfOverF.rst b/docs/source/pages/neurodata_types/core/DfOverF.rst new file mode 100644 index 00000000..35c40613 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/DfOverF.rst @@ -0,0 +1,7 @@ +DfOverF +======= + +.. mat:module:: types.core +.. autoclass:: types.core.DfOverF + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ElectricalSeries.rst b/docs/source/pages/neurodata_types/core/ElectricalSeries.rst new file mode 100644 index 00000000..15972a49 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ElectricalSeries.rst @@ -0,0 +1,7 @@ +ElectricalSeries +================ + +.. mat:module:: types.core +.. autoclass:: types.core.ElectricalSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst b/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst new file mode 100644 index 00000000..4ba91041 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst @@ -0,0 +1,7 @@ +ElectrodeGroup +============== + +.. mat:module:: types.core +.. autoclass:: types.core.ElectrodeGroup + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/EventDetection.rst b/docs/source/pages/neurodata_types/core/EventDetection.rst new file mode 100644 index 00000000..5633b916 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/EventDetection.rst @@ -0,0 +1,7 @@ +EventDetection +============== + +.. mat:module:: types.core +.. autoclass:: types.core.EventDetection + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/EventWaveform.rst b/docs/source/pages/neurodata_types/core/EventWaveform.rst new file mode 100644 index 00000000..3550c82a --- /dev/null +++ b/docs/source/pages/neurodata_types/core/EventWaveform.rst @@ -0,0 +1,7 @@ +EventWaveform +============= + +.. mat:module:: types.core +.. autoclass:: types.core.EventWaveform + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst b/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst new file mode 100644 index 00000000..ad31451b --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst @@ -0,0 +1,7 @@ +ExperimentalConditionsTable +=========================== + +.. mat:module:: types.core +.. autoclass:: types.core.ExperimentalConditionsTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/EyeTracking.rst b/docs/source/pages/neurodata_types/core/EyeTracking.rst new file mode 100644 index 00000000..124d9ebc --- /dev/null +++ b/docs/source/pages/neurodata_types/core/EyeTracking.rst @@ -0,0 +1,7 @@ +EyeTracking +=========== + +.. mat:module:: types.core +.. autoclass:: types.core.EyeTracking + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/FeatureExtraction.rst b/docs/source/pages/neurodata_types/core/FeatureExtraction.rst new file mode 100644 index 00000000..31c673cd --- /dev/null +++ b/docs/source/pages/neurodata_types/core/FeatureExtraction.rst @@ -0,0 +1,7 @@ +FeatureExtraction +================= + +.. mat:module:: types.core +.. autoclass:: types.core.FeatureExtraction + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/FilteredEphys.rst b/docs/source/pages/neurodata_types/core/FilteredEphys.rst new file mode 100644 index 00000000..380d26d2 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/FilteredEphys.rst @@ -0,0 +1,7 @@ +FilteredEphys +============= + +.. mat:module:: types.core +.. autoclass:: types.core.FilteredEphys + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Fluorescence.rst b/docs/source/pages/neurodata_types/core/Fluorescence.rst new file mode 100644 index 00000000..98568955 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Fluorescence.rst @@ -0,0 +1,7 @@ +Fluorescence +============ + +.. mat:module:: types.core +.. autoclass:: types.core.Fluorescence + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/GrayscaleImage.rst b/docs/source/pages/neurodata_types/core/GrayscaleImage.rst new file mode 100644 index 00000000..19ea6a07 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/GrayscaleImage.rst @@ -0,0 +1,7 @@ +GrayscaleImage +============== + +.. mat:module:: types.core +.. autoclass:: types.core.GrayscaleImage + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst b/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst new file mode 100644 index 00000000..933c36bd --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst @@ -0,0 +1,7 @@ +IZeroClampSeries +================ + +.. mat:module:: types.core +.. autoclass:: types.core.IZeroClampSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Image.rst b/docs/source/pages/neurodata_types/core/Image.rst new file mode 100644 index 00000000..f68e19c8 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Image.rst @@ -0,0 +1,7 @@ +Image +===== + +.. mat:module:: types.core +.. autoclass:: types.core.Image + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst b/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst new file mode 100644 index 00000000..a64fce4c --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst @@ -0,0 +1,7 @@ +ImageMaskSeries +=============== + +.. mat:module:: types.core +.. autoclass:: types.core.ImageMaskSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ImageReferences.rst b/docs/source/pages/neurodata_types/core/ImageReferences.rst new file mode 100644 index 00000000..f25c1dde --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ImageReferences.rst @@ -0,0 +1,7 @@ +ImageReferences +=============== + +.. mat:module:: types.core +.. autoclass:: types.core.ImageReferences + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ImageSegmentation.rst b/docs/source/pages/neurodata_types/core/ImageSegmentation.rst new file mode 100644 index 00000000..dbd7bf12 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ImageSegmentation.rst @@ -0,0 +1,7 @@ +ImageSegmentation +================= + +.. mat:module:: types.core +.. autoclass:: types.core.ImageSegmentation + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ImageSeries.rst b/docs/source/pages/neurodata_types/core/ImageSeries.rst new file mode 100644 index 00000000..2ca8ddaa --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ImageSeries.rst @@ -0,0 +1,7 @@ +ImageSeries +=========== + +.. mat:module:: types.core +.. autoclass:: types.core.ImageSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Images.rst b/docs/source/pages/neurodata_types/core/Images.rst new file mode 100644 index 00000000..59aa8822 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Images.rst @@ -0,0 +1,7 @@ +Images +====== + +.. mat:module:: types.core +.. autoclass:: types.core.Images + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ImagingPlane.rst b/docs/source/pages/neurodata_types/core/ImagingPlane.rst new file mode 100644 index 00000000..513c21db --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ImagingPlane.rst @@ -0,0 +1,7 @@ +ImagingPlane +============ + +.. mat:module:: types.core +.. autoclass:: types.core.ImagingPlane + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst b/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst new file mode 100644 index 00000000..47edb9b3 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst @@ -0,0 +1,7 @@ +ImagingRetinotopy +================= + +.. mat:module:: types.core +.. autoclass:: types.core.ImagingRetinotopy + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IndexSeries.rst b/docs/source/pages/neurodata_types/core/IndexSeries.rst new file mode 100644 index 00000000..5b2a7f40 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IndexSeries.rst @@ -0,0 +1,7 @@ +IndexSeries +=========== + +.. mat:module:: types.core +.. autoclass:: types.core.IndexSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IntervalSeries.rst b/docs/source/pages/neurodata_types/core/IntervalSeries.rst new file mode 100644 index 00000000..d316f0fe --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IntervalSeries.rst @@ -0,0 +1,7 @@ +IntervalSeries +============== + +.. mat:module:: types.core +.. autoclass:: types.core.IntervalSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst b/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst new file mode 100644 index 00000000..ab0f254b --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst @@ -0,0 +1,7 @@ +IntracellularElectrode +====================== + +.. mat:module:: types.core +.. autoclass:: types.core.IntracellularElectrode + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst b/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst new file mode 100644 index 00000000..d1903fcc --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst @@ -0,0 +1,7 @@ +IntracellularElectrodesTable +============================ + +.. mat:module:: types.core +.. autoclass:: types.core.IntracellularElectrodesTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst b/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst new file mode 100644 index 00000000..83af79be --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst @@ -0,0 +1,7 @@ +IntracellularRecordingsTable +============================ + +.. mat:module:: types.core +.. autoclass:: types.core.IntracellularRecordingsTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst b/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst new file mode 100644 index 00000000..e1be1bc1 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst @@ -0,0 +1,7 @@ +IntracellularResponsesTable +=========================== + +.. mat:module:: types.core +.. autoclass:: types.core.IntracellularResponsesTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst b/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst new file mode 100644 index 00000000..b01d0116 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst @@ -0,0 +1,7 @@ +IntracellularStimuliTable +========================= + +.. mat:module:: types.core +.. autoclass:: types.core.IntracellularStimuliTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/LFP.rst b/docs/source/pages/neurodata_types/core/LFP.rst new file mode 100644 index 00000000..62c80788 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/LFP.rst @@ -0,0 +1,7 @@ +LFP +=== + +.. mat:module:: types.core +.. autoclass:: types.core.LFP + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/LabMetaData.rst b/docs/source/pages/neurodata_types/core/LabMetaData.rst new file mode 100644 index 00000000..7dd71b67 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/LabMetaData.rst @@ -0,0 +1,7 @@ +LabMetaData +=========== + +.. mat:module:: types.core +.. autoclass:: types.core.LabMetaData + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/MotionCorrection.rst b/docs/source/pages/neurodata_types/core/MotionCorrection.rst new file mode 100644 index 00000000..4aa78836 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/MotionCorrection.rst @@ -0,0 +1,7 @@ +MotionCorrection +================ + +.. mat:module:: types.core +.. autoclass:: types.core.MotionCorrection + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/NWBContainer.rst b/docs/source/pages/neurodata_types/core/NWBContainer.rst new file mode 100644 index 00000000..4761b9c2 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/NWBContainer.rst @@ -0,0 +1,7 @@ +NWBContainer +============ + +.. mat:module:: types.core +.. autoclass:: types.core.NWBContainer + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/NWBData.rst b/docs/source/pages/neurodata_types/core/NWBData.rst new file mode 100644 index 00000000..c376686e --- /dev/null +++ b/docs/source/pages/neurodata_types/core/NWBData.rst @@ -0,0 +1,7 @@ +NWBData +======= + +.. mat:module:: types.core +.. autoclass:: types.core.NWBData + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/NWBDataInterface.rst b/docs/source/pages/neurodata_types/core/NWBDataInterface.rst new file mode 100644 index 00000000..dd29dca1 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/NWBDataInterface.rst @@ -0,0 +1,7 @@ +NWBDataInterface +================ + +.. mat:module:: types.core +.. autoclass:: types.core.NWBDataInterface + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/NWBFile.rst b/docs/source/pages/neurodata_types/core/NWBFile.rst new file mode 100644 index 00000000..9f390e5e --- /dev/null +++ b/docs/source/pages/neurodata_types/core/NWBFile.rst @@ -0,0 +1,7 @@ +NWBFile +======= + +.. mat:module:: types.core +.. autoclass:: types.core.NWBFile + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst b/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst new file mode 100644 index 00000000..93695fcd --- /dev/null +++ b/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst @@ -0,0 +1,7 @@ +OnePhotonSeries +=============== + +.. mat:module:: types.core +.. autoclass:: types.core.OnePhotonSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/OpticalChannel.rst b/docs/source/pages/neurodata_types/core/OpticalChannel.rst new file mode 100644 index 00000000..7f3e711a --- /dev/null +++ b/docs/source/pages/neurodata_types/core/OpticalChannel.rst @@ -0,0 +1,7 @@ +OpticalChannel +============== + +.. mat:module:: types.core +.. autoclass:: types.core.OpticalChannel + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/OpticalSeries.rst b/docs/source/pages/neurodata_types/core/OpticalSeries.rst new file mode 100644 index 00000000..a9fc655f --- /dev/null +++ b/docs/source/pages/neurodata_types/core/OpticalSeries.rst @@ -0,0 +1,7 @@ +OpticalSeries +============= + +.. mat:module:: types.core +.. autoclass:: types.core.OpticalSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst b/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst new file mode 100644 index 00000000..33071d10 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst @@ -0,0 +1,7 @@ +OptogeneticSeries +================= + +.. mat:module:: types.core +.. autoclass:: types.core.OptogeneticSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst b/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst new file mode 100644 index 00000000..bb8b9ace --- /dev/null +++ b/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst @@ -0,0 +1,7 @@ +OptogeneticStimulusSite +======================= + +.. mat:module:: types.core +.. autoclass:: types.core.OptogeneticStimulusSite + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/PatchClampSeries.rst b/docs/source/pages/neurodata_types/core/PatchClampSeries.rst new file mode 100644 index 00000000..67d1bbc9 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/PatchClampSeries.rst @@ -0,0 +1,7 @@ +PatchClampSeries +================ + +.. mat:module:: types.core +.. autoclass:: types.core.PatchClampSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst b/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst new file mode 100644 index 00000000..fd984fc6 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst @@ -0,0 +1,7 @@ +PlaneSegmentation +================= + +.. mat:module:: types.core +.. autoclass:: types.core.PlaneSegmentation + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Position.rst b/docs/source/pages/neurodata_types/core/Position.rst new file mode 100644 index 00000000..0b49fec6 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Position.rst @@ -0,0 +1,7 @@ +Position +======== + +.. mat:module:: types.core +.. autoclass:: types.core.Position + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ProcessingModule.rst b/docs/source/pages/neurodata_types/core/ProcessingModule.rst new file mode 100644 index 00000000..55bbc315 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ProcessingModule.rst @@ -0,0 +1,7 @@ +ProcessingModule +================ + +.. mat:module:: types.core +.. autoclass:: types.core.ProcessingModule + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/PupilTracking.rst b/docs/source/pages/neurodata_types/core/PupilTracking.rst new file mode 100644 index 00000000..e45a9981 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/PupilTracking.rst @@ -0,0 +1,7 @@ +PupilTracking +============= + +.. mat:module:: types.core +.. autoclass:: types.core.PupilTracking + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/RGBAImage.rst b/docs/source/pages/neurodata_types/core/RGBAImage.rst new file mode 100644 index 00000000..420307c2 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/RGBAImage.rst @@ -0,0 +1,7 @@ +RGBAImage +========= + +.. mat:module:: types.core +.. autoclass:: types.core.RGBAImage + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/RGBImage.rst b/docs/source/pages/neurodata_types/core/RGBImage.rst new file mode 100644 index 00000000..3dfc9870 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/RGBImage.rst @@ -0,0 +1,7 @@ +RGBImage +======== + +.. mat:module:: types.core +.. autoclass:: types.core.RGBImage + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/RepetitionsTable.rst b/docs/source/pages/neurodata_types/core/RepetitionsTable.rst new file mode 100644 index 00000000..71c33b5a --- /dev/null +++ b/docs/source/pages/neurodata_types/core/RepetitionsTable.rst @@ -0,0 +1,7 @@ +RepetitionsTable +================ + +.. mat:module:: types.core +.. autoclass:: types.core.RepetitionsTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst b/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst new file mode 100644 index 00000000..c48db5d9 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst @@ -0,0 +1,7 @@ +RoiResponseSeries +================= + +.. mat:module:: types.core +.. autoclass:: types.core.RoiResponseSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/ScratchData.rst b/docs/source/pages/neurodata_types/core/ScratchData.rst new file mode 100644 index 00000000..d121abdd --- /dev/null +++ b/docs/source/pages/neurodata_types/core/ScratchData.rst @@ -0,0 +1,7 @@ +ScratchData +=========== + +.. mat:module:: types.core +.. autoclass:: types.core.ScratchData + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst b/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst new file mode 100644 index 00000000..8a343944 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst @@ -0,0 +1,7 @@ +SequentialRecordingsTable +========================= + +.. mat:module:: types.core +.. autoclass:: types.core.SequentialRecordingsTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst b/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst new file mode 100644 index 00000000..0598bcb6 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst @@ -0,0 +1,7 @@ +SimultaneousRecordingsTable +=========================== + +.. mat:module:: types.core +.. autoclass:: types.core.SimultaneousRecordingsTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/SpatialSeries.rst b/docs/source/pages/neurodata_types/core/SpatialSeries.rst new file mode 100644 index 00000000..662c9c46 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/SpatialSeries.rst @@ -0,0 +1,7 @@ +SpatialSeries +============= + +.. mat:module:: types.core +.. autoclass:: types.core.SpatialSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst b/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst new file mode 100644 index 00000000..4cb93f2d --- /dev/null +++ b/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst @@ -0,0 +1,7 @@ +SpikeEventSeries +================ + +.. mat:module:: types.core +.. autoclass:: types.core.SpikeEventSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Subject.rst b/docs/source/pages/neurodata_types/core/Subject.rst new file mode 100644 index 00000000..a227a712 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Subject.rst @@ -0,0 +1,7 @@ +Subject +======= + +.. mat:module:: types.core +.. autoclass:: types.core.Subject + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/SweepTable.rst b/docs/source/pages/neurodata_types/core/SweepTable.rst new file mode 100644 index 00000000..0088de4f --- /dev/null +++ b/docs/source/pages/neurodata_types/core/SweepTable.rst @@ -0,0 +1,7 @@ +SweepTable +========== + +.. mat:module:: types.core +.. autoclass:: types.core.SweepTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/TimeIntervals.rst b/docs/source/pages/neurodata_types/core/TimeIntervals.rst new file mode 100644 index 00000000..7385bf7f --- /dev/null +++ b/docs/source/pages/neurodata_types/core/TimeIntervals.rst @@ -0,0 +1,7 @@ +TimeIntervals +============= + +.. mat:module:: types.core +.. autoclass:: types.core.TimeIntervals + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/TimeSeries.rst b/docs/source/pages/neurodata_types/core/TimeSeries.rst new file mode 100644 index 00000000..fbc71723 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/TimeSeries.rst @@ -0,0 +1,7 @@ +TimeSeries +========== + +.. mat:module:: types.core +.. autoclass:: types.core.TimeSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst b/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst new file mode 100644 index 00000000..93594259 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst @@ -0,0 +1,7 @@ +TimeSeriesReferenceVectorData +============================= + +.. mat:module:: types.core +.. autoclass:: types.core.TimeSeriesReferenceVectorData + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst b/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst new file mode 100644 index 00000000..e71db63d --- /dev/null +++ b/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst @@ -0,0 +1,7 @@ +TwoPhotonSeries +=============== + +.. mat:module:: types.core +.. autoclass:: types.core.TwoPhotonSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/Units.rst b/docs/source/pages/neurodata_types/core/Units.rst new file mode 100644 index 00000000..54a4a71f --- /dev/null +++ b/docs/source/pages/neurodata_types/core/Units.rst @@ -0,0 +1,7 @@ +Units +===== + +.. mat:module:: types.core +.. autoclass:: types.core.Units + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst b/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst new file mode 100644 index 00000000..b4ede53f --- /dev/null +++ b/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst @@ -0,0 +1,7 @@ +VoltageClampSeries +================== + +.. mat:module:: types.core +.. autoclass:: types.core.VoltageClampSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst b/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst new file mode 100644 index 00000000..d1cc01a3 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst @@ -0,0 +1,7 @@ +VoltageClampStimulusSeries +========================== + +.. mat:module:: types.core +.. autoclass:: types.core.VoltageClampStimulusSeries + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/core/index.rst b/docs/source/pages/neurodata_types/core/index.rst new file mode 100644 index 00000000..34437913 --- /dev/null +++ b/docs/source/pages/neurodata_types/core/index.rst @@ -0,0 +1,84 @@ +Neurodata Types +=============== + +These are the MatNWB neurodata types from the core schema specification. + +.. toctree:: + :maxdepth: 2 + :caption: Functions + + AbstractFeatureSeries + AnnotationSeries + BehavioralEpochs + BehavioralEvents + BehavioralTimeSeries + ClusterWaveforms + Clustering + CompassDirection + CorrectedImageStack + CurrentClampSeries + CurrentClampStimulusSeries + DecompositionSeries + Device + DfOverF + ElectricalSeries + ElectrodeGroup + EventDetection + EventWaveform + ExperimentalConditionsTable + EyeTracking + FeatureExtraction + FilteredEphys + Fluorescence + GrayscaleImage + IZeroClampSeries + Image + ImageMaskSeries + ImageReferences + ImageSegmentation + ImageSeries + Images + ImagingPlane + ImagingRetinotopy + IndexSeries + IntervalSeries + IntracellularElectrode + IntracellularElectrodesTable + IntracellularRecordingsTable + IntracellularResponsesTable + IntracellularStimuliTable + LFP + LabMetaData + MotionCorrection + NWBContainer + NWBData + NWBDataInterface + NWBFile + OnePhotonSeries + OpticalChannel + OpticalSeries + OptogeneticSeries + OptogeneticStimulusSite + PatchClampSeries + PlaneSegmentation + Position + ProcessingModule + PupilTracking + RGBAImage + RGBImage + RepetitionsTable + RoiResponseSeries + ScratchData + SequentialRecordingsTable + SimultaneousRecordingsTable + SpatialSeries + SpikeEventSeries + Subject + SweepTable + TimeIntervals + TimeSeries + TimeSeriesReferenceVectorData + TwoPhotonSeries + Units + VoltageClampSeries + VoltageClampStimulusSeries \ No newline at end of file diff --git a/docs/source/pages/overview_citing.rst b/docs/source/pages/overview_citing.rst new file mode 100644 index 00000000..7c67f175 --- /dev/null +++ b/docs/source/pages/overview_citing.rst @@ -0,0 +1,30 @@ +Citing MatNWB +============= + +BibTeX entry +------------ + +If you use MatNWB in your research, please use the following citation: + +.. code-block:: bibtex + + @article {10.7554/eLife.78362, + article_type = {journal}, + title = {{The Neurodata Without Borders ecosystem for neurophysiological data science}}, + author = {R\"ubel, Oliver and Tritt, Andrew and Ly, Ryan and Dichter, Benjamin K. and + Ghosh, Satrajit and Niu, Lawrence and Baker, Pamela and Soltesz, Ivan and + Ng, Lydia and Svoboda, Karel and Frank, Loren and Bouchard, Kristofer E.}, + editor = {Colgin, Laura L and Jadhav, Shantanu P}, + volume = {11{, + year = {2022}, + month = {oct}, + pub_date = {2022-10-04}, + pages = {e78362}, + citation = {eLife 2022;11:e78362}, + doi = {10.7554/eLife.78362}, + url = {https://doi.org/10.7554/eLife.78362}, + keywords = {Neurophysiology, data ecosystem, data language, data standard, FAIR data, archive}, + journal = {eLife}, + issn = {2050-084X}, + publisher = {eLife Sciences Publications, Ltd}, + } diff --git a/docs/source/pages/tutorials/basicUsage.rst b/docs/source/pages/tutorials/basicUsage.rst new file mode 100644 index 00000000..5e1c8f10 --- /dev/null +++ b/docs/source/pages/tutorials/basicUsage.rst @@ -0,0 +1,6 @@ +basicUsage +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/behavior.rst b/docs/source/pages/tutorials/behavior.rst new file mode 100644 index 00000000..c15fe726 --- /dev/null +++ b/docs/source/pages/tutorials/behavior.rst @@ -0,0 +1,6 @@ +behavior +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/convertTrials.rst b/docs/source/pages/tutorials/convertTrials.rst new file mode 100644 index 00000000..313e15ca --- /dev/null +++ b/docs/source/pages/tutorials/convertTrials.rst @@ -0,0 +1,6 @@ +convertTrials +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/dataPipe.rst b/docs/source/pages/tutorials/dataPipe.rst new file mode 100644 index 00000000..8f7436a3 --- /dev/null +++ b/docs/source/pages/tutorials/dataPipe.rst @@ -0,0 +1,6 @@ +dataPipe +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst new file mode 100644 index 00000000..8fb67911 --- /dev/null +++ b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst @@ -0,0 +1,6 @@ +dimensionMapNoDataPipes +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst new file mode 100644 index 00000000..27f0e4f6 --- /dev/null +++ b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst @@ -0,0 +1,6 @@ +dimensionMapWithDataPipes +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/dynamic_tables.rst b/docs/source/pages/tutorials/dynamic_tables.rst new file mode 100644 index 00000000..a74dbe21 --- /dev/null +++ b/docs/source/pages/tutorials/dynamic_tables.rst @@ -0,0 +1,6 @@ +dynamic_tables +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/dynamically_loaded_filters.rst b/docs/source/pages/tutorials/dynamically_loaded_filters.rst new file mode 100644 index 00000000..e7600fea --- /dev/null +++ b/docs/source/pages/tutorials/dynamically_loaded_filters.rst @@ -0,0 +1,6 @@ +dynamically_loaded_filters +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/ecephys.rst b/docs/source/pages/tutorials/ecephys.rst new file mode 100644 index 00000000..4e476bc2 --- /dev/null +++ b/docs/source/pages/tutorials/ecephys.rst @@ -0,0 +1,6 @@ +ecephys +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/icephys.rst b/docs/source/pages/tutorials/icephys.rst new file mode 100644 index 00000000..a1f3eb78 --- /dev/null +++ b/docs/source/pages/tutorials/icephys.rst @@ -0,0 +1,6 @@ +icephys +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/images.rst b/docs/source/pages/tutorials/images.rst new file mode 100644 index 00000000..bc088579 --- /dev/null +++ b/docs/source/pages/tutorials/images.rst @@ -0,0 +1,6 @@ +images +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/index.rst b/docs/source/pages/tutorials/index.rst new file mode 100644 index 00000000..1b349ecf --- /dev/null +++ b/docs/source/pages/tutorials/index.rst @@ -0,0 +1,24 @@ +Tutorials +========= + +.. toctree:: + :maxdepth: 1 + :caption: Tutorials + + basicUsage + behavior + convertTrials + dataPipe + dimensionMapNoDataPipes + dimensionMapWithDataPipes + dynamic_tables + dynamically_loaded_filters + ecephys + icephys + images + intro + ogen + ophys + read_demo + remote_read + scratch \ No newline at end of file diff --git a/docs/source/pages/tutorials/intro.rst b/docs/source/pages/tutorials/intro.rst new file mode 100644 index 00000000..24aadfa2 --- /dev/null +++ b/docs/source/pages/tutorials/intro.rst @@ -0,0 +1,6 @@ +intro +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/ogen.rst b/docs/source/pages/tutorials/ogen.rst new file mode 100644 index 00000000..ede496c7 --- /dev/null +++ b/docs/source/pages/tutorials/ogen.rst @@ -0,0 +1,6 @@ +ogen +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/ophys.rst b/docs/source/pages/tutorials/ophys.rst new file mode 100644 index 00000000..511360ae --- /dev/null +++ b/docs/source/pages/tutorials/ophys.rst @@ -0,0 +1,6 @@ +ophys +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/read_demo.rst b/docs/source/pages/tutorials/read_demo.rst new file mode 100644 index 00000000..7cb5392c --- /dev/null +++ b/docs/source/pages/tutorials/read_demo.rst @@ -0,0 +1,6 @@ +read_demo +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/remote_read.rst b/docs/source/pages/tutorials/remote_read.rst new file mode 100644 index 00000000..5b920117 --- /dev/null +++ b/docs/source/pages/tutorials/remote_read.rst @@ -0,0 +1,6 @@ +remote_read +=============================== + +.. raw:: html + + diff --git a/docs/source/pages/tutorials/scratch.rst b/docs/source/pages/tutorials/scratch.rst new file mode 100644 index 00000000..bcd17732 --- /dev/null +++ b/docs/source/pages/tutorials/scratch.rst @@ -0,0 +1,6 @@ +scratch +=============================== + +.. raw:: html + + diff --git a/docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc b/docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2fc5ab6f1cee68e3a9a9df0bc4813545bf29d35c GIT binary patch literal 6843 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 NI3|FttrzqY{tuH%9eV%( literal 0 HcmV?d00001 diff --git a/docs/source/sphinx_extensions/docstring_processors.py b/docs/source/sphinx_extensions/docstring_processors.py new file mode 100644 index 00000000..2b3c9d51 --- /dev/null +++ b/docs/source/sphinx_extensions/docstring_processors.py @@ -0,0 +1,149 @@ +import re + + +def process_matlab_docstring(app, what, name, obj, options, lines): + _format_matlab_type_as_code_literal(lines) + _make_syntax_examples_code_literals(lines) + _format_input_arguments(lines) + _split_and_format_example_lines(lines) + + +def _format_matlab_type_as_code_literal(lines): + # Full list of MATLAB base types + matlab_types = { + "double", "single", "int8", "uint8", "int16", "uint16", + "int32", "uint32", "int64", "uint64", "logical", "char", + "cell", "struct", "table", "categorical", "datetime", + "duration", "calendarDuration", "function_handle", + "string", "complex" + } + + # Regex pattern to match MATLAB types as whole words, optionally wrapped in parentheses + type_pattern = re.compile( + rf"(?\(?)" + rf"(?P{'|'.join(re.escape(t) for t in matlab_types)})" + rf"(?P\)?)(?!\w)" + ) + + for i, line in enumerate(lines): + # Replace matches with inline code formatting, preserving parentheses + lines[i] = type_pattern.sub( + lambda match: ( + f"{match.group('before') or ''}" + f"``{match.group('type')}``" + f"{match.group('after') or ''}" + ), + line + ) + + +def _make_syntax_examples_code_literals(lines): + """ + Process a MATLAB docstring to wrap expressions in the Syntax section with double backticks. + + Args: + lines (str): The original MATLAB docstring lines. + """ + + in_syntax_section = False + + # Regex to match MATLAB expressions + matlab_expr_pattern = re.compile( + r"^\s*((?:\[[\w,\s]*\]\s*=\s*|[\w]+\s*=\s*)?[A-Za-z]\w*\([^)]*\))" + ) + + for i, line in enumerate(lines): + # Check if the current line starts the Syntax section + if line.strip().lower().startswith("syntax:"): + in_syntax_section = True + continue + + # Check if the current line is another section header + if in_syntax_section and _is_section_header(line) and not line.strip().lower().startswith("syntax:"): + in_syntax_section = False + + if in_syntax_section: + # Wrap MATLAB expressions in double backticks + match = matlab_expr_pattern.search(line) + if match: + # Need group 1 as group 0 contains the leading whitespace...? + line = matlab_expr_pattern.sub(lambda m: f"``{m.group(1)}``", line) + # Need to prepend a leading space, no idea why. + lines[i] = " " + line + + +def _format_input_arguments(lines): + """ + Format the 'Input Arguments' section to add double ** around item names + and `` around types in parentheses. + + Args: + lines (list of str): List of lines in the Input Arguments section. + + Returns: + list of str: Formatted lines. + """ + # Regex pattern for list item names with optional types in parentheses + input_arg_pattern = re.compile( + r"(?P^\s*)-\s*(?P\w+)" # Match the name of the argument + r"(?:\s*\((?P.*?)\))?" # Optionally match the type in parentheses + ) + + for i, line in enumerate(lines): + # Apply formatting to each matching line + lines[i] = input_arg_pattern.sub( + lambda match: ( + f"{match.group('indent')}- **{match.group('name').strip()}**" + # Name + ( # Optional type + f" ({match.group('type').strip()})" # Preserve existing formatting + if match.group('type') and ( + match.group('type').strip().startswith("``") or # Already backtick-formatted + match.group('type').strip().startswith(":") # Sphinx directive + ) + else f" (``{match.group('type').strip()}``)" # Add backticks if unformatted + ) if match.group('type') else "" # No type provided + ), + line + ) + + return lines + + +def _split_and_format_example_lines(lines): + """ + Split and format example lines like: + 'Example 1 - Export an NWB file:' + into two lines: + '**Example 1.**' + '**Export an NWB file**::' + + Modifies the `lines` list in place. + + Args: + lines (list of str): List of lines in the Usage section. + """ + + # Regex pattern to match example lines with descriptions + example_pattern = re.compile( + r"^\s*(Example\s+\d+)\s*-\s*(.*)::\s*$" # Matches 'Example X - Description:' + ) + + i = 0 + while i < len(lines): + # Check if the current line matches the "Example X - Description:" format + match = example_pattern.match(lines[i]) + if match: + example, description = match.groups() + # Replace the original line with two formatted lines + lines[i] = f" **{example} -**" # Important: add one space at beginning of line for proper rst indent + lines.insert(i + 1, f" **{description}**::") # Important: add one space at beginning of line for proper rst indent + i += 2 # Skip over the newly added line + else: + i += 1 # Move to the next line if no match + + +def _is_section_header(line): + # Regex to identify section headers + section_header_pattern = re.compile(r"^\s*%?\s*[A-Za-z ]+:") + + return section_header_pattern.match(line) From a34b243c0aa7ab4ae597e77dc5788ecab7646bed Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 14:19:03 +0100 Subject: [PATCH 04/30] Add .readthedocs.yaml --- .gitignore | 2 ++ .readthedocs.yaml | 13 +++++++++++++ docs/requirements.txt | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 docs/requirements.txt diff --git a/.gitignore b/.gitignore index 988501fa..14397dfc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ workspace/ *.swp .DS_Store +tests/env.mat + +docs/build diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..9138a7fe --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,13 @@ +version: "2" + +build: + os: "ubuntu-22.04" + tools: + python: "3.10" + +python: + install: + - requirements: docs/requirements.txt + +sphinx: + configuration: docs/source/conf.py diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..4456a5f1 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +sphinx +sphinx-rtd-theme +sphinx-copybutton +sphinxcontrib-matlabdomain From 48ba248277b463a9757b1307143d3f9e54e2005a Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 14:46:42 +0100 Subject: [PATCH 05/30] Delete previous doc files --- doc/+types/+core/AbstractFeatureSeries.html | 399 ----- doc/+types/+core/AnnotationSeries.html | 375 ----- doc/+types/+core/BehavioralEpochs.html | 192 --- doc/+types/+core/BehavioralEvents.html | 192 --- doc/+types/+core/BehavioralTimeSeries.html | 192 --- doc/+types/+core/ClusterWaveforms.html | 231 --- doc/+types/+core/Clustering.html | 230 --- doc/+types/+core/CompassDirection.html | 192 --- doc/+types/+core/CorrectedImageStack.html | 218 --- doc/+types/+core/CurrentClampSeries.html | 459 ------ .../+core/CurrentClampStimulusSeries.html | 423 ------ doc/+types/+core/DecompositionSeries.html | 425 ------ doc/+types/+core/Device.html | 204 --- doc/+types/+core/DfOverF.html | 192 --- doc/+types/+core/ElectricalSeries.html | 427 ------ doc/+types/+core/ElectrodeGroup.html | 230 --- doc/+types/+core/EventDetection.html | 237 --- doc/+types/+core/EventWaveform.html | 192 --- .../+core/ExperimentalConditionsTable.html | 292 ---- doc/+types/+core/EyeTracking.html | 192 --- doc/+types/+core/FeatureExtraction.html | 228 --- doc/+types/+core/FilteredEphys.html | 192 --- doc/+types/+core/Fluorescence.html | 192 --- doc/+types/+core/GrayscaleImage.html | 216 --- doc/+types/+core/IZeroClampSeries.html | 459 ------ doc/+types/+core/Image.html | 216 --- doc/+types/+core/ImageMaskSeries.html | 460 ------ doc/+types/+core/ImageReferences.html | 192 --- doc/+types/+core/ImageSegmentation.html | 192 --- doc/+types/+core/ImageSeries.html | 448 ------ doc/+types/+core/Images.html | 218 --- doc/+types/+core/ImagingPlane.html | 385 ----- doc/+types/+core/ImagingRetinotopy.html | 560 ------- doc/+types/+core/IndexSeries.html | 399 ----- doc/+types/+core/IntervalSeries.html | 375 ----- doc/+types/+core/IntracellularElectrode.html | 290 ---- .../+core/IntracellularElectrodesTable.html | 280 ---- .../+core/IntracellularRecordingsTable.html | 335 ----- .../+core/IntracellularResponsesTable.html | 280 ---- .../+core/IntracellularStimuliTable.html | 280 ---- doc/+types/+core/LFP.html | 192 --- doc/+types/+core/LabMetaData.html | 175 --- doc/+types/+core/MotionCorrection.html | 192 --- doc/+types/+core/NWBContainer.html | 175 --- doc/+types/+core/NWBData.html | 192 --- doc/+types/+core/NWBDataInterface.html | 175 --- doc/+types/+core/NWBFile.html | 804 ---------- doc/+types/+core/OnePhotonSeries.html | 534 ------- doc/+types/+core/OpticalChannel.html | 204 --- doc/+types/+core/OpticalSeries.html | 484 ------ doc/+types/+core/OptogeneticSeries.html | 387 ----- doc/+types/+core/OptogeneticStimulusSite.html | 230 --- doc/+types/+core/PatchClampSeries.html | 423 ------ doc/+types/+core/PlaneSegmentation.html | 358 ----- doc/+types/+core/Position.html | 192 --- doc/+types/+core/ProcessingModule.html | 216 --- doc/+types/+core/PupilTracking.html | 192 --- doc/+types/+core/RGBAImage.html | 216 --- doc/+types/+core/RGBImage.html | 216 --- doc/+types/+core/RepetitionsTable.html | 292 ---- doc/+types/+core/RoiResponseSeries.html | 389 ----- doc/+types/+core/ScratchData.html | 204 --- .../+core/SequentialRecordingsTable.html | 304 ---- .../+core/SimultaneousRecordingsTable.html | 292 ---- doc/+types/+core/SpatialSeries.html | 387 ----- doc/+types/+core/SpikeEventSeries.html | 427 ------ doc/+types/+core/Subject.html | 300 ---- doc/+types/+core/SweepTable.html | 304 ---- doc/+types/+core/TimeIntervals.html | 340 ----- doc/+types/+core/TimeSeries.html | 375 ----- .../+core/TimeSeriesReferenceVectorData.html | 204 --- doc/+types/+core/TwoPhotonSeries.html | 498 ------- doc/+types/+core/Units.html | 429 ------ doc/+types/+core/VoltageClampSeries.html | 535 ------- .../+core/VoltageClampStimulusSeries.html | 423 ------ .../+hdmf_common/AlignedDynamicTable.html | 299 ---- doc/+types/+hdmf_common/CSRMatrix.html | 228 --- doc/+types/+hdmf_common/Container.html | 175 --- doc/+types/+hdmf_common/Data.html | 192 --- doc/+types/+hdmf_common/DynamicTable.html | 268 ---- .../+hdmf_common/DynamicTableRegion.html | 216 --- .../+hdmf_common/ElementIdentifiers.html | 192 --- .../+hdmf_common/SimpleMultiContainer.html | 204 --- doc/+types/+hdmf_common/VectorData.html | 204 --- doc/+types/+hdmf_common/VectorIndex.html | 216 --- doc/+types/+hdmf_experimental/EnumData.html | 216 --- .../+hdmf_experimental/ExternalResources.html | 240 --- .../+untyped/+datapipe/+dynamic/Filter.html | 1326 ----------------- .../+datapipe/+properties/DynamicFilter.html | 213 --- .../+datapipe/+properties/Shuffle.html | 208 --- doc/NwbFile.html | 829 ----------- doc/alpha.png | Bin 273 -> 0 bytes doc/c++.png | Bin 327 -> 0 bytes doc/c.png | Bin 252 -> 0 bytes doc/demoicon.gif | Bin 214 -> 0 bytes doc/down.png | Bin 133 -> 0 bytes doc/doxysearch.php | 329 ---- doc/fortran.png | Bin 265 -> 0 bytes doc/generateCore.html | 99 -- doc/generateExtension.html | 97 -- doc/helpwin.css | 149 -- doc/hp.png | Bin 255 -> 0 bytes doc/index.html | 130 -- doc/left.png | Bin 136 -> 0 bytes doc/linux.png | Bin 272 -> 0 bytes doc/m2html.css | 90 -- doc/matlabicon.gif | Bin 574 -> 0 bytes doc/mex.png | Bin 242 -> 0 bytes doc/nwbExport.html | 98 -- doc/nwbRead.html | 156 -- doc/pcode.png | Bin 212 -> 0 bytes doc/right.png | Bin 136 -> 0 bytes doc/sgi.png | Bin 263 -> 0 bytes doc/simulinkicon.gif | Bin 977 -> 0 bytes doc/solaris.png | Bin 286 -> 0 bytes doc/up.png | Bin 162 -> 0 bytes doc/windows.png | Bin 286 -> 0 bytes 117 files changed, 29325 deletions(-) delete mode 100644 doc/+types/+core/AbstractFeatureSeries.html delete mode 100644 doc/+types/+core/AnnotationSeries.html delete mode 100644 doc/+types/+core/BehavioralEpochs.html delete mode 100644 doc/+types/+core/BehavioralEvents.html delete mode 100644 doc/+types/+core/BehavioralTimeSeries.html delete mode 100644 doc/+types/+core/ClusterWaveforms.html delete mode 100644 doc/+types/+core/Clustering.html delete mode 100644 doc/+types/+core/CompassDirection.html delete mode 100644 doc/+types/+core/CorrectedImageStack.html delete mode 100644 doc/+types/+core/CurrentClampSeries.html delete mode 100644 doc/+types/+core/CurrentClampStimulusSeries.html delete mode 100644 doc/+types/+core/DecompositionSeries.html delete mode 100644 doc/+types/+core/Device.html delete mode 100644 doc/+types/+core/DfOverF.html delete mode 100644 doc/+types/+core/ElectricalSeries.html delete mode 100644 doc/+types/+core/ElectrodeGroup.html delete mode 100644 doc/+types/+core/EventDetection.html delete mode 100644 doc/+types/+core/EventWaveform.html delete mode 100644 doc/+types/+core/ExperimentalConditionsTable.html delete mode 100644 doc/+types/+core/EyeTracking.html delete mode 100644 doc/+types/+core/FeatureExtraction.html delete mode 100644 doc/+types/+core/FilteredEphys.html delete mode 100644 doc/+types/+core/Fluorescence.html delete mode 100644 doc/+types/+core/GrayscaleImage.html delete mode 100644 doc/+types/+core/IZeroClampSeries.html delete mode 100644 doc/+types/+core/Image.html delete mode 100644 doc/+types/+core/ImageMaskSeries.html delete mode 100644 doc/+types/+core/ImageReferences.html delete mode 100644 doc/+types/+core/ImageSegmentation.html delete mode 100644 doc/+types/+core/ImageSeries.html delete mode 100644 doc/+types/+core/Images.html delete mode 100644 doc/+types/+core/ImagingPlane.html delete mode 100644 doc/+types/+core/ImagingRetinotopy.html delete mode 100644 doc/+types/+core/IndexSeries.html delete mode 100644 doc/+types/+core/IntervalSeries.html delete mode 100644 doc/+types/+core/IntracellularElectrode.html delete mode 100644 doc/+types/+core/IntracellularElectrodesTable.html delete mode 100644 doc/+types/+core/IntracellularRecordingsTable.html delete mode 100644 doc/+types/+core/IntracellularResponsesTable.html delete mode 100644 doc/+types/+core/IntracellularStimuliTable.html delete mode 100644 doc/+types/+core/LFP.html delete mode 100644 doc/+types/+core/LabMetaData.html delete mode 100644 doc/+types/+core/MotionCorrection.html delete mode 100644 doc/+types/+core/NWBContainer.html delete mode 100644 doc/+types/+core/NWBData.html delete mode 100644 doc/+types/+core/NWBDataInterface.html delete mode 100644 doc/+types/+core/NWBFile.html delete mode 100644 doc/+types/+core/OnePhotonSeries.html delete mode 100644 doc/+types/+core/OpticalChannel.html delete mode 100644 doc/+types/+core/OpticalSeries.html delete mode 100644 doc/+types/+core/OptogeneticSeries.html delete mode 100644 doc/+types/+core/OptogeneticStimulusSite.html delete mode 100644 doc/+types/+core/PatchClampSeries.html delete mode 100644 doc/+types/+core/PlaneSegmentation.html delete mode 100644 doc/+types/+core/Position.html delete mode 100644 doc/+types/+core/ProcessingModule.html delete mode 100644 doc/+types/+core/PupilTracking.html delete mode 100644 doc/+types/+core/RGBAImage.html delete mode 100644 doc/+types/+core/RGBImage.html delete mode 100644 doc/+types/+core/RepetitionsTable.html delete mode 100644 doc/+types/+core/RoiResponseSeries.html delete mode 100644 doc/+types/+core/ScratchData.html delete mode 100644 doc/+types/+core/SequentialRecordingsTable.html delete mode 100644 doc/+types/+core/SimultaneousRecordingsTable.html delete mode 100644 doc/+types/+core/SpatialSeries.html delete mode 100644 doc/+types/+core/SpikeEventSeries.html delete mode 100644 doc/+types/+core/Subject.html delete mode 100644 doc/+types/+core/SweepTable.html delete mode 100644 doc/+types/+core/TimeIntervals.html delete mode 100644 doc/+types/+core/TimeSeries.html delete mode 100644 doc/+types/+core/TimeSeriesReferenceVectorData.html delete mode 100644 doc/+types/+core/TwoPhotonSeries.html delete mode 100644 doc/+types/+core/Units.html delete mode 100644 doc/+types/+core/VoltageClampSeries.html delete mode 100644 doc/+types/+core/VoltageClampStimulusSeries.html delete mode 100644 doc/+types/+hdmf_common/AlignedDynamicTable.html delete mode 100644 doc/+types/+hdmf_common/CSRMatrix.html delete mode 100644 doc/+types/+hdmf_common/Container.html delete mode 100644 doc/+types/+hdmf_common/Data.html delete mode 100644 doc/+types/+hdmf_common/DynamicTable.html delete mode 100644 doc/+types/+hdmf_common/DynamicTableRegion.html delete mode 100644 doc/+types/+hdmf_common/ElementIdentifiers.html delete mode 100644 doc/+types/+hdmf_common/SimpleMultiContainer.html delete mode 100644 doc/+types/+hdmf_common/VectorData.html delete mode 100644 doc/+types/+hdmf_common/VectorIndex.html delete mode 100644 doc/+types/+hdmf_experimental/EnumData.html delete mode 100644 doc/+types/+hdmf_experimental/ExternalResources.html delete mode 100644 doc/+types/+untyped/+datapipe/+dynamic/Filter.html delete mode 100644 doc/+types/+untyped/+datapipe/+properties/DynamicFilter.html delete mode 100644 doc/+types/+untyped/+datapipe/+properties/Shuffle.html delete mode 100644 doc/NwbFile.html delete mode 100644 doc/alpha.png delete mode 100644 doc/c++.png delete mode 100644 doc/c.png delete mode 100644 doc/demoicon.gif delete mode 100644 doc/down.png delete mode 100644 doc/doxysearch.php delete mode 100644 doc/fortran.png delete mode 100644 doc/generateCore.html delete mode 100644 doc/generateExtension.html delete mode 100644 doc/helpwin.css delete mode 100644 doc/hp.png delete mode 100644 doc/index.html delete mode 100644 doc/left.png delete mode 100644 doc/linux.png delete mode 100644 doc/m2html.css delete mode 100644 doc/matlabicon.gif delete mode 100644 doc/mex.png delete mode 100644 doc/nwbExport.html delete mode 100644 doc/nwbRead.html delete mode 100644 doc/pcode.png delete mode 100644 doc/right.png delete mode 100644 doc/sgi.png delete mode 100644 doc/simulinkicon.gif delete mode 100644 doc/solaris.png delete mode 100644 doc/up.png delete mode 100644 doc/windows.png diff --git a/doc/+types/+core/AbstractFeatureSeries.html b/doc/+types/+core/AbstractFeatureSeries.html deleted file mode 100644 index 5e91da1c..00000000 --- a/doc/+types/+core/AbstractFeatureSeries.html +++ /dev/null @@ -1,399 +0,0 @@ - - - - - - types.core.AbstractFeatureSeries - MATLAB File Help - - - - - - - - - - -
types.core.AbstractFeatureSeries - MATLAB File Help
-
types.core.AbstractFeatureSeries
-
  AbstractFeatureSeries Abstract features, such as quantitative descriptions of sensory stimuli. The TimeSeries::data field is a 2D array, storing those features (e.g., for visual grating stimulus this might be orientation, spatial frequency and contrast). Null stimuli (eg, uniform gray) can be marked as being an independent feature (eg, 1.0 for gray, 0.0 for actual stimulus) or by storing NaNs for feature values, or through use of the TimeSeries::control fields. A set of features is considered to persist until the next set of features is defined. The final set of features stored should be the null set. This is useful when storing the raw stimulus is impractical.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
AbstractFeatureSeriesConstructor for AbstractFeatureSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
feature_units(char) Units of each feature. 
featuresREQUIRED (char) Description of the features represented in TimeSeries::data. 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_feature_units 
-   - - validate_features 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/AnnotationSeries.html b/doc/+types/+core/AnnotationSeries.html deleted file mode 100644 index a9984b87..00000000 --- a/doc/+types/+core/AnnotationSeries.html +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - types.core.AnnotationSeries - MATLAB File Help - - - - - - - - - - -
types.core.AnnotationSeries - MATLAB File Help
-
types.core.AnnotationSeries
-
  AnnotationSeries Stores user annotations made during an experiment. The data[] field stores a text array, and timestamps are stored for each annotation (ie, interval=1). This is largely an alias to a standard TimeSeries storing a text array but that is identifiable as storing annotations in a machine-readable way.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
AnnotationSeriesConstructor for AnnotationSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/BehavioralEpochs.html b/doc/+types/+core/BehavioralEpochs.html deleted file mode 100644 index d301a7a4..00000000 --- a/doc/+types/+core/BehavioralEpochs.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.BehavioralEpochs - MATLAB File Help - - - - - - - - - - -
types.core.BehavioralEpochs - MATLAB File Help
-
types.core.BehavioralEpochs
-
  BehavioralEpochs TimeSeries for storing behavioral epochs.  The objective of this and the other two Behavioral interfaces (e.g. BehavioralEvents and BehavioralTimeSeries) is to provide generic hooks for software tools/scripts. This allows a tool/script to take the output one specific interface (e.g., UnitTimes) and plot that data relative to another data modality (e.g., behavioral events) without having to define all possible modalities in advance. Declaring one of these interfaces means that one or more TimeSeries of the specified type is published. These TimeSeries should reside in a group having the same name as the interface. For example, if a BehavioralTimeSeries interface is declared, the module will have one or more TimeSeries defined in the module sub-group 'BehavioralTimeSeries'. BehavioralEpochs should use IntervalSeries. BehavioralEvents is used for irregular events. BehavioralTimeSeries is for continuous data.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
BehavioralEpochsConstructor for BehavioralEpochs 
- -
Property Summary -
- - - - - -
intervalseries(IntervalSeries) IntervalSeries object containing start and stop times of epochs. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_intervalseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/BehavioralEvents.html b/doc/+types/+core/BehavioralEvents.html deleted file mode 100644 index b4168341..00000000 --- a/doc/+types/+core/BehavioralEvents.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.BehavioralEvents - MATLAB File Help - - - - - - - - - - -
types.core.BehavioralEvents - MATLAB File Help
-
types.core.BehavioralEvents
-
  BehavioralEvents TimeSeries for storing behavioral events. See description of BehavioralEpochs for more details.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
BehavioralEventsConstructor for BehavioralEvents 
- -
Property Summary -
- - - - - -
timeseries(TimeSeries) TimeSeries object containing behavioral events. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_timeseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/BehavioralTimeSeries.html b/doc/+types/+core/BehavioralTimeSeries.html deleted file mode 100644 index c8e775a8..00000000 --- a/doc/+types/+core/BehavioralTimeSeries.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.BehavioralTimeSeries - MATLAB File Help - - - - - - - - - - -
types.core.BehavioralTimeSeries - MATLAB File Help
-
types.core.BehavioralTimeSeries
-
  BehavioralTimeSeries TimeSeries for storing Behavoioral time series data. See description of BehavioralEpochs for more details.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
BehavioralTimeSeriesConstructor for BehavioralTimeSeries 
- -
Property Summary -
- - - - - -
timeseries(TimeSeries) TimeSeries object containing continuous behavioral data. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_timeseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ClusterWaveforms.html b/doc/+types/+core/ClusterWaveforms.html deleted file mode 100644 index d84c2607..00000000 --- a/doc/+types/+core/ClusterWaveforms.html +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - types.core.ClusterWaveforms - MATLAB File Help - - - - - - - - - - -
types.core.ClusterWaveforms - MATLAB File Help
-
types.core.ClusterWaveforms
-
  ClusterWaveforms DEPRECATED The mean waveform shape, including standard deviation, of the different clusters. Ideally, the waveform analysis should be performed on data that is only high-pass filtered. This is a separate module because it is expected to require updating. For example, IMEC probes may require different storage requirements to store/display mean waveforms, requiring a new interface or an extension of this one.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ClusterWaveformsConstructor for ClusterWaveforms 
- -
Property Summary -
- - - - - - - - - - - - - - - - - -
clustering_interfaceClustering 
waveform_filteringREQUIRED (char) Filtering applied to data before generating mean/sd 
waveform_meanREQUIRED (single) The mean waveform for each cluster, using the same indices for each wave as cluster numbers in the associated - Clustering module (i.e, cluster 3 is in array slot [3]). Waveforms corresponding to gaps in cluster sequence should be empty - (e.g., zero- filled)  -
waveform_sdREQUIRED (single) Stdev of waveforms for each cluster, using the same indices as in mean 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_clustering_interface 
-   - - validate_waveform_filtering 
-   - - validate_waveform_mean 
-   - - validate_waveform_sd 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Clustering.html b/doc/+types/+core/Clustering.html deleted file mode 100644 index 7723a88e..00000000 --- a/doc/+types/+core/Clustering.html +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - types.core.Clustering - MATLAB File Help - - - - - - - - - - -
types.core.Clustering - MATLAB File Help
-
types.core.Clustering
-
  Clustering DEPRECATED Clustered spike data, whether from automatic clustering tools (e.g., klustakwik) or as a result of manual sorting.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ClusteringConstructor for Clustering 
- -
Property Summary -
- - - - - - - - - - - - - - - - - -
descriptionREQUIRED (char) Description of clusters or clustering, (e.g. cluster 0 is noise, clusters curated using Klusters, etc) 
numREQUIRED (int32) Cluster number of each event 
peak_over_rmsREQUIRED (single) Maximum ratio of waveform peak to RMS on any channel in the cluster (provides a basic clustering metric). 
timesREQUIRED (double) Times of clustered events, in seconds. This may be a link to times field in associated FeatureExtraction - module.  -
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_num 
-   - - validate_peak_over_rms 
-   - - validate_times 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/CompassDirection.html b/doc/+types/+core/CompassDirection.html deleted file mode 100644 index 3f933306..00000000 --- a/doc/+types/+core/CompassDirection.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.CompassDirection - MATLAB File Help - - - - - - - - - - -
types.core.CompassDirection - MATLAB File Help
-
types.core.CompassDirection
-
  CompassDirection With a CompassDirection interface, a module publishes a SpatialSeries object representing a floating point value for theta. The SpatialSeries::reference_frame field should indicate what direction corresponds to 0 and which is the direction of rotation (this should be clockwise). The si_unit for the SpatialSeries should be radians or degrees.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
CompassDirectionConstructor for CompassDirection 
- -
Property Summary -
- - - - - -
spatialseries(SpatialSeries) SpatialSeries object containing direction of gaze travel. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_spatialseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/CorrectedImageStack.html b/doc/+types/+core/CorrectedImageStack.html deleted file mode 100644 index d66698f0..00000000 --- a/doc/+types/+core/CorrectedImageStack.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - types.core.CorrectedImageStack - MATLAB File Help - - - - - - - - - - -
types.core.CorrectedImageStack - MATLAB File Help
-
types.core.CorrectedImageStack
-
  CorrectedImageStack Reuslts from motion correction of an image stack.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
CorrectedImageStackConstructor for CorrectedImageStack 
- -
Property Summary -
- - - - - - - - - - - - - -
correctedREQUIRED (ImageSeries) Image stack with frames shifted to the common coordinates. 
originalImageSeries 
xy_translationREQUIRED (TimeSeries) Stores the x,y delta necessary to align each frame to the common coordinates, for example, to align - each frame to a reference image.  -
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_corrected 
-   - - validate_original 
-   - - validate_xy_translation 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/CurrentClampSeries.html b/doc/+types/+core/CurrentClampSeries.html deleted file mode 100644 index b836e728..00000000 --- a/doc/+types/+core/CurrentClampSeries.html +++ /dev/null @@ -1,459 +0,0 @@ - - - - - - types.core.CurrentClampSeries - MATLAB File Help - - - - - - - - - - -
types.core.CurrentClampSeries - MATLAB File Help
-
types.core.CurrentClampSeries
-
  CurrentClampSeries Voltage data from an intracellular current-clamp recording. A corresponding CurrentClampStimulusSeries (stored separately as a stimulus) is used to store the current injected.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.PatchClampSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
CurrentClampSeriesConstructor for CurrentClampSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bias_current(single) Bias current, in amps. 
bridge_balance(single) Bridge balance, in ohms. 
capacitance_compensation(single) Capacitance compensation, in farads. 
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodeIntracellularElectrode 
gain(single) Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
stimulus_description(char) Protocol/stimulus name for this patch-clamp dataset. 
sweep_number(uint32) Sweep number, allows to group different PatchClampSeries together. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_bias_current 
-   - - validate_bridge_balance 
-   - - validate_capacitance_compensation 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrode 
-   - - validate_gain 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_stimulus_description 
-   - - validate_sweep_number 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/CurrentClampStimulusSeries.html b/doc/+types/+core/CurrentClampStimulusSeries.html deleted file mode 100644 index 8dd09f5b..00000000 --- a/doc/+types/+core/CurrentClampStimulusSeries.html +++ /dev/null @@ -1,423 +0,0 @@ - - - - - - types.core.CurrentClampStimulusSeries - MATLAB File Help - - - - - - - - - - -
types.core.CurrentClampStimulusSeries - MATLAB File Help
-
types.core.CurrentClampStimulusSeries
-
  CurrentClampStimulusSeries Stimulus current applied during current clamp recording.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.PatchClampSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
CurrentClampStimulusSeriesConstructor for CurrentClampStimulusSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodeIntracellularElectrode 
gain(single) Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
stimulus_description(char) Protocol/stimulus name for this patch-clamp dataset. 
sweep_number(uint32) Sweep number, allows to group different PatchClampSeries together. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrode 
-   - - validate_gain 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_stimulus_description 
-   - - validate_sweep_number 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/DecompositionSeries.html b/doc/+types/+core/DecompositionSeries.html deleted file mode 100644 index f7bcc6d9..00000000 --- a/doc/+types/+core/DecompositionSeries.html +++ /dev/null @@ -1,425 +0,0 @@ - - - - - - types.core.DecompositionSeries - MATLAB File Help - - - - - - - - - - -
types.core.DecompositionSeries - MATLAB File Help
-
types.core.DecompositionSeries
-
  DecompositionSeries Spectral analysis of a time series, e.g. of an LFP or a speech signal.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
DecompositionSeriesConstructor for DecompositionSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bandsREQUIRED (DynamicTable) Table for describing the bands that this series was generated from. There should be one row in this - table for each band.  -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
metricREQUIRED (char) The metric used, e.g. phase, amplitude, power. 
source_channels(DynamicTableRegion) DynamicTableRegion pointer to the channels that this decomposition series was generated from. 
source_timeseriesTimeSeries 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_bands 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_metric 
-   - - validate_source_channels 
-   - - validate_source_timeseries 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Device.html b/doc/+types/+core/Device.html deleted file mode 100644 index a9d1b38d..00000000 --- a/doc/+types/+core/Device.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - types.core.Device - MATLAB File Help - - - - - - - - - - -
types.core.Device - MATLAB File Help
-
types.core.Device
-
  Device Metadata about a data acquisition device, e.g., recording system, electrode, microscope.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
DeviceConstructor for Device 
- -
Property Summary -
- - - - - - - - - -
description(char) Description of the device (e.g., model, firmware version, processing software version, etc.) as free-form text. 
manufacturer(char) The name of the manufacturer of the device. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_manufacturer 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/DfOverF.html b/doc/+types/+core/DfOverF.html deleted file mode 100644 index 405d1119..00000000 --- a/doc/+types/+core/DfOverF.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.DfOverF - MATLAB File Help - - - - - - - - - - -
types.core.DfOverF - MATLAB File Help
-
types.core.DfOverF
-
  DfOverF dF/F information about a region of interest (ROI). Storage hierarchy of dF/F should be the same as for segmentation (i.e., same names for ROIs and for image planes).
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
DfOverFConstructor for DfOverF 
- -
Property Summary -
- - - - - -
roiresponseseriesREQUIRED (RoiResponseSeries) RoiResponseSeries object(s) containing dF/F for a ROI. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_roiresponseseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ElectricalSeries.html b/doc/+types/+core/ElectricalSeries.html deleted file mode 100644 index 4202d853..00000000 --- a/doc/+types/+core/ElectricalSeries.html +++ /dev/null @@ -1,427 +0,0 @@ - - - - - - types.core.ElectricalSeries - MATLAB File Help - - - - - - - - - - -
types.core.ElectricalSeries - MATLAB File Help
-
types.core.ElectricalSeries
-
  ElectricalSeries A time series of acquired voltage data from extracellular recordings. The data field is an int or float array storing data in volts. The first dimension should always represent time. The second dimension, if present, should represent channels.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ElectricalSeriesConstructor for ElectricalSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
channel_conversion(single) Channel-specific conversion factor. Multiply the data in the 'data' dataset by these values along the channel axis - (as indicated by axis attribute) AND by the global conversion factor in the 'conversion' attribute of 'data' to get the data - values in Volts, i.e, data in Volts = data * data.conversion * channel_conversion. This approach allows for both global and - per-channel data conversion factors needed to support the storage of electrical recordings as native values generated by data - acquisition systems. If this dataset is not present, then there is no channel-specific conversion factor, i.e. it is 1 for - all channels.  -
channel_conversion_axis(int32) The zero-indexed axis of the 'data' dataset that the channel-specific conversion factor corresponds to. This value - is fixed to 1.  -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodesREQUIRED (DynamicTableRegion) DynamicTableRegion pointer to the electrodes that this time series was generated from. 
filtering(char) Filtering applied to all channels of the data. For example, if this ElectricalSeries represents high-pass-filtered - data (also known as AP Band), then this value could be "High-pass 4-pole Bessel filter at 500 Hz". If this ElectricalSeries - represents low-pass-filtered LFP data and the type of filter is unknown, then this value could be "Low-pass filter at 300 - Hz". If a non-standard filter type is used, provide as much detail about the filter properties as possible.  -
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_channel_conversion 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrodes 
-   - - validate_filtering 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ElectrodeGroup.html b/doc/+types/+core/ElectrodeGroup.html deleted file mode 100644 index 2a0e6320..00000000 --- a/doc/+types/+core/ElectrodeGroup.html +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - types.core.ElectrodeGroup - MATLAB File Help - - - - - - - - - - -
types.core.ElectrodeGroup - MATLAB File Help
-
types.core.ElectrodeGroup
-
  ElectrodeGroup A physical grouping of electrodes, e.g. a shank of an array.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ElectrodeGroupConstructor for ElectrodeGroup 
- -
Property Summary -
- - - - - - - - - - - - - - - - - -
description(char) Description of this electrode group. 
deviceDevice 
location(char) Location of electrode group. Specify the area, layer, comments on estimation of area/layer, etc. Use standard atlas - names for anatomical regions when possible.  -
position(Table with columns: (x = single, y = single, z = single)) stereotaxic or common framework coordinates 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_device 
-   - - validate_location 
-   - - validate_position 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/EventDetection.html b/doc/+types/+core/EventDetection.html deleted file mode 100644 index 87587d1b..00000000 --- a/doc/+types/+core/EventDetection.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - types.core.EventDetection - MATLAB File Help - - - - - - - - - - -
types.core.EventDetection - MATLAB File Help
-
types.core.EventDetection
-
  EventDetection Detected spike events from voltage trace(s).
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
EventDetectionConstructor for EventDetection 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - -
detection_methodREQUIRED (char) Description of how events were detected, such as voltage threshold, or dV/dT threshold, as well as relevant - values.  -
source_electricalseriesElectricalSeries 
source_idxREQUIRED (int32) Indices (zero-based) into source ElectricalSeries::data array corresponding to time of event. ''description'' - should define what is meant by time of event (e.g., .25 ms before action potential peak, zero-crossing time, etc). The index - points to each event from the raw data.  -
timesREQUIRED (double) Timestamps of events, in seconds. 
times_unit(char) Unit of measurement for event times, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_detection_method 
-   - - validate_source_electricalseries 
-   - - validate_source_idx 
-   - - validate_times 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/EventWaveform.html b/doc/+types/+core/EventWaveform.html deleted file mode 100644 index a77edc19..00000000 --- a/doc/+types/+core/EventWaveform.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.EventWaveform - MATLAB File Help - - - - - - - - - - -
types.core.EventWaveform - MATLAB File Help
-
types.core.EventWaveform
-
  EventWaveform Represents either the waveforms of detected events, as extracted from a raw data trace in /acquisition, or the event waveforms that were stored during experiment acquisition.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
EventWaveformConstructor for EventWaveform 
- -
Property Summary -
- - - - - -
spikeeventseries(SpikeEventSeries) SpikeEventSeries object(s) containing detected spike event waveforms. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_spikeeventseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ExperimentalConditionsTable.html b/doc/+types/+core/ExperimentalConditionsTable.html deleted file mode 100644 index 962826a6..00000000 --- a/doc/+types/+core/ExperimentalConditionsTable.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - types.core.ExperimentalConditionsTable - MATLAB File Help - - - - - - - - - - -
types.core.ExperimentalConditionsTable - MATLAB File Help
-
types.core.ExperimentalConditionsTable
-
  ExperimentalConditionsTable A table for grouping different intracellular recording repetitions together that belong to the same experimental condition.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ExperimentalConditionsTableConstructor for ExperimentalConditionsTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
repetitionsREQUIRED (DynamicTableRegion) A reference to one or more rows in the RepetitionsTable table. 
repetitions_indexREQUIRED (VectorIndex) Index dataset for the repetitions column. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_repetitions 
-   - - validate_repetitions_index 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/EyeTracking.html b/doc/+types/+core/EyeTracking.html deleted file mode 100644 index 719a9178..00000000 --- a/doc/+types/+core/EyeTracking.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.EyeTracking - MATLAB File Help - - - - - - - - - - -
types.core.EyeTracking - MATLAB File Help
-
types.core.EyeTracking
-
  EyeTracking Eye-tracking data, representing direction of gaze.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
EyeTrackingConstructor for EyeTracking 
- -
Property Summary -
- - - - - -
spatialseries(SpatialSeries) SpatialSeries object containing data measuring direction of gaze. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_spatialseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/FeatureExtraction.html b/doc/+types/+core/FeatureExtraction.html deleted file mode 100644 index 21ec5a93..00000000 --- a/doc/+types/+core/FeatureExtraction.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - types.core.FeatureExtraction - MATLAB File Help - - - - - - - - - - -
types.core.FeatureExtraction - MATLAB File Help
-
types.core.FeatureExtraction
-
  FeatureExtraction Features, such as PC1 and PC2, that are extracted from signals stored in a SpikeEventSeries or other source.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
FeatureExtractionConstructor for FeatureExtraction 
- -
Property Summary -
- - - - - - - - - - - - - - - - - -
descriptionREQUIRED (char) Description of features (eg, ''PC1'') for each of the extracted features. 
electrodesREQUIRED (DynamicTableRegion) DynamicTableRegion pointer to the electrodes that this time series was generated from. 
featuresREQUIRED (single) Multi-dimensional array of features extracted from each event. 
timesREQUIRED (double) Times of events that features correspond to (can be a link). 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_electrodes 
-   - - validate_features 
-   - - validate_times 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/FilteredEphys.html b/doc/+types/+core/FilteredEphys.html deleted file mode 100644 index 68c33da5..00000000 --- a/doc/+types/+core/FilteredEphys.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.FilteredEphys - MATLAB File Help - - - - - - - - - - -
types.core.FilteredEphys - MATLAB File Help
-
types.core.FilteredEphys
-
  FilteredEphys Electrophysiology data from one or more channels that has been subjected to filtering. Examples of filtered data include Theta and Gamma (LFP has its own interface). FilteredEphys modules publish an ElectricalSeries for each filtered channel or set of channels. The name of each ElectricalSeries is arbitrary but should be informative. The source of the filtered data, whether this is from analysis of another time series or as acquired by hardware, should be noted in each's TimeSeries::description field. There is no assumed 1::1 correspondence between filtered ephys signals and electrodes, as a single signal can apply to many nearby electrodes, and one electrode may have different filtered (e.g., theta and/or gamma) signals represented. Filter properties should be noted in the ElectricalSeries 'filtering' attribute.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
FilteredEphysConstructor for FilteredEphys 
- -
Property Summary -
- - - - - -
electricalseriesREQUIRED (ElectricalSeries) ElectricalSeries object(s) containing filtered electrophysiology data. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_electricalseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Fluorescence.html b/doc/+types/+core/Fluorescence.html deleted file mode 100644 index f5cf3dc7..00000000 --- a/doc/+types/+core/Fluorescence.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.Fluorescence - MATLAB File Help - - - - - - - - - - -
types.core.Fluorescence - MATLAB File Help
-
types.core.Fluorescence
-
  Fluorescence Fluorescence information about a region of interest (ROI). Storage hierarchy of fluorescence should be the same as for segmentation (ie, same names for ROIs and for image planes).
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
FluorescenceConstructor for Fluorescence 
- -
Property Summary -
- - - - - -
roiresponseseriesREQUIRED (RoiResponseSeries) RoiResponseSeries object(s) containing fluorescence data for a ROI. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_roiresponseseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/GrayscaleImage.html b/doc/+types/+core/GrayscaleImage.html deleted file mode 100644 index ae3ed87e..00000000 --- a/doc/+types/+core/GrayscaleImage.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.core.GrayscaleImage - MATLAB File Help - - - - - - - - - - -
types.core.GrayscaleImage - MATLAB File Help
-
types.core.GrayscaleImage
-
  GrayscaleImage A grayscale image.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.Image, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
GrayscaleImageConstructor for GrayscaleImage 
- -
Property Summary -
- - - - - - - - - - - - - -
dataREQUIRED any 
description(char) Description of the image. 
resolution(single) Pixel resolution of the image, in pixels per centimeter. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
-   - - validate_resolution 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IZeroClampSeries.html b/doc/+types/+core/IZeroClampSeries.html deleted file mode 100644 index f5eb34f4..00000000 --- a/doc/+types/+core/IZeroClampSeries.html +++ /dev/null @@ -1,459 +0,0 @@ - - - - - - types.core.IZeroClampSeries - MATLAB File Help - - - - - - - - - - -
types.core.IZeroClampSeries - MATLAB File Help
-
types.core.IZeroClampSeries
-
  IZeroClampSeries Voltage data from an intracellular recording when all current and amplifier settings are off (i.e., CurrentClampSeries fields will be zero). There is no CurrentClampStimulusSeries associated with an IZero series because the amplifier is disconnected and no stimulus can reach the cell.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.CurrentClampSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IZeroClampSeriesConstructor for IZeroClampSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bias_current(single) Bias current, in amps. 
bridge_balance(single) Bridge balance, in ohms. 
capacitance_compensation(single) Capacitance compensation, in farads. 
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodeIntracellularElectrode 
gain(single) Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
stimulus_description(char) Protocol/stimulus name for this patch-clamp dataset. 
sweep_number(uint32) Sweep number, allows to group different PatchClampSeries together. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_bias_current 
-   - - validate_bridge_balance 
-   - - validate_capacitance_compensation 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrode 
-   - - validate_gain 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_stimulus_description 
-   - - validate_sweep_number 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Image.html b/doc/+types/+core/Image.html deleted file mode 100644 index b4969735..00000000 --- a/doc/+types/+core/Image.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.core.Image - MATLAB File Help - - - - - - - - - - -
types.core.Image - MATLAB File Help
-
types.core.Image
-
  Image An abstract data type for an image. Shape can be 2-D (x, y), or 3-D where the third dimension can have three or four elements, e.g. (x, y, (r, g, b)) or (x, y, (r, g, b, a)).
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBData, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImageConstructor for Image 
- -
Property Summary -
- - - - - - - - - - - - - -
dataREQUIRED any 
description(char) Description of the image. 
resolution(single) Pixel resolution of the image, in pixels per centimeter. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
-   - - validate_resolution 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ImageMaskSeries.html b/doc/+types/+core/ImageMaskSeries.html deleted file mode 100644 index a02d0abc..00000000 --- a/doc/+types/+core/ImageMaskSeries.html +++ /dev/null @@ -1,460 +0,0 @@ - - - - - - types.core.ImageMaskSeries - MATLAB File Help - - - - - - - - - - -
types.core.ImageMaskSeries - MATLAB File Help
-
types.core.ImageMaskSeries
-
  ImageMaskSeries An alpha mask that is applied to a presented visual stimulus. The 'data' array contains an array of mask values that are applied to the displayed image. Mask values are stored as RGBA. Mask can vary with time. The timestamps array indicates the starting time of a mask, and that mask pattern continues until it's explicitly changed.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.ImageSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImageMaskSeriesConstructor for ImageMaskSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
deviceDevice 
dimension(int32) Number of pixels on x, y, (and z) axes. 
external_file(char) Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the - image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored - in another NWB file and that file is linked to this file.  -
external_file_starting_frame(int32) Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an - index to indicate which frames each file contains, to faciliate random access. The 'starting_frame' attribute, hence, contains - a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. - Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has - three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then - this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries - (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0].  -
format(char) Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image - files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not - present, then the default format='raw' case is assumed.  -
masked_imageseriesImageSeries 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_device 
-   - - validate_dimension 
-   - - validate_external_file 
-   - - validate_external_file_starting_frame 
-   - - validate_format 
-   - - validate_masked_imageseries 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ImageReferences.html b/doc/+types/+core/ImageReferences.html deleted file mode 100644 index 846ee11d..00000000 --- a/doc/+types/+core/ImageReferences.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.ImageReferences - MATLAB File Help - - - - - - - - - - -
types.core.ImageReferences - MATLAB File Help
-
types.core.ImageReferences
-
  ImageReferences Ordered dataset of references to Image objects.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBData, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImageReferencesConstructor for ImageReferences 
- -
Property Summary -
- - - - - -
dataREQUIRED any 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_dataReference to type `Image` 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ImageSegmentation.html b/doc/+types/+core/ImageSegmentation.html deleted file mode 100644 index 99f40f29..00000000 --- a/doc/+types/+core/ImageSegmentation.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.ImageSegmentation - MATLAB File Help - - - - - - - - - - -
types.core.ImageSegmentation - MATLAB File Help
-
types.core.ImageSegmentation
-
  ImageSegmentation Stores pixels in an image that represent different regions of interest (ROIs) or masks. All segmentation for a given imaging plane is stored together, with storage for multiple imaging planes (masks) supported. Each ROI is stored in its own subgroup, with the ROI group containing both a 2D mask and a list of pixels that make up this mask. Segments can also be used for masking neuropil. If segmentation is allowed to change with time, a new imaging plane (or module) is required and ROI names should remain consistent between them.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImageSegmentationConstructor for ImageSegmentation 
- -
Property Summary -
- - - - - -
planesegmentationREQUIRED (PlaneSegmentation) Results from image segmentation of a specific imaging plane. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_planesegmentation 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ImageSeries.html b/doc/+types/+core/ImageSeries.html deleted file mode 100644 index 68f05853..00000000 --- a/doc/+types/+core/ImageSeries.html +++ /dev/null @@ -1,448 +0,0 @@ - - - - - - types.core.ImageSeries - MATLAB File Help - - - - - - - - - - -
types.core.ImageSeries - MATLAB File Help
-
types.core.ImageSeries
-
  ImageSeries General image data that is common between acquisition and stimulus time series. Sometimes the image data is stored in the file in a raw format while other times it will be stored as a series of external image files in the host file system. The data field will either be binary data, if the data is stored in the NWB file, or empty, if the data is stored in an external image stack. [frame][x][y] or [frame][x][y][z].
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImageSeriesConstructor for ImageSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
deviceDevice 
dimension(int32) Number of pixels on x, y, (and z) axes. 
external_file(char) Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the - image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored - in another NWB file and that file is linked to this file.  -
external_file_starting_frame(int32) Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an - index to indicate which frames each file contains, to faciliate random access. The 'starting_frame' attribute, hence, contains - a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. - Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has - three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then - this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries - (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0].  -
format(char) Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image - files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not - present, then the default format='raw' case is assumed.  -
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_device 
-   - - validate_dimension 
-   - - validate_external_file 
-   - - validate_external_file_starting_frame 
-   - - validate_format 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Images.html b/doc/+types/+core/Images.html deleted file mode 100644 index 1666f6dc..00000000 --- a/doc/+types/+core/Images.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - types.core.Images - MATLAB File Help - - - - - - - - - - -
types.core.Images - MATLAB File Help
-
types.core.Images
-
  Images A collection of images with an optional way to specify the order of the images using the "order_of_images" dataset. An order must be specified if the images are referenced by index, e.g., from an IndexSeries.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImagesConstructor for Images 
- -
Property Summary -
- - - - - - - - - - - - - -
description(char) Description of this collection of images. 
imageREQUIRED (Image) Images stored in this collection. 
order_of_images(ImageReferences) Ordered dataset of references to Image objects stored in the parent group. Each Image object in the Images - group should be stored once and only once, so the dataset should have the same length as the number of images.  -
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_image 
-   - - validate_order_of_images 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ImagingPlane.html b/doc/+types/+core/ImagingPlane.html deleted file mode 100644 index 7451b3db..00000000 --- a/doc/+types/+core/ImagingPlane.html +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - types.core.ImagingPlane - MATLAB File Help - - - - - - - - - - -
types.core.ImagingPlane - MATLAB File Help
-
types.core.ImagingPlane
-
  ImagingPlane An imaging plane and its metadata.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImagingPlaneConstructor for ImagingPlane 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
description(char) Description of the imaging plane. 
deviceDevice 
excitation_lambdaREQUIRED (single) Excitation wavelength, in nm. 
grid_spacing(single) Space between pixels in (x, y) or voxels in (x, y, z) directions, in the specified unit. Assumes imaging plane is - a regular grid. See also reference_frame to interpret the grid.  -
grid_spacing_unit(char) Measurement units for grid_spacing. The default value is 'meters'. 
imaging_rate(single) Rate that images are acquired, in Hz. If the corresponding TimeSeries is present, the rate should be stored there - instead.  -
indicatorREQUIRED (char) Calcium indicator. 
locationREQUIRED (char) Location of the imaging plane. Specify the area, layer, comments on estimation of area/layer, stereotaxic - coordinates if in vivo, etc. Use standard atlas names for anatomical regions when possible.  -
manifold(single) DEPRECATED Physical position of each pixel. 'xyz' represents the position of the pixel relative to the defined coordinate - space. Deprecated in favor of origin_coords and grid_spacing.  -
manifold_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as pixels from x = -500 to - 499, y = -500 to 499 that correspond to a 2 m x 2 m range, then the 'conversion' multiplier to get from raw data acquisition - pixel units to meters is 2/1000.  -
manifold_unit(char) Base unit of measurement for working with the data. The default value is 'meters'. 
opticalchannelREQUIRED (OpticalChannel) An optical channel used to record from an imaging plane. 
origin_coords(single) Physical location of the first element of the imaging plane (0, 0) for 2-D data or (0, 0, 0) for 3-D data. See also - reference_frame for what the physical location is relative to (e.g., bregma).  -
origin_coords_unit(char) Measurement units for origin_coords. The default value is 'meters'. 
reference_frame(char) Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical - location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate - the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. - If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 - x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to - bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, - larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords - = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = "Origin coordinates are relative to bregma. First dimension - corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis - (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral)."  -
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_device 
-   - - validate_excitation_lambda 
-   - - validate_grid_spacing 
-   - - validate_grid_spacing_unit 
-   - - validate_imaging_rate 
-   - - validate_indicator 
-   - - validate_location 
-   - - validate_manifold 
-   - - validate_manifold_conversion 
-   - - validate_manifold_unit 
-   - - validate_opticalchannel 
-   - - validate_origin_coords 
-   - - validate_origin_coords_unit 
-   - - validate_reference_frame 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ImagingRetinotopy.html b/doc/+types/+core/ImagingRetinotopy.html deleted file mode 100644 index 81f2357b..00000000 --- a/doc/+types/+core/ImagingRetinotopy.html +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - types.core.ImagingRetinotopy - MATLAB File Help - - - - - - - - - - -
types.core.ImagingRetinotopy - MATLAB File Help
-
types.core.ImagingRetinotopy
-
  ImagingRetinotopy Intrinsic signal optical imaging or widefield imaging for measuring retinotopy. Stores orthogonal maps (e.g., altitude/azimuth; radius/theta) of responses to specific stimuli and a combined polarity map from which to identify visual areas. This group does not store the raw responses imaged during retinotopic mapping or the stimuli presented, but rather the resulting phase and power maps after applying a Fourier transform on the averaged responses. Note: for data consistency, all images and arrays are stored in the format [row][column] and [row, col], which equates to [y][x]. Field of view and dimension arrays may appear backward (i.e., y before x).
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ImagingRetinotopyConstructor for ImagingRetinotopy 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
axis_1_phase_mapREQUIRED (single) Phase response to stimulus on the first measured axis. 
axis_1_phase_map_dimension(int32) Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. 
axis_1_phase_map_field_of_view(single) Size of viewing area, in meters. 
axis_1_phase_map_unit(char) Unit that axis data is stored in (e.g., degrees). 
axis_1_power_map(single) Power response on the first measured axis. Response is scaled so 0.0 is no power in the response and 1.0 is maximum - relative power.  -
axis_1_power_map_dimension(int32) Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. 
axis_1_power_map_field_of_view(single) Size of viewing area, in meters. 
axis_1_power_map_unit(char) Unit that axis data is stored in (e.g., degrees). 
axis_2_phase_mapREQUIRED (single) Phase response to stimulus on the second measured axis. 
axis_2_phase_map_dimension(int32) Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. 
axis_2_phase_map_field_of_view(single) Size of viewing area, in meters. 
axis_2_phase_map_unit(char) Unit that axis data is stored in (e.g., degrees). 
axis_2_power_map(single) Power response on the second measured axis. Response is scaled so 0.0 is no power in the response and 1.0 is maximum - relative power.  -
axis_2_power_map_dimension(int32) Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. 
axis_2_power_map_field_of_view(single) Size of viewing area, in meters. 
axis_2_power_map_unit(char) Unit that axis data is stored in (e.g., degrees). 
axis_descriptionsREQUIRED (char) Two-element array describing the contents of the two response axis fields. Description should be something - like ['altitude', 'azimuth'] or '['radius', 'theta'].  -
focal_depth_image(uint16) Gray-scale image taken with same settings/parameters (e.g., focal depth, wavelength) as data collection. Array format: - [rows][columns].  -
focal_depth_image_bits_per_pixel(int32) Number of bits used to represent each value. This is necessary to determine maximum (white) pixel value. 
focal_depth_image_dimension(int32) Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. 
focal_depth_image_field_of_view(single) Size of viewing area, in meters. 
focal_depth_image_focal_depth(single) Focal depth offset, in meters. 
focal_depth_image_format(char) Format of image. Right now only 'raw' is supported. 
sign_map(single) Sine of the angle between the direction of the gradient in axis_1 and axis_2. 
sign_map_dimension(int32) Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. 
sign_map_field_of_view(single) Size of viewing area, in meters. 
vasculature_imageREQUIRED (uint16) Gray-scale anatomical image of cortical surface. Array structure: [rows][columns] 
vasculature_image_bits_per_pixel(int32) Number of bits used to represent each value. This is necessary to determine maximum (white) pixel value 
vasculature_image_dimension(int32) Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. 
vasculature_image_field_of_view(single) Size of viewing area, in meters. 
vasculature_image_format(char) Format of image. Right now only 'raw' is supported. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_axis_1_phase_map 
-   - - validate_axis_1_phase_map_dimension 
-   - - validate_axis_1_phase_map_field_of_view 
-   - - validate_axis_1_phase_map_unit 
-   - - validate_axis_1_power_map 
-   - - validate_axis_1_power_map_dimension 
-   - - validate_axis_1_power_map_field_of_view 
-   - - validate_axis_1_power_map_unit 
-   - - validate_axis_2_phase_map 
-   - - validate_axis_2_phase_map_dimension 
-   - - validate_axis_2_phase_map_field_of_view 
-   - - validate_axis_2_phase_map_unit 
-   - - validate_axis_2_power_map 
-   - - validate_axis_2_power_map_dimension 
-   - - validate_axis_2_power_map_field_of_view 
-   - - validate_axis_2_power_map_unit 
-   - - validate_axis_descriptions 
-   - - validate_focal_depth_image 
-   - - validate_focal_depth_image_bits_per_pixel 
-   - - validate_focal_depth_image_dimension 
-   - - validate_focal_depth_image_field_of_view 
-   - - validate_focal_depth_image_focal_depth 
-   - - validate_focal_depth_image_format 
-   - - validate_sign_map 
-   - - validate_sign_map_dimension 
-   - - validate_sign_map_field_of_view 
-   - - validate_vasculature_image 
-   - - validate_vasculature_image_bits_per_pixel 
-   - - validate_vasculature_image_dimension 
-   - - validate_vasculature_image_field_of_view 
-   - - validate_vasculature_image_format 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IndexSeries.html b/doc/+types/+core/IndexSeries.html deleted file mode 100644 index 4b986248..00000000 --- a/doc/+types/+core/IndexSeries.html +++ /dev/null @@ -1,399 +0,0 @@ - - - - - - types.core.IndexSeries - MATLAB File Help - - - - - - - - - - -
types.core.IndexSeries - MATLAB File Help
-
types.core.IndexSeries
-
  IndexSeries Stores indices to image frames stored in an ImageSeries. The purpose of the IndexSeries is to allow a static image stack to be stored in an Images object, and the images in the stack to be referenced out-of-order. This can be for the display of individual images, or of movie segments (as a movie is simply a series of images). The data field stores the index of the frame in the referenced Images object, and the timestamps array indicates when that image was displayed.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IndexSeriesConstructor for IndexSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
indexed_imagesImages 
indexed_timeseriesImageSeries 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_indexed_images 
-   - - validate_indexed_timeseries 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IntervalSeries.html b/doc/+types/+core/IntervalSeries.html deleted file mode 100644 index c63179da..00000000 --- a/doc/+types/+core/IntervalSeries.html +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - types.core.IntervalSeries - MATLAB File Help - - - - - - - - - - -
types.core.IntervalSeries - MATLAB File Help
-
types.core.IntervalSeries
-
  IntervalSeries Stores intervals of data. The timestamps field stores the beginning and end of intervals. The data field stores whether the interval just started (>0 value) or ended (<0 value). Different interval types can be represented in the same series by using multiple key values (eg, 1 for feature A, 2 for feature B, 3 for feature C, etc). The field data stores an 8-bit integer. This is largely an alias of a standard TimeSeries but that is identifiable as representing time intervals in a machine-readable way.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IntervalSeriesConstructor for IntervalSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IntracellularElectrode.html b/doc/+types/+core/IntracellularElectrode.html deleted file mode 100644 index 79d21fdb..00000000 --- a/doc/+types/+core/IntracellularElectrode.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - types.core.IntracellularElectrode - MATLAB File Help - - - - - - - - - - -
types.core.IntracellularElectrode - MATLAB File Help
-
types.core.IntracellularElectrode
-
  IntracellularElectrode An intracellular electrode and its metadata.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IntracellularElectrodeConstructor for IntracellularElectrode 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cell_id(char) unique ID of the cell 
descriptionREQUIRED (char) Description of electrode (e.g., whole-cell, sharp, etc.). 
deviceDevice 
filtering(char) Electrode specific filtering. 
initial_access_resistance(char) Initial access resistance. 
location(char) Location of the electrode. Specify the area, layer, comments on estimation of area/layer, stereotaxic coordinates if - in vivo, etc. Use standard atlas names for anatomical regions when possible.  -
resistance(char) Electrode resistance, in ohms. 
seal(char) Information about seal used for recording. 
slice(char) Information about slice used for recording. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_cell_id 
-   - - validate_description 
-   - - validate_device 
-   - - validate_filtering 
-   - - validate_initial_access_resistance 
-   - - validate_location 
-   - - validate_resistance 
-   - - validate_seal 
-   - - validate_slice 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IntracellularElectrodesTable.html b/doc/+types/+core/IntracellularElectrodesTable.html deleted file mode 100644 index 886d77a5..00000000 --- a/doc/+types/+core/IntracellularElectrodesTable.html +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - types.core.IntracellularElectrodesTable - MATLAB File Help - - - - - - - - - - -
types.core.IntracellularElectrodesTable - MATLAB File Help
-
types.core.IntracellularElectrodesTable
-
  IntracellularElectrodesTable Table for storing intracellular electrode related metadata.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IntracellularElectrodesTableConstructor for IntracellularElectrodesTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
electrodeREQUIRED (VectorData) Column for storing the reference to the intracellular electrode. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_electrode 
-   - - validate_id 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IntracellularRecordingsTable.html b/doc/+types/+core/IntracellularRecordingsTable.html deleted file mode 100644 index c8ea3022..00000000 --- a/doc/+types/+core/IntracellularRecordingsTable.html +++ /dev/null @@ -1,335 +0,0 @@ - - - - - - types.core.IntracellularRecordingsTable - MATLAB File Help - - - - - - - - - - -
types.core.IntracellularRecordingsTable - MATLAB File Help
-
types.core.IntracellularRecordingsTable
-
  IntracellularRecordingsTable A table to group together a stimulus and response from a single electrode and a single simultaneous recording. Each row in the table represents a single recording consisting typically of a stimulus and a corresponding response. In some cases, however, only a stimulus or a response is recorded as part of an experiment. In this case, both the stimulus and response will point to the same TimeSeries while the idx_start and count of the invalid column will be set to -1, thus, indicating that no values have been recorded for the stimulus or response, respectively. Note, a recording MUST contain at least a stimulus or a response. Typically the stimulus and response are PatchClampSeries. However, the use of AD/DA channels that are not associated to an electrode is also common in intracellular electrophysiology, in which case other TimeSeries may be used.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.AlignedDynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IntracellularRecordingsTableConstructor for IntracellularRecordingsTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
categories(char) The names of the categories in this AlignedDynamicTable. Each category is represented by one DynamicTable stored in - the parent group. This attribute should be used to specify an order of categories and the category names must match the names - of the corresponding DynamicTable in the group.  -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
dynamictable(DynamicTable) A DynamicTable representing a particular category for columns in the AlignedDynamicTable parent container. - The table MUST be aligned with (i.e., have the same number of rows) as all other DynamicTables stored in the AlignedDynamicTable - parent container. The name of the category is given by the name of the DynamicTable and its description by the description - attribute of the DynamicTable.  -
electrodesREQUIRED (IntracellularElectrodesTable) Table for storing intracellular electrode related metadata. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
responsesREQUIRED (IntracellularResponsesTable) Table for storing intracellular response related metadata. 
stimuliREQUIRED (IntracellularStimuliTable) Table for storing intracellular stimulus related metadata. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_categories 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_dynamictable 
-   - - validate_electrodes 
-   - - validate_id 
-   - - validate_responses 
-   - - validate_stimuli 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IntracellularResponsesTable.html b/doc/+types/+core/IntracellularResponsesTable.html deleted file mode 100644 index 21cca4d6..00000000 --- a/doc/+types/+core/IntracellularResponsesTable.html +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - types.core.IntracellularResponsesTable - MATLAB File Help - - - - - - - - - - -
types.core.IntracellularResponsesTable - MATLAB File Help
-
types.core.IntracellularResponsesTable
-
  IntracellularResponsesTable Table for storing intracellular response related metadata.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IntracellularResponsesTableConstructor for IntracellularResponsesTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
responseREQUIRED (TimeSeriesReferenceVectorData) Column storing the reference to the recorded response for the recording (rows) 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_response 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/IntracellularStimuliTable.html b/doc/+types/+core/IntracellularStimuliTable.html deleted file mode 100644 index a20c0fd4..00000000 --- a/doc/+types/+core/IntracellularStimuliTable.html +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - types.core.IntracellularStimuliTable - MATLAB File Help - - - - - - - - - - -
types.core.IntracellularStimuliTable - MATLAB File Help
-
types.core.IntracellularStimuliTable
-
  IntracellularStimuliTable Table for storing intracellular stimulus related metadata.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
IntracellularStimuliTableConstructor for IntracellularStimuliTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
stimulusREQUIRED (TimeSeriesReferenceVectorData) Column storing the reference to the recorded stimulus for the recording (rows). 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_stimulus 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/LFP.html b/doc/+types/+core/LFP.html deleted file mode 100644 index ab88421e..00000000 --- a/doc/+types/+core/LFP.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.LFP - MATLAB File Help - - - - - - - - - - -
types.core.LFP - MATLAB File Help
-
types.core.LFP
-
  LFP LFP data from one or more channels. The electrode map in each published ElectricalSeries will identify which channels are providing LFP data. Filter properties should be noted in the ElectricalSeries 'filtering' attribute.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
LFPConstructor for LFP 
- -
Property Summary -
- - - - - -
electricalseriesREQUIRED (ElectricalSeries) ElectricalSeries object(s) containing LFP data for one or more channels. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_electricalseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/LabMetaData.html b/doc/+types/+core/LabMetaData.html deleted file mode 100644 index 3b27632e..00000000 --- a/doc/+types/+core/LabMetaData.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - types.core.LabMetaData - MATLAB File Help - - - - - - - - - - -
types.core.LabMetaData - MATLAB File Help
-
types.core.LabMetaData
-
  LabMetaData Lab-specific meta-data.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
LabMetaDataConstructor for LabMetaData 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/MotionCorrection.html b/doc/+types/+core/MotionCorrection.html deleted file mode 100644 index b243c40c..00000000 --- a/doc/+types/+core/MotionCorrection.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.MotionCorrection - MATLAB File Help - - - - - - - - - - -
types.core.MotionCorrection - MATLAB File Help
-
types.core.MotionCorrection
-
  MotionCorrection An image stack where all frames are shifted (registered) to a common coordinate system, to account for movement and drift between frames. Note: each frame at each point in time is assumed to be 2-D (has only x & y dimensions).
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
MotionCorrectionConstructor for MotionCorrection 
- -
Property Summary -
- - - - - -
correctedimagestackREQUIRED (CorrectedImageStack) Reuslts from motion correction of an image stack. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_correctedimagestack 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/NWBContainer.html b/doc/+types/+core/NWBContainer.html deleted file mode 100644 index bb766466..00000000 --- a/doc/+types/+core/NWBContainer.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - types.core.NWBContainer - MATLAB File Help - - - - - - - - - - -
types.core.NWBContainer - MATLAB File Help
-
types.core.NWBContainer
-
  NWBContainer An abstract data type for a generic container storing collections of data and metadata. Base type for all data and metadata containers.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Container, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
NWBContainerConstructor for NWBContainer 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/NWBData.html b/doc/+types/+core/NWBData.html deleted file mode 100644 index bb28606d..00000000 --- a/doc/+types/+core/NWBData.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.NWBData - MATLAB File Help - - - - - - - - - - -
types.core.NWBData - MATLAB File Help
-
types.core.NWBData
-
  NWBData An abstract data type for a dataset.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Data, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
NWBDataConstructor for NWBData 
- -
Property Summary -
- - - - - -
dataREQUIRED any 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/NWBDataInterface.html b/doc/+types/+core/NWBDataInterface.html deleted file mode 100644 index c084c98c..00000000 --- a/doc/+types/+core/NWBDataInterface.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - types.core.NWBDataInterface - MATLAB File Help - - - - - - - - - - -
types.core.NWBDataInterface - MATLAB File Help
-
types.core.NWBDataInterface
-
  NWBDataInterface An abstract data type for a generic container storing collections of data, as opposed to metadata.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
NWBDataInterfaceConstructor for NWBDataInterface 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/NWBFile.html b/doc/+types/+core/NWBFile.html deleted file mode 100644 index 63e82d7f..00000000 --- a/doc/+types/+core/NWBFile.html +++ /dev/null @@ -1,804 +0,0 @@ - - - - - - types.core.NWBFile - MATLAB File Help - - - - - - - - - - -
types.core.NWBFile - MATLAB File Help
-
types.core.NWBFile
-
  NWBFile An NWB file storing cellular-based neurophysiology data from a single experimental session.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
NWBFileConstructor for NWBFile 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
acquisition(DynamicTable|NWBDataInterface) Tabular data that is relevent to acquisition | Acquired, raw data. 
analysis(DynamicTable|NWBContainer) Tabular data that is relevent to data stored in analysis | Custom analysis results. 
file_create_dateREQUIRED (datetime) A record of the date the file was created and of subsequent modifications. The date is stored in UTC with - local timezone offset as ISO 8601 extended formatted strings: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" - with no timezone offset. Date accuracy is up to milliseconds. The file can be created after the experiment was run, so this - may differ from the experiment start time. Each modification to the nwb file adds a new entry to the array.  -
general(LabMetaData) Place-holder than can be extended so that lab-specific meta-data can be placed in /general. 
general_data_collection(char) Notes about data collection and analysis. 
general_devices(Device) Data acquisition devices. 
general_experiment_description(char) General description of the experiment. 
general_experimenter(char) Name of person(s) who performed the experiment. Can also specify roles of different people involved. 
general_extracellular_ephys(ElectrodeGroup) Physical group of electrodes. 
general_extracellular_ephys_electrodes(DynamicTable) A table of all electrodes (i.e. channels) used for recording. 
general_institution(char) Institution(s) where experiment was performed. 
general_intracellular_ephys(IntracellularElectrode) An intracellular electrode. 
general_intracellular_ephys_experimental_conditions(ExperimentalConditionsTable) A table for grouping different intracellular recording repetitions together that belong to the - same experimental experimental_conditions.  -
general_intracellular_ephys_filtering(char) [DEPRECATED] Use IntracellularElectrode.filtering instead. Description of filtering used. Includes filtering type and - parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute - for each TimeSeries.  -
general_intracellular_ephys_intracellular_recordings(IntracellularRecordingsTable) A table to group together a stimulus and response from a single electrode and a single simultaneous - recording. Each row in the table represents a single recording consisting typically of a stimulus and a corresponding response. - In some cases, however, only a stimulus or a response are recorded as as part of an experiment. In this case both, the stimulus - and response will point to the same TimeSeries while the idx_start and count of the invalid column will be set to -1, thus, - indicating that no values have been recorded for the stimulus or response, respectively. Note, a recording MUST contain at - least a stimulus or a response. Typically the stimulus and response are PatchClampSeries. However, the use of AD/DA channels - that are not associated to an electrode is also common in intracellular electrophysiology, in which case other TimeSeries - may be used.  -
general_intracellular_ephys_repetitions(RepetitionsTable) A table for grouping different sequential intracellular recordings together. With each SequentialRecording - typically representing a particular type of stimulus, the RepetitionsTable table is typically used to group sets of stimuli - applied in sequence.  -
general_intracellular_ephys_sequential_recordings(SequentialRecordingsTable) A table for grouping different sequential recordings from the SimultaneousRecordingsTable table - together. This is typically used to group together sequential recordings where the a sequence of stimuli of the same type - with varying parameters have been presented in a sequence.  -
general_intracellular_ephys_simultaneous_recordings(SimultaneousRecordingsTable) A table for grouping different intracellular recordings from the IntracellularRecordingsTable - table together that were recorded simultaneously from different electrodes  -
general_intracellular_ephys_sweep_table(SweepTable) [DEPRECATED] Table used to group different PatchClampSeries. SweepTable is being replaced by IntracellularRecordingsTable - and SimultaneousRecordingsTable tabels. Additional SequentialRecordingsTable, RepetitionsTable and ExperimentalConditions - tables provide enhanced support for experiment metadata.  -
general_keywords(char) Terms to search over. 
general_lab(char) Laboratory where experiment was performed. 
general_notes(char) Notes about the experiment. 
general_optogenetics(OptogeneticStimulusSite) An optogenetic stimulation site. 
general_optophysiology(ImagingPlane) An imaging plane. 
general_pharmacology(char) Description of drugs used, including how and when they were administered. Anesthesia(s), painkiller(s), etc., plus - dosage, concentration, etc.  -
general_protocol(char) Experimental protocol, if applicable. e.g., include IACUC protocol number. 
general_related_publications(char) Publication information. PMID, DOI, URL, etc. 
general_session_id(char) Lab-specific ID for the session. 
general_slices(char) Description of slices, including information about preparation thickness, orientation, temperature, and bath solution. 
general_source_script(char) Script file or link to public source code used to create this NWB file. 
general_source_script_file_name(char) Name of script file. 
general_stimulus(char) Notes about stimuli, such as how and where they were presented. 
general_subject(Subject) Information about the animal or person from which the data was measured. 
general_surgery(char) Narrative description about surgery/surgeries, including date(s) and who performed surgery. 
general_virus(char) Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, - etc.  -
identifierREQUIRED (char) A unique text identifier for the file. For example, concatenated lab name, file creation date/time and experimentalist, - or a hash of these and/or other values. The goal is that the string should be unique to all other files.  -
intervals(TimeIntervals) Optional additional table(s) for describing other experimental time intervals. 
intervals_epochs(TimeIntervals) Divisions in time marking experimental stages or sub-divisions of a single recording session. 
intervals_invalid_times(TimeIntervals) Time intervals that should be removed from analysis. 
intervals_trials(TimeIntervals) Repeated experimental events that have a logical grouping. 
nwb_version(char) File version string. Use semantic versioning, e.g. 1.2.1. This will be the name of the format with trailing major, - minor and patch numbers.  -
processing(ProcessingModule) Intermediate analysis of acquired data. 
scratch(DynamicTable|NWBContainer|ScratchData) Any one-off tables | Any one-off containers | Any one-off datasets 
session_descriptionREQUIRED (char) A description of the experimental session and data in the file. 
session_start_timeREQUIRED (datetime) Date and time of the experiment/session start. The date is stored in UTC with local timezone offset as - ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" with no timezone offset. - Date accuracy is up to milliseconds.  -
stimulus_presentation(TimeSeries) TimeSeries objects containing data of presented stimuli. 
stimulus_templates(Images|TimeSeries) Images objects containing images of presented stimuli. | TimeSeries objects containing template data of - presented stimuli.  -
timestamps_reference_timeREQUIRED (datetime) Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone - offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" with no timezone - offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero).  -
units(Units) Data about sorted spike units. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_acquisition 
-   - - validate_analysis 
-   - - validate_file_create_date 
-   - - validate_general 
-   - - validate_general_data_collection 
-   - - validate_general_devices 
-   - - validate_general_experiment_description 
-   - - validate_general_experimenter 
-   - - validate_general_extracellular_ephys 
-   - - validate_general_extracellular_ephys_electrodes 
-   - - validate_general_institution 
-   - - validate_general_intracellular_ephys 
-   - - validate_general_intracellular_ephys_experimental_conditions 
-   - - validate_general_intracellular_ephys_filtering 
-   - - validate_general_intracellular_ephys_intracellular_recordings 
-   - - validate_general_intracellular_ephys_repetitions 
-   - - validate_general_intracellular_ephys_sequential_recordings 
-   - - validate_general_intracellular_ephys_simultaneous_recordings 
-   - - validate_general_intracellular_ephys_sweep_table 
-   - - validate_general_keywords 
-   - - validate_general_lab 
-   - - validate_general_notes 
-   - - validate_general_optogenetics 
-   - - validate_general_optophysiology 
-   - - validate_general_pharmacology 
-   - - validate_general_protocol 
-   - - validate_general_related_publications 
-   - - validate_general_session_id 
-   - - validate_general_slices 
-   - - validate_general_source_script 
-   - - validate_general_source_script_file_name 
-   - - validate_general_stimulus 
-   - - validate_general_subject 
-   - - validate_general_surgery 
-   - - validate_general_virus 
-   - - validate_identifier 
-   - - validate_intervals 
-   - - validate_intervals_epochs 
-   - - validate_intervals_invalid_times 
-   - - validate_intervals_trials 
-   - - validate_processing 
-   - - validate_scratch 
-   - - validate_session_description 
-   - - validate_session_start_time 
-   - - validate_stimulus_presentation 
-   - - validate_stimulus_templates 
-   - - validate_timestamps_reference_time 
-   - - validate_units 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/OnePhotonSeries.html b/doc/+types/+core/OnePhotonSeries.html deleted file mode 100644 index aa0887af..00000000 --- a/doc/+types/+core/OnePhotonSeries.html +++ /dev/null @@ -1,534 +0,0 @@ - - - - - - types.core.OnePhotonSeries - MATLAB File Help - - - - - - - - - - -
types.core.OnePhotonSeries - MATLAB File Help
-
types.core.OnePhotonSeries
-
  OnePhotonSeries Image stack recorded over time from 1-photon microscope.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.ImageSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
OnePhotonSeriesConstructor for OnePhotonSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
binning(uint8) Amount of pixels combined into 'bins'; could be 1, 2, 4, 8, etc. 
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
deviceDevice 
dimension(int32) Number of pixels on x, y, (and z) axes. 
exposure_time(single) Exposure time of the sample; often the inverse of the frequency. 
external_file(char) Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the - image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored - in another NWB file and that file is linked to this file.  -
external_file_starting_frame(int32) Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an - index to indicate which frames each file contains, to faciliate random access. The 'starting_frame' attribute, hence, contains - a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. - Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has - three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then - this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries - (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0].  -
format(char) Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image - files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not - present, then the default format='raw' case is assumed.  -
imaging_planeImagingPlane 
intensity(single) Intensity of the excitation in mW/mm^2, if known. 
pmt_gain(single) Photomultiplier gain. 
power(single) Power of the excitation in mW, if known. 
scan_line_rate(single) Lines imaged per second. This is also stored in /general/optophysiology but is kept here as it is useful information - for analysis, and so good to be stored w/ the actual data.  -
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_binning 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_device 
-   - - validate_dimension 
-   - - validate_exposure_time 
-   - - validate_external_file 
-   - - validate_external_file_starting_frame 
-   - - validate_format 
-   - - validate_imaging_plane 
-   - - validate_intensity 
-   - - validate_pmt_gain 
-   - - validate_power 
-   - - validate_scan_line_rate 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/OpticalChannel.html b/doc/+types/+core/OpticalChannel.html deleted file mode 100644 index 0469dbab..00000000 --- a/doc/+types/+core/OpticalChannel.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - types.core.OpticalChannel - MATLAB File Help - - - - - - - - - - -
types.core.OpticalChannel - MATLAB File Help
-
types.core.OpticalChannel
-
  OpticalChannel An optical channel used to record from an imaging plane.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
OpticalChannelConstructor for OpticalChannel 
- -
Property Summary -
- - - - - - - - - -
descriptionREQUIRED (char) Description or other notes about the channel. 
emission_lambdaREQUIRED (single) Emission wavelength for channel, in nm. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_emission_lambda 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/OpticalSeries.html b/doc/+types/+core/OpticalSeries.html deleted file mode 100644 index a84f0a81..00000000 --- a/doc/+types/+core/OpticalSeries.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - types.core.OpticalSeries - MATLAB File Help - - - - - - - - - - -
types.core.OpticalSeries - MATLAB File Help
-
types.core.OpticalSeries
-
  OpticalSeries Image data that is presented or recorded. A stimulus template movie will be stored only as an image. When the image is presented as stimulus, additional data is required, such as field of view (e.g., how much of the visual field the image covers, or how what is the area of the target being imaged). If the OpticalSeries represents acquired imaging data, orientation is also important.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.ImageSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
OpticalSeriesConstructor for OpticalSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
deviceDevice 
dimension(int32) Number of pixels on x, y, (and z) axes. 
distance(single) Distance from camera/monitor to target/eye. 
external_file(char) Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the - image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored - in another NWB file and that file is linked to this file.  -
external_file_starting_frame(int32) Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an - index to indicate which frames each file contains, to faciliate random access. The 'starting_frame' attribute, hence, contains - a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. - Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has - three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then - this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries - (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0].  -
field_of_view(single) Width, height and depth of image, or imaged area, in meters. 
format(char) Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image - files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not - present, then the default format='raw' case is assumed.  -
orientation(char) Description of image relative to some reference frame (e.g., which way is up). Must also specify frame of reference. 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_device 
-   - - validate_dimension 
-   - - validate_distance 
-   - - validate_external_file 
-   - - validate_external_file_starting_frame 
-   - - validate_field_of_view 
-   - - validate_format 
-   - - validate_orientation 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/OptogeneticSeries.html b/doc/+types/+core/OptogeneticSeries.html deleted file mode 100644 index 9ee2fcc0..00000000 --- a/doc/+types/+core/OptogeneticSeries.html +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - types.core.OptogeneticSeries - MATLAB File Help - - - - - - - - - - -
types.core.OptogeneticSeries - MATLAB File Help
-
types.core.OptogeneticSeries
-
  OptogeneticSeries An optogenetic stimulus.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
OptogeneticSeriesConstructor for OptogeneticSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
siteOptogeneticStimulusSite 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_site 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/OptogeneticStimulusSite.html b/doc/+types/+core/OptogeneticStimulusSite.html deleted file mode 100644 index 41239480..00000000 --- a/doc/+types/+core/OptogeneticStimulusSite.html +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - types.core.OptogeneticStimulusSite - MATLAB File Help - - - - - - - - - - -
types.core.OptogeneticStimulusSite - MATLAB File Help
-
types.core.OptogeneticStimulusSite
-
  OptogeneticStimulusSite A site of optogenetic stimulation.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
OptogeneticStimulusSiteConstructor for OptogeneticStimulusSite 
- -
Property Summary -
- - - - - - - - - - - - - - - - - -
descriptionREQUIRED (char) Description of stimulation site. 
deviceDevice 
excitation_lambdaREQUIRED (single) Excitation wavelength, in nm. 
locationREQUIRED (char) Location of the stimulation site. Specify the area, layer, comments on estimation of area/layer, stereotaxic - coordinates if in vivo, etc. Use standard atlas names for anatomical regions when possible.  -
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_device 
-   - - validate_excitation_lambda 
-   - - validate_location 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/PatchClampSeries.html b/doc/+types/+core/PatchClampSeries.html deleted file mode 100644 index db29d7c8..00000000 --- a/doc/+types/+core/PatchClampSeries.html +++ /dev/null @@ -1,423 +0,0 @@ - - - - - - types.core.PatchClampSeries - MATLAB File Help - - - - - - - - - - -
types.core.PatchClampSeries - MATLAB File Help
-
types.core.PatchClampSeries
-
  PatchClampSeries An abstract base class for patch-clamp data - stimulus or response, current or voltage.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
PatchClampSeriesConstructor for PatchClampSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodeIntracellularElectrode 
gain(single) Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
stimulus_description(char) Protocol/stimulus name for this patch-clamp dataset. 
sweep_number(uint32) Sweep number, allows to group different PatchClampSeries together. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrode 
-   - - validate_gain 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_stimulus_description 
-   - - validate_sweep_number 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/PlaneSegmentation.html b/doc/+types/+core/PlaneSegmentation.html deleted file mode 100644 index b98c0e15..00000000 --- a/doc/+types/+core/PlaneSegmentation.html +++ /dev/null @@ -1,358 +0,0 @@ - - - - - - types.core.PlaneSegmentation - MATLAB File Help - - - - - - - - - - -
types.core.PlaneSegmentation - MATLAB File Help
-
types.core.PlaneSegmentation
-
  PlaneSegmentation Results from image segmentation of a specific imaging plane.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
PlaneSegmentationConstructor for PlaneSegmentation 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
image_mask(VectorData) ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of - the ROI are finite non-zero.  -
imaging_planeImagingPlane 
pixel_mask(VectorData) Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing - of this dataset is maintained by the PlaneSegmentation  -
pixel_mask_index(VectorIndex) Index into pixel_mask. 
reference_images(ImageSeries) One or more image stacks that the masks apply to (can be one-element stack). 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
voxel_mask(VectorData) Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel masks are concatenated and parsing - of this dataset is maintained by the PlaneSegmentation  -
voxel_mask_index(VectorIndex) Index into voxel_mask. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_image_mask 
-   - - validate_imaging_plane 
-   - - validate_pixel_mask 
-   - - validate_pixel_mask_index 
-   - - validate_reference_images 
-   - - validate_vectordata 
-   - - validate_voxel_mask 
-   - - validate_voxel_mask_index 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Position.html b/doc/+types/+core/Position.html deleted file mode 100644 index be01a0b3..00000000 --- a/doc/+types/+core/Position.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.Position - MATLAB File Help - - - - - - - - - - -
types.core.Position - MATLAB File Help
-
types.core.Position
-
  Position Position data, whether along the x, x/y or x/y/z axis.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
PositionConstructor for Position 
- -
Property Summary -
- - - - - -
spatialseriesREQUIRED (SpatialSeries) SpatialSeries object containing position data. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_spatialseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ProcessingModule.html b/doc/+types/+core/ProcessingModule.html deleted file mode 100644 index bcd33e16..00000000 --- a/doc/+types/+core/ProcessingModule.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.core.ProcessingModule - MATLAB File Help - - - - - - - - - - -
types.core.ProcessingModule - MATLAB File Help
-
types.core.ProcessingModule
-
  ProcessingModule A collection of processed data.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ProcessingModuleConstructor for ProcessingModule 
- -
Property Summary -
- - - - - - - - - - - - - -
description(char) Description of this collection of processed data. 
dynamictable(DynamicTable) Tables stored in this collection. 
nwbdatainterface(NWBDataInterface) Data objects stored in this collection. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_description 
-   - - validate_dynamictable 
-   - - validate_nwbdatainterface 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/PupilTracking.html b/doc/+types/+core/PupilTracking.html deleted file mode 100644 index 287f2246..00000000 --- a/doc/+types/+core/PupilTracking.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.core.PupilTracking - MATLAB File Help - - - - - - - - - - -
types.core.PupilTracking - MATLAB File Help
-
types.core.PupilTracking
-
  PupilTracking Eye-tracking data, representing pupil size.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
PupilTrackingConstructor for PupilTracking 
- -
Property Summary -
- - - - - -
timeseriesREQUIRED (TimeSeries) TimeSeries object containing time series data on pupil size. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_timeseries 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/RGBAImage.html b/doc/+types/+core/RGBAImage.html deleted file mode 100644 index f07883e0..00000000 --- a/doc/+types/+core/RGBAImage.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.core.RGBAImage - MATLAB File Help - - - - - - - - - - -
types.core.RGBAImage - MATLAB File Help
-
types.core.RGBAImage
-
  RGBAImage A color image with transparency.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.Image, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
RGBAImageConstructor for RGBAImage 
- -
Property Summary -
- - - - - - - - - - - - - -
dataREQUIRED any 
description(char) Description of the image. 
resolution(single) Pixel resolution of the image, in pixels per centimeter. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
-   - - validate_resolution 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/RGBImage.html b/doc/+types/+core/RGBImage.html deleted file mode 100644 index e589b76c..00000000 --- a/doc/+types/+core/RGBImage.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.core.RGBImage - MATLAB File Help - - - - - - - - - - -
types.core.RGBImage - MATLAB File Help
-
types.core.RGBImage
-
  RGBImage A color image.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.Image, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
RGBImageConstructor for RGBImage 
- -
Property Summary -
- - - - - - - - - - - - - -
dataREQUIRED any 
description(char) Description of the image. 
resolution(single) Pixel resolution of the image, in pixels per centimeter. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
-   - - validate_resolution 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/RepetitionsTable.html b/doc/+types/+core/RepetitionsTable.html deleted file mode 100644 index b4c7feff..00000000 --- a/doc/+types/+core/RepetitionsTable.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - types.core.RepetitionsTable - MATLAB File Help - - - - - - - - - - -
types.core.RepetitionsTable - MATLAB File Help
-
types.core.RepetitionsTable
-
  RepetitionsTable A table for grouping different sequential intracellular recordings together. With each SequentialRecording typically representing a particular type of stimulus, the RepetitionsTable table is typically used to group sets of stimuli applied in sequence.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
RepetitionsTableConstructor for RepetitionsTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
sequential_recordingsREQUIRED (DynamicTableRegion) A reference to one or more rows in the SequentialRecordingsTable table. 
sequential_recordings_indexREQUIRED (VectorIndex) Index dataset for the sequential_recordings column. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_sequential_recordings 
-   - - validate_sequential_recordings_index 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/RoiResponseSeries.html b/doc/+types/+core/RoiResponseSeries.html deleted file mode 100644 index b6ae6977..00000000 --- a/doc/+types/+core/RoiResponseSeries.html +++ /dev/null @@ -1,389 +0,0 @@ - - - - - - types.core.RoiResponseSeries - MATLAB File Help - - - - - - - - - - -
types.core.RoiResponseSeries - MATLAB File Help
-
types.core.RoiResponseSeries
-
  RoiResponseSeries ROI responses over an imaging plane. The first dimension represents time. The second dimension, if present, represents ROIs.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
RoiResponseSeriesConstructor for RoiResponseSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
roisREQUIRED (DynamicTableRegion) DynamicTableRegion referencing into an ROITable containing information on the ROIs stored in - this timeseries.  -
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_rois 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/ScratchData.html b/doc/+types/+core/ScratchData.html deleted file mode 100644 index a7db6eb4..00000000 --- a/doc/+types/+core/ScratchData.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - types.core.ScratchData - MATLAB File Help - - - - - - - - - - -
types.core.ScratchData - MATLAB File Help
-
types.core.ScratchData
-
  ScratchData Any one-off datasets
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBData, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ScratchDataConstructor for ScratchData 
- -
Property Summary -
- - - - - - - - - -
dataREQUIRED any 
notes(char) Any notes the user has about the dataset being stored 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_notes 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/SequentialRecordingsTable.html b/doc/+types/+core/SequentialRecordingsTable.html deleted file mode 100644 index 78b79738..00000000 --- a/doc/+types/+core/SequentialRecordingsTable.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - types.core.SequentialRecordingsTable - MATLAB File Help - - - - - - - - - - -
types.core.SequentialRecordingsTable - MATLAB File Help
-
types.core.SequentialRecordingsTable
-
  SequentialRecordingsTable A table for grouping different sequential recordings from the SimultaneousRecordingsTable table together. This is typically used to group together sequential recordings where a sequence of stimuli of the same type with varying parameters have been presented in a sequence.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
SequentialRecordingsTableConstructor for SequentialRecordingsTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
simultaneous_recordingsREQUIRED (DynamicTableRegion) A reference to one or more rows in the SimultaneousRecordingsTable table. 
simultaneous_recordings_indexREQUIRED (VectorIndex) Index dataset for the simultaneous_recordings column. 
stimulus_typeREQUIRED (VectorData) The type of stimulus used for the sequential recording. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_simultaneous_recordings 
-   - - validate_simultaneous_recordings_index 
-   - - validate_stimulus_type 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/SimultaneousRecordingsTable.html b/doc/+types/+core/SimultaneousRecordingsTable.html deleted file mode 100644 index c5ae835a..00000000 --- a/doc/+types/+core/SimultaneousRecordingsTable.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - types.core.SimultaneousRecordingsTable - MATLAB File Help - - - - - - - - - - -
types.core.SimultaneousRecordingsTable - MATLAB File Help
-
types.core.SimultaneousRecordingsTable
-
  SimultaneousRecordingsTable A table for grouping different intracellular recordings from the IntracellularRecordingsTable table together that were recorded simultaneously from different electrodes.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
SimultaneousRecordingsTableConstructor for SimultaneousRecordingsTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
recordingsREQUIRED (DynamicTableRegion) A reference to one or more rows in the IntracellularRecordingsTable table. 
recordings_indexREQUIRED (VectorIndex) Index dataset for the recordings column. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_recordings 
-   - - validate_recordings_index 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/SpatialSeries.html b/doc/+types/+core/SpatialSeries.html deleted file mode 100644 index ed5b6d81..00000000 --- a/doc/+types/+core/SpatialSeries.html +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - types.core.SpatialSeries - MATLAB File Help - - - - - - - - - - -
types.core.SpatialSeries - MATLAB File Help
-
types.core.SpatialSeries
-
  SpatialSeries Direction, e.g., of gaze or travel, or position. The TimeSeries::data field is a 2D array storing position or direction relative to some reference frame. Array structure: [num measurements] [num dimensions]. Each SpatialSeries has a text dataset reference_frame that indicates the zero-position, or the zero-axes for direction. For example, if representing gaze direction, 'straight-ahead' might be a specific pixel on the monitor, or some other point in space. For position data, the 0,0 point might be the top-left corner of an enclosure, as viewed from the tracking camera. The unit of data will indicate how to interpret SpatialSeries values.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.TimeSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
SpatialSeriesConstructor for SpatialSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
reference_frame(char) Description defining what exactly 'straight-ahead' means. 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_reference_frame 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/SpikeEventSeries.html b/doc/+types/+core/SpikeEventSeries.html deleted file mode 100644 index 2835fe21..00000000 --- a/doc/+types/+core/SpikeEventSeries.html +++ /dev/null @@ -1,427 +0,0 @@ - - - - - - types.core.SpikeEventSeries - MATLAB File Help - - - - - - - - - - -
types.core.SpikeEventSeries - MATLAB File Help
-
types.core.SpikeEventSeries
-
  SpikeEventSeries Stores snapshots/snippets of recorded spike events (i.e., threshold crossings). This may also be raw data, as reported by ephys hardware. If so, the TimeSeries::description field should describe how events were detected. All SpikeEventSeries should reside in a module (under EventWaveform interface) even if the spikes were reported and stored by hardware. All events span the same recording channels and store snapshots of equal duration. TimeSeries::data array structure: [num events] [num channels] [num samples] (or [num events] [num samples] for single electrode).
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.ElectricalSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
SpikeEventSeriesConstructor for SpikeEventSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
channel_conversion(single) Channel-specific conversion factor. Multiply the data in the 'data' dataset by these values along the channel axis - (as indicated by axis attribute) AND by the global conversion factor in the 'conversion' attribute of 'data' to get the data - values in Volts, i.e, data in Volts = data * data.conversion * channel_conversion. This approach allows for both global and - per-channel data conversion factors needed to support the storage of electrical recordings as native values generated by data - acquisition systems. If this dataset is not present, then there is no channel-specific conversion factor, i.e. it is 1 for - all channels.  -
channel_conversion_axis(int32) The zero-indexed axis of the 'data' dataset that the channel-specific conversion factor corresponds to. This value - is fixed to 1.  -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodesREQUIRED (DynamicTableRegion) DynamicTableRegion pointer to the electrodes that this time series was generated from. 
filtering(char) Filtering applied to all channels of the data. For example, if this ElectricalSeries represents high-pass-filtered - data (also known as AP Band), then this value could be "High-pass 4-pole Bessel filter at 500 Hz". If this ElectricalSeries - represents low-pass-filtered LFP data and the type of filter is unknown, then this value could be "Low-pass filter at 300 - Hz". If a non-standard filter type is used, provide as much detail about the filter properties as possible.  -
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_channel_conversion 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrodes 
-   - - validate_filtering 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Subject.html b/doc/+types/+core/Subject.html deleted file mode 100644 index f78215a3..00000000 --- a/doc/+types/+core/Subject.html +++ /dev/null @@ -1,300 +0,0 @@ - - - - - - types.core.Subject - MATLAB File Help - - - - - - - - - - -
types.core.Subject - MATLAB File Help
-
types.core.Subject
-
  Subject Information about the animal or person from which the data was measured.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBContainer, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
SubjectConstructor for Subject 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
age(char) Age of subject. Can be supplied instead of 'date_of_birth'. 
age_reference(char) Age is with reference to this event. Can be 'birth' or 'gestational'. If reference is omitted, 'birth' is implied. 
date_of_birth(datetime) Date of birth of subject. Can be supplied instead of 'age'. 
description(char) Description of subject and where subject came from (e.g., breeder, if animal). 
genotype(char) Genetic strain. If absent, assume Wild Type (WT). 
sex(char) Gender of subject. 
species(char) Species of subject. 
strain(char) Strain of subject. 
subject_id(char) ID of animal/person used/participating in experiment (lab convention). 
weight(char) Weight at time of experiment, at time of surgery and at other important times. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_age 
-   - - validate_age_reference 
-   - - validate_date_of_birth 
-   - - validate_description 
-   - - validate_genotype 
-   - - validate_sex 
-   - - validate_species 
-   - - validate_strain 
-   - - validate_subject_id 
-   - - validate_weight 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/SweepTable.html b/doc/+types/+core/SweepTable.html deleted file mode 100644 index da5a2d3f..00000000 --- a/doc/+types/+core/SweepTable.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - types.core.SweepTable - MATLAB File Help - - - - - - - - - - -
types.core.SweepTable - MATLAB File Help
-
types.core.SweepTable
-
  SweepTable [DEPRECATED] Table used to group different PatchClampSeries. SweepTable is being replaced by IntracellularRecordingsTable and SimultaneousRecordingsTable tables. Additional SequentialRecordingsTable, RepetitionsTable, and ExperimentalConditions tables provide enhanced support for experiment metadata.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
SweepTableConstructor for SweepTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
seriesREQUIRED (VectorData) The PatchClampSeries with the sweep number in that row. 
series_indexREQUIRED (VectorIndex) Index for series. 
sweep_numberREQUIRED (VectorData) Sweep number of the PatchClampSeries in that row. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_series 
-   - - validate_series_index 
-   - - validate_sweep_number 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/TimeIntervals.html b/doc/+types/+core/TimeIntervals.html deleted file mode 100644 index 2053b354..00000000 --- a/doc/+types/+core/TimeIntervals.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - types.core.TimeIntervals - MATLAB File Help - - - - - - - - - - -
types.core.TimeIntervals - MATLAB File Help
-
types.core.TimeIntervals
-
  TimeIntervals A container for aggregating epoch data and the TimeSeries that each epoch applies to.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
TimeIntervalsConstructor for TimeIntervals 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
start_timeREQUIRED (VectorData) Start time of epoch, in seconds. 
stop_timeREQUIRED (VectorData) Stop time of epoch, in seconds. 
tags(VectorData) User-defined tags that identify or categorize events. 
tags_index(VectorIndex) Index for tags. 
timeseries(TimeSeriesReferenceVectorData) An index into a TimeSeries object. 
timeseries_index(VectorIndex) Index for timeseries. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_start_time 
-   - - validate_stop_time 
-   - - validate_tags 
-   - - validate_tags_index 
-   - - validate_timeseries 
-   - - validate_timeseries_index 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/TimeSeries.html b/doc/+types/+core/TimeSeries.html deleted file mode 100644 index 42274298..00000000 --- a/doc/+types/+core/TimeSeries.html +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - types.core.TimeSeries - MATLAB File Help - - - - - - - - - - -
types.core.TimeSeries - MATLAB File Help
-
types.core.TimeSeries
-
  TimeSeries General purpose time series.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBDataInterface, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
TimeSeriesConstructor for TimeSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/TimeSeriesReferenceVectorData.html b/doc/+types/+core/TimeSeriesReferenceVectorData.html deleted file mode 100644 index 6c413c25..00000000 --- a/doc/+types/+core/TimeSeriesReferenceVectorData.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - types.core.TimeSeriesReferenceVectorData - MATLAB File Help - - - - - - - - - - -
types.core.TimeSeriesReferenceVectorData - MATLAB File Help
-
types.core.TimeSeriesReferenceVectorData
-
  TimeSeriesReferenceVectorData Column storing references to a TimeSeries (rows). For each TimeSeries this VectorData column stores the start_index and count to indicate the range in time to be selected as well as an object reference to the TimeSeries.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.VectorData, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
TimeSeriesReferenceVectorDataConstructor for TimeSeriesReferenceVectorData 
- -
Property Summary -
- - - - - - - - - -
dataREQUIRED any 
description(char) Description of what these vectors represent. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/TwoPhotonSeries.html b/doc/+types/+core/TwoPhotonSeries.html deleted file mode 100644 index e4066f25..00000000 --- a/doc/+types/+core/TwoPhotonSeries.html +++ /dev/null @@ -1,498 +0,0 @@ - - - - - - types.core.TwoPhotonSeries - MATLAB File Help - - - - - - - - - - -
types.core.TwoPhotonSeries - MATLAB File Help
-
types.core.TwoPhotonSeries
-
  TwoPhotonSeries Image stack recorded over time from 2-photon microscope.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.ImageSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
TwoPhotonSeriesConstructor for TwoPhotonSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
deviceDevice 
dimension(int32) Number of pixels on x, y, (and z) axes. 
external_file(char) Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the - image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored - in another NWB file and that file is linked to this file.  -
external_file_starting_frame(int32) Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an - index to indicate which frames each file contains, to faciliate random access. The 'starting_frame' attribute, hence, contains - a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. - Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has - three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then - this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries - (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0].  -
field_of_view(single) Width, height and depth of image, or imaged area, in meters. 
format(char) Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image - files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not - present, then the default format='raw' case is assumed.  -
imaging_planeImagingPlane 
pmt_gain(single) Photomultiplier gain. 
scan_line_rate(single) Lines imaged per second. This is also stored in /general/optophysiology but is kept here as it is useful information - for analysis, and so good to be stored w/ the actual data.  -
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_device 
-   - - validate_dimension 
-   - - validate_external_file 
-   - - validate_external_file_starting_frame 
-   - - validate_field_of_view 
-   - - validate_format 
-   - - validate_imaging_plane 
-   - - validate_pmt_gain 
-   - - validate_scan_line_rate 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/Units.html b/doc/+types/+core/Units.html deleted file mode 100644 index 7954b1f4..00000000 --- a/doc/+types/+core/Units.html +++ /dev/null @@ -1,429 +0,0 @@ - - - - - - types.core.Units - MATLAB File Help - - - - - - - - - - -
types.core.Units - MATLAB File Help
-
types.core.Units
-
  Units Data about spiking units. Event times of observed units (e.g. cell, synapse, etc.) should be concatenated and stored in spike_times.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
UnitsConstructor for Units 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
electrode_group(VectorData) Electrode group that each spike unit came from. 
electrodes(DynamicTableRegion) Electrode that each spike unit came from, specified using a DynamicTableRegion. 
electrodes_index(VectorIndex) Index into electrodes. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
obs_intervals(VectorData) Observation intervals for each unit. 
obs_intervals_index(VectorIndex) Index into the obs_intervals dataset. 
spike_times(VectorData) Spike times for each unit in seconds. 
spike_times_index(VectorIndex) Index into the spike_times dataset. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
waveform_mean(VectorData) Spike waveform mean for each spike unit. 
waveform_sd(VectorData) Spike waveform standard deviation for each spike unit. 
waveforms(VectorData) Individual waveforms for each spike on each electrode. This is a doubly indexed column. The 'waveforms_index' - column indexes which waveforms in this column belong to the same spike event for a given unit, where each waveform was recorded - from a different electrode. The 'waveforms_index_index' column indexes the 'waveforms_index' column to indicate which spike - events belong to a given unit. For example, if the 'waveforms_index_index' column has values [2, 5, 6], then the first 2 elements - of the 'waveforms_index' column correspond to the 2 spike events of the first unit, the next 3 elements of the 'waveforms_index' - column correspond to the 3 spike events of the second unit, and the next 1 element of the 'waveforms_index' column corresponds - to the 1 spike event of the third unit. If the 'waveforms_index' column has values [3, 6, 8, 10, 12, 13], then the first 3 - elements of the 'waveforms' column contain the 3 spike waveforms that were recorded from 3 different electrodes for the first - spike time of the first unit. See https://nwb-schema.readthedocs.io/en/stable/format_description.html#doubly-ragged-arrays - for a graphical representation of this example. When there is only one electrode for each unit (i.e., each spike time is associated - with a single waveform), then the 'waveforms_index' column will have values 1, 2, ..., N, where N is the number of spike events. - The number of electrodes for each spike event should be the same within a given unit. The 'electrodes' column should be used - to indicate which electrodes are associated with each unit, and the order of the waveforms within a given unit x spike event - should be in the same order as the electrodes referenced in the 'electrodes' column of this table. The number of samples for - each waveform must be the same.  -
waveforms_index(VectorIndex) Index into the waveforms dataset. One value for every spike event. See 'waveforms' for more detail. 
waveforms_index_index(VectorIndex) Index into the waveforms_index dataset. One value for every unit (row in the table). See 'waveforms' for more - detail.  -
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_electrode_group 
-   - - validate_electrodes 
-   - - validate_electrodes_index 
-   - - validate_id 
-   - - validate_obs_intervals 
-   - - validate_obs_intervals_index 
-   - - validate_spike_times 
-   - - validate_spike_times_index 
-   - - validate_vectordata 
-   - - validate_waveform_mean 
-   - - validate_waveform_sd 
-   - - validate_waveforms 
-   - - validate_waveforms_index 
-   - - validate_waveforms_index_index 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/VoltageClampSeries.html b/doc/+types/+core/VoltageClampSeries.html deleted file mode 100644 index 272363c9..00000000 --- a/doc/+types/+core/VoltageClampSeries.html +++ /dev/null @@ -1,535 +0,0 @@ - - - - - - types.core.VoltageClampSeries - MATLAB File Help - - - - - - - - - - -
types.core.VoltageClampSeries - MATLAB File Help
-
types.core.VoltageClampSeries
-
  VoltageClampSeries Current data from an intracellular voltage-clamp recording. A corresponding VoltageClampStimulusSeries (stored separately as a stimulus) is used to store the voltage injected.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.PatchClampSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
VoltageClampSeriesConstructor for VoltageClampSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
capacitance_fast(single) Fast capacitance, in farads. 
capacitance_fast_unit(char) Unit of measurement for capacitance_fast, which is fixed to 'farads'. 
capacitance_slow(single) Slow capacitance, in farads. 
capacitance_slow_unit(char) Unit of measurement for capacitance_fast, which is fixed to 'farads'. 
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodeIntracellularElectrode 
gain(single) Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). 
resistance_comp_bandwidth(single) Resistance compensation bandwidth, in hertz. 
resistance_comp_bandwidth_unit(char) Unit of measurement for resistance_comp_bandwidth, which is fixed to 'hertz'. 
resistance_comp_correction(single) Resistance compensation correction, in percent. 
resistance_comp_correction_unit(char) Unit of measurement for resistance_comp_correction, which is fixed to 'percent'. 
resistance_comp_prediction(single) Resistance compensation prediction, in percent. 
resistance_comp_prediction_unit(char) Unit of measurement for resistance_comp_prediction, which is fixed to 'percent'. 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
stimulus_description(char) Protocol/stimulus name for this patch-clamp dataset. 
sweep_number(uint32) Sweep number, allows to group different PatchClampSeries together. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
whole_cell_capacitance_comp(single) Whole cell capacitance compensation, in farads. 
whole_cell_capacitance_comp_unit(char) Unit of measurement for whole_cell_capacitance_comp, which is fixed to 'farads'. 
whole_cell_series_resistance_comp(single) Whole cell series resistance compensation, in ohms. 
whole_cell_series_resistance_comp_unit(char) Unit of measurement for whole_cell_series_resistance_comp, which is fixed to 'ohms'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_capacitance_fast 
-   - - validate_capacitance_slow 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrode 
-   - - validate_gain 
-   - - validate_resistance_comp_bandwidth 
-   - - validate_resistance_comp_correction 
-   - - validate_resistance_comp_prediction 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_stimulus_description 
-   - - validate_sweep_number 
-   - - validate_timestamps 
-   - - validate_whole_cell_capacitance_comp 
-   - - validate_whole_cell_series_resistance_comp 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+core/VoltageClampStimulusSeries.html b/doc/+types/+core/VoltageClampStimulusSeries.html deleted file mode 100644 index 4b61d805..00000000 --- a/doc/+types/+core/VoltageClampStimulusSeries.html +++ /dev/null @@ -1,423 +0,0 @@ - - - - - - types.core.VoltageClampStimulusSeries - MATLAB File Help - - - - - - - - - - -
types.core.VoltageClampStimulusSeries - MATLAB File Help
-
types.core.VoltageClampStimulusSeries
-
  VoltageClampStimulusSeries Stimulus voltage applied during a voltage clamp recording.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.PatchClampSeries, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
VoltageClampStimulusSeriesConstructor for VoltageClampStimulusSeries 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
comments(char) Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, - or descriptive information if the primary description field is populated with a computer-readable string.  -
control(uint8) Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. - If present, the length of this array should be the same size as the first dimension of data.  -
control_description(char) Description of each control value. Must be present if control is present. If present, control_description[0] should - describe time points where control == 0.  -
dataREQUIRED (any) Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can - also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file.  -
data_continuity(char) Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage - trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", - because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains - the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. - It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable.  -
data_conversion(single) Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition - system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the - data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 - range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then - the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9.  -
data_offset(single) Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common - examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and - (b) specialized recording devices that naturally cause a scalar offset with respect to the true units.  -
data_resolution(single) Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value - of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0.  -
data_unit(char) Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. - To access the data in these units, multiply 'data' by 'conversion' and add 'offset'.  -
description(char) Description of the time series. 
electrodeIntracellularElectrode 
gain(single) Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). 
starting_time(double) Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample - can be specified and all subsequent ones calculated from the sampling rate attribute.  -
starting_time_rate(single) Sampling rate, in Hz. 
starting_time_unit(char) Unit of measurement for time, which is fixed to 'seconds'. 
stimulus_description(char) Protocol/stimulus name for this patch-clamp dataset. 
sweep_number(uint32) Sweep number, allows to group different PatchClampSeries together. 
timestamps(double) Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. 
timestamps_interval(int32) Value is '1' 
timestamps_unit(char) Unit of measurement for timestamps, which is fixed to 'seconds'. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_comments 
-   - - validate_control 
-   - - validate_control_description 
-   - - validate_data 
-   - - validate_data_continuity 
-   - - validate_data_conversion 
-   - - validate_data_offset 
-   - - validate_data_resolution 
-   - - validate_data_unit 
-   - - validate_description 
-   - - validate_electrode 
-   - - validate_gain 
-   - - validate_starting_time 
-   - - validate_starting_time_rate 
-   - - validate_stimulus_description 
-   - - validate_sweep_number 
-   - - validate_timestamps 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/AlignedDynamicTable.html b/doc/+types/+hdmf_common/AlignedDynamicTable.html deleted file mode 100644 index c8924672..00000000 --- a/doc/+types/+hdmf_common/AlignedDynamicTable.html +++ /dev/null @@ -1,299 +0,0 @@ - - - - - - types.hdmf_common.AlignedDynamicTable - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.AlignedDynamicTable - MATLAB File Help
-
types.hdmf_common.AlignedDynamicTable
-
  AlignedDynamicTable DynamicTable container that supports storing a collection of sub-tables. Each sub-table is a DynamicTable itself that is aligned with the main table by row index. I.e., all DynamicTables stored in this group MUST have the same number of rows. This type effectively defines a 2-level table in which the main data is stored in the main table implemented by this type and additional columns of the table are grouped into categories, with each category being represented by a separate DynamicTable stored within the group.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.DynamicTable, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
AlignedDynamicTableConstructor for AlignedDynamicTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - -
categories(char) The names of the categories in this AlignedDynamicTable. Each category is represented by one DynamicTable stored in - the parent group. This attribute should be used to specify an order of categories and the category names must match the names - of the corresponding DynamicTable in the group.  -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
dynamictable(DynamicTable) A DynamicTable representing a particular category for columns in the AlignedDynamicTable parent container. - The table MUST be aligned with (i.e., have the same number of rows) as all other DynamicTables stored in the AlignedDynamicTable - parent container. The name of the category is given by the name of the DynamicTable and its description by the description - attribute of the DynamicTable.  -
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_categories 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_dynamictable 
-   - - validate_id 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/CSRMatrix.html b/doc/+types/+hdmf_common/CSRMatrix.html deleted file mode 100644 index 47ba9089..00000000 --- a/doc/+types/+hdmf_common/CSRMatrix.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - types.hdmf_common.CSRMatrix - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.CSRMatrix - MATLAB File Help
-
types.hdmf_common.CSRMatrix
-
  CSRMatrix A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]].
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Container, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
CSRMatrixConstructor for CSRMatrix 
- -
Property Summary -
- - - - - - - - - - - - - - - - - -
dataREQUIRED (any) The non-zero values in the matrix. 
indicesREQUIRED (uint) The column indices. 
indptrREQUIRED (uint) The row index pointer. 
shape(uint) The shape (number of rows, number of columns) of this sparse matrix. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_indices 
-   - - validate_indptr 
-   - - validate_shape 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/Container.html b/doc/+types/+hdmf_common/Container.html deleted file mode 100644 index b70d0886..00000000 --- a/doc/+types/+hdmf_common/Container.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - types.hdmf_common.Container - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.Container - MATLAB File Help
-
types.hdmf_common.Container
-
  Container An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.untyped.MetaClass, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ContainerConstructor for Container 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/Data.html b/doc/+types/+hdmf_common/Data.html deleted file mode 100644 index fb163661..00000000 --- a/doc/+types/+hdmf_common/Data.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.hdmf_common.Data - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.Data - MATLAB File Help
-
types.hdmf_common.Data
-
  Data An abstract data type for a dataset.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.untyped.MetaClass, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
DataConstructor for Data 
- -
Property Summary -
- - - - - -
dataREQUIRED any 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/DynamicTable.html b/doc/+types/+hdmf_common/DynamicTable.html deleted file mode 100644 index c61b63b8..00000000 --- a/doc/+types/+hdmf_common/DynamicTable.html +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - types.hdmf_common.DynamicTable - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.DynamicTable - MATLAB File Help
-
types.hdmf_common.DynamicTable
-
  DynamicTable A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Container, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
DynamicTableConstructor for DynamicTable 
- -
Property Summary -
- - - - - - - - - - - - - - - - - -
colnames(char) The names of the columns in this table. This should be used to specify an order to the columns. 
description(char) Description of what is in this dynamic table. 
idREQUIRED (ElementIdentifiers) Array of unique identifiers for the rows of this dynamic table. 
vectordata(VectorData) Vector columns, including index columns, of this dynamic table. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addColumn 
-   - - addRow 
-   - - addlistenerAdd listener for event. 
-   - - clear 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - getRow 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - toTable 
-   - - validate_colnames 
-   - - validate_description 
-   - - validate_id 
-   - - validate_vectordata 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/DynamicTableRegion.html b/doc/+types/+hdmf_common/DynamicTableRegion.html deleted file mode 100644 index 3f2ed8e9..00000000 --- a/doc/+types/+hdmf_common/DynamicTableRegion.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.hdmf_common.DynamicTableRegion - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.DynamicTableRegion - MATLAB File Help
-
types.hdmf_common.DynamicTableRegion
-
  DynamicTableRegion DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.VectorData, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
DynamicTableRegionConstructor for DynamicTableRegion 
- -
Property Summary -
- - - - - - - - - - - - - -
dataREQUIRED any 
description(char) Description of what these vectors represent. 
table(Object Reference to DynamicTable) Reference to the DynamicTable object that this region applies to. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
-   - - validate_tableReference to type `DynamicTable` 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/ElementIdentifiers.html b/doc/+types/+hdmf_common/ElementIdentifiers.html deleted file mode 100644 index c40c623a..00000000 --- a/doc/+types/+hdmf_common/ElementIdentifiers.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - types.hdmf_common.ElementIdentifiers - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.ElementIdentifiers - MATLAB File Help
-
types.hdmf_common.ElementIdentifiers
-
  ElementIdentifiers A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Data, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ElementIdentifiersConstructor for ElementIdentifiers 
- -
Property Summary -
- - - - - -
dataREQUIRED any 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/SimpleMultiContainer.html b/doc/+types/+hdmf_common/SimpleMultiContainer.html deleted file mode 100644 index c97cebd3..00000000 --- a/doc/+types/+hdmf_common/SimpleMultiContainer.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - types.hdmf_common.SimpleMultiContainer - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.SimpleMultiContainer - MATLAB File Help
-
types.hdmf_common.SimpleMultiContainer
-
  SimpleMultiContainer A simple Container for holding onto multiple containers.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Container, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
SimpleMultiContainerConstructor for SimpleMultiContainer 
- -
Property Summary -
- - - - - - - - - -
container(Container) Container objects held within this SimpleMultiContainer. 
data(Data) Data objects held within this SimpleMultiContainer. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_container 
-   - - validate_data 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/VectorData.html b/doc/+types/+hdmf_common/VectorData.html deleted file mode 100644 index ec011b76..00000000 --- a/doc/+types/+hdmf_common/VectorData.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - types.hdmf_common.VectorData - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.VectorData - MATLAB File Help
-
types.hdmf_common.VectorData
-
  VectorData An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Data, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
VectorDataConstructor for VectorData 
- -
Property Summary -
- - - - - - - - - -
dataREQUIRED any 
description(char) Description of what these vectors represent. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_common/VectorIndex.html b/doc/+types/+hdmf_common/VectorIndex.html deleted file mode 100644 index edd7d53c..00000000 --- a/doc/+types/+hdmf_common/VectorIndex.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.hdmf_common.VectorIndex - MATLAB File Help - - - - - - - - - - -
types.hdmf_common.VectorIndex - MATLAB File Help
-
types.hdmf_common.VectorIndex
-
  VectorIndex Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by "_index".
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.VectorData, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
VectorIndexConstructor for VectorIndex 
- -
Property Summary -
- - - - - - - - - - - - - -
dataREQUIRED any 
description(char) Description of what these vectors represent. 
target(Object Reference to VectorData) Reference to the target dataset that this index applies to. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
-   - - validate_targetReference to type `VectorData` 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_experimental/EnumData.html b/doc/+types/+hdmf_experimental/EnumData.html deleted file mode 100644 index cb7d0569..00000000 --- a/doc/+types/+hdmf_experimental/EnumData.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - types.hdmf_experimental.EnumData - MATLAB File Help - - - - - - - - - - -
types.hdmf_experimental.EnumData - MATLAB File Help
-
types.hdmf_experimental.EnumData
-
  EnumData Data that come from a fixed set of values. A data value of i corresponds to the i-th value in the VectorData referenced by the 'elements' attribute.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.VectorData, types.untyped.DatasetClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
EnumDataConstructor for EnumData 
- -
Property Summary -
- - - - - - - - - - - - - -
dataREQUIRED any 
description(char) Description of what these vectors represent. 
elements(Object Reference to VectorData) Reference to the VectorData object that contains the enumerable elements 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_data 
-   - - validate_description 
-   - - validate_elementsReference to type `VectorData` 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+hdmf_experimental/ExternalResources.html b/doc/+types/+hdmf_experimental/ExternalResources.html deleted file mode 100644 index 9e122751..00000000 --- a/doc/+types/+hdmf_experimental/ExternalResources.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - types.hdmf_experimental.ExternalResources - MATLAB File Help - - - - - - - - - - -
types.hdmf_experimental.ExternalResources - MATLAB File Help
-
types.hdmf_experimental.ExternalResources
-
  ExternalResources A set of four tables for tracking external resource references in a file. NOTE: this data type is in beta testing and is subject to change in a later version.
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.hdmf_common.Container, types.untyped.GroupClass
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ExternalResourcesConstructor for ExternalResources 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - -
entitiesREQUIRED (Data) A table for mapping user terms (i.e., keys) to resource entities. 
keysREQUIRED (Data) A table for storing user terms that are used to refer to external resources. 
object_keysREQUIRED (Data) A table for identifying which objects use which keys. 
objectsREQUIRED (Data) A table for identifying which objects in a file contain references to external resources. 
resourcesREQUIRED (Data) A table for mapping user terms (i.e., keys) to resource entities. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - export 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - validate_entities 
-   - - validate_keys 
-   - - validate_object_keys 
-   - - validate_objects 
-   - - validate_resources 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+untyped/+datapipe/+dynamic/Filter.html b/doc/+types/+untyped/+datapipe/+dynamic/Filter.html deleted file mode 100644 index 2bf6ae54..00000000 --- a/doc/+types/+untyped/+datapipe/+dynamic/Filter.html +++ /dev/null @@ -1,1326 +0,0 @@ - - - - - - types.untyped.datapipe.dynamic.Filter - MATLAB File Help - - - - - - - - - - -
types.untyped.datapipe.dynamic.Filter - MATLAB File Help
-
types.untyped.datapipe.dynamic.Filter
-
 Filter Compression filter registered to HDF5
-  as defined by (https://portal.hdfgroup.org/display/support/Filters)
-  Submit an issue if we're missing one you wish to use!
- -
Class Details
- - - - - - - - - - - - - -
Superclassesuint64
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
FilterCompression filter registered to HDF5 
- -
Enumeration Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
APAX 
B3D 
BLOSC 
BZIP2 
BitGroom 
BitShuffle 
CBF 
CCSDS_123 
FAPEC 
FCIDECOMP 
FPZip 
GBRGranular BitRound 
JPEG 
JPEG_LS 
JPEG_XR 
LPC_Rice 
LZ4 
LZF 
LZO 
MAFISC 
SPDP 
SZ 
SZ3 
Snappy 
VBZ 
ZFP 
ZStandard 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - abs 
-   - - accumarray 
-   - - all 
-   - - and 
-   - - any 
-   - - balance 
-   - - bandwidth 
-   - - bitand 
-   - - bitcmp 
-   - - bitget 
-   - - bitor 
-   - - bitset 
-   - - bitshift 
-   - - bitxor 
-   - - bsxfun 
-   - - cat 
-   - - ceil 
-   - - cell 
-   - - cellstr 
-   - - char 
-   - - chol 
-   - - cholupdate 
-   - - complex 
-   - - conj 
-   - - conv2 
-   - - ctranspose 
-   - - cummax 
-   - - cummin 
-   - - cumprod 
-   - - cumsum 
-   - - diag 
-   - - diff 
-   - - double 
-   - - eig 
-   - - end 
-   - - eq 
-   - - fft 
-   - - fftn 
-   - - filter 
-   - - find 
-   - - fix 
-   - - floor 
-   - - full 
-   - - function_handle 
-   - - ge 
-   - - gt 
-   - - horzcat 
-   - - ifft 
-   - - ifftn 
-   - - imag 
-   - - int16 
-   - - int32 
-   - - int64 
-   - - int8 
-   - - intersect 
-   - - isempty 
-   - - isequal 
-   - - isequaln 
-   - - isequalwithequalnans 
-   - - isfinite 
-   - - isfloat 
-   - - isinf 
-   - - isinteger 
-   - - islogical 
-   - - ismember 
-   - - isnan 
-   - - isnumeric 
-   - - isreal 
-   - - isscalar 
-   - - issorted 
-   - - issortedrows 
-   - - issparse 
-   - - isvector 
-   - - ldivide 
-   - - ldl 
-   - - le 
-   - - length 
-   - - linsolve 
-   - - logical 
-   - - lt 
-   - - lu 
-   - - max 
-   - - maxk 
-   - - min 
-   - - mink 
-   - - minus 
-   - - mldivide 
-   - - mod 
-   - - mpower 
-   - - mrdivide 
-   - - mtimes 
-   - - ndims 
-   - - ne 
-   - - nnz 
-   - - nonzeros 
-   - - norm 
-   - - not 
-   - - numel 
-   - - nzmax 
-   - - or 
-   - - ordqz 
-   - - ordschur 
-   - - permute 
-   - - plot 
-   - - plus 
-   - - power 
-   - - prod 
-   - - qr 
-   - - qz 
-   - - rdivide 
-   - - real 
-   - - rem 
-   - - reshape 
-   - - round 
-   - - schur 
-   - - setdiff 
-   - - setxor 
-   - - sign 
-   - - single 
-   - - size 
-   - - sort 
-   - - sortrowsc 
-   - - strcmp 
-   - - strcmpi 
-   - - string 
-   - - strncmp 
-   - - strncmpi 
-   - - subsasgn 
-   - - subsindex 
-   - - subsref 
-   - - sum 
-   - - svd 
-   - - times 
-   - - transpose 
-   - - tril 
-   - - triu 
-   - - uint16 
-   - - uint32 
-   - - uint8 
-   - - uminus 
-   - - union 
-   - - uplus 
-   - - vecnorm 
-   - - vertcat 
-   - - xor 
- - \ No newline at end of file diff --git a/doc/+types/+untyped/+datapipe/+properties/DynamicFilter.html b/doc/+types/+untyped/+datapipe/+properties/DynamicFilter.html deleted file mode 100644 index bec8e035..00000000 --- a/doc/+types/+untyped/+datapipe/+properties/DynamicFilter.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - types.untyped.datapipe.properties.DynamicFilter - MATLAB File Help - - - - - - - - - - -
types.untyped.datapipe.properties.DynamicFilter - MATLAB File Help
-
types.untyped.datapipe.properties.DynamicFilter
-
types.untyped.datapipe.properties.DynamicFilter is a class.
-    obj = DynamicFilter(filter, parameters)
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.untyped.datapipe.Property
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
DynamicFilter 
- -
Property Summary -
- - - - - - - - - -
dynamicFilter 
parameters 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addTo 
-   - - addlistenerAdd listener for event. 
Sealed -   - - catConcatenation for heterogeneous arrays 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - horzcatHorizontal concatenation for heterogeneous arrays 
-   - - isInDcpl 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
Sealed -   - - vertcatVertical concatenation for heterogeneous arrays 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/+types/+untyped/+datapipe/+properties/Shuffle.html b/doc/+types/+untyped/+datapipe/+properties/Shuffle.html deleted file mode 100644 index 5faa95da..00000000 --- a/doc/+types/+untyped/+datapipe/+properties/Shuffle.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - types.untyped.datapipe.properties.Shuffle - MATLAB File Help - - - - - - - - - - -
types.untyped.datapipe.properties.Shuffle - MATLAB File Help
-
types.untyped.datapipe.properties.Shuffle
-
 Shuffle Shuffle Filter
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.untyped.datapipe.Property
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
ShuffleShuffle Filter 
- -
Property Summary -
- - - - - -
FILTER_NAME 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addTo 
-   - - addlistenerAdd listener for event. 
Sealed -   - - catConcatenation for heterogeneous arrays 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - horzcatHorizontal concatenation for heterogeneous arrays 
Static -   - - isInDcpl 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
Sealed -   - - vertcatVertical concatenation for heterogeneous arrays 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/NwbFile.html b/doc/NwbFile.html deleted file mode 100644 index e793d6fd..00000000 --- a/doc/NwbFile.html +++ /dev/null @@ -1,829 +0,0 @@ - - - - - - NwbFile - MATLAB File Help - - - - - - - - - - -
NwbFile - MATLAB File Help
-
NwbFile
-
  NwbFile Root object representing data read from an NWB file.
- 
-  Requires that core and extension NWB types have been generated
-  and reside in a 'types' package on the matlab path.
- 
-  Example. Construct an object from scratch for export:
-     nwb = NwbFile;
-     nwb.epochs = types.core.Epochs;
-     nwbExport(nwb, 'epoch.nwb');
See also
- -
Class Details
- - - - - - - - - - - - - -
Superclassestypes.core.NWBFile
Sealedfalse
Construct on loadfalse
- -
Constructor Summary -
- - - - - -
NwbFileRoot object representing data read from an NWB file. 
- -
Property Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
acquisition(DynamicTable|NWBDataInterface) Tabular data that is relevent to acquisition | Acquired, raw data. 
analysis(DynamicTable|NWBContainer) Tabular data that is relevent to data stored in analysis | Custom analysis results. 
file_create_dateREQUIRED (datetime) A record of the date the file was created and of subsequent modifications. The date is stored in UTC with - local timezone offset as ISO 8601 extended formatted strings: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" - with no timezone offset. Date accuracy is up to milliseconds. The file can be created after the experiment was run, so this - may differ from the experiment start time. Each modification to the nwb file adds a new entry to the array.  -
general(LabMetaData) Place-holder than can be extended so that lab-specific meta-data can be placed in /general. 
general_data_collection(char) Notes about data collection and analysis. 
general_devices(Device) Data acquisition devices. 
general_experiment_description(char) General description of the experiment. 
general_experimenter(char) Name of person(s) who performed the experiment. Can also specify roles of different people involved. 
general_extracellular_ephys(ElectrodeGroup) Physical group of electrodes. 
general_extracellular_ephys_electrodes(DynamicTable) A table of all electrodes (i.e. channels) used for recording. 
general_institution(char) Institution(s) where experiment was performed. 
general_intracellular_ephys(IntracellularElectrode) An intracellular electrode. 
general_intracellular_ephys_experimental_conditions(ExperimentalConditionsTable) A table for grouping different intracellular recording repetitions together that belong to the - same experimental experimental_conditions.  -
general_intracellular_ephys_filtering(char) [DEPRECATED] Use IntracellularElectrode.filtering instead. Description of filtering used. Includes filtering type and - parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute - for each TimeSeries.  -
general_intracellular_ephys_intracellular_recordings(IntracellularRecordingsTable) A table to group together a stimulus and response from a single electrode and a single simultaneous - recording. Each row in the table represents a single recording consisting typically of a stimulus and a corresponding response. - In some cases, however, only a stimulus or a response are recorded as as part of an experiment. In this case both, the stimulus - and response will point to the same TimeSeries while the idx_start and count of the invalid column will be set to -1, thus, - indicating that no values have been recorded for the stimulus or response, respectively. Note, a recording MUST contain at - least a stimulus or a response. Typically the stimulus and response are PatchClampSeries. However, the use of AD/DA channels - that are not associated to an electrode is also common in intracellular electrophysiology, in which case other TimeSeries - may be used.  -
general_intracellular_ephys_repetitions(RepetitionsTable) A table for grouping different sequential intracellular recordings together. With each SequentialRecording - typically representing a particular type of stimulus, the RepetitionsTable table is typically used to group sets of stimuli - applied in sequence.  -
general_intracellular_ephys_sequential_recordings(SequentialRecordingsTable) A table for grouping different sequential recordings from the SimultaneousRecordingsTable table - together. This is typically used to group together sequential recordings where the a sequence of stimuli of the same type - with varying parameters have been presented in a sequence.  -
general_intracellular_ephys_simultaneous_recordings(SimultaneousRecordingsTable) A table for grouping different intracellular recordings from the IntracellularRecordingsTable - table together that were recorded simultaneously from different electrodes  -
general_intracellular_ephys_sweep_table(SweepTable) [DEPRECATED] Table used to group different PatchClampSeries. SweepTable is being replaced by IntracellularRecordingsTable - and SimultaneousRecordingsTable tabels. Additional SequentialRecordingsTable, RepetitionsTable and ExperimentalConditions - tables provide enhanced support for experiment metadata.  -
general_keywords(char) Terms to search over. 
general_lab(char) Laboratory where experiment was performed. 
general_notes(char) Notes about the experiment. 
general_optogenetics(OptogeneticStimulusSite) An optogenetic stimulation site. 
general_optophysiology(ImagingPlane) An imaging plane. 
general_pharmacology(char) Description of drugs used, including how and when they were administered. Anesthesia(s), painkiller(s), etc., plus - dosage, concentration, etc.  -
general_protocol(char) Experimental protocol, if applicable. e.g., include IACUC protocol number. 
general_related_publications(char) Publication information. PMID, DOI, URL, etc. 
general_session_id(char) Lab-specific ID for the session. 
general_slices(char) Description of slices, including information about preparation thickness, orientation, temperature, and bath solution. 
general_source_script(char) Script file or link to public source code used to create this NWB file. 
general_source_script_file_name(char) Name of script file. 
general_stimulus(char) Notes about stimuli, such as how and where they were presented. 
general_subject(Subject) Information about the animal or person from which the data was measured. 
general_surgery(char) Narrative description about surgery/surgeries, including date(s) and who performed surgery. 
general_virus(char) Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, - etc.  -
identifierREQUIRED (char) A unique text identifier for the file. For example, concatenated lab name, file creation date/time and experimentalist, - or a hash of these and/or other values. The goal is that the string should be unique to all other files.  -
intervals(TimeIntervals) Optional additional table(s) for describing other experimental time intervals. 
intervals_epochs(TimeIntervals) Divisions in time marking experimental stages or sub-divisions of a single recording session. 
intervals_invalid_times(TimeIntervals) Time intervals that should be removed from analysis. 
intervals_trials(TimeIntervals) Repeated experimental events that have a logical grouping. 
nwb_version(char) File version string. Use semantic versioning, e.g. 1.2.1. This will be the name of the format with trailing major, - minor and patch numbers.  -
processing(ProcessingModule) Intermediate analysis of acquired data. 
scratch(DynamicTable|NWBContainer|ScratchData) Any one-off tables | Any one-off containers | Any one-off datasets 
session_descriptionREQUIRED (char) A description of the experimental session and data in the file. 
session_start_timeREQUIRED (datetime) Date and time of the experiment/session start. The date is stored in UTC with local timezone offset as - ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" with no timezone offset. - Date accuracy is up to milliseconds.  -
stimulus_presentation(TimeSeries) TimeSeries objects containing data of presented stimuli. 
stimulus_templates(Images|TimeSeries) Images objects containing images of presented stimuli. | TimeSeries objects containing template data of - presented stimuli.  -
timestamps_reference_timeREQUIRED (datetime) Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone - offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" with no timezone - offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero).  -
units(Units) Data about sorted spike units. 
- -
Method Summary -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-   - - addlistenerAdd listener for event. 
-   - - deleteDelete a handle object. 
-   - - eq== (EQ) Test handle equality. 
-   - - exportadd to file create date 
-   - - findobjFind objects matching specified conditions. 
-   - - findpropFind property of MATLAB handle object. 
-   - - ge>= (GE) Greater than or equal relation for handles. 
-   - - gt> (GT) Greater than relation for handles. 
Sealed -   - - isvalidTest handle validity. 
-   - - le<= (LE) Less than or equal relation for handles. 
-   - - listenerAdd listener for event without binding the listener to the source object. 
-   - - loadAll 
-   - - lt< (LT) Less than relation for handles. 
-   - - ne~= (NE) Not equal relation for handles. 
-   - - notifyNotify listeners of event. 
-   - - resolve 
-   - - searchForSearches this NwbFile object for a given typename 
-   - - validate_acquisition 
-   - - validate_analysis 
-   - - validate_file_create_date 
-   - - validate_general 
-   - - validate_general_data_collection 
-   - - validate_general_devices 
-   - - validate_general_experiment_description 
-   - - validate_general_experimenter 
-   - - validate_general_extracellular_ephys 
-   - - validate_general_extracellular_ephys_electrodes 
-   - - validate_general_institution 
-   - - validate_general_intracellular_ephys 
-   - - validate_general_intracellular_ephys_experimental_conditions 
-   - - validate_general_intracellular_ephys_filtering 
-   - - validate_general_intracellular_ephys_intracellular_recordings 
-   - - validate_general_intracellular_ephys_repetitions 
-   - - validate_general_intracellular_ephys_sequential_recordings 
-   - - validate_general_intracellular_ephys_simultaneous_recordings 
-   - - validate_general_intracellular_ephys_sweep_table 
-   - - validate_general_keywords 
-   - - validate_general_lab 
-   - - validate_general_notes 
-   - - validate_general_optogenetics 
-   - - validate_general_optophysiology 
-   - - validate_general_pharmacology 
-   - - validate_general_protocol 
-   - - validate_general_related_publications 
-   - - validate_general_session_id 
-   - - validate_general_slices 
-   - - validate_general_source_script 
-   - - validate_general_source_script_file_name 
-   - - validate_general_stimulus 
-   - - validate_general_subject 
-   - - validate_general_surgery 
-   - - validate_general_virus 
-   - - validate_identifier 
-   - - validate_intervals 
-   - - validate_intervals_epochs 
-   - - validate_intervals_invalid_times 
-   - - validate_intervals_trials 
-   - - validate_processing 
-   - - validate_scratch 
-   - - validate_session_description 
-   - - validate_session_start_time 
-   - - validate_stimulus_presentation 
-   - - validate_stimulus_templates 
-   - - validate_timestamps_reference_time 
-   - - validate_units 
- -
Event Summary -
-
- - \ No newline at end of file diff --git a/doc/alpha.png b/doc/alpha.png deleted file mode 100644 index c73de7b05000443a0842f2e915d30dc5efec0dd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmV+s0q*{ZP)z zQ4WJJ2!`q6(c~&{CcFyX%{{O*3+<>*r)K@)L#;sgtx%hY2zp`w01>%5jgw&J!A6Ov z{oxj&d7k&j`4Qtd0>I2H(i=Rwz9Tqxf(W|?ki|=ctL(aA|es#WFKbc?zg4C=hd_}m12PXzTKJB+iB?u XpQ?QQ=I-(g00000NkvXXu0mjf#?^X; diff --git a/doc/c++.png b/doc/c++.png deleted file mode 100644 index 24f56e6293df813f7699555d0fdd0224a33bdac9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^0YJ>p!2~4#tiFE(NUrMq>1fP(BLp1!W^mpLQ_Wwp7rPrm~SUGa2r45^s&cKTi3W&@s7VZD}9S!LiPTl|j(*)-gYW;;XAZ?5N!pnK5hG z+s}VG5+xWO6mIF=yOWvujML^HlXZ+Yu?Qyj=7(iWd!%tXIGN|}`LH!?g3;OMg0piY zXCLcLf98Km#%@Dm=Gw_oB65#C&Ig=Xv$e7}bMZ3wpl2`Gw(%Wrdp_NvM0oStJBj<- zw>I9sUd+GU{%T3g&-nYj{hnNcohMx1C8q6LzjX6wt1Wi%t17>Iemh??{C9K})9DTU VtbG#CZUOzr;OXk;vd$@?2>_TKh~WSL diff --git a/doc/c.png b/doc/c.png deleted file mode 100644 index c39fbf0e25ee296c5da082458363c45d945e55ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmVqm`?`neZy@-MWWPW6I1ran`JUPt^Pp2nEy;5sZWf01@dO#z`=%5EER@AM$q-T7II}~u7He5MzB*^2($9;r8$^sTRx!mG z*Y={Rp11gKlvQkAY5N${nZR6`uQXd6Q{e@Bh2dQhbZ(t1?070Ye8{5GRQ*|k);Is- zyZYFOBO(zQWDhg*y_Wu+*U+*r-a`3&yE1OHzojSttaK8#KYM}z0000F(pUKia5*{N xtO-?WJ(Rf1$C7KR)AZ0H0j(>SR=H$6kJ)6-DZGJ)<+b45x|97Z4cdwf)&TUlSJMCh diff --git a/doc/down.png b/doc/down.png deleted file mode 100644 index d41104a26f3d09deda6ab54281affd82c981abb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^{2gTe~DWM4fiX0)Z diff --git a/doc/doxysearch.php b/doc/doxysearch.php deleted file mode 100644 index 36112a42..00000000 --- a/doc/doxysearch.php +++ /dev/null @@ -1,329 +0,0 @@ -$word, - "match"=>$w, - "index"=>$statIdx, - "full"=>strlen($w)==strlen($word), - "docs"=>array() - ); - } - $w = readString($file); - } - $totalFreq=0; - for ($count=$start;$count$idx,"freq"=>$freq,"rank"=>0.0); - $totalFreq+=$freq; - if ($statInfo["full"]) $totalfreq+=$freq; - } - // read name an url info for the doc - for ($i=0;$i<$numDocs;$i++) - { - fseek($file,$docInfo[$i]["idx"]); - $docInfo[$i]["name"]=readString($file); - $docInfo[$i]["url"]=readString($file); - } - $statInfo["docs"]=$docInfo; - } - for ($count=$start;$count$key, - "name"=>$di["name"], - "rank"=>$rank - ); - } - $docs[$key]["words"][] = array( - "word"=>$wordInfo["word"], - "match"=>$wordInfo["match"], - "freq"=>$di["freq"] - ); - } - } - return $docs; -} - -function normalize_ranking(&$docs) -{ - $maxRank = 0.0000001; - // compute maximal rank - foreach ($docs as $doc) - { - if ($doc["rank"]>$maxRank) - { - $maxRank=$doc["rank"]; - } - } - reset($docs); - // normalize rankings - while (list ($key, $val) = each ($docs)) - { - $docs[$key]["rank"]*=100/$maxRank; - } -} - -function filter_results($docs,&$requiredWords,&$forbiddenWords) -{ - $filteredDocs=array(); - while (list ($key, $val) = each ($docs)) - { - $words = &$docs[$key]["words"]; - $copy=1; // copy entry by default - if (sizeof($requiredWords)>0) - { - foreach ($requiredWords as $reqWord) - { - $found=0; - foreach ($words as $wordInfo) - { - $found = $wordInfo["word"]==$reqWord; - if ($found) break; - } - if (!$found) - { - $copy=0; // document contains none of the required words - break; - } - } - } - if (sizeof($forbiddenWords)>0) - { - foreach ($words as $wordInfo) - { - if (in_array($wordInfo["word"],$forbiddenWords)) - { - $copy=0; // document contains a forbidden word - break; - } - } - } - if ($copy) $filteredDocs[$key]=$docs[$key]; - } - return $filteredDocs; -} - -function compare_rank($a,$b) -{ - return ($a["rank"]>$b["rank"]) ? -1 : 1; -} - -function sort_results($docs,&$sorted) -{ - $sorted = $docs; - usort($sorted,"compare_rank"); - return $sorted; -} - -function report_results(&$docs) -{ - echo "\n"; - echo " \n"; - echo " \n"; - echo " \n"; - $numDocs = sizeof($docs); - if ($numDocs==0) - { - echo " \n"; - echo " \n"; - echo " \n"; - } - else - { - echo " \n"; - echo " \n"; - echo " \n"; - $num=1; - foreach ($docs as $doc) - { - echo " \n"; - echo " "; - echo "\n"; - echo " \n"; - echo " \n"; - echo " \n"; - $num++; - } - } - echo "

Search Results

".matches_text(0)."
".matches_text($numDocs); - echo "\n"; - echo "
$num.".$doc["name"]."
Matches: "; - foreach ($doc["words"] as $wordInfo) - { - $word = $wordInfo["word"]; - $matchRight = substr($wordInfo["match"],strlen($word)); - echo "$word$matchRight(".$wordInfo["freq"].") "; - } - echo "
\n"; -} - -function matches_text($num) -{ - if ($num==0) - { - return 'Sorry, no documents matching your query.'; - } - else if ($num==1) - { - return 'Found 1 document matching your query.'; - } - else // $num>1 - { - return 'Found '.$num.' documents matching your query. Showing best matches first.'; - } -} - -function main($idxfile) -{ - if(strcmp('4.1.0', phpversion()) > 0) - { - die("Error: PHP version 4.1.0 or above required!"); - } - if (!($file=fopen($idxfile,"rb"))) - { - die("Error: Search index file could NOT be opened!"); - } - if (readHeader($file)!="DOXS") - { - die("Error: Header of index file is invalid!"); - } - $query=""; - if (array_key_exists("query", $_GET)) - { - $query=$_GET["query"]; - } - $results = array(); - $requiredWords = array(); - $forbiddenWords = array(); - $foundWords = array(); - $word=strtolower(strtok($query," ")); - while ($word) // for each word in the search query - { - if (($word{0}=='+')) { $word=substr($word,1); $requiredWords[]=$word; } - if (($word{0}=='-')) { $word=substr($word,1); $forbiddenWords[]=$word; } - if (!in_array($word,$foundWords)) - { - $foundWords[]=$word; - search($file,$word,$results); - } - $word=strtolower(strtok(" ")); - } - $docs = array(); - combine_results($results,$docs); - // filter out documents with forbidden word or that do not contain - // required words - $filteredDocs = filter_results($docs,$requiredWords,$forbiddenWords); - // normalize rankings so they are in the range [0-100] - normalize_ranking($filteredDocs); - // sort the results based on rank - $sorted = array(); - sort_results($filteredDocs,$sorted); - // report results to the user - report_results($sorted); - fclose($file); -} - -?> diff --git a/doc/fortran.png b/doc/fortran.png deleted file mode 100644 index 350c572ee38fc5aa8cef025284caa41a46921cbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmV+k0rvihP)z zQ4Yf(2!?U#(c~&{CVCaUn|okwjJWCAv@Y{YACUYt5I|c;L@*Nz0Eo!wFm8gG7quL( z=eJ8jF~;-zT!i-?03M^mxSQz{PD1(=o)3a5h-huxIH1YJR@TpAUHLpGAzkjqF33Gq z5HW;M#1ulv?Ojv&dqVG&6;xWfSwIyu&pDe3Y?b*+^Nvd@T*dL)Y-z<7!R9u;!a16o zt!IDE1;u=7(6;lN@7iT2j)+8Ll6#n$r@xl|p4XvuY#=TA`S#1W+5VQEsg98Zwj;#Z P00000NkvXXu0mjf06Tac diff --git a/doc/generateCore.html b/doc/generateCore.html deleted file mode 100644 index aefc5dd1..00000000 --- a/doc/generateCore.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - Description of generateCore - - - - - - - - - -
Home > . > generateCore.m
- - - -

generateCore -

- -

PURPOSE ^

-
GENERATECORE Generate Matlab classes from NWB core schema files
- -

SYNOPSIS ^

-
function generateCore(varargin)
- -

DESCRIPTION ^

-
 GENERATECORE Generate Matlab classes from NWB core schema files
-   GENERATECORE()  Generate classes (Matlab m-files) from the
-   NWB:N core namespace file.
-
-   GENERATECORE(core_or_extension_paths,...)  Generate classes for the
-   core namespace as well as one or more extenstions.  Each input filename
-   should be an NWB namespace file.
-
-   A cache of schema data is generated in the 'namespaces' subdirectory in
-   the current working directory.  This is for allowing cross-referencing
-   classes between multiple namespaces.
-
-   Output files are generated placed in a '+types' subdirectory in the
-   current working directory.
-
-   Example:
-      generateCore();
-      generateCore('schema/core/nwb.namespace.yaml');
-      generateCore('schema/my_ext/myext.namespace.yaml');
-
-   See also GENERATEEXTENSION
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function generateCore(varargin)
-0002 % GENERATECORE Generate Matlab classes from NWB core schema files
-0003 %   GENERATECORE()  Generate classes (Matlab m-files) from the
-0004 %   NWB:N core namespace file.
-0005 %
-0006 %   GENERATECORE(core_or_extension_paths,...)  Generate classes for the
-0007 %   core namespace as well as one or more extenstions.  Each input filename
-0008 %   should be an NWB namespace file.
-0009 %
-0010 %   A cache of schema data is generated in the 'namespaces' subdirectory in
-0011 %   the current working directory.  This is for allowing cross-referencing
-0012 %   classes between multiple namespaces.
-0013 %
-0014 %   Output files are generated placed in a '+types' subdirectory in the
-0015 %   current working directory.
-0016 %
-0017 %   Example:
-0018 %      generateCore();
-0019 %      generateCore('schema/core/nwb.namespace.yaml');
-0020 %      generateCore('schema/my_ext/myext.namespace.yaml');
-0021 %
-0022 %   See also GENERATEEXTENSION
-0023 if nargin == 0
-0024     [nwbLocation, ~, ~] = fileparts(mfilename('fullpath'));
-0025     namespacePath = fullfile(nwbLocation, 'schema', 'core', 'nwb.namespace.yaml');
-0026     generateExtension(namespacePath);
-0027 else
-0028     for i=1:length(varargin)
-0029         generateExtension(varargin{i});
-0030     end
-0031 end
-0032 end
-
Generated on Fri 09-Aug-2019 14:27:49 by m2html © 2005
- - \ No newline at end of file diff --git a/doc/generateExtension.html b/doc/generateExtension.html deleted file mode 100644 index c88cad81..00000000 --- a/doc/generateExtension.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - Description of generateExtension - - - - - - - - - -
Home > . > generateExtension.m
- - - -

generateExtension -

- -

PURPOSE ^

-
GENERATEEXTENSION Generate Matlab classes from NWB extension schema file
- -

SYNOPSIS ^

-
function generateExtension(source)
- -

DESCRIPTION ^

-
 GENERATEEXTENSION Generate Matlab classes from NWB extension schema file
-   GENERATECORE(extension_path...)  Generate classes 
-   (Matlab m-files) from one or more NWB:N schema extension namespace 
-   files.  A registry of already generated core types is used to resolve 
-   dependent types.
-   
-   A cache of schema data is generated in the 'namespaces' subdirectory in
-   the current working directory.  This is for allowing cross-referencing
-   classes between multiple namespaces.
-
-   Output files are generated placed in a '+types' subdirectory in the
-   current working directory.
-   
-   Example:
-      generateCore('schema\core\nwb.namespace.yaml');
-      generateExtension('schema\myext\myextension.namespace.yaml')
- 
-   See also GENERATECORE
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function generateExtension(source)
-0002 % GENERATEEXTENSION Generate Matlab classes from NWB extension schema file
-0003 %   GENERATECORE(extension_path...)  Generate classes
-0004 %   (Matlab m-files) from one or more NWB:N schema extension namespace
-0005 %   files.  A registry of already generated core types is used to resolve
-0006 %   dependent types.
-0007 %
-0008 %   A cache of schema data is generated in the 'namespaces' subdirectory in
-0009 %   the current working directory.  This is for allowing cross-referencing
-0010 %   classes between multiple namespaces.
-0011 %
-0012 %   Output files are generated placed in a '+types' subdirectory in the
-0013 %   current working directory.
-0014 %
-0015 %   Example:
-0016 %      generateCore('schema\core\nwb.namespace.yaml');
-0017 %      generateExtension('schema\myext\myextension.namespace.yaml')
-0018 %
-0019 %   See also GENERATECORE
-0020 validateattributes(source, {'char', 'string'}, {'scalartext'});
-0021 
-0022 %find jar from source and generate Schema
-0023 schema = spec.loadSchema();
-0024 
-0025 [localpath, ~, ~] = fileparts(source);
-0026 assert(2 == exist(source, 'file'),...
-0027     'MATNWB:FILE', 'Path to file `%s` could not be found.', source);
-0028 fid = fopen(source);
-0029 namespace_map = schema.read(fread(fid, '*char') .');
-0030 fclose(fid);
-0031 
-0032 spec.generate(namespace_map, localpath);
-0033 end
-
Generated on Fri 09-Aug-2019 14:27:49 by m2html © 2005
- - \ No newline at end of file diff --git a/doc/helpwin.css b/doc/helpwin.css deleted file mode 100644 index 53f47d6c..00000000 --- a/doc/helpwin.css +++ /dev/null @@ -1,149 +0,0 @@ -body { - font-family: Helvetica, Arial, sans-serif; - font-size: 14px; -} - -a { - color:#000099; -} - -a:visited { - color:#840084; -} - -a:active { - color:#000099; -} - -a:hover { - color:#000033; -} - -hr { - border-style: solid; - border-width: 5px 0px 0px 0px; - height: 4px; - width: 100%; - color: #0080ff; -} - -p { - margin-left: 5px; -} - -td { - font-family: Helvetica, Arial, sans-serif; - font-size: 12px; -} - -.title { - color:#990000; - font-size:24px; - padding:2px 0px 2px 0px; - margin:10px 0px 0px 5px; - vertical-align: middle; -} - -.sectiontitle { - color:#990000; - font-size:16px; - font-weight: bold; - width:100%; - padding:2px 0px 2px 5px; - margin: 15px 0px 0px 0px; -} - -.headertitle { - font-weight:bold; - padding: 3px 5px 3px 5px; -} - -.helptopic { - font-weight:bold; - color:#990000; -} - -.helptext { - margin-left: 5px; - font-family: monospace; - white-space: pre; - font-size: 13px; -} - -.subheader { - background-color: #E7EBF7; - width: 100%; -} - -.subheader-left { - padding: 3px 5px 3px 5px; -} - -.subheader-right { - text-align: right; - padding: 3px 5px 3px 5px; -} - -.footerlinktitle { - margin: 15px 0px 0px 5px; - color:#990000; - font-size: 16px; -} - -.footerlink { - padding: 3px 5px 3px 15px; -} - -.topiclinks { - margin: 5px 0px 0px 5px; -} - -a.topiclink { - padding-right: 10px; -} - -.class-details { - border-width: 0px 0px 0px 0px; - margin: 0px 0px 0px 5px; -} - -.class-detail-label { - font-weight: bold; - padding: 0px 10px 0px 0px; - spacing: 0px 0px 0px 0px; -} -.name { - color:#663d00; - font-weight: bold; -} -.summary-list .m-help { - white-space: nowrap; -} - -.summary-list { - margin: 0px 15px 0px 15px; -} - -.summary-list .summary-item .name { - border-width: 0px 0px 1px 0px; - border-style: solid; - border-color:#E7EBF7; - padding: 2px 15px 2px 5px; -} - -.summary-list .summary-item .m-help { - border-width: 0px 0px 1px 0px; - border-style: solid; - border-color:#E7EBF7; - padding: 2px 5px 2px 5px; - width: 95%; - white-space: normal; -} - -.summary-list .summary-item .attributes { - border-width: 0px 0px 1px 0px; - border-style: solid; - border-color:#E7EBF7; - padding: 2px 15px 2px 5px; - white-space: nowrap; -} diff --git a/doc/hp.png b/doc/hp.png deleted file mode 100644 index d09f988fb289d659869dbb1b2f199fd967a93958..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmVz z%MHUI3_wlAXs`;N$*f{_qX$Rw9FO zez{B`r8K|JmvGJjz-@IJXS2Omg-^gRJ-6O z&=syLR&U^^a}=eRZLpT@KTChlV@n^^it_t*Wz253r911aWh5p(A;kay002ovPDHLk FV1l-WYkL3y diff --git a/doc/index.html b/doc/index.html deleted file mode 100644 index c0f21dcd..00000000 --- a/doc/index.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - Index for Directory . - - - - - - - - - - -
< Master indexIndex for MatNWB >
- -

Index for MatNWB

- -

Matlab files in this directory:

- -
 NwbFileNWBFILE NWB:N file object representation
 generateCoreGENERATECORE Generate Matlab classes from NWB core schema files
 generateExtensionGENERATEEXTENSION Generate Matlab classes from NWB extension schema file
 nwbExportNWBEXPORT Writes an NWB file.
 nwbReadNWBREAD Reads an NWB file.
- - - - -

Class Directories:

-

Core:

- -

HDMF Common:

- -

HDMF Experimental:

- -

DataPipe Properties:

- -

DataPipe:

- -
Generated on Thu 27-May-2021 14:13:15 by m2html © 2005
- - diff --git a/doc/left.png b/doc/left.png deleted file mode 100644 index 404df045f40970496c71ca6b8c1f1357af271e27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^{2}w1qqB3TN5VEvJYV8 a;bDlI$G>{!EB$9cJq(_%elF{r5}E)JMz zQ4WJ342H|%(dbp$Gu5lqySaxtLO>W(rpx%_LnVK(UrGTT5y42B06;|UPU9jm^JJ0Z z`TTUN(6TJ&=lPPn_WX zId~2>0yJfGb3Up-MDlH7S}Vx-I*N`iO3-5Ff$K-Ed9Nepzm`zojRl WMtq7>mNL`;0000slDkd7sMUhm~nn@(#}qULHtXTzoc}*_9Non9cn|mMot$6xao(HD{Btemfo7KftJ(gq<~Y~G z@qZoHkA=+tS8)7W&Uw9q`Tr8me+#(&&u0HSm+S9Tw*NEO-*s^O@8dn4!TPz8qu!P4 zK_1Jw46grGoai}Etj@tK_}C@RCk z%#`S6Zs-xzlqxAD$1kf8AM0wN?c%>SMNC?Phc6~Pz{yO{A+R?kRa_)AA;iir?(CQ-4ncIv-nB)Nx{hL?F`wWg|rwizO*(ixxOASOWm3r><21 diff --git a/doc/mex.png b/doc/mex.png deleted file mode 100644 index 396f1bc943e311539c64aa839ba14242c3b1493c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^GC<7D!VDzmefZDNn{1``2&1H zT>k^XH6VupNCU}&+~7Mvim@cfFPOpM*^M+1C&}C0g`tC0)&t1lEbxdd1{y2`!i<;h z*8KqrvX^-Jy0Ty9km3|#4DWEh2o&=1ba4!+m=oK_$albigXt^#BmNKn`(>_}oOavV zHb=Afht|Vg%9B``@)jwvpOrK#Ny(Hf&TUq{A5ne4dvCr*%)z?JLGp XX&kFA6^pR}&1LX(^>bP0l+XkK#Yar! diff --git a/doc/nwbExport.html b/doc/nwbExport.html deleted file mode 100644 index 35812094..00000000 --- a/doc/nwbExport.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - Description of nwbExport - - - - - - - - - -
Home > . > nwbExport.m
- - - -

nwbExport -

- -

PURPOSE ^

-
NWBEXPORT Writes an NWB file.
- -

SYNOPSIS ^

-
function nwbExport(nwb, filenames)
- -

DESCRIPTION ^

-
NWBEXPORT Writes an NWB file.
-  nwbRead(nwb,filename) Writes the nwb object to a file at filename.
-
-  Example:
-    % Generate Matlab code for the NWB objects from the core schema.
-    % This only needs to be done once.
-    generateCore('schema\core\nwb.namespace.yaml');
-    % Create some fake fata and write
-    nwb = NwbFile;
-    nwb.session_start_time = datetime('now');
-    nwb.identifier = 'EMPTY';
-    nwb.session_description = 'empty test file';
-    nwbExport(nwb, 'empty.nwb');
-
-  See also GENERATECORE, GENERATEEXTENSION, NWBFILE, NWBREAD
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function nwbExport(nwb, filenames)
-0002 %NWBEXPORT Writes an NWB file.
-0003 %  nwbRead(nwb,filename) Writes the nwb object to a file at filename.
-0004 %
-0005 %  Example:
-0006 %    % Generate Matlab code for the NWB objects from the core schema.
-0007 %    % This only needs to be done once.
-0008 %    generateCore('schema\core\nwb.namespace.yaml');
-0009 %    % Create some fake fata and write
-0010 %    nwb = NwbFile;
-0011 %    nwb.session_start_time = datetime('now');
-0012 %    nwb.identifier = 'EMPTY';
-0013 %    nwb.session_description = 'empty test file';
-0014 %    nwbExport(nwb, 'empty.nwb');
-0015 %
-0016 %  See also GENERATECORE, GENERATEEXTENSION, NWBFILE, NWBREAD
-0017 validateattributes(nwb, {'NwbFile'}, {'nonempty'});
-0018 validateattributes(filenames, {'cell', 'string', 'char'}, {'nonempty'});
-0019 if iscell(filenames)
-0020     assert(iscellstr(filenames), 'filename cell array must consist of strings');
-0021 end
-0022 if ~isscalar(nwb)
-0023     assert(~ischar(filenames) && length(filenames) == length(nwb), ...
-0024         'NwbFile and filename array dimensions must match.');
-0025 end
-0026 
-0027 for i=1:length(nwb)
-0028     if iscellstr(filenames)
-0029         filename = filenames{i};
-0030     elseif isstring(filenames)
-0031         filename = filenames(i);
-0032     else
-0033         filename = filenames;
-0034     end
-0035     export(nwb(i), filename);
-0036 end
-0037 end
-
Generated on Fri 09-Aug-2019 14:27:49 by m2html © 2005
- - \ No newline at end of file diff --git a/doc/nwbRead.html b/doc/nwbRead.html deleted file mode 100644 index 170a7454..00000000 --- a/doc/nwbRead.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - Description of nwbRead - - - - - - - - - -
Home > . > nwbRead.m
- - - -

nwbRead -

- -

PURPOSE ^

-
NWBREAD Reads an NWB file.
- -

SYNOPSIS ^

-
function nwb = nwbRead(filename, varargin)
- -

DESCRIPTION ^

-
NWBREAD Reads an NWB file.
-  nwb = nwbRead(filename) Reads the nwb file at filename and returns an
-  NWBFile object representing its contents.
-
-  Requires that core and extension NWB types have been generated
-  and reside in a 'types' package on the matlab path.
-
-  Example:
-    %Generate Matlab code for the NWB objects from the core schema.
-    %This only needs to be done once.
-    generateCore('schema\core\nwb.namespace.yaml');
-    %Now we can read nwb files!
-    nwb=nwbRead('data.nwb');
-
-  See also GENERATECORE, GENERATEEXTENSION, NWBFILE, NWBEXPORT
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function nwb = nwbRead(filename, varargin)
-0002 %NWBREAD Reads an NWB file.
-0003 %  nwb = nwbRead(filename) Reads the nwb file at filename and returns an
-0004 %  NWBFile object representing its contents.
-0005 %
-0006 %  Requires that core and extension NWB types have been generated
-0007 %  and reside in a 'types' package on the matlab path.
-0008 %
-0009 %  Example:
-0010 %    %Generate Matlab code for the NWB objects from the core schema.
-0011 %    %This only needs to be done once.
-0012 %    generateCore('schema\core\nwb.namespace.yaml');
-0013 %    %Now we can read nwb files!
-0014 %    nwb=nwbRead('data.nwb');
-0015 %
-0016 %  See also GENERATECORE, GENERATEEXTENSION, NWBFILE, NWBEXPORT
-0017 ignorecache = ~isempty(varargin) && ischar(varargin{1}) &&...
-0018     strcmp('ignorecache', varargin{1});
-0019 if ischar(filename)
-0020     validateattributes(filename, {'char'}, {'scalartext', 'nonempty'});
-0021     info = h5info(filename);
-0022     try
-0023         %check for .specloc
-0024         fid = H5F.open(filename);
-0025         attr_id = H5A.open(fid, '.specloc');
-0026         ref_data = H5A.read(attr_id);
-0027         blacklist = H5R.get_name(attr_id, 'H5R_OBJECT', ref_data);
-0028         if ~ignorecache
-0029             generateSpec(fid, h5info(filename, blacklist));
-0030             rehash(); %required if we want parseGroup to read the right files.
-0031         end
-0032         info.Attributes(strcmp('.specloc', {info.Attributes.Name})) = [];
-0033         H5A.close(attr_id);
-0034         H5F.close(fid);
-0035     catch ME
-0036         if ~strcmp(ME.identifier, 'MATLAB:imagesci:hdf5lib:libraryError')
-0037             rethrow(ME);
-0038         end
-0039         blacklist = '';
-0040     end
-0041     nwb = io.parseGroup(filename, info, blacklist);
-0042     return;
-0043 elseif isstring(filename)
-0044     validateattributes(filename, {'string'}, {'nonempty'});
-0045 else
-0046     validateattributes(filename, {'cell'}, {'nonempty'});
-0047     assert(iscellstr(filename));
-0048 end
-0049 nwb = NwbFile.empty(length(filename), 0);
-0050 isStringArray = isstring(filename);
-0051 for i=1:length(filename)
-0052     if isStringArray
-0053         fnm = filename(i);
-0054     else
-0055         fnm = filename{i};
-0056     end
-0057     info = h5info(fnm);
-0058     nwb(i) = io.parseGroup(fnm, info);
-0059 end
-0060 end
-0061 
-0062 function generateSpec(fid, specinfo)
-0063 schema = spec.loadSchema();
-0064 
-0065 for i=1:length(specinfo.Groups)
-0066     location = specinfo.Groups(i).Groups(1);
-0067     
-0068     namespace_name = split(specinfo.Groups(i).Name, '/');
-0069     namespace_name = namespace_name{end};
-0070     
-0071     filenames = {location.Datasets.Name};
-0072     if ~any(strcmp('namespace', filenames))
-0073         warning('MATNWB:INVALIDCACHE',...
-0074         'Couldn''t find a `namespace` in namespace `%s`.  Skipping cache generation.',...
-0075         namespace_name);
-0076         return;
-0077     end
-0078     source_names = {location.Datasets.Name};
-0079     file_loc = strcat(location.Name, '/', source_names);
-0080     schema_map = containers.Map;
-0081     for j=1:length(file_loc)
-0082         did = H5D.open(fid, file_loc{j});
-0083         if strcmp('namespace', source_names{j})
-0084             namespace_map = schema.read(H5D.read(did));
-0085         else
-0086             schema_map(source_names{j}) = H5D.read(did);    
-0087         end
-0088         H5D.close(did);
-0089     end
-0090     
-0091     spec.generate(namespace_map, schema_map);
-0092 end
-0093 end
-
Generated on Fri 09-Aug-2019 14:27:49 by m2html © 2005
- - \ No newline at end of file diff --git a/doc/pcode.png b/doc/pcode.png deleted file mode 100644 index 6801bd9a259131e6de7d7957789598a451c04334..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^Qb5ej!3-qBCsR(zDBw1As!D z1s;*bKxKj;%vhfiKM^R%lJ4m1$iT3%pZiZDD@aj*Pl)S(AYk|pNdQueB|(0{ z3=Yq3qyagOo-U3d6}OTT4lqj`WYF%P$jKq`kl`G&cO%P#Bm=oF5vkS`gujP)z zZ3@F642I+A(ex_uOz|q|-Q16CA~DKXHy!h#6nPc#A=(dzh_Dj}01(mYWlSQ*cu32| zbbY!^c%J9=dA@}A9sq8umob~|g)?E>&V+Ie#O(HQu@+QR?< N002ovPDHLkV1hx>d=3Br diff --git a/doc/simulinkicon.gif b/doc/simulinkicon.gif deleted file mode 100644 index 1386ddd4ce654071015610e81fdea82461d8e28b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 977 zcmbu8u}_m>5XK)83^3Uu8;SX77?CI^3B-XItA?P13elB7PXr>Q5E~tEP-6{~F%D-K z)WpFqI=OT~2IKIO5C(b~XFmOKi{R_TBlgr&b_q*r5UY@)(Ha=TG0e=CHm)y-= z-NhXdrR2#xsVDJ-NLq3;H+2&?M6D)Qb5&PyMYKwCF&A|a7f{KO9Oh7mI2cfy($r!z zs}XbJSxTCcq=XpD$Sj#AlVk`ACe@N^Qb~${Vy##bO(ID^Mz$tcf+k1;r;?gtgeE1i znB!n-Q<_?AMl2LDpPGpoVn2>$q-rXrh;>Q9q^OCQfZG#_wFWhaf$Px^VzQfdaDPH@ zLsCc`!iKu|Sa$Qm|sSpKyq|-EjTr8*2+?H0- z4`Q;Lc94k+b3;;7jL_^P7CAVW+LT6|D5{xe%m-eKMsh!nWh97?BYe&89m)KJVRL?! zsLhQI*K_ZpHMb*1{#n2u&8oRKvdcS(-_QHDh{>=FJST?;-$*#_> zFWg+IF0Bn^lXvHav-8W#Szfc2RV!b=VRFa{1fJ1Bvn_=*1$x~u5G{^07%?ML0m zHxB3bGCb|+8kyh!`1T@vWbw!Thc`P>6y|3Oo@O9$!+|lO2pBq=~ z?6HkkzgKp4CuH{I*7l2^yRRR1_MTc?@7vp&8a;gcZ2H%?cU{-M_dgh2`cl7D{M1<} G;M_lk-mF{z diff --git a/doc/solaris.png b/doc/solaris.png deleted file mode 100644 index e31e8a2a48078233e65bf10502de055609469206..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmV+(0pb3MP)LJX0GRn|87F~=qO=0d z$LTWTx~|80zJzI-0N{4DjI%Mk;moMpnNcohj7EB(iPVo~iqGKm_8{s|W(+hpPU=N2 zz2$E5rj(XtF(bOm8L*eKNAeVg;o46LO1s$e-HK3o_b;p)l_XJ5>4v`(0TG#bkaL)bhCi169@`rB kx1WB<=i4tMjP|#52ZwT~UP>&M!~g&Q07*qoM6N<$g3~#GJpcdz diff --git a/doc/up.png b/doc/up.png deleted file mode 100644 index b348b9a1b19bd5030efd3df293d84a18b41ef6a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^{2x4R4De4J#NJjgjrM8h9Te~(8G kcWyrh{e1gnl+pf{?#tSrFr=&ZY5)KL07*qoM6N<$f*5RoFaQ7m From 8dddf6ecd0a1d2f6ef2c1a3017330293a726a541 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 15:19:19 +0100 Subject: [PATCH 06/30] Update export location for tutorial html files --- tools/documentation/matnwb_exportTutorials.m | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/documentation/matnwb_exportTutorials.m b/tools/documentation/matnwb_exportTutorials.m index b514a56f..7e18faa4 100644 --- a/tools/documentation/matnwb_exportTutorials.m +++ b/tools/documentation/matnwb_exportTutorials.m @@ -18,17 +18,22 @@ function matnwb_exportTutorials(options) options.IgnoreFiles (1,:) string = ["basicUsage", "read_demo", "remote_read"]; options.RunLivescript (1,1) logical = true end + + EXPORT_FOLDERS = dictionary(... + '.m', fullfile(misc.getMatnwbDir, "tutorials", "private", "mcode"), ... + '.html', fullfile(misc.getMatnwbDir, "docs", "source", "_static", "html", "tutorials") ); [exportFormat, targetFolderNames] = deal(options.ExportFormat); targetFolderNames = extractAfter(targetFolderNames, "."); - targetFolderNames(strcmp(targetFolderNames, "m")) = fullfile("private", "mcode"); - nwbTutorialDir = fullfile(misc.getMatnwbDir, "tutorials"); targetFolderPaths = fullfile(nwbTutorialDir, targetFolderNames); - - for folderPath = targetFolderPaths - if ~isfolder(folderPath); mkdir(folderPath); end + + for i = 1:numel(exportFormat) + if isKey(EXPORT_FOLDERS, exportFormat(i)) + targetFolderPaths(i) = EXPORT_FOLDERS(exportFormat(i)); + end + if ~isfolder(targetFolderPaths(i)); mkdir(targetFolderPaths(i)); end end if isempty(options.FilePaths) From f9899842126478ccf42a6aeede6023751a55770a Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 15:20:13 +0100 Subject: [PATCH 07/30] remove html files from tutorials/html (files are now located in docs/source/_static/html/tutorials) --- tutorials/html/UnitTimes.png | Bin 71834 -> 0 bytes tutorials/html/basicUsage.html | 431 ----- tutorials/html/behavior.html | 369 ---- tutorials/html/convertTrials.html | 1119 ------------ tutorials/html/dataPipe.html | 441 ----- tutorials/html/dimensionMapNoDataPipes.html | 92 - tutorials/html/dimensionMapWithDataPipes.html | 110 -- tutorials/html/dynamic_tables.html | 577 ------- .../html/dynamically_loaded_filters.html | 141 -- tutorials/html/ecephys.html | 1502 ----------------- tutorials/html/ecephys.png | Bin 1670 -> 0 bytes tutorials/html/ecephys_01.png | Bin 10206 -> 0 bytes tutorials/html/ecephys_data_deps.png | Bin 102809 -> 0 bytes tutorials/html/icephys.html | 558 ------ tutorials/html/images.html | 371 ---- tutorials/html/intro.html | 511 ------ tutorials/html/ogen.html | 201 --- tutorials/html/ophys.html | 564 ------- tutorials/html/ophys_tutorial_schematic.png | Bin 77678 -> 0 bytes tutorials/html/read_demo.html | 356 ---- tutorials/html/remote_read.html | 58 - tutorials/html/scratch.html | 164 -- 22 files changed, 7565 deletions(-) delete mode 100644 tutorials/html/UnitTimes.png delete mode 100644 tutorials/html/basicUsage.html delete mode 100644 tutorials/html/behavior.html delete mode 100644 tutorials/html/convertTrials.html delete mode 100644 tutorials/html/dataPipe.html delete mode 100644 tutorials/html/dimensionMapNoDataPipes.html delete mode 100644 tutorials/html/dimensionMapWithDataPipes.html delete mode 100644 tutorials/html/dynamic_tables.html delete mode 100644 tutorials/html/dynamically_loaded_filters.html delete mode 100644 tutorials/html/ecephys.html delete mode 100644 tutorials/html/ecephys.png delete mode 100644 tutorials/html/ecephys_01.png delete mode 100644 tutorials/html/ecephys_data_deps.png delete mode 100644 tutorials/html/icephys.html delete mode 100644 tutorials/html/images.html delete mode 100644 tutorials/html/intro.html delete mode 100644 tutorials/html/ogen.html delete mode 100644 tutorials/html/ophys.html delete mode 100644 tutorials/html/ophys_tutorial_schematic.png delete mode 100644 tutorials/html/read_demo.html delete mode 100644 tutorials/html/remote_read.html delete mode 100644 tutorials/html/scratch.html diff --git a/tutorials/html/UnitTimes.png b/tutorials/html/UnitTimes.png deleted file mode 100644 index d31f8b5502ec3d96c69a0fe13db67dc5169d6c54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71834 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 -Using NWB Data \ No newline at end of file diff --git a/tutorials/html/behavior.html b/tutorials/html/behavior.html deleted file mode 100644 index 4f70f592..00000000 --- a/tutorials/html/behavior.html +++ /dev/null @@ -1,369 +0,0 @@ - -Behavior Data

Behavior Data

This tutorial will guide you in writing behavioral data to NWB.

Creating an NWB File

Create an NWBFile object with the required fields (session_description, identifier, and session_start_time) and additional metadata.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'My Name', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', 'DOI:10.1016/j.neuron.2016.12.011'); % optional
nwb
nwb =
NwbFile with properties: - - nwb_version: '2.7.0' - file_create_date: [] - identifier: 'Mouse5_Day3' - session_description: 'mouse in open exploration' - session_start_time: {[2018-04-25T02:30:03.000000+02:00]} - timestamps_reference_time: [] - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [0×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: 'My Name' - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [0×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] - -Warning: The following required properties are missing for instance for type "NwbFile": - timestamps_reference_time

SpatialSeries: Storing continuous spatial data

SpatialSeries is a subclass of TimeSeries that represents data in space, such as the spatial direction e.g., of gaze or travel or position of an animal over time.
Create data that corresponds to x, y position over time.
position_data = [linspace(0, 10, 50); linspace(0, 8, 50)]; % 2 x nT array
In SpatialSeries data, the first dimension is always time (in seconds), the second dimension represents the x, y position. However, as described in the dimensionMapNoDataPipes tutorial, when a MATLAB array is exported to HDF5, the array is transposed. Therefore, in order to correctly export the data, in MATLAB the last dimension of an array should be time. SpatialSeries data should be stored as one continuous stream as it is acquired, not by trials as is often reshaped for analysis. Data can be trial-aligned on-the-fly using the trials table. See the trials tutorial for further information.
For position data reference_frame indicates the zero-position, e.g. the 0,0 point might be the bottom-left corner of an enclosure, as viewed from the tracking camera.
timestamps = linspace(0, 50, 50)/ 200;
position_spatial_series = types.core.SpatialSeries( ...
'description', 'Postion (x, y) in an open field.', ...
'data', position_data, ...
'timestamps', timestamps, ...
'reference_frame', '(0,0) is the bottom left corner.' ...
)
position_spatial_series =
SpatialSeries with properties: - - reference_frame: '(0,0) is the bottom left corner.' - starting_time_unit: 'seconds' - timestamps_interval: 1 - timestamps_unit: 'seconds' - data: [2×50 double] - comments: 'no comments' - control: [] - control_description: '' - data_continuity: '' - data_conversion: 1 - data_offset: 0 - data_resolution: -1 - data_unit: 'meters' - description: 'Postion (x, y) in an open field.' - starting_time: [] - starting_time_rate: [] - timestamps: [0 0.0051 0.0102 0.0153 0.0204 0.0255 0.0306 0.0357 0.0408 0.0459 0.0510 0.0561 0.0612 0.0663 0.0714 0.0765 0.0816 0.0867 0.0918 0.0969 0.1020 0.1071 0.1122 0.1173 0.1224 0.1276 0.1327 0.1378 0.1429 0.1480 0.1531 … ] (1×50 double) -

Position: Storing position measured over time

To help data analysis and visualization tools know that this SpatialSeries object represents the position of the subject, store the SpatialSeries object inside a Position object, which can hold one or more SpatialSeries objects.
position = types.core.Position();
position.spatialseries.set('SpatialSeries', position_spatial_series);

Create a Behavior Processing Module

Create a processing module called "behavior" for storing behavioral data in the NWBFile, then add the Position object to the processing module.
behavior_processing_module = types.core.ProcessingModule('description', 'stores behavioral data.');
behavior_processing_module.nwbdatainterface.set("Position", position);
nwb.processing.set("behavior", behavior_processing_module);

CompassDirection: Storing view angle measured over time

Analogous to how position can be stored, we can create a SpatialSeries object for representing the view angle of the subject.
For direction data reference_frame indicates the zero direction, for instance in this case "straight ahead" is 0 radians.
view_angle_data = linspace(0, 4, 50);
direction_spatial_series = types.core.SpatialSeries( ...
'description', 'View angle of the subject measured in radians.', ...
'data', view_angle_data, ...
'timestamps', timestamps, ...
'reference_frame', 'straight ahead', ...
'data_unit', 'radians' ...
);
direction = types.core.CompassDirection();
direction.spatialseries.set('spatial_series', direction_spatial_series);
We can add a CompassDirection object to the behavior processing module the same way we have added the position data.
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('CompassDirection', direction);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehaviorTimeSeries: Storing continuous behavior data

BehavioralTimeSeries is an interface for storing continuous behavior data, such as the speed of a subject.
speed_data = linspace(0, 0.4, 50);
 
speed_time_series = types.core.TimeSeries( ...
'data', speed_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 10.0, ... % Hz
'description', 'he speed of the subject measured over time.', ...
'data_unit', 'm/s' ...
);
 
behavioral_time_series = types.core.BehavioralTimeSeries();
behavioral_time_series.timeseries.set('speed', speed_time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralTimeSeries', behavioral_time_series);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehavioralEvents: Storing behavioral events

BehavioralEvents is an interface for storing behavioral events. We can use it for storing the timing and amount of rewards (e.g. water amount) or lever press times.
reward_amount = [1.0, 1.5, 1.0, 1.5];
event_timestamps = [1.0, 2.0, 5.0, 6.0];
 
time_series = types.core.TimeSeries( ...
'data', reward_amount, ...
'timestamps', event_timestamps, ...
'description', 'The water amount the subject received as a reward.', ...
'data_unit', 'ml' ...
);
 
behavioral_events = types.core.BehavioralEvents();
behavioral_events.timeseries.set('lever_presses', time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it
Storing only the timestamps of the events is possible with the ndx-events NWB extension. You can also add labels associated with the events with this extension. You can find information about installation and example usage here.

BehavioralEpochs: Storing intervals of behavior data

BehavioralEpochs is for storing intervals of behavior data. BehavioralEpochs uses IntervalSeries to represent the time intervals. Create an IntervalSeries object that represents the time intervals when the animal was running. IntervalSeries uses 1 to indicate the beginning of an interval and -1 to indicate the end.
run_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was running.', ...
'data', [1, -1, 1, -1, 1, -1], ...
'timestamps', [0.5, 1.5, 3.5, 4.0, 7.0, 7.3] ...
);
 
behavioral_epochs = types.core.BehavioralEpochs();
behavioral_epochs.intervalseries.set('running', run_intervals);
You can add more than one IntervalSeries to a BehavioralEpochs object.
sleep_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was sleeping', ...
'data', [1, -1, 1, -1], ...
'timestamps', [15.0, 30.0, 60.0, 95.0] ...
);
behavioral_epochs.intervalseries.set('sleeping', sleep_intervals);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
% behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
% nwb.processing.set('behavior', behavior_processing_module);

Another approach: TimeIntervals

Using TimeIntervals to represent time intervals is often preferred over BehavioralEpochs and IntervalSeries. TimeIntervals is a subclass of DynamicTable, which offers flexibility for tabular data by allowing the addition of optional columns which are not defined in the standard DynamicTable class.
sleep_intervals = types.core.TimeIntervals( ...
'description', 'Intervals when the animal was sleeping.', ...
'colnames', {'start_time', 'stop_time', 'stage'} ...
);
 
sleep_intervals.addRow('start_time', 0.3, 'stop_time', 0.35, 'stage', 1);
sleep_intervals.addRow('start_time', 0.7, 'stop_time', 0.9, 'stage', 2);
sleep_intervals.addRow('start_time', 1.3, 'stop_time', 3.0, 'stage', 3);
 
nwb.intervals.set('sleep_intervals', sleep_intervals);

EyeTracking: Storing continuous eye-tracking data of gaze direction

EyeTracking is for storing eye-tracking data which represents direction of gaze as measured by an eye tracking algorithm. An EyeTracking object holds one or more SpatialSeries objects that represent the gaze direction over time extracted from a video.
eye_position_data = [linspace(-20, 30, 50); linspace(30, -20, 50)];
 
right_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
left_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
eye_tracking = types.core.EyeTracking();
eye_tracking.spatialseries.set('right_eye_position', right_eye_position);
eye_tracking.spatialseries.set('left_eye_position', left_eye_position);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('EyeTracking', eye_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

PupilTracking: Storing continuous eye-tracking data of pupil size

PupilTracking is for storing eye-tracking data which represents pupil size. PupilTracking holds one or more TimeSeries objects that can represent different features such as the dilation of the pupil measured over time by a pupil tracking algorithm.
pupil_diameter = types.core.TimeSeries( ...
'description', 'Pupil diameter extracted from the video of the right eye.', ...
'data', linspace(0.001, 0.002, 50), ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 20.0, ... % Hz
'data_unit', 'meters' ...
);
 
pupil_tracking = types.core.PupilTracking();
pupil_tracking.timeseries.set('pupil_diameter', pupil_diameter);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('PupilTracking', pupil_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

Writing the behavior data to an NWB file

All of the above commands build an NWBFile object in-memory. To write this file, use nwbExport.
% Save to tutorials/tutorial_nwb_files folder
nwbFilePath = misc.getTutorialNwbFilePath('behavior_tutorial.nwb');
nwbExport(nwb, nwbFilePath);
fprintf('Exported NWB file to "%s"\n', 'behavior_tutorial.nwb')
Exported NWB file to "behavior_tutorial.nwb"
-
- -
\ No newline at end of file diff --git a/tutorials/html/convertTrials.html b/tutorials/html/convertTrials.html deleted file mode 100644 index 1df73a43..00000000 --- a/tutorials/html/convertTrials.html +++ /dev/null @@ -1,1119 +0,0 @@ - - - - - -NWB File Conversion Tutorial - - - - - - - -
-

NWB File Conversion Tutorial

- -

How to convert trial-based experimental data to the Neurodata Without Borders file format using MatNWB. This example uses the CRCNS ALM-3 data set. Information on how to download the data can be found on the CRCNS Download Page. One should first familiarize themselves with the file format, which can be found on the ALM-3 About Page under the Documentation files.

-
author: Lawrence Niu
-contact: lawrence@vidriotech.com
-last updated: Sep 14, 2024
- -

Contents

- -

Script Configuration

-

The following section describes configuration parameters specific to the publishing script, and can be skipped when implementing your own conversion. The parameters can be changed to fit any of the available sessions.

-
animal = 'ANM255201';
-session = '20141124';
-
-identifier = [animal '_' session];
-
-% Specify the local path for the downloaded data:
-data_root_path = 'data';
-
-metadata_loc = fullfile(data_root_path, 'metadata', ['meta_data_' identifier '.mat']);
-datastructure_loc = fullfile(data_root_path, 'data_structure_files',...
-    ['data_structure_' identifier '.mat']);
-rawdata_loc = fullfile(data_root_path, 'RawVoltageTraces', [identifier '.tar']);
-
-

The animal and session specifier can be changed with the animal and session variable name respectively. metadata_loc, datastructure_loc, and rawdata_loc should refer to the metadata .mat file, the data structure .mat file, and the raw .tar file.

-
output_directory = 'out';
-
-if ~isfolder(output_directory)
-    mkdir(output_directory);
-end
-
-source_file = [mfilename() '.m'];
-[~, source_script, ~] = fileparts(source_file);
-
-

The NWB file will be saved in the output directory indicated by output_directory -

-

General Information

-
nwb = NwbFile();
-nwb.identifier = identifier;
-nwb.general_source_script = source_script;
-nwb.general_source_script_file_name = source_file;
-nwb.general_lab = 'Svoboda';
-nwb.general_keywords = {'Network models', 'Premotor cortex', 'Short-term memory'};
-nwb.general_institution = ['Janelia Research Campus,'...
-    ' Howard Huges Medical Institute, Ashburn, Virginia 20147, USA'];
-nwb.general_related_publications = ...
-    ['Li N, Daie K, Svoboda K, Druckmann S (2016).',...
-    ' Robust neuronal dynamics in premotor cortex during motor planning.',...
-    ' Nature. 7600:459-64. doi: 10.1038/nature17643'];
-nwb.general_stimulus = 'photostim';
-nwb.general_protocol = 'IACUC';
-nwb.general_surgery = ['Mice were prepared for photoinhibition and ',...
-    'electrophysiology with a clear-skull cap and a headpost. ',...
-    'The scalp and periosteum over the dorsal surface of the skull were removed. ',...
-    'A layer of cyanoacrylate adhesive (Krazy glue, Elmer''s Products Inc.) ',...
-    'was directly applied to the intact skull. A custom made headpost ',...
-    'was placed on the skull with its anterior edge aligned with the suture lambda ',...
-    '(approximately over cerebellum) and cemented in place ',...
-    'with clear dental acrylic (Lang Dental Jet Repair Acrylic; 1223-clear). ',...
-    'A thin layer of clear dental acrylic was applied over the cyanoacrylate adhesive ',...
-    'covering the entire exposed skull, ',...
-    'followed by a thin layer of clear nail polish (Electron Microscopy Sciences, 72180).'];
-nwb.session_description = sprintf('Animal `%s` on Session `%s`', animal, session);
-
-

All properties with the prefix general contain context for the entire experiment such as lab, institution, and experimentors. For session-delimited data from the same experiment, these fields will all be the same. Note that most of this information was pulled from the publishing paper and not from any of the downloadable data.

-

The only required property is the identifier, which distinguishes one session from another within an experiment. In our case, the ALM-3 data uses a combination of session date and animal ID.

-

The ALM-3 File Structure

-

Each ALM-3 session has three files: a metadata .mat file describing the experiment, a data structures .mat file containing analyzed data, and a raw .tar archive containing multiple raw electrophysiology data separated by trials as .mat files. All files will be merged into a single NWB file.

-

Metadata

-

ALM-3 Metadata contains information about the reference times, experimental context, methodology, as well as details of the electrophysiology, optophysiology, and behavioral portions of the experiment. A vast majority of these details are placed in general prefixed properties in NWB.

-
fprintf('Processing Meta Data from `%s`\n', metadata_loc);
-loaded = load(metadata_loc, 'meta_data');
-meta = loaded.meta_data;
-
-% Experiment-specific treatment for animals with the ReaChR gene modification
-isreachr = any(cell2mat(strfind(meta.animalGeneModification, 'ReaChR')));
-
-% Sessions are separated by date of experiment.
-nwb.general_session_id = meta.dateOfExperiment;
-
-% ALM-3 data start time is equivalent to the reference time.
-nwb.session_start_time = datetime([meta.dateOfExperiment meta.timeOfExperiment],...
-    'InputFormat', 'yyyyMMddHHmmss', 'TimeZone', 'America/New_York'); % Eastern Daylight Time
-nwb.timestamps_reference_time = nwb.session_start_time;
-
-nwb.general_experimenter = strjoin(meta.experimenters, ', ');
-
-
Processing Meta Data from `data/metadata/meta_data_ANM255201_20141124.mat`
-
-
nwb.general_subject = types.core.Subject(...
-    'species', meta.species{1}, ...
-    'subject_id', meta.animalID{1}(1,:), ... %weird case with duplicate Animal ID
-    'sex', meta.sex, ...
-    'age', meta.dateOfBirth, ...
-    'description', [...
-        'Whisker Config: ' strjoin(meta.whiskerConfig, ', ') newline...
-        'Animal Source: ' strjoin(meta.animalSource, ', ')]);
-
-

Ideally, if a raw data field does not correspond directly to a NWB field, one would create their own using a custom NWB extension class. However, since these fields are mostly experimental annotations, we instead pack the extra values into the description field as a string.

-
-% The formatStruct function simply prints the field and values given the struct.
-% An optional cell array of field names specifies whitelist of fields to print.
-% This function is provided with this script in the tutorials directory.
-nwb.general_subject.genotype = formatStruct(...
-    meta, ...
-    {'animalStrain'; 'animalGeneModification'; 'animalGeneCopy';...
-    'animalGeneticBackground'});
-
-weight = {};
-if ~isempty(meta.weightBefore)
-    weight{end+1} = 'weightBefore';
-end
-if ~isempty(meta.weightAfter)
-    weight{end+1} = 'weightAfter';
-end
-weight = weight(~cellfun('isempty', weight));
-if ~isempty(weight)
-    nwb.general_subject.weight = formatStruct(meta, weight);
-end
-
-% general/experiment_description
-nwb.general_experiment_description = [...
-    formatStruct(meta, {'experimentType'; 'referenceAtlas'}), ...
-    newline, ...
-    formatStruct(meta.behavior, {'task_keyword'})];
-
-% Miscellaneous collection information from ALM-3 that didn't quite fit any NWB
-% properties are stored in general/data_collection.
-nwb.general_data_collection = formatStruct(meta.extracellular,...
-    {'extracellularDataType';'cellType';'identificationMethod';'amplifierRolloff';...
-    'spikeSorting';'ADunit'});
-
-% Device objects are essentially just a list of device names.  We store the probe
-% and laser hardware names here.
-probetype = meta.extracellular.probeType{1};
-probeSource = meta.extracellular.probeSource{1};
-deviceName = [probetype ' (' probeSource ')'];
-nwb.general_devices.set(deviceName, types.core.Device());
-
-if isreachr
-    laserName = 'laser-594nm (Cobolt Inc., Cobolt Mambo 100)';
-else
-    laserName = 'laser-473nm (Laser Quantum, Gem 473)';
-end
-nwb.general_devices.set(laserName, types.core.Device());
-
-
structDesc = {'recordingCoordinates';'recordingMarker';'recordingType';'penetrationN';...
-    'groundCoordinates'};
-if ~isempty(meta.extracellular.referenceCoordinates)
-    structDesc{end+1} = 'referenceCoordinates';
-end
-recordingLocation = meta.extracellular.recordingLocation{1};
-egroup = types.core.ElectrodeGroup(...
-    'description', formatStruct(meta.extracellular, structDesc),...
-    'location', recordingLocation,...
-    'device', types.untyped.SoftLink(['/general/devices/' deviceName]));
-nwb.general_extracellular_ephys.set(deviceName, egroup);
-
-

The NWB ElectrodeGroup object stores experimental information regarding a group of probes. Doing so requires a SoftLink to the probe specified under general_devices. SoftLink objects are direct maps to HDF5 Soft Links on export, and thus, require a true HDF5 path.

-
-% You can specify column names and values as key-value arguments in the DynamicTable
-% constructor.
-dtColNames = {'x', 'y', 'z', 'imp', 'location', 'filtering','group', 'group_name'};
-dynTable = types.hdmf_common.DynamicTable(...
-    'colnames', dtColNames,...
-    'description', 'Electrodes',...
-    'x', types.hdmf_common.VectorData('description', 'x coordinate of the channel location in the brain (+x is posterior).'),...
-    'y', types.hdmf_common.VectorData('description', 'y coordinate of the channel location in the brain (+y is inferior).'),...
-    'z', types.hdmf_common.VectorData('description', 'z coordinate of the channel location in the brain (+z is right).'),...
-    'imp', types.hdmf_common.VectorData('description', 'Impedance of the channel.'),...
-    'location', types.hdmf_common.VectorData('description', ['Location of the electrode (channel). '...
-    'Specify the area, layer, comments on estimation of area/layer, stereotaxic coordinates if '...
-    'in vivo, etc. Use standard atlas names for anatomical regions when possible.']),...
-    'filtering', types.hdmf_common.VectorData('description', 'Description of hardware filtering.'),...
-    'group', types.hdmf_common.VectorData('description', 'Reference to the ElectrodeGroup this electrode is a part of.'),...
-    'group_name', types.hdmf_common.VectorData('description', 'Name of the ElectrodeGroup this electrode is a part of.'));
-
-% Raw HDF5 path to the above electrode group. Referenced by
-% the general/extracellular_ephys Dynamic Table
-egroupPath = ['/general/extracellular_ephys/' deviceName];
-eGroupReference = types.untyped.ObjectView(egroupPath);
-for i = 1:length(meta.extracellular.siteLocations)
-    location = meta.extracellular.siteLocations{i};
-    % Add each row in the dynamic table. The `id` column is populated
-    % dynamically.
-    dynTable.addRow(...
-        'x', location(1), 'y', location(2), 'z', location(3),...
-        'imp', 0,...
-        'location', recordingLocation,...
-        'filtering', '',...
-        'group', eGroupReference,...
-        'group_name', probetype);
-end
-
-

The group column in the Dynamic Table contains an ObjectView to the previously created ElectrodeGroup. An ObjectView can be best thought of as a direct pointer to another typed object. It also directly maps to a HDF5 Object Reference, thus the HDF5 path requirement. ObjectViews are slightly different from SoftLinks in that they can be stored in datasets (data columns, tables, and data fields in NWBData objects).

-
nwb.general_extracellular_ephys_electrodes = dynTable;
-
-

The electrodes property in extracellular_ephys is a special keyword in NWB that must be paired with a Dynamic Table. These are tables which can have an unbounded number of columns and rows, each as their own dataset. With the exception of the id column, all other columns must be VectorData or VectorIndex objects. The id column, meanwhile, must be an ElementIdentifiers object. The names of all used columns are specified in the in the colnames property as a cell array of strings.

-
-% general/optogenetics/photostim
-nwb.general_optogenetics.set('photostim', ...
-    types.core.OptogeneticStimulusSite(...
-    'excitation_lambda', meta.photostim.photostimWavelength{1}, ...
-    'location', meta.photostim.photostimLocation{1}, ...
-    'device', types.untyped.SoftLink(['/general/devices/' laserName]), ...
-    'description', formatStruct(meta.photostim, {...
-    'stimulationMethod';'photostimCoordinates';'identificationMethod'})));
-
-

Analysis Data Structure

-

The ALM-3 data structures .mat file contains analyzed spike data, trial-specific parameters, and behavioral analysis data.

-

Hashes

-

ALM-3 stores its data structures in the form of hashes which are essentially the same as python's dictionaries or MATLAB's maps but where the keys and values are stored under separate struct fields. Getting a hashed value from a key involves retrieving the array index that the key is in and applying it to the parallel array in the values field.

-

You can find more information about hashes and how they're used on the ALM-3 about page.

-
fprintf('Processing Data Structure `%s`\n', datastructure_loc);
-loaded = load(datastructure_loc, 'obj');
-data = loaded.obj;
-
-% wherein each cell is one trial. We must populate this way because trials
-% may not be in trial order.
-% Trial timeseries will be a compound type under intervals/trials.
-trial_timeseries = cell(size(data.trialIds));
-
-
Processing Data Structure `data/data_structure_files/data_structure_ANM255201_20141124.mat`
-
-

NWB comes with default support for trial-based data. These must be TimeIntervals that are placed in the intervals property. Note that trials is a special keyword that is required for PyNWB compatibility.

-
ephus = data.timeSeriesArrayHash.value{1};
-ephusUnit = data.timeUnitNames{data.timeUnitIds(ephus.timeUnit)};
-
-% Lick direction and timestamps trace
-tsIdx = strcmp(ephus.idStr, 'lick_trace');
-bts = types.core.BehavioralTimeSeries();
-
-bts.timeseries.set('lick_trace_ts', ...
-    types.core.TimeSeries(...
-    'data', ephus.valueMatrix(:,tsIdx),...
-    'data_unit', ephusUnit,...
-    'description', ephus.idStrDetailed{tsIdx}, ...
-    'timestamps', ephus.time, ...
-    'timestamps_unit', ephusUnit));
-nwb.acquisition.set('lick_trace', bts);
-bts_ref = types.untyped.ObjectView('/acquisition/lick_trace/lick_trace_ts');
-
-% Acousto-optic modulator input trace
-tsIdx = strcmp(ephus.idStr, 'aom_input_trace');
-ts = types.core.TimeSeries(...
-    'data', ephus.valueMatrix(:,tsIdx), ...
-    'data_unit', 'Volts', ...
-    'description', ephus.idStrDetailed{tsIdx}, ...
-    'timestamps', ephus.time, ...
-    'timestamps_unit', ephusUnit);
-nwb.stimulus_presentation.set('aom_input_trace', ts);
-ts_ref = types.untyped.ObjectView('/stimulus/presentation/aom_input_trace');
-
-% Laser power
-tsIdx = strcmp(ephus.idStr, 'laser_power');
-ots = types.core.OptogeneticSeries(...
-    'data', ephus.valueMatrix(:, tsIdx), ...
-    'data_conversion', 1e-3, ... % data is stored in mW, data unit for OptogeneticSeries is watts
-    'description', ephus.idStrDetailed{tsIdx}, ...
-    'timestamps', ephus.time, ...
-    'timestamps_unit', ephusUnit, ...
-    'site', types.untyped.SoftLink('/general/optogenetics/photostim'));
-nwb.stimulus_presentation.set('laser_power', ots);
-ots_ref = types.untyped.ObjectView('/stimulus/presentation/laser_power');
-
-% Append trials timeseries references in order
-[ephus_trials, ~, trials_to_data] = unique(ephus.trial);
-for i=1:length(ephus_trials)
-    i_loc = i == trials_to_data;
-    t_start = find(i_loc, 1);
-    t_count = sum(i_loc);
-    trial = ephus_trials(i);
-
-    trial_timeseries{trial}(end+(1:3), :) = {...
-        bts_ref int64(t_start) int64(t_count);...
-        ts_ref  int64(t_start) int64(t_count);...
-        ots_ref int64(t_start) int64(t_count)};
-end
-
-

The timeseries property of the TimeIntervals object is an example of a compound data type. These types are essentially tables of data in HDF5 and can be represented by a MATLAB table, an array of structs, or a struct of arrays. Beware: validation of column lengths here is not guaranteed by the type checker until export.

-

-VectorIndex objects index into a larger VectorData column. The object that is being referenced is indicated by the target property, which uses an ObjectView. Each element in the VectorIndex marks the last element in the corresponding vector data object for the VectorIndex row. Thus, the starting index for this row would be the previous index + 1. Note that these indices must be 0-indexed for compatibility with pynwb. You can see this in effect with the timeseries property which is indexed by the timeseries_index property.

-

Though TimeIntervals is a subclass of the DynamicTable type, we opt for populating the Dynamic Table data by column instead of using `addRow` here because of how the data is formatted. DynamicTable is flexible enough to accomodate both styles of data conversion.

-
trials_epoch = types.core.TimeIntervals(...
-    'colnames', {'start_time'}, ...
-    'description', 'trial data and properties', ...
-    'start_time', types.hdmf_common.VectorData(...
-        'data', data.trialStartTimes', ...
-        'description', 'Start time of epoch, in seconds.'), ...
-    'id', types.hdmf_common.ElementIdentifiers(...
-        'data', data.trialIds' ) );
-
-% Add columns for the trial types
-for i=1:length(data.trialTypeStr)
-    columnName = data.trialTypeStr{i};
-    columnData = types.hdmf_common.VectorData(...
-         'data', data.trialTypeMat(i,:)', ... % transpose for column vector
-         'description', data.trialTypeStr{i});
-    trials_epoch.addColumn( columnName, columnData )
-end
-
-% Add columns for the trial properties
-for i=1:length(data.trialPropertiesHash.keyNames)
-    columnName = data.trialPropertiesHash.keyNames{i};
-    descr = data.trialPropertiesHash.descr{i};
-    if iscellstr(descr)
-        descr = strjoin(descr, newline);
-    end
-    columnData = types.hdmf_common.VectorData(...
-         'data', data.trialPropertiesHash.value{i},...
-         'description', data.trialTypeStr{i});
-    trials_epoch.addColumn( columnName, columnData )
-end
-
-nwb.intervals_trials = trials_epoch;
-
-

Ephus spike data is separated into units which directly maps to the NWB property of the same name. Each such unit contains a group of analysed waveforms and spike times, all linked to a different subset of trials IDs.

-

The waveforms are placed in the analysis Set and are paired with their unit name ('unitx' where 'x' is some unit ID).

-

Trial IDs, wherever they are used, are placed in a relevent control property in the data object and will indicate what data is associated with what trial as defined in trials's id column.

-
nwb.units = types.core.Units('colnames',...
-    {'spike_times', 'trials', 'waveforms'},...
-    'description', 'Analysed Spike Events');
-esHash = data.eventSeriesHash;
-ids = regexp(esHash.keyNames, '^unit(\d+)$', 'once', 'tokens');
-ids = str2double([ids{:}]);
-nwb.units.spike_times = types.hdmf_common.VectorData(...
-    'description', 'timestamps of spikes');
-
-for i=1:length(ids)
-    esData = esHash.value{i};
-    % Add trials ID reference
-
-    good_trials_mask = ismember(esData.eventTrials, nwb.intervals_trials.id.data);
-    eventTrials = esData.eventTrials(good_trials_mask);
-    eventTimes = esData.eventTimes(good_trials_mask);
-    waveforms = esData.waveforms(good_trials_mask,:);
-    channel = esData.channel(good_trials_mask);
-
-    % Add waveform data to "unitx" and associate with "waveform" column as ObjectView.
-    ses = types.core.SpikeEventSeries(...
-        'control', ids(i),...
-        'control_description', 'Units Table ID',...
-        'data', waveforms .', ...
-        'description', esHash.descr{i}, ...
-        'timestamps', eventTimes, ...
-        'timestamps_unit', data.timeUnitNames{data.timeUnitIds(esData.timeUnit)},...
-        'electrodes', types.hdmf_common.DynamicTableRegion(...
-            'description', 'Electrodes involved with these spike events',...
-            'table', types.untyped.ObjectView('/general/extracellular_ephys/electrodes'),...
-            'data', channel - 1));
-    ses_name = esHash.keyNames{i};
-    ses_ref = types.untyped.ObjectView(['/analysis/', ses_name]);
-    if ~isempty(esData.cellType)
-        ses.comments = ['cellType: ' esData.cellType{1}];
-    end
-    nwb.analysis.set(ses_name, ses);
-    nwb.units.addRow(...
-        'id', ids(i), 'trials', eventTrials, 'spike_times', eventTimes, 'waveforms', ses_ref);
-
-    % Add this timeseries into the trials table as well.
-    [s_trials, ~, trials_to_data] = unique(eventTrials);
-    for j=1:length(s_trials)
-        trial = s_trials(j);
-        j_loc = j == trials_to_data;
-        t_start = find(j_loc, 1);
-        t_count = sum(j_loc);
-
-        trial_timeseries{trial}(end+1, :) = {ses_ref int64(t_start) int64(t_count)};
-    end
-end
-
-

To better understand how spike_times_index and spike_times map to each other, refer to this diagram from the Extracellular Electrophysiology Tutorial.

-

Raw Acquisition Data

-

Each ALM-3 session is associated with a large number of raw voltage data grouped by trial ID. To map this data to NWB, each trial is created as its own ElectricalSeries object under the name 'trial n' where 'n' is the trial ID. The trials are then linked to the trials dynamic table for easy referencing.

-
fprintf('Processing Raw Acquisition Data from `%s` (will take a while)\n', rawdata_loc);
-untarLoc = strrep(rawdata_loc, '.tar', '');
-if ~isfolder(untarLoc)
-    untar(rawdata_loc, fileparts(rawdata_loc));
-end
-
-rawfiles = dir(untarLoc);
-rawfiles = fullfile(untarLoc, {rawfiles(~[rawfiles.isdir]).name});
-
-nrows = length(nwb.general_extracellular_ephys_electrodes.id.data);
-tablereg = types.hdmf_common.DynamicTableRegion(...
-    'description','Relevent Electrodes for this Electrical Series',...
-    'table',types.untyped.ObjectView('/general/extracellular_ephys/electrodes'),...
-    'data',(1:nrows) - 1);
-objrefs = cell(size(rawfiles));
-
-endTimestamps = trials_epoch.start_time.data;
-for i=1:length(rawfiles)
-    tnumstr = regexp(rawfiles{i}, '_trial_(\d+)\.mat$', 'tokens', 'once');
-    tnumstr = tnumstr{1};
-    rawdata = load(rawfiles{i}, 'ch_MUA', 'TimeStamps');
-    tnum = str2double(tnumstr);
-
-    if tnum > length(endTimestamps)
-        continue; % sometimes there are extra trials without an associated start time.
-    end
-
-    es = types.core.ElectricalSeries(...
-        'data', rawdata.ch_MUA,...
-        'description', ['Raw Voltage Acquisition for trial ' tnumstr],...
-        'electrodes', tablereg,...
-        'timestamps', rawdata.TimeStamps);
-    tname = ['trial ' tnumstr];
-    nwb.acquisition.set(tname, es);
-
-    endTimestamps(tnum) = endTimestamps(tnum) + rawdata.TimeStamps(end);
-    objrefs{tnum} = types.untyped.ObjectView(['/acquisition/' tname]);
-end
-
-% Link to the raw data by adding the acquisition column with ObjectViews
-% to the data
-emptyrefs = cellfun('isempty', objrefs);
-objrefs(emptyrefs) = {types.untyped.ObjectView('')};
-
-trials_epoch.addColumn('acquisition', types.hdmf_common.VectorData(...
-    'description', 'soft link to acquisition data for this trial',...
-    'data', [objrefs{:}]'));
-
-trials_epoch.stop_time = types.hdmf_common.VectorData(...
-     'data', endTimestamps',...
-     'description', 'the end time of each trial');
-trials_epoch.colnames{end+1} = 'stop_time';
-
-
Processing Raw Acquisition Data from `data/RawVoltageTraces/ANM255201_20141124.tar` (will take a while)
-
-

Add timeseries to trials_epoch

-

First, we'll format and store trial_timeseries into intervals_trials. note that timeseries_index data is 0-indexed.

-
ts_len = cellfun('size', trial_timeseries, 1);
-nwb.intervals_trials.timeseries_index = types.hdmf_common.VectorIndex(...
-    'description', 'Index into Timeseries VectorData', ...
-    'data', cumsum(ts_len)', ...
-    'target', types.untyped.ObjectView('/intervals/trials/timeseries') );
-
-% Intervals/trials/timeseries is a compound type so we use cell2table to
-% convert this 2-d cell array into a compatible table.
-is_len_nonzero = ts_len > 0;
-trial_timeseries_table = cell2table(vertcat(trial_timeseries{is_len_nonzero}),...
-    'VariableNames', {'timeseries', 'idx_start', 'count'});
-trial_timeseries_table = movevars(trial_timeseries_table, 'timeseries', 'After', 'count');
-
-interval_trials_timeseries = types.core.TimeSeriesReferenceVectorData(...
-    'description', 'Index into TimeSeries data', ...
-    'data', trial_timeseries_table);
-nwb.intervals_trials.timeseries = interval_trials_timeseries;
-nwb.intervals_trials.colnames{end+1} = 'timeseries';
-
-

Export

-
nwbFilePath = fullfile(output_directory, [identifier '.nwb']);
-if isfile(nwbFilePath)
-    delete(nwbFilePath);
-end
-nwbExport(nwb, nwbFilePath);
-
- -
- - - diff --git a/tutorials/html/dataPipe.html b/tutorials/html/dataPipe.html deleted file mode 100644 index 1d404b4f..00000000 --- a/tutorials/html/dataPipe.html +++ /dev/null @@ -1,441 +0,0 @@ - - - - - -Neurodata Without Borders (NWB) advanced write using DataPipe - - - - - - - -
-

Neurodata Without Borders (NWB) advanced write using DataPipe

- -

How to utilize HDF5 compression using dataPipe

-
authors: Ivan Smalianchuk and Ben Dichter
-contact: smalianchuk.ivan@gmail.com, ben.dichter@catalystneuro.com
-last edited: Jan 04, 2021
- -

Contents

- -

Neurophysiology data can be quite large, often in the 10s of GB per session and sometimes much larger. Here, we demonstrate methods in MatNWB that allow you to deal with large datasets. These methods are compression and iterative write. Both of these techniques use the types.untyped.DataPipe object, which sends specific instructions to the HDF5 backend about how to store data.

-

Compression - basic implementation

-

To compress experimental data (in this case a 3D matrix with dimensions [250 250 70]) one must assign it as a DataPipe type:

-
DataToCompress = randi(100, 250, 250, 70);
-DataPipe = types.untyped.DataPipe('data', DataToCompress);
-
-

This is the most basic way to acheive compression, and all of the optimization decisions are automatically determined by MatNWB.

-

Background

-

HDF5 has built-in ability to compress and decompress individual datasets. If applied intelligently, this can dramatically reduce the amount of space used on the hard drive to represent the data. The end user does not need to worry about the compression status of the dataset- HDF5 will automatically decompress the dataset on read.

-

The above example uses default chunk size and compression level (3). To optimize compression, compressionLevel and chunkSize must be considered. compressionLevel ranges from 0 - 9 where 9 is the highest level of compression and 0 is the lowest. chunkSize is less intuitive to adjust; to implement compression, chunk size must be less than data size.

-

-DataPipe Arguments

-

- - - - - - - -
maxSizeSets the maximum size of the HDF5 Dataset. Unless using iterative writing, this should match the size of Data. To append data later, use the maxSize for the full dataset. You can use Inf for a value of a dimension if you do not know its final size.
dataThe data to compress. Must be numerical data.
axisSet which axis to increment when appending more data.
dataTypeSets the type of the experimental data. This must be a numeric data type. Useful to include when using iterative write to append data as the appended data must be the same data type. If data is provided and dataType is not, the dataType is inferred from the provided data.
chunkSizeSets chunk size for the compression. Must be less than maxSize.
compressionLevelLevel of compression ranging from 0-9 where 9 is the highest level of compression. The default is level 3.
offsetAxis offset of dataset to append. May be used to overwrite data.
-

-

Chunking

-

HDF5 Datasets can be either stored in continuous or chunked mode. Continuous means that all of the data is written to one continuous block on the hard drive, and chunked means that the dataset is automatically split into chunks that are distributed across the hard drive. The user does not need to know the mode used- HDF5 handles the gathering of chunks automatically. However, it is worth understanding these chunks because they can have a big impact on space used and read and write speed. When using compression, the dataset MUST be chunked. HDF5 is not able to apply compression to continuous datasets.

-

If chunkSize is not explicitly specified, dataPipe will determine an appropriate chunk size. However, you can optimize the performance of the compression by manually specifying the chunk size using chunkSize argument.

-

We can demonstrate the benefit of chunking by exploring the following scenario. The following code utilizes DataPipe's default chunk size:

-
fData = randi(250, 100, 1000); % Create fake data
-
-% create an nwb structure with required fields
-nwb = NwbFile( ...
-    'session_start_time', datetime('2020-01-01 00:00:00', 'TimeZone', 'local'), ...
-    'identifier', 'ident1', ...
-    'session_description', 'DataPipeTutorial');
-
-fData_compressed = types.untyped.DataPipe('data', fData);
-
-fdataNWB=types.core.TimeSeries( ...
-    'data', fData_compressed, ...
-    'data_unit', 'mV', ...
-    'starting_time', 0.0, ...
-    'starting_time_rate', 30.0);
-
-nwb.acquisition.set('data', fdataNWB);
-
-nwbExport(nwb, 'DefaultChunks.nwb');
-
-

This results in a file size of 47MB (too large), and the process takes 11 seconds (far too long). Setting the chunk size manually as in the example code below resolves these issues:

-
fData_compressed = types.untyped.DataPipe( ...
-    'data', fData, ...
-    'chunkSize', [1, 1000], ...
-    'axis', 1);
-
-

This change results in the operation completing in 0.7 seconds and resulting file size of 1.1MB. The chunk size was chosen such that it spans each individual row of the matrix.

-

Use the combination of arugments that fit your need. When dealing with large datasets, you may want to use iterative write to ensure that you stay within the bounds of your system memory and use chunking and compression to optimize storage, read and write of the data.

-

Iterative Writing

-

If experimental data is close to, or exceeds the available system memory, performance issues may arise. To combat this effect of large data, DataPipe can utilize iterative writing, where only a portion of the data is first compressed and saved, and then additional portions are appended.

-

To demonstrate, we can create a nwb file with a compressed time series data:

-
dataPart1 = randi(250, 1, 1000); % "load" 1/4 of the entire dataset
-fullDataSize = [1 40000]; % this is the size of the TOTAL dataset
-
-% create an nwb structure with required fields
-nwb=NwbFile( ...
-    'session_start_time', datetime('2020-01-01 00:00:00', 'TimeZone', 'local'), ...
-    'identifier', 'ident1', ...
-    'session_description', 'DataPipeTutorial');
-
-% compress the data
-fData_use = types.untyped.DataPipe( ...
-    'data', dataPart1, ...
-    'maxSize', fullDataSize, ...
-    'axis', 2);
-
-%Set the compressed data as a time series
-fdataNWB = types.core.TimeSeries( ...
-    'data', fData_use, ...
-    'data_unit', 'mV', ...
-    'starting_time', 0.0, ...
-    'starting_time_rate', 30.0);
-
-nwb.acquisition.set('time_series', fdataNWB);
-
-nwbExport(nwb, 'DataPipeTutorial_iterate.nwb');
-
-

To append the rest of the data, simply load the NWB file and use the append method:

-
nwb = nwbRead('DataPipeTutorial_iterate.nwb', 'ignorecache'); %load the nwb file with partial data
-
-% "load" each of the remaining 1/4ths of the large dataset
-for i = 2:4 % iterating through parts of data
-    dataPart_i=randi(250, 1, 10000); % faked data chunk as if it was loaded
-    nwb.acquisition.get('time_series').data.append(dataPart_i); % append the loaded data
-end
-
-

The axis property defines the dimension in which additional data will be appended. In the above example, the resulting dataset will be 4000x1. However, if we set axis to 2 (and change fullDataSize appropriately), then the resulting dataset will be 1000x4.

-

Timeseries example

-

Following is an example of how to compress and add a timeseries to an NWB file:

-
fData=randi(250, 1, 10000); % create fake data;
-
-%assign data without compression
-nwb=NwbFile(...
-    'session_start_time', datetime(2020, 1, 1, 0, 0, 0, 'TimeZone', 'local'), ...
-    'identifier','ident1', ...
-    'session_description', 'DataPipeTutorial');
-
-ephys_module = types.core.ProcessingModule( ...
-    'description', 'holds processed ephys data');
-
-nwb.processing.set('ephys', ephys_module);
-
-% compress the data
-fData_compressed=types.untyped.DataPipe( ...
-    'data', fData, ...
-    'compressionLevel', 3, ...
-    'chunkSize', [100 1], ...
-    'axis', 1);
-
-% Assign the data to appropriate module and write the NWB file
-fdataNWB=types.core.TimeSeries( ...
-    'data', fData_compressed, ...
-    'data_unit', 'mV', ...
-    'starting_time', 0.0, ...
-    'starting_time_rate', 30.0);
-
-ephys_module.nwbdatainterface.set('data', fdataNWB);
-nwb.processing.set('ephys', ephys_module);
-
-% write the file
-nwbExport(nwb, 'Compressed.nwb');
-
- -
- - - diff --git a/tutorials/html/dimensionMapNoDataPipes.html b/tutorials/html/dimensionMapNoDataPipes.html deleted file mode 100644 index 34e8d692..00000000 --- a/tutorials/html/dimensionMapNoDataPipes.html +++ /dev/null @@ -1,92 +0,0 @@ - -MatNWB <-> HDF5 Dimension Mapping

MatNWB <-> HDF5 Dimension Mapping

This tutorial demonstrates how the dimensions of a MATLAB array maps onto a dataset in HDF5. There are two main differences between the way MATLAB and HDF5 represents dimensions:
  1. C-ordering vs F-ordering: HDF5 is C-ordered, which means it stores data in a rows-first pattern, whereas MATLAB is F-ordered, storing data in the reverse pattern, with the last dimension of the array stored consecutively. The result is that the data in HDF5 is effectively the transpose of the array in MATLAB.
  2. 1D data (i.e vectors): HDF5 can store 1-D arrays, but in MATLAB the lowest dimensionality of an array is 2-D.
Due to differences in how MATLAB and HDF5 represent data, the dimensions of datasets are flipped when writing to/from file in MatNWB. Additionally, MATLAB represents 1D vectors in a 2D format, either as row vectors or column vectors, whereas HDF5 treats vectors as truly 1D. Consequently, when a 1D dataset from HDF5 is loaded into MATLAB, it is always represented as a column vector. To avoid unintentional changes in data dimensions, it is therefore recommended to avoid writing row vectors into an NWB file for 1D datasets.
Contrast this tutorial with the dimensionMapWithDataPipes tutorial that illustrates how vectors are represented differently when using DataPipe objects within VectorData objects.

Create Table

First, create a TimeIntervals table of height 10.
% Define VectorData objects for each column
% 1D column
start_col = types.hdmf_common.VectorData( ...
'description', 'start_times column', ...
'data', (1:10)' ... # maps onto HDF5 dataset of size (10,)
);
% 1D column
stop_col = types.hdmf_common.VectorData( ...
'description', 'stop_times column', ...
'data', (2:11)' ... # maps onto HDF5 dataset of size (10,)
);
% 4D column
randomval_col = types.hdmf_common.VectorData( ...
'description', 'randomvalues column', ...
'data', rand(5,2,3,10) ... # maps onto HDF5 dataset of size (10, 3, 2, 5)
);
 
% 1D column
id_col = types.hdmf_common.ElementIdentifiers('data', int64(0:9)'); % maps onto HDF5 dataset of size (10,)
 
% Create table
trials_table = types.core.TimeIntervals(...
'description', 'test dynamic table column',...
'colnames', {'start_time','stop_time','randomvalues'}, ...
'start_time', start_col, ...
'stop_time', stop_col, ...
'randomvalues', randomval_col, ...
'id', id_col ...
);

Export Table

Create NWB file with TimeIntervals table and export.
% Create NwbFile object with required arguments
file = NwbFile( ...
'session_start_time', datetime('2022-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test file' ...
);
% Assign to intervals_trials
file.intervals_trials = trials_table;
% Export
nwbExport(file, 'testFileNoDataPipes.nwb');
You can examine the dimensions of the datasets on file using HDFView. Screenshots for this file are below.
Screen Shot 2022-01-07 at 11.07.25 AM.png
Screen Shot 2022-01-07 at 11.07.19 AM.png
-
- -
\ No newline at end of file diff --git a/tutorials/html/dimensionMapWithDataPipes.html b/tutorials/html/dimensionMapWithDataPipes.html deleted file mode 100644 index fb17536d..00000000 --- a/tutorials/html/dimensionMapWithDataPipes.html +++ /dev/null @@ -1,110 +0,0 @@ - -MatNWB <-> HDF5 Dimension Mapping

MatNWB <-> HDF5 Dimension Mapping

This tutorial is easier to follow if you have already looked at the dimensionMapNoDataPipes tutorial or if you compare these side by side.
The key difference when using DataPipe instead of VectorData is that 1D data can be represented in HDF5 as 2D, thus allowing you to write either row or column vectors. This is made possible because of the maxSize property of the DataPipe class, which lets you specify a max size for each dimension. By setting the maxSize to [1, N] or [N, 1], vectors in HDF5 are represented as 2D arrays, just like in MATLAB. The flipping of the dimension order still applies, so a row vector in MATLAB becomes a column vector in HDF5 and vice versa.
Please note: The following tutorial mixes row and column vectors and does not produce a valid dynamic table. The tutorial is only meant to showcase how data maps onto HDF5 datasets when using DataPipe objects.

Create Table

First, create an expandable TimeIntervals table of height 10.
% 1D column
start_col = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', 1:10, ... # maps onto HDF5 dataset of size (10, )
'maxSize', Inf ...
) ...
);
% 1D column
stop_col = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', 1:10, ... # maps onto HDF5 dataset of size (10, 1)
'maxSize', [1 Inf], ...
'axis', 2 ...
) ...
);
% 1D column
cond_col = types.hdmf_common.VectorData( ...
'description', 'condition column', ...
'data', types.untyped.DataPipe( ...
'data', randi(2,10,1), ... # maps onto HDF5 dataset of size (1, 10)
'maxSize', [Inf, 1], ...
'axis', 1 ...
) ...
);
% 4D column
randomval_col = types.hdmf_common.VectorData( ...
'description', 'randomvalues column', ...
'data', types.untyped.DataPipe( ...
'data', rand(5,2,3,10), ... # maps onto HDF5 dataset of size (10, 3, 2, 5)
'maxSize', [5, 2, 3, Inf], ...
'axis', 4 ...
) ...
);
% 1D column
ids_col = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int64(0:9), ... # maps onto HDF5 dataset of size (10, )
'maxSize', Inf ...
) ...
);
% Create table
trials_table = types.core.TimeIntervals(...
'description', 'test dynamic table column',...
'colnames', {'start_time', 'stop_time', 'randomvalues', 'conditions'}, ...
'start_time', start_col, ...
'stop_time', stop_col, ...
'conditions', cond_col, ...
'randomvalues', randomval_col, ...
'id', ids_col ...
);

Export Table

Create NWB file with expandable TimeIntervals table and export.
% Create NwbFile object with required arguments
file = NwbFile( ...
'session_start_time', datetime('2022-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test file' ...
);
% Assign to intervals_trials
file.intervals_trials = trials_table;
% Export
nwbExport(file, 'testFileWithDataPipes.nwb');
You can examine the dimensions of the datasets on file using HDFView. Screenshots for this file are below.
Screen Shot 2022-01-12 at 1.12.42 PM.png
Screen Shot 2022-01-12 at 1.12.47 PM.png
Screen Shot 2022-01-07 at 4.26.21 PM.png
Screen Shot 2022-01-07 at 4.26.12 PM.png
-
- -
\ No newline at end of file diff --git a/tutorials/html/dynamic_tables.html b/tutorials/html/dynamic_tables.html deleted file mode 100644 index af3f6c88..00000000 --- a/tutorials/html/dynamic_tables.html +++ /dev/null @@ -1,577 +0,0 @@ - -DynamicTables Tutorial

DynamicTables Tutorial

This is a user guide to interacting with DynamicTable objects in MatNWB.

MatNWB Setup

Start by setting up your MATLAB workspace. The code below adds the directory containing the MatNWB package to the MATLAB search path. MatNWB works by automatically creating API classes based on a defined schema.
%{
path_to_matnwb = '~/Repositories/matnwb'; % change to your own path location
addpath(genpath(pwd));
%}

Constructing a table with initialized columns

The DynamicTable class represents a column-based table to which you can add custom columns. It consists of a description, a list of columns , and a list of row IDs. You can create a DynamicTable by first defining the VectorData objects that will make up the columns of the table. Each VectorData object must contain the same number of rows. A list of rows IDs may be passed to the DynamicTable using the id argument. Row IDs are a useful way to access row information independent of row location index. The list of row IDs must be cast as an ElementIdentifiers object before being passed to the DynamicTable object. If no value is passed to id, an ElementIdentifiers object with 0-indexed row IDs will be created for you automatically.
MATLAB Syntax Note: Using column vectors is crucial to properly build vectors and tables. When defining individual values, make sure to use semi-colon (;) instead of instead of comma (,) when defining the data fields of these.
col1 = types.hdmf_common.VectorData( ...
'description', 'column #1', ...
'data', [1;2] ...
);
 
col2 = types.hdmf_common.VectorData( ...
'description', 'column #2', ...
'data', {'a';'b'} ...
);
 
my_table = types.hdmf_common.DynamicTable( ...
'description', 'an example table', ...
'colnames', {'col1', 'col2'}, ...
'col1', col1, ...
'col2', col2, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0;1]) ... % 0-indexed, for compatibility with Python
);
my_table
my_table =
DynamicTable with properties: - - id: [1×1 types.hdmf_common.ElementIdentifiers] - colnames: {'col1' 'col2'} - description: 'an example table' - vectordata: [2×1 types.untyped.Set] -

Adding rows

You can add rows to an existing DynamicTable using the object's addRow method. One way of using this method is to pass in the names of columns as parameter names followed by the elements to append. The class of the elements of the column must match the elements to append.
my_table.addRow('col1', 3, 'col2', {'c'}, 'id', 2);

Adding columns

You can add new columns to an existing DynamicTable object using the addColumn method. One way of using this method is to pass in the names of each new column followed by the corresponding values for each new column. The height of the new columns must match the height of the table.
col3 = types.hdmf_common.VectorData('description', 'column #3', ...
'data', [100; 200; 300]);
col4 = types.hdmf_common.VectorData('description', 'column #4', ...
'data', {'a1'; 'b2'; 'c3'});
 
my_table.addColumn('col3', col3,'col4', col4);

Create MATLAB table and convert to dynamic table

As an alternative to building a dynamic table using the DynamicTable and VectorData data types, it is also possible to create a MATLAB table and convert it to a dynamic table. Lets create the same table as before, but using MATLAB's table class:
% Create a table with two variables (columns):
T = table([1;2], {'a';'b'}, 'VariableNames', {'col1', 'col2'});
T.Properties.VariableDescriptions = {'column #1', 'column #2'};

Adding rows

T(end+1, :) = {3, 'c'};

Adding variables (columns)

T = addvars(T, [100;200;300], 'NewVariableNames',{'col3'});
T.Properties.VariableDescriptions{3} = 'column #3';
 
% Alternatively, a new variable can be added directly using dot syntax.
T.col4 = {'a1'; 'b2'; 'c3'};
T.Properties.VariableDescriptions{4} = 'column #4';
T
T = 3×4 table
 col1col2col3col4
11'a'100'a1'
22'b'200'b2'
33'c'300'c3'

Convert to dynamic table

dynamic_table = util.table2nwb(T, 'A MATLAB table that was converted to a dynamic table')
dynamic_table =
DynamicTable with properties: - - id: [1×1 types.hdmf_common.ElementIdentifiers] - colnames: {'col1' 'col2' 'col3' 'col4'} - description: 'A MATLAB table that was converted to a dynamic table' - vectordata: [4×1 types.untyped.Set] -

Enumerated (categorical) data

EnumData is a special type of column for storing an enumerated data type. This way each unique value is stored once, and the data references those values by index. Using this method is more efficient than storing a single value many times, and has the advantage of communicating to downstream tools that the data is categorical in nature.

Warning Regarding EnumData

EnumData is currently an experimental feature and as such should not be used in a production environment.
CellTypeElements = types.hdmf_common.VectorData(...
'description', 'fixed set of elements referenced by cell_type' ...
, 'data', {'aa', 'bb', 'cc'} ... % the enumerated elements
);
CellType = types.hdmf_experimental.EnumData( ...
'description', 'this column holds categorical variables' ... % properties derived from VectorData
, 'data', [0, 1, 2, 1, 0] ... % zero-indexed offset to elements.
, 'elements', types.untyped.ObjectView(CellTypeElements) ...
);
 
MyTable = types.hdmf_common.DynamicTable('description', 'an example table');
MyTable.vectordata.set('cell_type_elements', CellTypeElements); % the *_elements format is required for compatibility with pynwb
MyTable.addColumn('cell_type', CellType);

Ragged array columns

A table column with a different number of elements for each row is called a "ragged array column." To define a table with a ragged array column, pass both the VectorData and the corresponding VectorIndex as columns of the DynamicTable object. The VectorData columns will contain the data values. The VectorIndex column serves to indicate how to arrange the data across rows. By convention the VectorIndex object corresponding to a particular column must have have the same name with the addition of the '_index' suffix.
Below, the VectorIndex values indicate to place the 1st to 3rd (inclusive) elements of the VectorData into the first row and 4th element into the second row. The resulting table will have the cell {'1a'; '1b'; '1c'} in the first row and the cell {'2a'} in the second row.
 
col1 = types.hdmf_common.VectorData( ...
'description', 'column #1', ...
'data', {'1a'; '1b'; '1c'; '2a'} ...
);
 
col1_index = types.hdmf_common.VectorIndex( ...
'description', 'column #1 index', ...
'target',types.untyped.ObjectView(col1), ... % object view of target column
'data', [3; 4] ...
);
 
table_ragged_col = types.hdmf_common.DynamicTable( ...
'description', 'an example table', ...
'colnames', {'col1'}, ...
'col1', col1, ...
'col1_index', col1_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1]) ... % 0-indexed, for compatibility with Python
);

Adding ragged array rows

You can add a new row to the ragged array column. Under the hood, the addRow method will add the appropriate value to the VectorIndex column to maintain proper formatting.
table_ragged_col.addRow('col1', {'3a'; '3b'; '3c'}, 'id', 2);

Accessing row elements

You can access data from entire rows of a DynamicTable object by calling the getRow method for the corresponding object. You can supply either an individual row number or a list of row numbers.
my_table.getRow(1)
ans = 1×4 table
 col1col2col3col4
11'a'100'a1'
If you want to access values for just a subset of columns you can pass in the 'columns' argument along with a cell array with the desired column names
my_table.getRow(1:3, 'columns', {'col1'})
ans = 3×1 table
 col1
11
22
33
You can also access specific rows by their corresponding row ID's, if they have been defined, by supplying a 'true' Boolean to the 'useId' parameter
my_table.getRow(1, 'useId', true)
ans = 1×4 table
 col1col2col3col4
12'b'200'b2'
For a ragged array columns, the getRow method will return a cell with different number of elements for each row
table_ragged_col.getRow(1:2)
ans = 2×1 table
 col1
1[{'1a'};{'1b'};{'1c'}]
21×1 cell

Accessing column elements

To access all rows from a particular column use the .get method on the vectordata field of the DynamicTable object
 
my_table.vectordata.get('col2').data
ans = 3×1 cell
'a'
'b'
'c'

Referencing rows of other tables

You can create a column that references rows of other tables by adding a DynamicTableRegion object as a column of a DynamicTable. This is analogous to a foreign key in a relational database. The DynamicTableRegion class takes in an ObjectView object as argument. ObjectView objects create links from one object type referencing another.
dtr_col = types.hdmf_common.DynamicTableRegion( ...
'description', 'references multiple rows of earlier table', ...
'data', [0; 1; 1; 0], ... # 0-indexed
'table',types.untyped.ObjectView(my_table) ... % object view of target table
);
 
data_col = types.hdmf_common.VectorData( ...
'description', 'data column', ...
'data', {'a'; 'b'; 'c'; 'd'} ...
);
 
dtr_table = types.hdmf_common.DynamicTable( ...
'description', 'test table with DynamicTableRegion', ...
'colnames', {'data_col', 'dtr_col'}, ...
'dtr_col', dtr_col, ...
'data_col',data_col, ...
'id',types.hdmf_common.ElementIdentifiers('data', [0; 1; 2; 3]) ...
);

Converting a DynamicTable to a MATLAB table

You can convert a DynamicTable object to a MATLAB table by making use of the object's toTable method. This is a useful way to view the whole table in a human-readable format.
my_table.toTable()
ans = 3×5 table
 idcol1col2col3col4
101'a'100'a1'
212'b'200'b2'
323'c'300'c3'
When the DynamicTable object contains a column that references other tables, you can pass in a Boolean to indicate whether to include just the row indices of the referenced table. Passing in false will result in inclusion of the referenced rows as nested tables.
dtr_table.toTable(false)
ans = 4×3 table
 iddata_coldtr_col
10'a'1×4 table
21'b'1×4 table
32'c'1×4 table
43'd'1×4 table

Creating an expandable table

When using the default HDF5 backend, each column of these tables is an HDF5 Dataset, which by default are set to an unchangeable size. This means that once a file is written, it is not possible to add a new row. If you want to be able to save this file, load it, and add more rows to the table, you will need to set this up when you create the VectorData and ElementIdentifiers columns of a DynamicTable. Specifically, you must wrap the column data with a DataPipe object. The DataPipe class takes in maxSize and axis as arguments to indicate the maximum desired size for each axis and the axis to which to append to, respectively. For example, creating a DataPipe object with a maxSize value equal to [Inf, 1] indicates that the number of rows may increase indifinetely. In contrast, setting maxSize equal to [8, 1] would allow the column to grow to a maximum height of 8.
% create NwbFile object with required fields
file= NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'ExpandableTableTutorial' ...
);
 
% create VectorData objects with DataPipe objects
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', [1, 2], ... # data must be numerical
'maxSize', Inf ...
) ...
);
 
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', [2, 3], ... #data must be numerical
'maxSize', Inf ...
) ...
);
 
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(5, 2), ... #data must be numerical
'maxSize', [5, Inf], ...
'axis', 2 ...
) ...
);
 
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int32([0; 1]), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'id', ids_exp ...
);
% export file
nwbExport(file, 'expandableTableTestFile.nwb');
Now, you can read in the file, add more rows, and save again to file
readFile = nwbRead('expandableTableTestFile.nwb', 'ignorecache');
readFile.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 4, ...
'randomvalues', rand(5,1), ...
'id', 2 ...
)
nwbExport(readFile, 'expandableTableTestFile.nwb');
Note: DataPipe objects change how the dimension of the datasets for each column map onto the shape of HDF5 datasets. See README for more details.

Multidimensional Columns

The order of dimensions of multidimensional columns in MatNWB is reversed relative to the Python HDMF package (see README for detailed explanation). Therefore, the height of a multidimensional column belonging to a DynamicTable object is defined by the shape of its last dimension. A valid DynamicTable must have matched height across columns.

Constructing multidimensional columns

% Define 1D column
simple_col = types.hdmf_common.VectorData( ...
'description', '1D column',...
'data', rand(10,1) ...
);
% Define ND column
multi_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional column',...
'data', rand(3,2,10) ...
);
% construct table
multi_dim_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'simple','multi'}, ...
'simple', simple_col, ...
'multi', multi_col, ...
'id', types.hdmf_common.ElementIdentifiers('data', (0:9)') ... % 0-indexed, for compatibility with Python
);
 

Multidimensional ragged array columns

DynamicTable objects with multidimensional ragged array columns can be constructed by passing in the corresponding VectorIndex column
% Define column with data
multi_ragged_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional ragged array column',...
'data', rand(2,3,5) ...
);
% Define column with VectorIndex
multi_ragged_index = types.hdmf_common.VectorIndex( ...
'description', 'index to multi_ragged_col', ...
'target', types.untyped.ObjectView(multi_ragged_col),'data', [2; 3; 5] ...
);
 
multi_ragged_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'multi_ragged'}, ...
'multi_ragged', multi_ragged_col, ...
'multi_ragged_index', multi_ragged_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1; 2]) ... % 0-indexed, for compatibility with Python
);

Adding rows to multidimensional array columns

DynamicTable objects with multidimensional array columns can also be constructed by adding a single row at a time. This method makes use of DataPipe objects due to the fact that MATLAB doesn't support singleton dimensions for arrays with more than 2 dimensions. The code block below demonstrates how to build a DynamicTable object with a mutidimensional raaged array column in this manner.
% Create file
file = NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test_file' ...
);
 
% Define Vector Data Objects with first row of table
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', 1, ...
'maxSize', Inf ...
) ...
);
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', 10, ...
'maxSize', Inf ...
) ...
);
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(3,2,5), ... #random data
'maxSize', [3, 2, Inf], ...
'axis', 3 ...
) ...
);
random_exp_index = types.hdmf_common.VectorIndex( ...
'description', 'index to random data column', ...
'target',types.untyped.ObjectView(random_exp), ...
'data', types.untyped.DataPipe( ...
'data', uint64(5), ...
'maxSize', Inf ...
) ...
);
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int64(0), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% Create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'randomvalues_index', random_exp_index, ...
'id', ids_exp ...
);
% Export file
nwbExport(file, 'multiRaggedExpandableTableTest.nwb');
% Read in file
read_file = nwbRead('multiRaggedExpandableTableTest.nwb', 'ignorecache');
% add individual rows
read_file.intervals_trials.addRow( ...
'start_time', 2, ...
'stop_time', 20, ...
'randomvalues', rand(3,2,6), ...
'id', 1 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 30, ...
'randomvalues', rand(3,2,3), ...
'id', 2 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 4, ...
'stop_time', 40, ...
'randomvalues', rand(3,2,8), ...
'id', 3 ...
);
 

Learn More!

Python Tutorial

-
- -
\ No newline at end of file diff --git a/tutorials/html/dynamically_loaded_filters.html b/tutorials/html/dynamically_loaded_filters.html deleted file mode 100644 index 7250e315..00000000 --- a/tutorials/html/dynamically_loaded_filters.html +++ /dev/null @@ -1,141 +0,0 @@ - -Using Dynamically Loaded Filters in MatMWB

Using Dynamically Loaded Filters in MatMWB

Installing Dynamically Loaded Filters

HDF5 can use various filters to compress data when writing datasets. GZIP is the default filter, and it can be read with any HDF5 installation without any setup, but many users find that other filters, such as Zstd, offer better performance. If you want to read an HDF5 Dataset that was compressed using another filter in MATLAB, such as Zstd, you will need to configure MATLAB to read using dynamically loaded filters.
The easiest way we have found to set up dynamically loaded filters is to use the Python package hdf5plugin. This library has a sophisticated installation process that compiles several of the most popular dynamically loaded filters and works across popular operating systems. Installing this Python package is a trick that allows us to offload the tricky parts of installing dynamically loaded filters in MATLAB.

Linux or Mac

1. In your Terminal window, install hdf5plugin:
pip install hdf5plugin
2. In that same Terminal window, set the environment variable HDF5_PLUGIN_PATH:
export HDF5_PLUGIN_PATH=$(python -c "import hdf5plugin; print(hdf5plugin.PLUGINS_PATH)");
3. From that same Terminal window, launch MATLAB:
/Applications/MATLAB_R2021b.app/bin/matlab
The path above is an example of a common location for OSX. The exact path of MATLAB may vary on your computer.

Windows

1. Install hdf5plugin in the Command Prompt:
pip install hdf5plugin
2. Determine the path of the plugin installation. In the Command Prompt, run:
python -c "import hdf5plugin; print(hdf5plugin.PLUGINS_PATH)
2. Set the environment variable HDF5_PLUGIN_PATH to point to the local installation of the plugins (from hdf5plugin.PLUGINS_PATH) through System Properties > Advanced > Environment Variables:
path-screenshot.png
3. Restart MATLAB.
That's it! Now you can read datasets that use the following filters:
  • Bitshuffle
  • Blosc
  • FciDecomp
  • LZ4
  • Zfp
  • Zstd
The beauty of HDF5 is that it handles the rest under the hood. When you read a dataset that uses any of these filters, HDF5 will identify the correct decompression algorithm and decompress the data on-the-fly as you access it from the dataset.
For more information about installing filter plugins, see the MATLAB documentation.

Writing with Dynamically Loaded Filters

To write with dynamically loaded filters, first follow the installation steps above. This feature requires MATLAB version ≥ 2022a.
DataPipe objects can be used to write using Dynamically loaded filters. This tutorial will be using the Zstd dynamic filter as an example.
The DynamicFilter property takes in an enumerated type Filter which is a hard-coded list of all listed registered filter plugins in HDF5.
import types.untyped.datapipe.properties.DynamicFilter
import types.untyped.datapipe.dynamic.Filter
import types.untyped.datapipe.properties.Shuffle
 
zstdProperty = DynamicFilter(Filter.ZStandard);

Parameters

Some filter plugins allow for setting special configuration parameters to modify the filter's behavior. The DynamicFilter property type contains a modifiable parameters field which can be used to set your parameters. This is equivalent to setting the cd_values argument in HDF5. In the case of the Zstandard HDF5 plugin, the first (and only) array argument value indicates the compression level.
zstdProperty.parameters = 4; % compression level.

Multiple Filters

You can use multiple dynamic filters by concatenating multiple DynamicFilter properties together. They will be applied in order of the inserted array.
ShuffleProperty = Shuffle();
dynamicProperties = [ShuffleProperty zstdProperty];

Writing

The DataPipe class takes in a keyword argument called filters which is an array of DynamicFilter objects. Supplying a 'filters' argument will deactivate the default GZIP compression.
% We're already compressing using zstd so we should disable
% compressionLevel (gzip).
dataPipe = types.untyped.DataPipe('data', rand(1, 10000), 'filters', dynamicProperties);
 
timeseries = types.core.TimeSeries(...
'data', dataPipe, ...
'data_unit', 'data-unit', ...
'starting_time_rate', 1.0, ...
'starting_time', 0.0);
 
nwbFile = NwbFile(...
'identifier', 'dynamically_loaded_filters_tutorial', ...
'session_description', 'test_datapipe_filters', ...
'session_start_time', datetime("now", 'TimeZone', 'local') );
nwbFile.acquisition.set('ts', timeseries);
 
nwbExport(nwbFile, 'test.nwb');
 
The data is now compressed using Zstandard compression using a compression level of 4 and Shuffled
-
- -
\ No newline at end of file diff --git a/tutorials/html/ecephys.html b/tutorials/html/ecephys.html deleted file mode 100644 index 6b51b3ff..00000000 --- a/tutorials/html/ecephys.html +++ /dev/null @@ -1,1502 +0,0 @@ - -Neurodata Without Borders Extracellular Electrophysiology Tutorial

Neurodata Without Borders Extracellular Electrophysiology Tutorial

About This Tutorial

This tutorial describes storage of hypothetical data from extracellular electrophysiology experiments in NWB for the following data categories:
  • Raw voltage recording
  • Local field potential (LFP) and filtered electrical signals
  • Spike times

Before You Begin

It is recommended to first work through the Introduction to MatNWB tutorial, which demonstrates installing MatNWB and creating an NWB file with subject information, animal position, and trials, as well as writing and reading NWB files in MATLAB.
Important: The dimensions of timeseries data in MatNWB should be defined in the opposite order of how it is defined in the nwb-schemas. In NWB, time is always stored in the first dimension of the data, whereas in MatNWB time should be stored in the last dimension of the data. This is explained in more detail here: MatNWB <-> HDF5 Dimension Mapping.

Setting up the NWB File

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session_start_time. Create a new NWBFile object these required fields along with any additional metadata. In MatNWB, arguments are specified using MATLAB's keyword argument pair convention, where each argument name is followed by its value.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'Last Name, First Name', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: - - nwb_version: '2.8.0' - file_create_date: [] - identifier: 'Mouse5_Day3' - session_description: 'mouse in open exploration' - session_start_time: {[2018-04-25T02:30:03.000000+02:00]} - timestamps_reference_time: {[2018-04-25T03:00:45.000000+02:00]} - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [0×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: 'Last Name, First Name' - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: {'DOI:10.1016/j.neuron.2016.12.011'} - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - general_was_generated_by: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [0×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] -

Electrode Information

In order to store extracellular electrophysiology data, you first must create an electrodes table describing the electrodes that generated this data. Extracellular electrodes are stored in an electrodes table, which is also a DynamicTable. electrodes has several required fields: x, y, z, impedance, location, filtering, and electrode_group.
The electrodes table references a required ElectrodeGroup, which is used to represent a group of electrodes. Before creating an ElectrodeGroup, you must define a Device object. The fields description, manufacturer, model_number, model_name, and serial_number are optional, but recommended.
device = types.core.Device(...
'description', 'A 12-channel array with 4 shanks and 3 channels per shank', ...
'manufacturer', 'Array Technologies', ...
'model_number', 'PRB_1_4_0480_123', ...
'model_name', 'Neurovoxels 0.99', ...
'serial_number', '1234567890' ...
);
 
% Add device to nwb object
nwb.general_devices.set('array', device);

Electrodes Table

Since this is a DynamicTable, we can add additional metadata fields. We will be adding a "label" column to the table.
numShanks = 4;
numChannelsPerShank = 3;
numChannels = numShanks * numChannelsPerShank;
 
electrodesDynamicTable = types.hdmf_common.DynamicTable(...
'colnames', {'location', 'group', 'group_name', 'label'}, ...
'description', 'all electrodes');
 
for iShank = 1:numShanks
shankGroupName = sprintf('shank%d', iShank);
electrodeGroup = types.core.ElectrodeGroup( ...
'description', sprintf('electrode group for %s', shankGroupName), ...
'location', 'brain area', ...
'device', types.untyped.SoftLink(device) ...
);
nwb.general_extracellular_ephys.set(shankGroupName, electrodeGroup);
for iElectrode = 1:numChannelsPerShank
electrodesDynamicTable.addRow( ...
'location', 'unknown', ...
'group', types.untyped.ObjectView(electrodeGroup), ...
'group_name', shankGroupName, ...
'label', sprintf('%s-electrode%d', shankGroupName, iElectrode));
end
end
electrodesDynamicTable.toTable() % Display the table
ans = 12×5 table
 idlocationgroupgroup_namelabel
10'unknown'1×1 ObjectView'shank1''shank1-electrode1'
21'unknown'1×1 ObjectView'shank1''shank1-electrode2'
32'unknown'1×1 ObjectView'shank1''shank1-electrode3'
43'unknown'1×1 ObjectView'shank2''shank2-electrode1'
54'unknown'1×1 ObjectView'shank2''shank2-electrode2'
65'unknown'1×1 ObjectView'shank2''shank2-electrode3'
76'unknown'1×1 ObjectView'shank3''shank3-electrode1'
87'unknown'1×1 ObjectView'shank3''shank3-electrode2'
98'unknown'1×1 ObjectView'shank3''shank3-electrode3'
109'unknown'1×1 ObjectView'shank4''shank4-electrode1'
1110'unknown'1×1 ObjectView'shank4''shank4-electrode2'
1211'unknown'1×1 ObjectView'shank4''shank4-electrode3'
nwb.general_extracellular_ephys_electrodes = electrodesDynamicTable;

Links

In the above loop, we create ElectrodeGroup objects. The electrodes table then uses an ObjectView in each row to link to the corresponding ElectrodeGroup object. An ObjectView is a construct that enables linking one neurodata type to another, allowing a neurodata type to reference another within the NWB file.

Recorded Extracellular Signals

Voltage data are stored using the ElectricalSeries class, a subclass of the TimeSeries class specialized for voltage data.

Referencing Electrodes

In order to create our ElectricalSeries object, we first need to reference a set of rows in the electrodes table to indicate which electrode (channel) each entry in the electrical series were recorded from. We will do this by creating a DynamicTableRegion, which is a type of link that allows you to reference specific rows of a DynamicTable, such as the electrodes table, using row indices.
Create a DynamicTableRegion that references all rows of the electrodes table.
electrode_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(electrodesDynamicTable), ...
'description', 'all electrodes', ...
'data', (0:length(electrodesDynamicTable.id.data)-1)');

Raw Voltage Data

Now create an ElectricalSeries object to hold acquisition data collected during the experiment.
raw_electrical_series = types.core.ElectricalSeries( ...
'starting_time', 0.0, ... % seconds
'starting_time_rate', 30000., ... % Hz
'data', randn(numChannels, 3000), ... % nChannels x nTime
'electrodes', electrode_table_region, ...
'data_unit', 'volts');
This is the voltage data recorded directly from our electrodes, so it goes in the acquisition group.
nwb.acquisition.set('ElectricalSeries', raw_electrical_series);

Processed Extracellular Electrical Signals

LFP

LFP refers to data that has been low-pass filtered, typically below 300 Hz. This data may also be downsampled. Because it is filtered and potentially resampled, it is categorized as processed data. LFP data would also be stored in an ElectricalSeries. To help data analysis and visualization tools know that this ElectricalSeries object represents LFP data, we store it inside an LFP object and then place the LFP object in a ProcessingModule named 'ecephys'. This is analogous to how we stored the SpatialSeries object inside of a Position object and stored the Position object in a ProcessingModule named 'behavior' in the behavior tutorial
lfp_electrical_series = types.core.ElectricalSeries( ...
'starting_time', 0.0, ... % seconds
'starting_time_rate', 1000., ... % Hz
'data', randn(numChannels, 100), ... nChannels x nTime
'filtering', 'Low-pass filter at 300 Hz', ...
'electrodes', electrode_table_region, ...
'data_unit', 'volts');
 
lfp = types.core.LFP('ElectricalSeries', lfp_electrical_series);
 
ecephys_module = types.core.ProcessingModule(...
'description', 'extracellular electrophysiology');
 
ecephys_module.nwbdatainterface.set('LFP', lfp);
nwb.processing.set('ecephys', ecephys_module);

Other Types of Filtered Electrical Signals

If your acquired data is filtered for frequency ranges other than LFP—such as Gamma or Theta—you can store the result in an ElectricalSeries and encapsulate it within a FilteredEphys object instead of the LFP object.
% Generate filtered data
filtered_data = randn(50, 12); % 50 time points, 12 channels
filtered_data = permute(filtered_data, [2, 1]); % permute timeseries for matnwb
 
% Create an ElectricalSeries object
filtered_electrical_series = types.core.ElectricalSeries( ...
'description', 'Data filtered in the theta range', ...
'data', filtered_data, ...
'electrodes', electrode_table_region, ...
'filtering', 'Band-pass filtered between 4 and 8 Hz', ...
'starting_time', 0.0, ...
'starting_time_rate', 200.0 ...
);
 
% Create a FilteredEphys object and add the filtered electrical series
filtered_ephys = types.core.FilteredEphys();
filtered_ephys.electricalseries.set('FilteredElectricalSeries', filtered_electrical_series);
 
% Add the FilteredEphys object to the ecephys module
ecephys_module.nwbdatainterface.set('FilteredEphys', filtered_ephys);

Decomposition of LFP Data into Frequency Bands

In some cases, you may want to further process the LFP data and decompose the signal into different frequency bands for additional downstream analyses. You can then store the processed data from these spectral analyses using a DecompositionSeries object. This object allows you to include metadata about the frequency bands and metric used (e.g., power, phase, amplitude), as well as link the decomposed data to the original TimeSeries signal the data was derived from.
In this tutorial, the examples for FilteredEphys and DecompositionSeries may appear similar. However, the key difference is that DecompositionSeries is specialized for storing the results of spectral analyses of timeseries data in general, whereas FilteredEphys is defined specifically as a container for filtered electrical signals.
Note: When adding data to a DecompositionSeries, the data argument is assumed to be 3D where the first dimension is time, the second dimension is channels, and the third dimension is bands. As mentioned in the beginning of this tutorial, in MatNWB the data needs to be permuted because the dimensions are written to file in reverse order (See the dimensionMapNoDataPipes tutorial)
% Define the frequency bands of interest (in Hz):
band_names = {'theta'; 'beta'; 'gamma'};
band_mean = [8; 21; 55];
band_stdev = [2; 4.5; 12.5];
band_limits = [band_mean - 2*band_stdev, band_mean + 2*band_stdev];
 
% The bands should be added to the DecompositionSeries as a dynamic table
bands = table(band_names, band_mean, band_stdev, band_limits, ...
'VariableNames', {'band_name', 'band_mean', 'band_stdev', 'band_limits'})
bands = 3×4 table
 band_nameband_meanband_stdevband_limits
12
1'theta'82412
2'beta'214.50001230
3'gamma'5512.50003080
 
bands = util.table2nwb( bands );
 
% Generate random phase data for the demonstration.
phase_data = randn(50, 12, numel(band_names)); % 50 samples, 12 channels, 3 frequency bands
phase_data = permute(phase_data, [3,2,1]); % See dimensionMapNoDataPipes tutorial
 
decomp_series = types.core.DecompositionSeries(...
'data', phase_data, ...
'bands', bands, ...
'metric', 'phase', ...
'starting_time', 0.0, ... % seconds
'starting_time_rate', 1000.0, ... % Hz
'source_channels', electrode_table_region, ...
'source_timeseries', lfp_electrical_series);
 
% Add decomposition series to ecephys module
ecephys_module.nwbdatainterface.set('theta', decomp_series);

Spike Times and Extracellular Events

Sorted Spike Times

Spike times are stored in a Units table, a specialization of the DynamicTable class. The default Units table is located at /units in the HDF5 file. You can add columns to the Units table just like you did for electrodes and trials (see convertTrials). Here, we generate some random spike data and populate the table.
num_cells = 10;
spikes = cell(1, num_cells);
for iShank = 1:num_cells
spikes{iShank} = rand(1, randi([16, 28]));
end
spikes
spikes = 1×10 cell
 12345678910
11×18 double1×24 double1×22 double1×19 double1×16 double1×28 double1×19 double1×27 double1×26 double1×19 double

Ragged Arrays

Spike times are an example of a ragged array- it's like a matrix, but each row has a different number of elements. We can represent this type of data as an indexed column of the Units table. These indexed columns have two components, the VectorData object that holds the data and the VectorIndex object that holds the indices in the vector that indicate the row breaks. You can use the convenience function util.create_indexed_column to create these objects. For more information about ragged arrays, we refer you to the "Ragged Array Columns" section of the dynamic table tutorial.
[spike_times_vector, spike_times_index] = util.create_indexed_column(spikes);
 
nwb.units = types.core.Units( ...
'colnames', {'spike_times'}, ...
'description', 'units table', ...
'spike_times', spike_times_vector, ...
'spike_times_index', spike_times_index ...
);
 
nwb.units.toTable
ans = 10×2 table
 idspike_times
1118×1 double
2224×1 double
3322×1 double
4419×1 double
5516×1 double
6628×1 double
7719×1 double
8827×1 double
9926×1 double
101019×1 double

Unsorted Spike Times

While the Units table is used to store spike times and waveform data for spike-sorted, single-unit activity, you may also want to store spike times and waveform snippets of unsorted spiking activity. This is useful for recording multi-unit activity detected via threshold crossings during data acquisition. Such information can be stored using SpikeEventSeries objects.
% In the SpikeEventSeries the dimensions should be ordered as
% [num_events, num_channels, num_samples].
% Define spike snippets: 20 events, 3 channels, 40 samples per event.
spike_snippets = rand(20, 3, 40);
% Permute spike snippets (See dimensionMapNoDataPipes tutorial)
spike_snippets = permute(spike_snippets, [3,2,1])
spike_snippets =
spike_snippets(:,:,1) = - - 0.8840 0.4104 0.0773 - 0.2895 0.4434 0.7276 - 0.7282 0.2321 0.1961 - 0.8443 0.0524 0.5851 - 0.3713 0.0241 0.4969 - 0.5494 0.6839 0.7355 - 0.1517 0.7949 0.5979 - 0.6635 0.3582 0.3362 - 0.2304 0.5858 0.8594 - 0.3852 0.2620 0.3264 - 0.3118 0.3305 0.5870 - 0.4677 0.4784 0.8121 - 0.5636 0.3811 0.2998 - 0.5963 0.1882 0.1086 - 0.6762 0.3520 0.0824 - 0.0599 0.0252 0.1052 - 0.1278 0.7587 0.7405 - 0.0422 0.7312 0.6667 - 0.2422 0.8595 0.1119 - 0.6675 0.0296 0.8458 - 0.2531 0.6969 0.2860 - 0.8549 0.6434 0.0730 - 0.9224 0.1372 0.5922 - 0.8046 0.2026 0.3796 - 0.0283 0.4356 0.2686 - 0.0220 0.7392 0.6113 - 0.4983 0.7065 0.8138 - 0.1084 0.7089 0.0159 - 0.4491 0.8796 0.8570 - 0.2666 0.8124 0.1619 - 0.8874 0.7056 0.0604 - 0.7425 0.2605 0.2477 - 0.4856 0.2008 0.1097 - 0.1616 0.0121 0.0490 - 0.1388 0.9127 0.5684 - 0.1459 0.9058 0.1248 - 0.1711 0.1007 0.3990 - 0.8393 0.6710 0.8213 - 0.6889 0.1315 0.7412 - 0.3700 0.8719 0.8248 - - -spike_snippets(:,:,2) = - - 0.3587 0.9811 0.2971 - 0.7277 0.8138 0.2456 - 0.7136 0.7200 0.9779 - 0.8198 0.6446 0.2123 - 0.7078 0.3514 0.5493 - 0.8665 0.6074 0.3950 - 0.9498 0.9263 0.6231 - 0.4755 0.7082 0.7975 - 0.1605 0.2079 0.8562 - 0.0512 0.8677 0.3511 - 0.1897 0.9574 0.7471 - 0.6744 0.6732 0.0225 - 0.6024 0.5067 0.0387 - 0.8992 0.5272 0.5283 - 0.5722 0.9761 0.7628 - 0.7082 0.5364 0.8801 - 0.1330 0.8471 0.4737 - 0.6811 0.2800 0.9775 - 0.5363 0.9029 0.5870 - 0.7977 0.0398 0.4918 - 0.4657 0.2871 0.0126 - 0.9780 0.3657 0.7575 - 0.8543 0.7694 0.6129 - 0.3676 0.3227 0.3590 - 0.4902 0.7841 0.7379 - 0.7595 0.7320 0.6265 - 0.2180 0.4572 0.1383 - 0.3310 0.0001 0.5000 - 0.0245 0.9370 0.0438 - 0.7992 0.6612 0.7073 - 0.3443 0.9668 0.3462 - 0.9261 0.6006 0.5526 - 0.4018 0.0765 0.4504 - 0.0286 0.3908 0.4233 - 0.6364 0.7242 0.6814 - 0.4236 0.0721 0.5432 - 0.5599 0.7267 0.6644 - 0.1459 0.2461 0.6893 - 0.2536 0.3381 0.0641 - 0.6326 0.7257 0.8844 - - -spike_snippets(:,:,3) = - - 0.1362 0.1425 0.3644 - 0.2234 0.6658 0.9637 - 0.4727 0.6631 0.2876 - 0.0085 0.0553 0.4585 - 0.9502 0.0977 0.6954 - 0.2312 0.1565 0.5087 - 0.2705 0.3535 0.5659 - 0.2368 0.0900 0.8627 - 0.1081 0.7041 0.3725 - 0.6482 0.0005 0.1544 - 0.2362 0.6108 0.6114 - 0.8803 0.9088 0.2091 - 0.0856 0.8648 0.1002 - 0.3896 0.2870 0.4510 - 0.0470 0.1266 0.1895 - 0.9096 0.4791 0.3934 - 0.8571 0.5306 0.4322 - 0.5716 0.3844 0.8254 - 0.8413 0.8704 0.3305 - 0.5318 0.7753 0.7935 - 0.4743 0.8884 0.4022 - 0.8342 0.4738 0.8089 - 0.8143 0.2582 0.8370 - 0.8134 0.4044 0.0324 - 0.2955 0.2959 0.2695 - 0.6966 0.4155 0.6720 - 0.4360 0.3938 0.8744 - 0.4421 0.9780 0.2194 - 0.2874 0.1777 0.7541 - 0.8277 0.5363 0.0101 - 0.1075 0.8672 0.3565 - 0.5107 0.4739 0.3066 - 0.9965 0.3262 0.0391 - 0.6357 0.1903 0.3097 - 0.2875 0.9710 0.5432 - 0.2359 0.5163 0.9214 - 0.8104 0.6590 0.3723 - 0.2666 0.0595 0.3578 - 0.8609 0.1882 0.3043 - 0.6504 0.3113 0.0771 - - -spike_snippets(:,:,4) = - - 0.8050 0.2022 0.5860 - 0.5796 0.4442 0.5489 - 0.3618 0.1478 0.7466 - 0.9093 0.7029 0.2356 - 0.0460 0.1836 0.8784 - 0.2005 0.4046 0.1156 - 0.4469 0.5336 0.6318 - 0.9771 0.7806 0.7635 - 0.4451 0.4810 0.1803 - 0.1741 0.2258 0.4179 - 0.7091 0.6490 0.2747 - 0.4820 0.6451 0.5042 - 0.6605 0.6045 0.9469 - 0.0161 0.7395 0.0059 - 0.4782 0.2793 0.8853 - 0.4091 0.9862 0.4804 - 0.8455 0.2913 0.5672 - 0.0649 0.6610 0.3551 - 0.9321 0.3362 0.8347 - 0.4598 0.4687 0.1846 - 0.7372 0.5780 0.8394 - 0.3125 0.0901 0.3703 - 0.4837 0.2241 0.0313 - 0.0275 0.7825 0.1160 - 0.6096 0.8158 0.9808 - 0.4574 0.8434 0.0143 - 0.1786 0.6356 0.4819 - 0.8691 0.1741 0.4058 - 0.0887 0.9107 0.3661 - 0.9826 0.7998 0.8946 - 0.9549 0.2801 0.6857 - 0.9784 0.3684 0.1300 - 0.6258 0.8026 0.6570 - 0.5787 0.1236 0.1830 - 0.5000 0.9966 0.2871 - 0.0470 0.1272 0.4818 - 0.8635 0.1213 0.7935 - 0.8270 0.6987 0.0980 - 0.0250 0.9571 0.9466 - 0.4383 0.4128 0.4984 - - -spike_snippets(:,:,5) = - - 0.5913 0.8706 0.2520 - 0.7161 0.0618 0.4407 - 0.5908 0.8786 0.0151 - 0.6327 0.6154 0.5395 - 0.1973 0.7438 0.3234 - 0.0870 0.2944 0.4277 - 0.4847 0.1451 0.3072 - 0.3449 0.5248 0.3223 - 0.5565 0.7196 0.2811 - 0.3149 0.0960 0.2301 - 0.3564 0.1228 0.4846 - 0.5336 0.1059 0.1092 - 0.2556 0.8666 0.5237 - 0.2334 0.4155 0.5159 - 0.4103 0.3421 0.3923 - 0.2627 0.2856 0.4128 - 0.6458 0.6598 0.5874 - 0.5963 0.0843 0.2972 - 0.0502 0.6843 0.4237 - 0.3206 0.3378 0.9895 - 0.5622 0.4088 0.9491 - 0.5645 0.6614 0.4398 - 0.3181 0.7743 0.4310 - 0.5152 0.8521 0.4793 - 0.6703 0.7846 0.1971 - 0.4306 0.8096 0.6323 - 0.1371 0.0387 0.8975 - 0.8258 0.9360 0.5596 - 0.8419 0.4748 0.2337 - 0.0046 0.9465 0.5231 - 0.8142 0.1619 0.4782 - 0.8274 0.6917 0.5729 - 0.0124 0.7184 0.6751 - 0.2896 0.4857 0.0238 - 0.1628 0.7326 0.8865 - 0.8080 0.9400 0.3402 - 0.8077 0.0893 0.3769 - 0.9437 0.2364 0.8215 - 0.6212 0.9167 0.2558 - 0.0951 0.8936 0.9542 - - -spike_snippets(:,:,6) = - - 0.8355 0.5637 0.7755 - 0.2274 0.3269 0.3431 - 0.3749 0.3844 0.4181 - 0.6306 0.5490 0.0857 - 0.3787 0.4608 0.9933 - 0.5684 0.2314 0.9910 - 0.8612 0.8184 0.2795 - 0.0594 0.1986 0.1672 - 0.4588 0.4749 0.5851 - 0.2411 0.0279 0.5333 - 0.2000 0.8661 0.3145 - 0.1040 0.8509 0.0429 - 0.4510 0.3810 0.4695 - 0.6253 0.1327 0.9410 - 0.2672 0.1820 0.3754 - 0.5340 0.0297 0.2965 - 0.1050 0.1508 0.4010 - 0.8741 0.9200 0.5657 - 0.4239 0.5883 0.4612 - 0.5997 0.0074 0.4411 - 0.2558 0.1972 0.9108 - 0.7621 0.7016 0.3741 - 0.1205 0.0695 0.5496 - 0.2904 0.0255 0.0484 - 0.8102 0.7424 0.2196 - 0.1073 0.4464 0.9746 - 0.0747 0.1256 0.1908 - 0.0275 0.2822 0.3473 - 0.9470 0.4516 0.2231 - 0.5762 0.6301 0.6778 - 0.7522 0.1073 0.8839 - 0.4510 0.6959 0.3521 - 0.3911 0.9126 0.5941 - 0.9814 0.8108 0.5995 - 0.2263 0.3503 0.1219 - 0.2262 0.0686 0.8908 - 0.9722 0.6025 0.8860 - 0.0521 0.6195 0.3267 - 0.6933 0.6580 0.1238 - 0.7840 0.2751 0.9997 - - -spike_snippets(:,:,7) = - - 0.9348 0.5672 0.6494 - 0.6230 0.3300 0.7007 - 0.4442 0.0659 0.1242 - 0.7114 0.5661 0.2602 - 0.0429 0.1881 0.5538 - 0.8699 0.9545 0.7000 - 0.2914 0.0736 0.5799 - 0.8541 0.5021 0.3541 - 0.5734 0.4741 0.6012 - 0.5879 0.1169 0.9234 - 0.6626 0.3217 0.6066 - 0.4561 0.3203 0.5923 - 0.9674 0.8924 0.0659 - 0.2196 0.8062 0.2753 - 0.6065 0.3989 0.9592 - 0.0842 0.8840 0.8314 - 0.4492 0.3714 0.4889 - 0.2868 0.8496 0.4936 - 0.1131 0.4815 0.8227 - 0.7724 0.3689 0.2368 - 0.1026 0.7261 0.7530 - 0.6204 0.1422 0.7569 - 0.9188 0.9783 0.5233 - 0.0023 0.2101 0.1290 - 0.6068 0.1084 0.7324 - 0.4695 0.5852 0.6174 - 0.6207 0.7780 0.2677 - 0.1279 0.1070 0.0026 - 0.6935 0.1865 0.4121 - 0.5727 0.1962 0.7182 - 0.3159 0.0386 0.3871 - 0.3459 0.4791 0.8994 - 0.2445 0.5204 0.6407 - 0.6440 0.6374 0.8091 - 0.8246 0.1185 0.8605 - 0.8813 0.6919 0.5799 - 0.1067 0.4817 0.2726 - 0.3286 0.4898 0.6359 - 0.8793 0.0062 0.7552 - 0.2744 0.2255 0.8729 - - -spike_snippets(:,:,8) = - - 0.3559 0.3502 0.4184 - 0.1456 0.7047 0.4624 - 0.0364 0.5867 0.9092 - 0.5403 0.8551 0.8671 - 0.4563 0.5706 0.5792 - 0.4000 0.5206 0.9052 - 0.9328 0.8906 0.1171 - 0.5250 0.7959 0.7344 - 0.2594 0.6278 0.1148 - 0.8432 0.2758 0.7820 - 0.7348 0.1972 0.3360 - 0.0840 0.1389 0.4690 - 0.6533 0.9474 0.6290 - 0.1758 0.2443 0.1279 - 0.1706 0.1410 0.9089 - 0.4944 0.0307 0.0575 - 0.6134 0.0540 0.3751 - 0.3314 0.0149 0.7502 - 0.8902 0.2016 0.9204 - 0.3945 0.1937 0.0392 - 0.8257 0.9887 0.1202 - 0.2154 0.2457 0.1660 - 0.0023 0.6066 0.1006 - 0.2223 0.0890 0.2271 - 0.1103 0.1547 0.5031 - 0.4946 0.7406 0.5282 - 0.1234 0.0530 0.4693 - 0.7059 0.9016 0.4029 - 0.1858 0.9035 0.7578 - 0.8333 0.7042 0.3734 - 0.9886 0.5343 0.1843 - 0.1650 0.4507 0.9497 - 0.8576 0.5598 0.4163 - 0.3344 0.4154 0.3729 - 0.5832 0.1692 0.8046 - 0.0764 0.7459 0.2572 - 0.0343 0.4037 0.8575 - 0.3879 0.8997 0.3826 - 0.4534 0.1001 0.7087 - 0.5255 0.2042 0.7357 - - -spike_snippets(:,:,9) = - - 0.7075 0.3583 0.2809 - 0.9943 0.8379 0.8414 - 0.4010 0.6446 0.2016 - 0.2224 0.3620 0.1538 - 0.0410 0.2821 0.2583 - 0.9369 0.0431 0.9306 - 0.9925 0.3225 0.9261 - 0.1077 0.2515 0.5068 - 0.4240 0.3376 0.0569 - 0.5236 0.5543 0.9493 - 0.6657 0.5300 0.8385 - 0.3563 0.7412 0.3676 - 0.8088 0.1033 0.4451 - 0.3500 0.2520 0.6293 - 0.7315 0.7107 0.5051 - 0.3474 0.7358 0.2772 - 0.3127 0.1042 0.4424 - 0.0544 0.1513 0.2170 - 0.9940 0.9846 0.5657 - 0.3050 0.7328 0.5181 - 0.7067 0.5802 0.5910 - 0.0262 0.1544 0.0785 - 0.0299 0.2458 0.1297 - 0.3248 0.4770 0.5600 - 0.9692 0.0887 0.6627 - 0.4190 0.9029 0.1486 - 0.1847 0.4102 0.0110 - 0.8229 0.2542 0.8645 - 0.8748 0.7377 0.6509 - 0.8114 0.8655 0.0641 - 0.7379 0.4173 0.7943 - 0.1262 0.2913 0.3552 - 0.7535 0.4450 0.5206 - 0.9617 0.1137 0.8812 - 0.9513 0.4632 0.9043 - 0.0408 0.3225 0.1974 - 0.0124 0.4705 0.4695 - 0.8570 0.5250 0.1845 - 0.3925 0.4026 0.9171 - 0.2274 0.9037 0.7655 - - -spike_snippets(:,:,10) = - - 0.7941 0.5149 0.8517 - 0.7471 0.8854 0.1999 - 0.2383 0.5179 0.0416 - 0.0682 0.5410 0.5133 - 0.3799 0.4434 0.9443 - 0.0974 0.8834 0.6782 - 0.0608 0.0798 0.6398 - 0.4075 0.8149 0.1136 - 0.8780 0.0440 0.9261 - 0.5841 0.8798 0.2789 - 0.1936 0.6428 0.6757 - 0.2792 0.4726 0.1642 - 0.7121 0.9948 0.7833 - 0.7346 0.2496 0.0774 - 0.2914 0.9707 0.6684 - 0.8672 0.1230 0.0800 - 0.8510 0.3240 0.8299 - 0.0924 0.2497 0.7994 - 0.1204 0.1693 0.3222 - 0.6565 0.4299 0.4249 - 0.9711 0.2600 0.8885 - 0.0870 0.0539 0.9095 - 0.6804 0.5909 0.8776 - 0.9831 0.9556 0.0354 - 0.4005 0.2000 0.8917 - 0.7770 0.7415 0.7880 - 0.4564 0.4864 0.3968 - 0.1782 0.2553 0.2720 - 0.9537 0.1915 0.3335 - 0.9620 0.9273 0.4582 - 0.5031 0.2392 0.0366 - 0.2139 0.8565 0.3517 - 0.0704 0.9866 0.9983 - 0.8044 0.9471 0.0526 - 0.9997 0.5544 0.4836 - 0.5022 0.2241 0.1854 - 0.1620 0.1630 0.6789 - 0.0508 0.6460 0.2120 - 0.3283 0.4058 0.2809 - 0.8139 0.0804 0.7395 - - -spike_snippets(:,:,11) = - - 0.2544 0.4666 0.6415 - 0.7723 0.3364 0.3455 - 0.3722 0.6145 0.6607 - 0.0373 0.8671 0.8135 - 0.9431 0.6031 0.3475 - 0.5092 0.7629 0.6814 - 0.8818 0.7412 0.0964 - 0.4394 0.9440 0.6040 - 0.9009 0.4173 0.6211 - 0.5267 0.2959 0.3061 - 0.1462 0.9594 0.5161 - 0.3670 0.2076 0.1559 - 0.9726 0.5897 0.8215 - 0.5163 0.2440 0.4610 - 0.7033 0.8081 0.0302 - 0.7474 0.2464 0.0298 - 0.0537 0.4461 0.1025 - 0.5831 0.2761 0.1187 - 0.4606 0.5784 0.3064 - 0.8775 0.7498 0.3991 - 0.9276 0.5925 0.3863 - 0.4750 0.3527 0.7902 - 0.3017 0.6283 0.7300 - 0.1315 0.8961 0.5218 - 0.6252 0.2800 0.2163 - 0.7672 0.1983 0.2008 - 0.2827 0.0721 0.6766 - 0.0958 0.6591 0.6342 - 0.5979 0.5047 0.9257 - 0.3368 0.1198 0.3731 - 0.2173 0.1262 0.7610 - 0.5702 0.7797 0.4054 - 0.4685 0.7710 0.0751 - 0.6500 0.0987 0.0612 - 0.6616 0.7665 0.9412 - 0.9498 0.5447 0.3660 - 0.5139 0.9977 0.5679 - 0.0635 0.3845 0.9624 - 0.6494 0.0416 0.4315 - 0.6296 0.1024 0.7642 - - -spike_snippets(:,:,12) = - - 0.6146 0.0968 0.3336 - 0.3430 0.2655 0.0046 - 0.1619 0.0499 0.5731 - 0.6363 0.8267 0.4098 - 0.6908 0.1887 0.9967 - 0.2547 0.3581 0.4260 - 0.5295 0.4325 0.6309 - 0.6853 0.4064 0.5448 - 0.8801 0.5851 0.9206 - 0.2101 0.5392 0.8670 - 0.4839 0.2279 0.0945 - 0.1867 0.9327 0.1141 - 0.8110 0.4286 0.8918 - 0.0652 0.8428 0.6683 - 0.6747 0.7311 0.8710 - 0.8184 0.4235 0.1510 - 0.0058 0.8850 0.5862 - 0.4190 0.4357 0.1666 - 0.8585 0.5593 0.4555 - 0.3209 0.5544 0.3689 - 0.5356 0.1007 0.9436 - 0.1546 0.4301 0.4500 - 0.0562 0.7332 0.8797 - 0.4663 0.0001 0.5342 - 0.1850 0.7657 0.5038 - 0.9125 0.2055 0.2562 - 0.5721 0.0714 0.6559 - 0.8022 0.8499 0.5639 - 0.6421 0.5074 0.3886 - 0.8348 0.8373 0.2105 - 0.1514 0.8200 0.3957 - 0.3065 0.4698 0.9103 - 0.5984 0.3031 0.2229 - 0.0662 0.6775 0.7322 - 0.3514 0.1481 0.1377 - 0.2903 0.8945 0.4549 - 0.2797 0.1967 0.2417 - 0.8218 0.1114 0.6109 - 0.1070 0.9263 0.5711 - 0.6670 0.0539 0.6385 - - -spike_snippets(:,:,13) = - - 0.0384 0.0447 0.7745 - 0.2359 0.0803 0.5582 - 0.8278 0.1725 0.9556 - 0.6773 0.0045 0.6783 - 0.7831 0.7674 0.2515 - 0.7814 0.6090 0.8158 - 0.9209 0.5476 0.1611 - 0.7194 0.7287 0.4161 - 0.7315 0.6364 0.0511 - 0.9443 0.4515 0.2623 - 0.0348 0.6175 0.1363 - 0.8028 0.9249 0.8991 - 0.6904 0.8000 0.9812 - 0.5899 0.2797 0.2728 - 0.2293 0.3397 0.7309 - 0.7477 0.0589 0.0045 - 0.4752 0.1478 0.5416 - 0.4774 0.9201 0.1033 - 0.7057 0.4585 0.3031 - 0.9951 0.6517 0.5719 - 0.5022 0.0639 0.1790 - 0.4090 0.1917 0.6295 - 0.6272 0.0826 0.9543 - 0.3429 0.6129 0.5725 - 0.6568 0.0692 0.4979 - 0.9034 0.4953 0.0361 - 0.4969 0.8789 0.0178 - 0.3767 0.8545 0.4115 - 0.8487 0.4793 0.3571 - 0.6909 0.0469 0.2186 - 0.7016 0.1703 0.6514 - 0.3019 0.9341 0.4815 - 0.0172 0.5732 0.9753 - 0.9003 0.7146 0.9119 - 0.8645 0.1699 0.7649 - 0.4176 0.7374 0.9153 - 0.1825 0.2433 0.8153 - 0.6800 0.9977 0.3573 - 0.9317 0.1497 0.5918 - 0.9806 0.8552 0.7741 - - -spike_snippets(:,:,14) = - - 0.9529 0.4197 0.7940 - 0.0680 0.2133 0.2558 - 0.3934 0.9782 0.8711 - 0.9798 0.4954 0.1090 - 0.8314 0.6794 0.1408 - 0.6423 0.9708 0.2159 - 0.6231 0.8391 0.7335 - 0.2810 0.8298 0.2176 - 0.3974 0.6474 0.8730 - 0.2421 0.9274 0.3335 - 0.7245 0.3462 0.8549 - 0.6980 0.8378 0.7992 - 0.2055 0.2089 0.5516 - 0.5671 0.4157 0.0663 - 0.4671 0.0046 0.2549 - 0.6323 0.2667 0.4567 - 0.2121 0.4947 0.0204 - 0.7162 0.4741 0.5615 - 0.7168 0.2574 0.1147 - 0.9861 0.7936 0.2834 - 0.8550 0.3711 0.7613 - 0.2810 0.6640 0.2897 - 0.5666 0.7711 0.8337 - 0.0302 0.8985 0.0659 - 0.3424 0.3668 0.0051 - 0.1083 0.3183 0.8842 - 0.2733 0.8660 0.7756 - 0.1294 0.2436 0.2996 - 0.4480 0.2132 0.7510 - 0.2924 0.7984 0.5947 - 0.4414 0.0408 0.9539 - 0.5015 0.4748 0.2327 - 0.3004 0.1017 0.0860 - 0.4600 0.1732 0.4097 - 0.4972 0.3785 0.1298 - 0.9853 0.8222 0.8488 - 0.9130 0.3340 0.2946 - 0.0938 0.2253 0.0173 - 0.0004 0.3570 0.9206 - 0.1234 0.8649 0.5750 - - -spike_snippets(:,:,15) = - - 0.9922 0.6567 0.1626 - 0.7361 0.5865 0.8551 - 0.3929 0.8085 0.1534 - 0.0604 0.4683 0.8830 - 0.9938 0.9102 0.9857 - 0.9197 0.5770 0.0050 - 0.3592 0.8451 0.9703 - 0.4458 0.3356 0.3232 - 0.0204 0.0565 0.8186 - 0.0823 0.8806 0.2045 - 0.7850 0.5394 0.5331 - 0.4210 0.1433 0.4199 - 0.9766 0.7337 0.7132 - 0.9982 0.2258 0.4000 - 0.6702 0.7083 0.9060 - 0.4952 0.0332 0.1472 - 0.6573 0.1771 0.7804 - 0.1723 0.3458 0.1954 - 0.9137 0.3648 0.7029 - 0.3399 0.3422 0.3585 - 0.2941 0.7883 0.9296 - 0.3875 0.4254 0.1448 - 0.5145 0.9283 0.1949 - 0.3563 0.7005 0.8120 - 0.2778 0.9611 0.9273 - 0.6046 0.2139 0.9705 - 0.4420 0.0302 0.1635 - 0.0088 0.6501 0.5761 - 0.7116 0.0831 0.9489 - 0.8314 0.4768 0.4114 - 0.1151 0.1037 0.5107 - 0.3827 0.2916 0.8996 - 0.4661 0.2260 0.1121 - 0.7078 0.4180 0.0222 - 0.7247 0.5719 0.9316 - 0.1403 0.8271 0.9396 - 0.2412 0.1451 0.0554 - 0.2553 0.1747 0.9921 - 0.6871 0.4399 0.3530 - 0.1298 0.2656 0.9714 - - -spike_snippets(:,:,16) = - - 0.9972 0.9936 0.5482 - 0.8326 0.0840 0.4691 - 0.7708 0.0330 0.8159 - 0.5131 0.1295 0.2345 - 0.7615 0.2317 0.1027 - 0.1724 0.8394 0.5767 - 0.0137 0.1014 0.1270 - 0.0739 0.5132 0.3428 - 0.2915 0.8071 0.9026 - 0.3845 0.5590 0.0266 - 0.8303 0.0359 0.5994 - 0.1240 0.6085 0.5076 - 0.8980 0.9474 0.3432 - 0.6499 0.3732 0.0730 - 0.3303 0.4004 0.7405 - 0.2498 0.7835 0.6898 - 0.1249 0.7027 0.7510 - 0.6946 0.5686 0.2033 - 0.6918 0.5278 0.6515 - 0.8156 0.7957 0.5573 - 0.4032 0.3450 0.7350 - 0.4818 0.8283 0.2974 - 0.0384 0.0808 0.7130 - 0.1391 0.3200 0.3603 - 0.6069 0.4092 0.1513 - 0.3444 0.3243 0.9263 - 0.9033 0.2053 0.4476 - 0.1142 0.2854 0.8082 - 0.0272 0.1698 0.8336 - 0.5026 0.4346 0.5423 - 0.7878 0.6306 0.2140 - 0.9513 0.9625 0.7080 - 0.5834 0.2035 0.9237 - 0.2557 0.0934 0.4052 - 0.9567 0.7467 0.0456 - 0.5091 0.9380 0.0553 - 0.9153 0.0366 0.1606 - 0.3306 0.0036 0.8340 - 0.4779 0.5815 0.6836 - 0.0585 0.7621 0.6835 - - -spike_snippets(:,:,17) = - - 0.3960 0.3974 0.5588 - 0.0424 0.4685 0.4894 - 0.8798 0.5952 0.9892 - 0.7382 0.6502 0.6027 - 0.5835 0.2012 0.6278 - 0.3606 0.6030 0.2146 - 0.8293 0.6913 0.4075 - 0.8117 0.7003 0.9414 - 0.7901 0.7728 0.7922 - 0.5847 0.0021 0.7396 - 0.3876 0.2996 0.2556 - 0.7419 0.4900 0.2650 - 0.0490 0.5944 0.5732 - 0.3899 0.8482 0.7060 - 0.6078 0.9582 0.0290 - 0.2723 0.3243 0.8536 - 0.0005 0.7109 0.3435 - 0.9130 0.8274 0.2376 - 0.2565 0.1573 0.0270 - 0.5802 0.9857 0.7257 - 0.2650 0.6423 0.3761 - 0.8502 0.5365 0.7145 - 0.4163 0.4096 0.5124 - 0.1079 0.8086 0.8176 - 0.1324 0.4778 0.9827 - 0.5382 0.3201 0.7392 - 0.7771 0.2693 0.9050 - 0.4876 0.0728 0.7989 - 0.0328 0.2198 0.0266 - 0.9892 0.3329 0.9052 - 0.3862 0.1512 0.2777 - 0.5593 0.2181 0.0367 - 0.6248 0.3320 0.7972 - 0.8769 0.6821 0.0563 - 0.4199 0.6120 0.9375 - 0.2787 0.7777 0.2754 - 0.8244 0.1253 0.8188 - 0.5485 0.7533 0.2894 - 0.6503 0.1747 0.0322 - 0.0543 0.7022 0.9705 - - -spike_snippets(:,:,18) = - - 0.6586 0.8381 0.9346 - 0.3564 0.1914 0.4731 - 0.5518 0.6622 0.0321 - 0.6634 0.5777 0.4223 - 0.1716 0.2335 0.0385 - 0.2029 0.5658 0.7820 - 0.0426 0.4652 0.7967 - 0.0360 0.2248 0.1815 - 0.7329 0.8276 0.1524 - 0.3917 0.7722 0.6973 - 0.1874 0.7361 0.2067 - 0.2838 0.8671 0.4181 - 0.5444 0.8385 0.8334 - 0.9438 0.5089 0.4384 - 0.3220 0.1924 0.7713 - 0.4797 0.0161 0.6776 - 0.0509 0.9474 0.4790 - 0.5528 0.1435 0.8937 - 0.1712 0.2664 0.4000 - 0.3756 0.1580 0.7842 - 0.7943 0.8386 0.6555 - 0.8971 0.8120 0.6511 - 0.3538 0.1422 0.5621 - 0.4175 0.1287 0.7695 - 0.6317 0.3817 0.7950 - 0.8559 0.0167 0.8612 - 0.8622 0.3802 0.3890 - 0.0369 0.2987 0.0346 - 0.1610 0.0036 0.4241 - 0.7199 0.1141 0.7388 - 0.6749 0.6053 0.3875 - 0.9807 0.8228 0.6587 - 0.2027 0.1864 0.9194 - 0.9936 0.7345 0.3757 - 0.6885 0.1653 0.3631 - 0.8941 0.1107 0.6917 - 0.7975 0.2033 0.0698 - 0.7628 0.9172 0.6178 - 0.8866 0.9178 0.1358 - 0.5250 0.7230 0.1884 - - -spike_snippets(:,:,19) = - - 0.0843 0.0789 0.0190 - 0.9539 0.2201 0.2193 - 0.3115 0.9336 0.0849 - 0.3433 0.4676 0.2098 - 0.2704 0.2124 0.2048 - 0.1612 0.4393 0.1003 - 0.8375 0.4583 0.4274 - 0.0563 0.4216 0.9778 - 0.4400 0.4879 0.2062 - 0.4732 0.5376 0.3918 - 0.9230 0.5727 0.4353 - 0.7881 0.6609 0.1073 - 0.9234 0.2811 0.0459 - 0.5888 0.4915 0.8911 - 0.2365 0.1278 0.2981 - 0.1332 0.9264 0.4458 - 0.3365 0.7043 0.6547 - 0.8653 0.3776 0.0418 - 0.1819 0.6848 0.7250 - 0.0428 0.6453 0.1657 - 0.7494 0.2659 0.1569 - 0.3866 0.6769 0.7458 - 0.0382 0.7546 0.4898 - 0.7645 0.3238 0.0739 - 0.7457 0.2271 0.3200 - 0.9157 0.1533 0.4651 - 0.7660 0.2698 0.7659 - 0.9227 0.1591 0.3390 - 0.8408 0.8849 0.8486 - 0.1186 0.6178 0.3768 - 0.4187 0.6784 0.4346 - 0.3279 0.7755 0.4404 - 0.8957 0.8531 0.9597 - 0.8631 0.8959 0.0828 - 0.1697 0.9542 0.9433 - 0.2106 0.2864 0.0747 - 0.1872 0.6363 0.1239 - 0.0568 0.2628 0.8335 - 0.9199 0.8350 0.3247 - 0.3773 0.3289 0.0652 - - -spike_snippets(:,:,20) = - - 0.3211 0.6437 0.8537 - 0.1520 0.5627 0.1590 - 0.9570 0.1282 0.0198 - 0.5171 0.9608 0.5398 - 0.5639 0.0015 0.5351 - 0.7871 0.4368 0.6525 - 0.0547 0.4123 0.4774 - 0.2071 0.3338 0.3417 - 0.9039 0.8778 0.9688 - 0.5819 0.2111 0.0526 - 0.7195 0.6208 0.2210 - 0.8373 0.6037 0.2933 - 0.5257 0.4083 0.5324 - 0.3001 0.5219 0.6432 - 0.6423 0.4282 0.2968 - 0.0757 0.3441 0.9642 - 0.8420 0.1641 0.5281 - 0.2294 0.1050 0.7394 - 0.4970 0.7150 0.6435 - 0.2402 0.3646 0.3062 - 0.8428 0.1960 0.4764 - 0.4495 0.6721 0.6090 - 0.3322 0.9081 0.6180 - 0.3987 0.8989 0.3979 - 0.4572 0.4035 0.3269 - 0.7665 0.9586 0.1624 - 0.4178 0.9179 0.0901 - 0.3773 0.9761 0.4298 - 0.8494 0.8965 0.7878 - 0.8920 0.3539 0.2586 - 0.0978 0.6594 0.7246 - 0.3655 0.8161 0.2763 - 0.5166 0.0215 0.4210 - 0.8319 0.7026 0.3662 - 0.1834 0.9958 0.9970 - 0.9425 0.3698 0.1860 - 0.5619 0.5043 0.2714 - 0.1227 0.7896 0.3180 - 0.3038 0.2416 0.8651 - 0.0602 0.9943 0.4567 -
 
% Create electrode table region referencing electrodes 0, 1, and 2
shank0_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(electrodesDynamicTable), ...
'description', 'shank0', ...
'data', (0:2)');
 
% Define spike event series for unsorted spike times
spike_events = types.core.SpikeEventSeries( ...
'data', spike_snippets, ...
'timestamps', (0:19)', ... % Timestamps for each event
'description', 'events detected with 100uV threshold', ...
'electrodes', shank0_table_region ...
);
 
% Add spike event series to NWB file acquisition
nwb.acquisition.set('SpikeEvents_Shank0', spike_events);

Detected Events

If you need to store the complete, continuous raw voltage traces, along with unsorted spike times, you should store the traces in ElectricalSeries objects in the acquisition group, and use the EventDetection class to identify the spike events in your raw traces.
% Create the EventDetection object
event_detection = types.core.EventDetection( ...
'detection_method', 'thresholding, 1.5 * std', ...
'source_electricalseries', types.untyped.SoftLink(raw_electrical_series), ...
'source_idx', [1000; 2000; 3000], ...
'times', [.033, .066, .099] ...
);
 
% Add the EventDetection object to the ecephys module
ecephys_module.nwbdatainterface.set('ThresholdEvents', event_detection);

Storing Spike Features (e.g Principal Components)

NWB also provides a way to store features of spikes, such as principal components, using the FeatureExtraction class.
% Generate random feature data (time x channel x feature)
features = rand(3, 12, 4); % 3 time points, 12 channels, 4 features
features = permute(features, [3,2,1]); % reverse dimension order for matnwb
 
% Create the FeatureExtraction object
feature_extraction = types.core.FeatureExtraction( ...
'description', {'PC1', 'PC2', 'PC3', 'PC4'}, ... % Feature descriptions
'electrodes', electrode_table_region, ... % DynamicTableRegion referencing the electrodes table
'times', [.033; .066; .099], ... % Column vector for times
'features', features ...
);
 
% Add the FeatureExtraction object to the ecephys module (if required)
ecephys_module.nwbdatainterface.set('PCA_features', feature_extraction);

Choosing NWB-Types for Electrophysiology Data (A Summary)

As mentioned above, ElectricalSeries objects are meant for storing electrical timeseries data like raw voltage signals or processed signals like LFP or other filtered signals. In addition to the ElectricalSeries class, NWB provides some more classes for storing event-based electropysiological data. We will briefly discuss them here, and refer the reader to the API documentation and the section on Extracellular Physiology in the "NWB Format Specification" for more details on using these objects.
For storing unsorted spiking data, there are two options. Which one you choose depends on what data you have available. If you need to store complete and/or continuous raw voltage traces, you should store the traces with ElectricalSeries objects as acquisition data, and use the EventDetection class for identifying the spike events in your raw traces. If you do not want to store the entire raw voltage traces, only the waveform ‘snippets’ surrounding spike events, you should use SpikeEventSeries objects.
The results of spike sorting (or clustering) should be stored in the top-level Units table. The Units table can hold just the spike times of sorted units or, optionally, include additional waveform information. You can use the optional predefined columns waveform_mean, waveform_sd, and waveforms in the Units table to store individual and mean waveform data.

Writing the NWB File

nwbExport(nwb, 'ecephys_tutorial.nwb')

Reading NWB Data

Data arrays are read passively from the file. Calling TimeSeries.data does not read the data values, but presents an HDF5 object that can be indexed to read data. This allows you to conveniently work with datasets that are too large to fit in RAM all at once. load with no input arguments reads the entire dataset:
nwb2 = nwbRead('ecephys_tutorial.nwb', 'ignorecache');
nwb2.processing.get('ecephys'). ...
nwbdatainterface.get('LFP'). ...
electricalseries.get('ElectricalSeries'). ...
data.load;

Accessing Data Regions

If all you need is a data region, you can index a DataStub object like you would any normal array in MATLAB, as shown below. When indexing the dataset this way, only the selected region is read from disk into RAM. This allows you to handle very large datasets that would not fit entirely into RAM.
% read section of LFP
nwb2.processing.get('ecephys'). ...
nwbdatainterface.get('LFP'). ...
electricalseries.get('ElectricalSeries'). ...
data(1:5, 1:10)
ans = 5×10
-3.5690 0.9190 1.1629 0.1770 -1.1149 -0.0227 1.7513 0.0439 0.3346 -0.6137 - -0.8413 1.7329 0.4917 0.5800 -2.9249 0.5906 0.0099 0.5686 1.5835 -0.2693 - -0.0354 0.5030 -0.1769 0.9896 -0.5627 -2.1664 0.5403 -0.6677 0.4199 -0.2109 - 0.7809 -0.3773 -0.7599 -0.6833 -1.2777 -1.6061 0.4971 0.1317 0.0585 -0.8233 - 0.2562 0.5045 0.0044 0.2529 0.9348 2.2901 -0.0942 0.2287 -0.5769 -0.1247 -
 
% You can use the getRow method of the table to load spike times of a specific unit.
% To get the values, unpack from the returned table.
nwb.units.getRow(1).spike_times{1}
ans = 18×1
0.0591 - 0.2652 - 0.3253 - 0.6815 - 0.7179 - 0.4488 - 0.0672 - 0.6978 - 0.4125 - 0.7288 -

Learn more!

See the API documentation to learn what data types are available.

MATLAB tutorials

Python tutorials

See our tutorials for more details about your data type:
Check out other tutorials that teach advanced NWB topics:
-
- -
\ No newline at end of file diff --git a/tutorials/html/ecephys.png b/tutorials/html/ecephys.png deleted file mode 100644 index 90f0770ffaaea6675f89328c07f8b13c108d4784..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1670 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 diff --git a/tutorials/html/ecephys_data_deps.png b/tutorials/html/ecephys_data_deps.png deleted file mode 100644 index 8854d00098bec86e777bddea5ed99f2d8485dbcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102809 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 diff --git a/tutorials/html/icephys.html b/tutorials/html/icephys.html deleted file mode 100644 index 440dfc36..00000000 --- a/tutorials/html/icephys.html +++ /dev/null @@ -1,558 +0,0 @@ - -Intracellular electrophysiology \ No newline at end of file diff --git a/tutorials/html/images.html b/tutorials/html/images.html deleted file mode 100644 index bbdaecdf..00000000 --- a/tutorials/html/images.html +++ /dev/null @@ -1,371 +0,0 @@ - -Storing Image Data in NWB

Storing Image Data in NWB

Image data can be a collection of individual images or movie segments (as a movie is simply a series of images), about the subject, the environment, the presented stimuli, or other parts related to the experiment. This tutorial focuses in particular on the usage of:

Create an NWB File

nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'LastName, FirstName', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', 'DOI:10.1016/j.neuron.2016.12.011' ... % optional
);
nwb
nwb =
NwbFile with properties: - - nwb_version: '2.7.0' - file_create_date: [] - identifier: 'Mouse5_Day3' - session_description: 'mouse in open exploration' - session_start_time: {[2018-04-25T02:30:03.000000+02:00]} - timestamps_reference_time: {[2018-04-25T03:00:45.000000+02:00]} - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [0×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: 'LastName, FirstName' - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [0×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] -

OpticalSeries: Storing series of images as stimuli

OpticalSeries is for time series of images that were presented to the subject as stimuli. We will create an OpticalSeries object with the name "StimulusPresentation" representing what images were shown to the subject and at what times.
Image data can be stored either in the HDF5 file or as an external image file. For this tutorial, we will use fake image data with shape of ('time', 'x', 'y', 'RGB') = (200, 50, 50, 3). As in all TimeSeries, the first dimension is time. The second and third dimensions represent x and y. The fourth dimension represents the RGB value (length of 3) for color images. Please note: As described in the dimensionMapNoDataPipes tutorial, when a MATLAB array is exported to HDF5, the array is transposed. Therefore, in order to correctly export the data, we will need to create a transposed array, where the dimensions are in reverse order compared to the type specification.
NWB differentiates between acquired data and data that was presented as stimulus. We can add it to the NWBFile object as stimulus data.
If the sampling rate is constant, use rate and starting_time to specify time. For irregularly sampled recordings, use timestamps to specify time for each sample image.
image_data = randi(255, [3, 50, 50, 200]); % NB: Array is transposed
optical_series = types.core.OpticalSeries( ...
'distance', 0.7, ... % required
'field_of_view', [0.2, 0.3, 0.7], ... % required
'orientation', 'lower left', ... % required
'data', image_data, ...
'data_unit', 'n.a.', ...
'starting_time_rate', 1.0, ...
'starting_time', 0.0, ...
'description', 'The images presented to the subject as stimuli' ...
);
 
nwb.stimulus_presentation.set('StimulusPresentation', optical_series);

AbstractFeatureSeries: Storing features of visual stimuli

While it is usually recommended to store the entire image data as an OpticalSeries, sometimes it is useful to store features of the visual stimuli instead of or in addition to the raw image data. For example, you may want to store the mean luminance of the image, the contrast, or the spatial frequency. This can be done using an instance of AbstractFeatureSeries. This class is a general container for storing time series of features that are derived from the raw image data.
% Create some fake feature data
feature_data = rand(3, 200); % 200 time points, 3 features
 
% Create an AbstractFeatureSeries object
abstract_feature_series = types.core.AbstractFeatureSeries( ...
'data', feature_data, ...
'timestamps', linspace(0, 1, 200), ...
'description', 'Features of the visual stimuli', ...
'features', {'luminance', 'contrast', 'spatial frequency'}, ...
'feature_units', {'n.a.', 'n.a.', 'cycles/degree'} ...
);
% Add the AbstractFeatureSeries to the NWBFile
nwb.stimulus_presentation.set('StimulusFeatures', abstract_feature_series);

ImageSeries: Storing series of images as acquisition

ImageSeries is a general container for time series of images acquired during the experiment. Image data can be stored either in the HDF5 file or as an external image file. When color images are stored in the HDF5 file the color channel order is expected to be RGB.
image_data = randi(255, [3, 50, 50, 200]);
behavior_images = types.core.ImageSeries( ...
'data', image_data, ...
'description', 'Image data of an animal in environment', ...
'data_unit', 'n.a.', ...
'starting_time_rate', 1.0, ...
'starting_time', 0.0 ...
);
 
nwb.acquisition.set('ImageSeries', behavior_images);

External Files

External files (e.g. video files of the behaving animal) can be added to the NWBFile by creating an ImageSeries object using the external_file attribute that specifies the path to the external file(s) on disk. The file(s) path must be relative to the path of the NWB file. Either external_file or data must be specified, but not both. external_file can be a cell array of multiple video files.
The starting_frame attribute serves as an index to indicate the starting frame of each external file, allowing you to skip the beginning of videos.
external_files = {'video1.pmp4', 'video2.pmp4'};
 
timestamps = [0.0, 0.04, 0.07, 0.1, 0.14, 0.16, 0.21];
behavior_external_file = types.core.ImageSeries( ...
'description', 'Behavior video of animal moving in environment', ...
'data_unit', 'n.a.', ...
'external_file', external_files, ...
'format', 'external', ...
'external_file_starting_frame', [0, 2, 4], ...
'timestamps', timestamps ...
);
 
nwb.acquisition.set('ExternalVideos', behavior_external_file);

Static Images

Static images can be stored in an NWBFile object by creating an RGBAImage, RGBImage or GrayscaleImage object with the image data. All of these image types provide an optional description parameter to include text description about the image and the resolution parameter to specify the pixels/cm resolution of the image.

RGBAImage: for color images with transparency

RGBAImage is for storing data of color image with transparency. data must be 3D where the first and second dimensions represent x and y. The third dimension has length 4 and represents the RGBA value.
image_data = randi(255, [4, 200, 200]);
 
rgba_image = types.core.RGBAImage( ...
'data', image_data, ... % required
'resolution', 70.0, ...
'description', 'RGBA image' ...
);

RGBImage: for color images

RGBImage is for storing data of RGB color image. data must be 3D where the first and second dimensions represent x and y. The third dimension has length 3 and represents the RGB value.
image_data = randi(255, [3, 200, 200]);
 
rgb_image = types.core.RGBImage( ...
'data', image_data, ... % required
'resolution', 70.0, ...
'description', 'RGB image' ...
);

GrayscaleImage: for grayscale images

GrayscaleImage is for storing grayscale image data. data must be 2D where the first and second dimensions represent x and y.
image_data = randi(255, [200, 200]);
 
grayscale_image = types.core.GrayscaleImage( ...
'data', image_data, ... % required
'resolution', 70.0, ...
'description', 'Grayscale image' ...
);

Images: a container for images

Add the images to an Images container that accepts any of these image types.
image_collection = types.core.Images( ...
'description', 'A collection of logo images presented to the subject.'...
);
 
image_collection.image.set('rgba_image', rgba_image);
image_collection.image.set('rgb_image', rgb_image);
image_collection.image.set('grayscale_image', grayscale_image);
 
nwb.acquisition.set('image_collection', image_collection);

Index Series for Repeated Images

You may want to set up a time series of images where some images are repeated many times. You could create an ImageSeries that repeats the data each time the image is shown, but that would be inefficient, because it would store the same data multiple times. A better solution would be to store the unique images once and reference those images. This is how IndexSeries works. First, create an Images container with the order of images defined using an ImageReferences. Then create an IndexSeries that indexes into the Images.
rgbImage = imread('street2.jpg');
grayImage = uint8(sum(double(rgbImage), 3) ./ double(max(max(max(rgbImage)))));
GsStreet = types.core.GrayscaleImage(...
'data', grayImage, ...
'description', 'grayscale image of a street.', ...
'resolution', 28 ...
);
 
RgbStreet = types.core.RGBImage( ...
'data', rgbImage, ...
'resolution', 28, ...
'description', 'RGB Street' ...
);
 
ImageOrder = types.core.ImageReferences(...
'data', [types.untyped.ObjectView(RgbStreet), types.untyped.ObjectView(GsStreet)] ...
);
Images = types.core.Images( ...
'gs_face', GsStreet, ...
'rgb_face', RgbStreet, ...
'description', 'A collection of streets.', ...
'order_of_images', ImageOrder ...
);
 
types.core.IndexSeries(...
'data', [0, 1, 0, 1], ... % NOTE: 0-indexed
'indexed_images', Images, ...
'timestamps', [0.1, 0.2, 0.3, 0.4] ...
)
ans =
IndexSeries with properties: - - indexed_images: [1×1 types.core.Images] - indexed_timeseries: [] - starting_time_unit: 'seconds' - timestamps_interval: 1 - timestamps_unit: 'seconds' - data: [0 1 0 1] - comments: 'no comments' - control: [] - control_description: '' - data_continuity: '' - data_conversion: [] - data_offset: [] - data_resolution: [] - data_unit: 'N/A' - description: 'no description' - starting_time: [] - starting_time_rate: [] - timestamps: [0.1000 0.2000 0.3000 0.4000] -
Here data contains the (0-indexed) index of the displayed image as they are ordered in the ImageReference.

Writing the images to an NWB File

Now use nwbExport to write the file.
nwbExport(nwb, "images_test.nwb");
-
- -
\ No newline at end of file diff --git a/tutorials/html/intro.html b/tutorials/html/intro.html deleted file mode 100644 index b73348b8..00000000 --- a/tutorials/html/intro.html +++ /dev/null @@ -1,511 +0,0 @@ - -Introduction to MatNWB

Introduction to MatNWB

Installing MatNWB

Use the code below within the brackets to install MatNWB from source. MatNWB works by automatically creating API classes based on the schema.
%{
!git clone https://github.com/NeurodataWithoutBorders/matnwb.git
addpath(genpath(pwd));
%}

Set up the NWB File

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata using the NwbFile command. For all MatNWB classes and functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value. Ellipses are used for clarity.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'Last, First', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: - - nwb_version: '2.7.0' - file_create_date: [] - identifier: 'Mouse5_Day3' - session_description: 'mouse in open exploration' - session_start_time: {[2018-04-25T02:30:03.000000+02:00]} - timestamps_reference_time: [] - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [0×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: 'Last, First' - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: {'DOI:10.1016/j.neuron.2016.12.011'} - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [0×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] - -Warning: The following required properties are missing for instance for type "NwbFile": - timestamps_reference_time

Subject Information

You can also provide information about your subject in the NWB file. Create a Subject object to store information such as age, species, genotype, sex, and a freeform description. Then set nwb.general_subject to the Subject object.
Each of these fields is free-form, so any values will be valid, but here are our recommendations:
  • For age, we recommend using the ISO 8601 Duration format
  • For species, we recommend using the formal latin binomal name (e.g. mouse -> Mus musculus, human -> Homo sapiens)
  • For sex, we recommend using F (female), M (male), U (unknown), and O (other)
subject = types.core.Subject( ...
'subject_id', '001', ...
'age', 'P90D', ...
'description', 'mouse 5', ...
'species', 'Mus musculus', ...
'sex', 'M' ...
);
nwb.general_subject = subject;
 
subject
subject =
Subject with properties: - - age: 'P90D' - age_reference: 'birth' - date_of_birth: [] - description: 'mouse 5' - genotype: '' - sex: 'M' - species: 'Mus musculus' - strain: '' - subject_id: '001' - weight: '' -
Note: the DANDI archive requires all NWB files to have a subject object with subject_id specified, and strongly encourages specifying the other fields.

Time Series Data

TimeSeries is a common base class for measurements sampled over time, and provides fields for data and timestamps (regularly or irregularly sampled). You will also need to supply the name and unit of measurement (SI unit).
For instance, we can store a TimeSeries data where recording started 0.0 seconds after start_time and sampled every second (1 Hz):
time_series_with_rate = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0);
For irregularly sampled recordings, we need to provide the timestamps for the data:
time_series_with_timestamps = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'timestamps', linspace(0, 1, 10));
The TimeSeries class serves as the foundation for all other time series types in the NWB format. Several specialized subclasses extend the functionality of TimeSeries, each tailored to handle specific kinds of data. In the next section, we’ll explore one of these specialized types. For a full overview, please check out the type hierarchy in the NWB schema documentation.

Other Types of Time Series

As mentioned previously, there are many subtypes of TimeSeries in MatNWB that are used to store different kinds of data. One example is AnnotationSeries, a subclass of TimeSeries that stores text-based records about the experiment. Similar to our TimeSeries example above, we can create an AnnotationSeries object with text information about a stimulus and add it to the stimulus_presentation group in the NWBFile. Below is an example where we create an AnnotationSeries object with annotations for airpuff stimuli and add it to the NWBFile.
% Create an AnnotationSeries object with annotations for airpuff stimuli
annotations = types.core.AnnotationSeries( ...
'description', 'Airpuff events delivered to the animal', ...
'data', {'Left Airpuff', 'Right Airpuff', 'Right Airpuff'}, ...
'timestamps', [1.0, 3.0, 8.0] ...
);
 
% Add the AnnotationSeries to the NWBFile's stimulus group
nwb.stimulus_presentation.set('Airpuffs', annotations)
ans =
Set with properties: - - Airpuffs: [types.core.AnnotationSeries] -

Behavior

SpatialSeries and Position

Many types of data have special data types in NWB. To store the spatial position of a subject, we will use the SpatialSeries and Position classes.
Note: These diagrams follow a standard convention called "UML class diagram" to express the object-oriented relationships between NWB classes. For our purposes, all you need to know is that an open triangle means "extends" (i.e., is a specialized subtype of), and an open diamond means "is contained within." Learn more about class diagrams on the wikipedia page.
SpatialSeries is a subclass of TimeSeries, a common base class for measurements sampled over time, and provides fields for data and time (regularly or irregularly sampled). Here, we put a SpatialSeries object called 'SpatialSeries' in a Position object. If the data is sampled at a regular interval, it is recommended to specify the starting_time and the sampling rate (starting_time_rate), although it is still possible to specify timestamps as in the time_series_with_timestamps example above.
% create SpatialSeries object
spatial_series_ts = types.core.SpatialSeries( ...
'data', [linspace(0,10,100); linspace(0,8,100)], ...
'reference_frame', '(0,0) is bottom left corner', ...
'starting_time', 0, ...
'starting_time_rate', 200 ...
);
 
% create Position object and add SpatialSeries
position = types.core.Position('SpatialSeries', spatial_series_ts);
NWB differentiates between raw, acquired data, which should never change, and processed data, which are the results of preprocessing algorithms and could change. Let's assume that the animal's position was computed from a video tracking algorithm, so it would be classified as processed data. Since processed data can be very diverse, NWB allows us to create processing modules, which are like folders, to store related processed data or data that comes from a single algorithm.
Create a processing module called "behavior" for storing behavioral data in the NWBFile and add the Position object to the module.
% create processing module
behavior_module = types.core.ProcessingModule('description', 'contains behavioral data');
 
% add the Position object (that holds the SpatialSeries object) to the module
% and name the Position object "Position"
behavior_module.nwbdatainterface.set('Position', position);
 
% add the processing module to the NWBFile object, and name the processing module "behavior"
nwb.processing.set('behavior', behavior_module);

Trials

Trials are stored in a TimeIntervals object which is a subclass of DynamicTable. DynamicTable objects are used to store tabular metadata throughout NWB, including for trials, electrodes, and sorted units. They offer flexibility for tabular data by allowing required columns, optional columns, and custom columns.
The trials DynamicTable can be thought of as a table with this structure:
Trials are stored in a TimeIntervals object which subclasses DynamicTable. Here, we are adding 'correct', which will be a logical array.
trials = types.core.TimeIntervals( ...
'colnames', {'start_time', 'stop_time', 'correct'}, ...
'description', 'trial data and properties');
 
trials.addRow('start_time', 0.1, 'stop_time', 1.0, 'correct', false)
trials.addRow('start_time', 1.5, 'stop_time', 2.0, 'correct', true)
trials.addRow('start_time', 2.5, 'stop_time', 3.0, 'correct', false)
 
trials.toTable() % visualize the table
ans = 3×4 table
 idstart_timestop_timecorrect
100.100010
211.500021
322.500030
nwb.intervals_trials = trials;
 
% If you have multiple trials tables, you will need to use custom names for
% each one:
nwb.intervals.set('custom_intervals_table_name', trials);

Write

Now, to write the NWB file that we have built so far:
nwbExport(nwb, 'intro_tutorial.nwb')
We can use the HDFView application to inspect the resulting NWB file.

Read

We can then read the file back in using MatNWB and inspect its contents.
read_nwbfile = nwbRead('intro_tutorial.nwb', 'ignorecache')
read_nwbfile =
NwbFile with properties: - - nwb_version: '2.7.0' - file_create_date: [1×1 types.untyped.DataStub] - identifier: 'Mouse5_Day3' - session_description: 'mouse in open exploration' - session_start_time: [1×1 types.untyped.DataStub] - timestamps_reference_time: [1×1 types.untyped.DataStub] - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [0×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: [1×1 types.untyped.DataStub] - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: [1×1 types.untyped.DataStub] - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [1×1 types.core.Subject] - general_surgery: '' - general_virus: '' - intervals: [1×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [1×1 types.core.TimeIntervals] - processing: [1×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [1×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] -
We can print the SpatialSeries data traversing the hierarchy of objects. The processing module called 'behavior' contains our Position object named 'Position'. The Position object contains our SpatialSeries object named 'SpatialSeries'.
read_spatial_series = read_nwbfile.processing.get('behavior'). ...
nwbdatainterface.get('Position').spatialseries.get('SpatialSeries')
read_spatial_series =
SpatialSeries with properties: - - reference_frame: '(0,0) is bottom left corner' - starting_time_unit: 'seconds' - timestamps_interval: 1 - timestamps_unit: 'seconds' - data: [1×1 types.untyped.DataStub] - comments: 'no comments' - control: [] - control_description: '' - data_continuity: '' - data_conversion: 1 - data_offset: 0 - data_resolution: -1 - data_unit: 'meters' - description: 'no description' - starting_time: 0 - starting_time_rate: 200 - timestamps: [] -

Reading Data

Counter to normal MATLAB workflow, data arrays are read passively from the file. Calling read_spatial_series.data does not read the data values, but presents a DataStub object that can be indexed to read data.
read_spatial_series.data
ans =
DataStub with properties: - - filename: 'intro_tutorial.nwb' - path: '/processing/behavior/Position/SpatialSeries/data' - dims: [2 100] - ndims: 2 - dataType: 'double' -
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access all the data in the matrix using the load method with no arguments.
read_spatial_series.data.load
ans = 2×100
0 0.1010 0.2020 0.3030 0.4040 0.5051 0.6061 0.7071 0.8081 0.9091 1.0101 1.1111 1.2121 1.3131 1.4141 1.5152 1.6162 1.7172 1.8182 1.9192 2.0202 2.1212 2.2222 2.3232 2.4242 2.5253 2.6263 2.7273 2.8283 2.9293 3.0303 3.1313 3.2323 3.3333 3.4343 3.5354 3.6364 3.7374 3.8384 3.9394 4.0404 4.1414 4.2424 4.3434 4.4444 4.5455 4.6465 4.7475 4.8485 4.9495 - 0 0.0808 0.1616 0.2424 0.3232 0.4040 0.4848 0.5657 0.6465 0.7273 0.8081 0.8889 0.9697 1.0505 1.1313 1.2121 1.2929 1.3737 1.4545 1.5354 1.6162 1.6970 1.7778 1.8586 1.9394 2.0202 2.1010 2.1818 2.2626 2.3434 2.4242 2.5051 2.5859 2.6667 2.7475 2.8283 2.9091 2.9899 3.0707 3.1515 3.2323 3.3131 3.3939 3.4747 3.5556 3.6364 3.7172 3.7980 3.8788 3.9596 -
If you only need a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_spatial_series.data(:, 1:10)
ans = 2×10
0 0.1010 0.2020 0.3030 0.4040 0.5051 0.6061 0.7071 0.8081 0.9091 - 0 0.0808 0.1616 0.2424 0.3232 0.4040 0.4848 0.5657 0.6465 0.7273 -

Next Steps

This concludes the introductory tutorial. Please proceed to one of the specialized tutorials, which are designed to follow this one.
See the API documentation to learn what data types are available.
-
- -
\ No newline at end of file diff --git a/tutorials/html/ogen.html b/tutorials/html/ogen.html deleted file mode 100644 index 4e19478b..00000000 --- a/tutorials/html/ogen.html +++ /dev/null @@ -1,201 +0,0 @@ - -Optogenetics

Optogenetics

This tutorial will demonstrate how to write optogenetics data.

Creating an NWBFile object

When creating a NWB file, the first step is to create the NWBFile object using NwbFile.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', char(java.util.UUID.randomUUID), ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'Last, First M.', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', 'DOI:10.1016/j.neuron.2016.12.011'); % optional
nwb
nwb =
NwbFile with properties: - - nwb_version: '2.6.0' - file_create_date: [] - identifier: 'b843652e-3404-48c7-8686-4904e786ea4c' - session_description: 'mouse in open exploration' - session_start_time: {[2018-04-25T02:30:03.000000+02:00]} - timestamps_reference_time: [] - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [0×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: 'Last, First M.' - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [0×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] -

Adding optogenetic data

The ogen module contains two data types that you will need to write optogenetics data, OptogeneticStimulusSite, which contains metadata about the stimulus site, and OptogeneticSeries, which contains the values of the time series.
First, you need to create a Device object linked to the NWBFile:
device = types.core.Device();
nwb.general_devices.set('Device', device);
Now, you can create and add an OptogeneticStimulusSite.
ogen_stim_site = types.core.OptogeneticStimulusSite( ...
'device', types.untyped.SoftLink(device), ...
'description', 'This is an example optogenetic site.', ...
'excitation_lambda', 600.0, ...
'location', 'VISrl');
 
nwb.general_optogenetics.set('OptogeneticStimulusSite', ogen_stim_site);
With the OptogeneticStimulusSite added, you can now create and add a OptogeneticSeries. Here, we will generate some random data and specify the timing using rate. If you have samples at irregular intervals, you should use timestamps instead.
ogen_series = types.core.OptogeneticSeries( ...
'data', randn(20, 1), ...
'site', types.untyped.SoftLink(ogen_stim_site), ...
'starting_time', 0.0, ...
'starting_time_rate', 30.0); % Hz
nwb.stimulus_presentation.set('OptogeneticSeries', ogen_series);
 
nwb
nwb =
NwbFile with properties: - - nwb_version: '2.6.0' - file_create_date: [] - identifier: 'b843652e-3404-48c7-8686-4904e786ea4c' - session_description: 'mouse in open exploration' - session_start_time: {[2018-04-25T02:30:03.000000+02:00]} - timestamps_reference_time: [] - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [1×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: 'Last, First M.' - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [1×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: 'DOI:10.1016/j.neuron.2016.12.011' - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [1×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] -
Now you can write the NWB file.
nwbExport(nwb, 'ogen_tutorial.nwb');
-
- -
\ No newline at end of file diff --git a/tutorials/html/ophys.html b/tutorials/html/ophys.html deleted file mode 100644 index bbf83ab4..00000000 --- a/tutorials/html/ophys.html +++ /dev/null @@ -1,564 +0,0 @@ - -MatNWB Optical Physiology Tutorial

MatNWB Optical Physiology Tutorial

Introduction

In this tutorial, we will create fake data for a hypothetical optical physiology experiment with a freely moving animal. The types of data we will convert are:
  • Acquired two-photon images
  • Image segmentation (ROIs)
  • Fluorescence and dF/F response
It is recommended to first work through the Introduction to MatNWB tutorial, which demonstrates installing MatNWB and creating an NWB file with subject information, animal position, and trials, as well as writing and reading NWB files in MATLAB.
Please note: The dimensions of timeseries data in MatNWB should be defined in the opposite order of how it is defined in the nwb-schemas. In NWB, time is always stored in the first dimension of the data, whereas in MatNWB data should be specified with time along the last dimension. This is explained in more detail here: MatNWB <-> HDF5 Dimension Mapping.

Set up the NWB file

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata. For all MatNWB functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'LastName, FirstName', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: - - nwb_version: '2.8.0' - file_create_date: [] - identifier: 'Mouse5_Day3' - session_description: 'mouse in open exploration' - session_start_time: {[2018-04-25T02:30:03.000000+02:00]} - timestamps_reference_time: {[2018-04-25T03:00:45.000000+02:00]} - acquisition: [0×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [0×1 types.untyped.Set] - general_experiment_description: '' - general_experimenter: 'LastName, FirstName' - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of My Institution' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [] - general_intracellular_ephys_repetitions: [] - general_intracellular_ephys_sequential_recordings: [] - general_intracellular_ephys_simultaneous_recordings: [] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: '' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: {'DOI:10.1016/j.neuron.2016.12.011'} - general_session_id: 'session_1234' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - general_was_generated_by: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [0×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] -

Optical Physiology

Optical physiology results are written in four steps:
  1. Create imaging plane
  2. Acquired two-photon images
  3. Image segmentation
  4. Fluorescence and dF/F responses

Imaging Plane

First, you must create an ImagingPlane object, which will hold information about the area and method used to collect the optical imaging data. This requires creation of a Device object for the microscope and an OpticalChannel object. Then you can create an ImagingPlane.
Create a Device representing a two-photon microscope. The fields description, manufacturer, model_number, model_name, and serial_number are optional, but recommended. Then create an OpticalChannel and add both of these to the ImagingPlane.
device = types.core.Device( ...
'description', 'My two-photon microscope', ...
'manufacturer', 'Loki Labs', ...
'model_number', 'ABC-123', ...
'model_name', 'Loki 1.0', ...
'serial_number', '1234567890');
 
% Add device to nwb object
nwb.general_devices.set('Device', device);
 
optical_channel = types.core.OpticalChannel( ...
'description', 'description', ...
'emission_lambda', 500.);
 
imaging_plane_name = 'imaging_plane';
imaging_plane = types.core.ImagingPlane( ...
'optical_channel', optical_channel, ...
'description', 'a very interesting part of the brain', ...
'device', types.untyped.SoftLink(device), ...
'excitation_lambda', 600., ...
'imaging_rate', 5., ...
'indicator', 'GFP', ...
'location', 'my favorite brain location');
 
nwb.general_optophysiology.set(imaging_plane_name, imaging_plane);

Storing Two-Photon Data

You can create a TwoPhotonSeries class representing two photon imaging data. TwoPhotonSeries, like SpatialSeries, inherits from TimeSeries and is similar in behavior to OnePhotonSeries.
InternalTwoPhoton = types.core.TwoPhotonSeries( ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0.0, ...
'starting_time_rate', 3.0, ...
'data', ones(200, 100, 1000), ...
'data_unit', 'lumens');
 
nwb.acquisition.set('2pInternal', InternalTwoPhoton);

Storing One-Photon Data

Now that we have our ImagingPlane, we can create a OnePhotonSeries object to store raw one-photon imaging data.
% using internal data. this data will be stored inside the NWB file
InternalOnePhoton = types.core.OnePhotonSeries( ...
'data', ones(100, 100, 1000), ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0., ...
'starting_time_rate', 1.0, ...
'data_unit', 'normalized amplitude' ...
);
nwb.acquisition.set('1pInternal', InternalOnePhoton);

Motion Correction (optional)

You can also store the result of motion correction using a MotionCorrection object, a container type that can hold one or more CorrectedImageStack objects.
% Create the corrected ImageSeries
corrected = types.core.ImageSeries( ...
'description', 'A motion corrected image stack', ...
'data', ones(100, 100, 1000), ... % 3D data array
'data_unit', 'n/a', ...
'format', 'raw', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);
 
% Create the xy_translation TimeSeries
xy_translation = types.core.TimeSeries( ...
'description', 'x,y translation in pixels', ...
'data', ones(2, 1000), ... % 2D data array
'data_unit', 'pixels', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);
 
% Create the CorrectedImageStack
corrected_image_stack = types.core.CorrectedImageStack( ...
'corrected', corrected, ...
'original', types.untyped.SoftLink(InternalOnePhoton), ... % Ensure `InternalOnePhoton` exists
'xy_translation', xy_translation ...
);
 
% Create the MotionCorrection object
motion_correction = types.core.MotionCorrection();
motion_correction.correctedimagestack.set('CorrectedImageStack', corrected_image_stack);
The motion corrected data is considered processed data and will be added to the processing field of the nwb object using a ProcessingModule called "ophys". First, create the ProcessingModule object and then add the motion_correction object to it, naming it "MotionCorrection".
ophys_module = types.core.ProcessingModule( ...
'description', 'Contains optical physiology data');
ophys_module.nwbdatainterface.set('MotionCorrection', motion_correction);
Finally, add the "ophys" ProcessingModule to the nwb (Note that we can continue adding objects to the "ophys" ProcessingModule without needing to explicitly update the nwb):
nwb.processing.set('ophys', ophys_module);

Plane Segmentation

Image segmentation stores the detected regions of interest in the TwoPhotonSeries data. ImageSegmentation allows you to have more than one segmentation by creating more PlaneSegmentation objects.

Regions of interest (ROIs)

ROIs can be added to a PlaneSegmentation either as an image_mask or as a pixel_mask. An image mask is an array that is the same size as a single frame of the TwoPhotonSeries, and indicates where a single region of interest is. This image mask may be boolean or continuous between 0 and 1. A pixel_mask, on the other hand, is a list of indices (i.e coordinates) and weights for the ROI. The pixel_mask is represented as a compound data type using a ragged array and below is an example demonstrating how to create either an image_mask or a pixel_mask. Changing the dropdown selection will update the PlaneSegmentation object accordingly.
selection = "Create Image Mask"; % "Create Image Mask" or "Create Pixel Mask"
 
% 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);
center = randi(90,2,n_rois);
for i = 1:n_rois
image_mask(center(1,i):center(1,i)+10, center(2,i):center(2,i)+10, i) = 1;
end
 
if selection == "Create Pixel Mask"
ind = find(image_mask);
[y_ind, x_ind, roi_ind] = ind2sub(size(image_mask), ind);
 
pixel_mask_struct = struct();
pixel_mask_struct.x = uint32(x_ind); % Add x coordinates to struct field x
pixel_mask_struct.y = uint32(y_ind); % Add y coordinates to struct field y
pixel_mask_struct.weight = single(ones(size(x_ind)));
% Create pixel mask vector data
pixel_mask = types.hdmf_common.VectorData(...
'data', struct2table(pixel_mask_struct), ...
'description', 'pixel masks');
 
% When creating a pixel mask, it is also necessary to specify a
% pixel_mask_index vector. See the documentation for ragged arrays linked
% above to learn more.
num_pixels_per_roi = zeros(n_rois, 1); % Column vector
for i_roi = 1:n_rois
num_pixels_per_roi(i_roi) = sum(roi_ind == i_roi);
end
 
pixel_mask_index = uint16(cumsum(num_pixels_per_roi)); % Note: Use an integer
% type that can accommodate the maximum value of the cumulative sum
 
% Create pixel_mask_index vector
pixel_mask_index = types.hdmf_common.VectorIndex(...
'description', 'Index into pixel_mask VectorData', ...
'data', pixel_mask_index, ...
'target', types.untyped.ObjectView(pixel_mask) );
 
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'pixel_mask'}, ...
'description', 'roi pixel position (x,y) and pixel weight', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'pixel_mask_index', pixel_mask_index, ...
'pixel_mask', pixel_mask ...
);
 
else % selection == "Create Image Mask"
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'image_mask'}, ...
'description', 'output from segmenting my favorite imaging plane', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'image_mask', types.hdmf_common.VectorData(...
'data', image_mask, ...
'description', 'image masks') ...
);
end

Adding ROIs to NWB file

Now create an ImageSegmentation object and put the plane_segmentation object inside of it, naming it "PlaneSegmentation".
img_seg = types.core.ImageSegmentation();
img_seg.planesegmentation.set('PlaneSegmentation', plane_segmentation);
Add the img_seg object to the "ophys" ProcessingModule we created before, naming it "ImageSegmentation".
ophys_module.nwbdatainterface.set('ImageSegmentation', img_seg);

Storing fluorescence of ROIs over time

Now that ROIs are stored, you can store fluorescence data for these regions of interest. This type of data is stored using the RoiResponseSeries class.
To create a RoiResponseSeries object, we will need to reference a set of rows from the PlaneSegmentation table to indicate which ROIs correspond to which rows of your recorded data matrix. This is done using a DynamicTableRegion, which is a type of link that allows you to reference specific rows of a DynamicTable, such as a PlaneSegmentation table by row indices.
First, we create a DynamicTableRegion that references the ROIs of the PlaneSegmentation table.
roi_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(plane_segmentation), ...
'description', 'all_rois', ...
'data', (0:n_rois-1)');
Then we create a RoiResponseSeries object to store fluorescence data for those ROIs.
roi_response_series = types.core.RoiResponseSeries( ...
'rois', roi_table_region, ...
'data', NaN(n_rois, 100), ... % [nRoi, nT]
'data_unit', 'lumens', ...
'starting_time_rate', 3.0, ...
'starting_time', 0.0);
To help data analysis and visualization tools know that this RoiResponseSeries object represents fluorescence data, we will store the RoiResponseSeries object inside of a Fluorescence object. Then we add the Fluorescence object into the same ProcessingModule named "ophys" that we created earlier.
fluorescence = types.core.Fluorescence();
fluorescence.roiresponseseries.set('RoiResponseSeries', roi_response_series);
 
ophys_module.nwbdatainterface.set('Fluorescence', fluorescence);
Tip: If you want to store dF/F data instead of fluorescence data, then store the RoiResponseSeries object in a DfOverF object, which works the same way as the Fluorescence class.

Writing the NWB file

nwb_file_name = 'ophys_tutorial.nwb';
if isfile(nwb_file_name); delete(nwb_file_name); end
nwbExport(nwb, nwb_file_name);
Warning: The property "grid_spacing_unit" of type "types.core.ImagingPlane" was not exported to file location "/general/optophysiology/imaging_plane" because it depends on the property "grid_spacing" which is unset.
Warning: The property "origin_coords_unit" of type "types.core.ImagingPlane" was not exported to file location "/general/optophysiology/imaging_plane" because it depends on the property "origin_coords" which is unset.

Reading the NWB file

read_nwb = nwbRead(nwb_file_name, 'ignorecache');
Data arrays are read passively from the file. Calling TimeSeries.data does not read the data values, but presents an HDF5 object that can be indexed to read data.
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence')...
.roiresponseseries.get('RoiResponseSeries').data
ans =
DataStub with properties: - - filename: 'ophys_tutorial.nwb' - path: '/processing/ophys/Fluorescence/RoiResponseSeries/data' - dims: [20 100] - ndims: 2 - dataType: 'double' -
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access the data in the matrix using the load method.
load with no input arguments reads the entire dataset:
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries').data.load
ans = 20×100
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN -
If all you need is a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries'). ...
data(1:5, 1:10)
ans = 5×10
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN - NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN -
% read back the image/pixel masks and display the first roi
plane_segmentation = read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('ImageSegmentation'). ...
planesegmentation.get('PlaneSegmentation');
 
if ~isempty(plane_segmentation.image_mask)
roi_mask = plane_segmentation.image_mask.data(:,:,1);
elseif ~isempty(plane_segmentation.pixel_mask)
row = plane_segmentation.getRow(1, 'columns', {'pixel_mask'});
pixel_mask = row.pixel_mask{1};
roi_mask = zeros(imaging_shape);
ind = sub2ind(imaging_shape, pixel_mask.y, pixel_mask.x);
roi_mask(ind) = pixel_mask.weight;
end
imshow(roi_mask)

Learn more!

See the API documentation to learn what data types are available.

Other MatNWB tutorials

Python tutorials

See our tutorials for more details about your data type:
Check out other tutorials that teach advanced NWB topics:

-
- -
\ No newline at end of file diff --git a/tutorials/html/ophys_tutorial_schematic.png b/tutorials/html/ophys_tutorial_schematic.png deleted file mode 100644 index 7e8a94e4f2fac07ac11577c339e2b90afba900e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77678 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 z 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

({u2Vdx-ORb7k! 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 C=7_xj diff --git a/tutorials/html/read_demo.html b/tutorials/html/read_demo.html deleted file mode 100644 index 46c9dafb..00000000 --- a/tutorials/html/read_demo.html +++ /dev/null @@ -1,356 +0,0 @@ - -Reading NWB Data in MATLAB

Reading NWB Data in MATLAB

Introduction

Download the data

First, let's download an NWB data file from the DANDI neurophysiology data archive.
A NWB file represents a single session of an experiment. It contains all the data of that session and the metadata required to understand the data.
We will use data from one session of an experiment by Chandravadia et al. (2020), where the authors recorded single neuron electrophysiological activity from the medial temporal lobes of human subjects while they performed a visual recognition memory task.
  1. Go to the DANDI page for this dataset: https://dandiarchive.org/dandiset/000004/draft
  2. Toward the top middle of the page, click the "Files" button.
demo_dandi_view_files_in_dataset.png
3. Click on the folder "sub-P11MHM" (click the folder name, not the checkbox).
demo_dandi_select_folder.png
4. Then click on the download symbol to the right of the filename "sub-P11HMH_ses-20061101_ecephys+image.nwb" to download the data file (69 MB) to your computer.
demo_dandi_download_data.png

Installing matnwb

Use the code below to install MatNWB from source using git. Ensure git is on your path before running this line.
!git clone https://github.com/NeurodataWithoutBorders/matnwb.git
Cloning into 'matnwb'... -Updating files: 95% (520/542) -Updating files: 96% (521/542) -Updating files: 97% (526/542) -Updating files: 98% (532/542) -Updating files: 99% (537/542) -Updating files: 100% (542/542) -Updating files: 100% (542/542), done.
MatNWB works by automatically creating API classes based on the schema. For most NWB files, the classes are generated automatically by calling nwbRead farther down. This particular NWB file was created before this feature was supported, so we must ensure that these classes for the correct schema versions are properly generated before attempting to read from the file.
% add the path to matnwb and generate the core classes
addpath('matnwb');
 
% Reminder: YOU DO NOT NORMALLY NEED TO CALL THIS FUNCTION. Only attempt this method if you
% encounter read errors.
generateCore(util.getSchemaVersion('sub-P11HMH_ses-20061101_ecephys+image.nwb'));

Read the NWB file

You can read any NWB file using nwbRead. You will find that the print out for this shows a summary of the data within.
% ignorecache informs the `nwbRead` call to not generate files by default. Since we have already
% done this, we can skip this automated step when reading. If you are reading the file before
% generating, you can omit this argument flag.
nwb = nwbRead('sub-P11HMH_ses-20061101_ecephys+image.nwb', 'ignorecache')
nwb =
NwbFile with properties: - - nwb_version: '2.1.0' - file_create_date: [1×1 types.untyped.DataStub] - general_devices: [1×1 types.untyped.Set] - identifier: 'H11_9' - session_description: 'New/Old recognition task for ID: 9. ' - session_start_time: 2006-11-01 - timestamps_reference_time: 2006-11-01 - acquisition: [2×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: 'learning: 80, recognition: 81' - general_experiment_description: 'The data contained within this file describes a new/old recogntion task performed in patients with intractable epilepsy implanted with depth electrodes and Behnke-Fried microwires in the human Medical Temporal Lobe (MTL).' - general_experimenter: '' - general_extracellular_ephys: [9×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [1×1 types.core.DynamicTable] - general_institution: 'Hunigton Memorial Hospital' - general_intracellular_ephys: [0×1 types.untyped.Set] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_sweep_table: [] - general_keywords: [1×1 types.untyped.DataStub] - general_lab: 'Rutishauser' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: [1×1 types.untyped.DataStub] - general_session_id: '' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [1×1 types.core.Subject] - general_surgery: '' - general_virus: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [1×1 types.core.TimeIntervals] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [1×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [1×1 types.core.Units] -
You can also use util.nwbTree to actively explore the NWB file.
util.nwbTree(nwb);

Stimulus

Now lets take a look at the visual stimuli presented to the subject. They will be in nwb.stimulus_presentation
nwb.stimulus_presentation
ans =
Set with properties: - - StimulusPresentation: [types.core.OpticalSeries] -
This results shows us that nwb.stimulus_presentation is a Set object that contains a single data object called StimulusPresentation, which is an OpticalSeries neurodata type. Use the get method to return this OpticalSeries. Set objects store a collection of other NWB objects.
nwb.stimulus_presentation.get('StimulusPresentation')
ans =
OpticalSeries with properties: - - distance: 0.7000 - field_of_view: [1×1 types.untyped.DataStub] - orientation: 'lower left' - dimension: [1×1 types.untyped.DataStub] - external_file: '' - external_file_starting_frame: [] - format: 'raw' - starting_time_unit: 'seconds' - timestamps_interval: 1 - timestamps_unit: 'seconds' - data: [1×1 types.untyped.DataStub] - comments: 'no comments' - control: [] - control_description: '' - data_conversion: 1 - data_resolution: -1 - data_unit: 'meters' - description: 'no description' - starting_time: [] - starting_time_rate: [] - timestamps: [1×1 types.untyped.DataStub] -
OpticalSeries is a neurodata type that stores information about visual stimuli presented to subjects. This print out shows all of the attributes in the OpticalSeries object named StimulusPresentation. The images are stored in StimulusPresentation.data
StimulusImageData = nwb.stimulus_presentation.get('StimulusPresentation').data
StimulusImageData =
DataStub with properties: - - filename: 'sub-P11HMH_ses-20061101_ecephys+image.nwb' - path: '/stimulus/presentation/StimulusPresentation/data' - dims: [3 300 400 200] - ndims: 4 - dataType: 'uint8' -
When calling a data object directly, the data is not read but instead a DataStub is returned. This is because data is read "lazily" in MatNWB. Instead of reading the entire dataset into memory, this provides a "window" into the data stored on disk that allows you to read only a section of the data. In this case, the last dimension indexes over images. You can index into any DataStub as you would any MATLAB matrix.
% get the image and display it
% the dimension order is provided as follows:
% [rgb, y, x, image index]
img = StimulusImageData(1:3, 1:300, 1:400, 32);
A bit of manipulation allows us to display the image using MATLAB's imshow.
img = permute(img,[3, 2, 1]); % fix orientation
img = flip(img, 3); % reverse color order
F = figure();
imshow(img, 'InitialMagnification', 'fit');
daspect([3, 5, 5]);
To read an entire dataset, use the DataStub.load method without any input arguments. We will use this approach to read all of the image display timestamps into memory.
stimulus_times = nwb.stimulus_presentation.get('StimulusPresentation').timestamps.load();

Quick PSTH and raster

Here, I will pull out spike times of a particular unit, align them to the image display times, and finally display the results.
First, let us show the first row of the NWB Units table representing the first unit.
nwb.units.getRow(1)
ans = 1×8 table
 origClusterIDwaveform_mean_encodingwaveform_mean_recognitionIsolationDistSNRwaveform_mean_sampling_ratespike_timeselectrodes
11102256×1 double256×1 double11.29171.440798400373×1 double0
Let us specify some parameters for creating a cell array of spike times aligned to each stimulus time.
%% Align spikes by stimulus presentations
 
unit_ind =8;
before =1;
after =3;
getRow provides a convenient method for reading this data out.
unit_spikes = nwb.units.getRow(unit_ind, 'columns', {'spike_times'}).spike_times{1}
unit_spikes = 2116×1
103 ×
5.9338 - 5.9343 - 5.9346 - 5.9358 - 5.9364 - 5.9375 - 6.0772 - 6.0776 - 6.0797 - 6.0798 -
Spike times from this unit are aligned to each stimulus time and compiled in a cell array
results = cell(1, length(stimulus_times));
for itime = 1:length(stimulus_times)
stimulus_time = stimulus_times(itime);
spikes = unit_spikes - stimulus_time;
spikes = spikes(spikes > -before);
spikes = spikes(spikes < after);
results{itime} = spikes;
end

Plot results

Finally, here is a (slightly sloppy) peri-stimulus time histogram
figure();
hold on
for i = 1:length(results)
spikes = results{i};
yy = ones(length(spikes)) * i;
 
plot(spikes, yy, 'k.');
end
hold off
ylabel('trial');
xlabel('time (s)');
axis('tight')
figure();
all_spikes = cat(1, results{:});
histogram(all_spikes, 30);
ylabel('count')
xlabel('time (s)');
axis('tight')

Conclusion

This is an example of how to get started with understanding and analyzing public NWB datasets. This particular dataset was published with an extensive open analysis conducted in both MATLAB and Python, which you can find here. For more datasets, or to publish your own NWB data for free, check out the DANDI archive here. Also, make sure to check out the DANDI breakout session later in this event.
-
- -
\ No newline at end of file diff --git a/tutorials/html/remote_read.html b/tutorials/html/remote_read.html deleted file mode 100644 index 9746120e..00000000 --- a/tutorials/html/remote_read.html +++ /dev/null @@ -1,58 +0,0 @@ - -Remote read of NWB files

Remote read of NWB files

It is possible to read an NWB file (or any HDF5 file) in MATLAB directly from several different kinds of remote locations, including AWS, Azure Blob Storage and HDFS. This tutorial will walk you through specifically loading a MATLAB file from AWS S3, which is the storage used by the DANDI archive. See MATLAB documentation for more general information.
To read an NWB file file from an s3 store, first you need to figure out the s3 path of that resource. The easiest way to do this is to use the DANDI web client.
  • (skip if on DANDI Hub) Make sure you do not have a file ~/.aws/credentials. If you do, rename it to something else. On Windows this file would be somewhere like C:/Users/username/.aws/credentials.
  • Find and select a dandiset you want on the DANDI Archive, then click
  • Navigate to the NWB file of interest and click
  • Find the second entry of "contentURL"
  • In your MATLAB session, take the end of that url (the blob id) and add it to this expression: s3 = 's3://dandiarchive/blobs/<blob_id>'. In this case, you would have:
s3 = 's3://dandiarchive/blobs/7ee/415/7ee41580-9b0b-44ca-8675-6959ddd8dc33';
  • Read from that s3 path directly with:
nwbfile = nwbRead(s3);
That's it! MATLAB will automatically detect that this is an S3 path instead of a local filepath and will set up a remote read interface for that NWB file. This appoach works on any computer with a fairly recent version of MATLAB and an internet connection. It works particularly well on the DANDI Hub, which has a very fast connection to the DANDI S3 store and which provides a MATLAB environment for free provided you have a license.

Note: MATLAB vs. Python remote read

Python also allows you to remotely read a file, and has several advantages over MATLAB. Reading in Python is faster. On DANDI Hub, for MATLAB, reading the file takes about 51 seconds, while the analogous operation takes less than a second in Python. Python also allows you to create a local cache so you are not repeatedly requesting the same data, which can further speed up data access. Overall, we recommend remote reading using Python instead of MATLAB.
-
- -
\ No newline at end of file diff --git a/tutorials/html/scratch.html b/tutorials/html/scratch.html deleted file mode 100644 index fe467263..00000000 --- a/tutorials/html/scratch.html +++ /dev/null @@ -1,164 +0,0 @@ - -Scratch Data

Scratch Data

This tutorial will focus on the basics of working with a NWBFile for storing non-standardizable data. For example, you may want to store results from one-off analyses of some temporary utility. NWB provides in-file scratch space as a dedicated location where miscellaneous non-standard data may be written.

Setup

Let us first set up an environment with some "acquired data".
ContextFile = NwbFile(...
'session_description', 'demonstrate NWBFile scratch', ... % required
'identifier', 'SCRATCH-0', ... % required
'session_start_time', datetime(2019, 4, 3, 11, 0, 0, 'TimeZone', 'local'), ... % required
'file_create_date', datetime(2019, 4, 15, 12, 0, 0, 'TimeZone', 'local'), ... % optional
'general_experimenter', 'Niu, Lawrence', ...
'general_institution', 'NWB' ...
);
% simulate some data
timestamps = 0:100:1024;
data = sin(0.333 .* timestamps) ...
+ cos(0.1 .* timestamps) ...
+ randn(1, length(timestamps));
RawTs = types.core.TimeSeries(...
'data', data, ...
'data_unit', 'm', ...
'starting_time', 0., ...
'starting_time_rate', 100, ...
'description', 'simulated acquired data' ...
);
ContextFile.acquisition.set('raw_timeseries', RawTs);
 
% "analyze" the simulated data
% we provide a re-implementation of scipy.signal.correlate(..., mode='same')
% Ideally, you should use MATLAB-native code though using its equivalent function (xcorr) requires
% the Signal Processing Toolbox
correlatedData = sameCorr(RawTs.data, ones(128, 1)) ./ 128;
% If you are unsure of how HDF5 paths map to MatNWB property structures, we suggest using HDFView to
% verify. In most cases, MatNWB properties map directly to HDF5 paths.
FilteredTs = types.core.TimeSeries( ...
'data', correlatedData, ...
'data_unit', 'm', ...
'starting_time', 0, ...
'starting_time_rate', 100, ...
'description', 'cross-correlated data' ...
)
FilteredTs =
TimeSeries with properties: - - starting_time_unit: 'seconds' - timestamps_interval: 1 - timestamps_unit: 'seconds' - data: [0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461 0.0461] - comments: 'no comments' - control: [] - control_description: '' - data_continuity: '' - data_conversion: 1 - data_offset: 0 - data_resolution: -1 - data_unit: 'm' - description: 'cross-correlated data' - starting_time: 0 - starting_time_rate: 100 - timestamps: [] -
ProcModule = types.core.ProcessingModule( ...
'description', 'a module to store filtering results', ...
'filtered_timeseries', FilteredTs ...
);
ContextFile.processing.set('core', ProcModule);
nwbExport(ContextFile, 'context_file.nwb');

Warning Regarding the Usage of Scratch Space

Scratch data written into the scratch space should not be intended for reuse or sharing. Standard NWB types, along with any extensions, should always be used for any data intended to be shared. Published data should not include scratch data and any reuse should not require scratch data for data processing.

Writing Data to Scratch Space

Let us first copy what we need from the processed data file.
ScratchFile = NwbFile('identifier', 'SCRATCH-1');
ContextFile = nwbRead('./context_file.nwb', 'ignorecache');
% again, copy the required metadata from the processed file.
ScratchFile.session_description = ContextFile.session_description;
ScratchFile.session_start_time = ContextFile.session_start_time;
We can now do an analysis lacking specification but that we still wish to store results for.
% ProcessingModule stores its timeseries inside of the "nwbdatainterface" property which is a Set of
% NWBDataInterface objects. This is not directly mapped to the NWB file but is used to distinguish
% it and DynamicTable objects which it stores under the "dynamictable" property.
FilteredTs = ContextFile.processing.get('core').nwbdatainterface.get('filtered_timeseries');
% note: MatNWB does not currently support complex numbers. If you wish to store the data, consider
% storing each number as a struct which will write the data to HDF5 using compound types.
dataFft = real(fft(FilteredTs.data.load()));
ScratchData = types.core.ScratchData( ...
'data', dataFft, ...
'notes', 'discrete Fourier transform from filtered data' ...
)
ScratchData =
ScratchData with properties: - - notes: 'discrete Fourier transform from filtered data' - data: [11×1 double] -
ScratchFile.scratch.set('dft_filtered', ScratchData);
nwbExport(ScratchFile, 'scratch_analysis.nwb');
The scratch_analysis.nwb file will now have scratch data stored in it:
scratch_filtered.png
function C = sameCorr(A, B)
% SAMECORR scipy.signals.correlate(..., mode="same") equivalent
for iDim = 1:ndims(B)
B = flip(B, iDim);
end
C = conv(A, conj(B), 'same');
end
-
- -
\ No newline at end of file From 8aa83c388629583e0a5a2a051efb37c812b60632 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 17:06:32 +0100 Subject: [PATCH 08/30] Fix indentation in function docstrings --- generateCore.m | 24 ++++++++++++------------ generateExtension.m | 16 ++++++++-------- nwbClearGenerated.m | 4 ++-- nwbExport.m | 2 +- nwbRead.m | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/generateCore.m b/generateCore.m index c2b8fe6e..27a3a39e 100644 --- a/generateCore.m +++ b/generateCore.m @@ -2,22 +2,22 @@ function generateCore(version, options) % GENERATECORE - Generate Matlab classes from NWB core schema files % % Syntax: -% GENERATECORE() Generate classes (Matlab m-files) from the -% NWB core namespace file. By default, generates off of the most recent -% nwb-schema release. +% GENERATECORE() Generate classes (Matlab m-files) from the +% NWB core namespace file. By default, generates off of the most recent +% nwb-schema release. % -% GENERATECORE(version) Generate classes for the -% core namespace of the listed version. +% GENERATECORE(version) Generate classes for the +% core namespace of the listed version. % -% GENERATECORE(__, Name, Value) Generate classes based on optional -% name-value pairs controlling the output . +% GENERATECORE(__, Name, Value) Generate classes based on optional +% name-value pairs controlling the output . % -% A cache of schema data is generated in the ``namespaces`` subdirectory in -% the matnwb root directory. This is for allowing cross-referencing -% classes between multiple namespaces. +% A cache of schema data is generated in the ``namespaces`` subdirectory in +% the matnwb root directory. This is for allowing cross-referencing +% classes between multiple namespaces. % -% Output files are placed in a ``+types`` subdirectory in the -% matnwb root directory directory. +% Output files are placed in a ``+types`` subdirectory in the +% matnwb root directory directory. % % Usage: % Example 1 - Generate core schemas for the latest version of NWB:: diff --git a/generateExtension.m b/generateExtension.m index 7bf14c93..21f6f6fa 100644 --- a/generateExtension.m +++ b/generateExtension.m @@ -2,16 +2,16 @@ function generateExtension(namespaceFilePath, options) % GENERATEEXTENSION - Generate Matlab classes from NWB extension schema file % % Syntax: -% GENERATEEXTENSION(extension_path...) Generate classes (Matlab m-files) -% from one or more NWB schema extension namespace files. A registry of -% already generated core types is used to resolve dependent types. +% GENERATEEXTENSION(extension_path...) Generate classes (Matlab m-files) +% from one or more NWB schema extension namespace files. A registry of +% already generated core types is used to resolve dependent types. % -% A cache of schema data is generated in the ``namespaces`` subdirectory in -% the matnwb root directory. This is for allowing cross-referencing -% classes between multiple namespaces. +% A cache of schema data is generated in the ``namespaces`` subdirectory in +% the matnwb root directory. This is for allowing cross-referencing +% classes between multiple namespaces. % -% Output files are placed in a ``+types`` subdirectory in the -% matnwb root directory directory. +% Output files are placed in a ``+types`` subdirectory in the +% matnwb root directory directory. % % Input Arguments: % - namespaceFilePath (string) - diff --git a/nwbClearGenerated.m b/nwbClearGenerated.m index 3e8cbc9c..54e929be 100644 --- a/nwbClearGenerated.m +++ b/nwbClearGenerated.m @@ -2,8 +2,8 @@ % NWBCLEARGENERATED - Clear generated class files. % % Syntax: -% NWBCLEARGENERATED() Clear generated class files from the ``+types`` -% folder in the matnwb root directory. +% NWBCLEARGENERATED() Clear generated class files from the ``+types`` +% folder in the matnwb root directory. % % Input Arguments: % - targetFolder (string) - diff --git a/nwbExport.m b/nwbExport.m index a481cec7..a108e0ae 100644 --- a/nwbExport.m +++ b/nwbExport.m @@ -2,7 +2,7 @@ function nwbExport(nwbFileObjects, filePaths, mode) %NWBEXPORT - Writes an NWB file. % % Syntax: -% NWBEXPORT(nwb, filename) Writes the nwb object to a file at filename. +% NWBEXPORT(nwb, filename) Writes the nwb object to a file at filename. % % Input Arguments: % - nwb (NwbFile) - Nwb file object diff --git a/nwbRead.m b/nwbRead.m index 03b54791..16752f34 100644 --- a/nwbRead.m +++ b/nwbRead.m @@ -18,8 +18,8 @@ % % - flags (string) - % Flag for setting the mode for the NWBREAD operation. Available options are: -% 'ignorecache'. If the 'ignorecache' flag is used, classes for NWB data types -% are not re-generated based on the embedded schemas in the file. +% 'ignorecache'. If the 'ignorecache' flag is used, classes for NWB data +% types are not re-generated based on the embedded schemas in the file. % % - options (name-value pairs) - % Optional name-value pairs. Available options: From d913c6b73de807e852c7e750e07c55d042823838 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 21:47:53 +0100 Subject: [PATCH 09/30] Add proper titles and "open in MATLAB Online" badges to tutorial pages --- docs/source/_config/tutorial_config.json | 19 ++++++++++++ docs/source/pages/tutorials/basicUsage.rst | 10 +++++-- docs/source/pages/tutorials/behavior.rst | 10 +++++-- docs/source/pages/tutorials/convertTrials.rst | 8 +++-- docs/source/pages/tutorials/dataPipe.rst | 10 +++++-- .../tutorials/dimensionMapNoDataPipes.rst | 10 +++++-- .../tutorials/dimensionMapWithDataPipes.rst | 10 +++++-- .../source/pages/tutorials/dynamic_tables.rst | 10 +++++-- .../tutorials/dynamically_loaded_filters.rst | 10 +++++-- docs/source/pages/tutorials/ecephys.rst | 10 +++++-- docs/source/pages/tutorials/icephys.rst | 8 +++-- docs/source/pages/tutorials/images.rst | 10 +++++-- docs/source/pages/tutorials/index.rst | 29 ++++++++++++++----- docs/source/pages/tutorials/intro.rst | 10 +++++-- docs/source/pages/tutorials/ogen.rst | 10 +++++-- docs/source/pages/tutorials/ophys.rst | 10 +++++-- docs/source/pages/tutorials/read_demo.rst | 10 +++++-- docs/source/pages/tutorials/remote_read.rst | 10 +++++-- docs/source/pages/tutorials/scratch.rst | 10 +++++-- .../_rst_templates/tutorial.rst.template | 10 +++++-- .../private/generateRstForTutorials.m | 8 ++++- 21 files changed, 171 insertions(+), 61 deletions(-) create mode 100644 docs/source/_config/tutorial_config.json diff --git a/docs/source/_config/tutorial_config.json b/docs/source/_config/tutorial_config.json new file mode 100644 index 00000000..6ee7d734 --- /dev/null +++ b/docs/source/_config/tutorial_config.json @@ -0,0 +1,19 @@ +{ + "intro": "Getting Started with MatNWB", + "read_demo": "Reading NWB Files with MatNWB", + "basicUsage": "Basic Usage of MatNWB", + "dimensionMapNoDataPipes": "Mapping Dimensions without DataPipes", + "dimensionMapWithDataPipes": "Mapping Dimensions with DataPipes", + "convertTrials": "Converting Trials to NWB Format", + "dynamic_tables": "Using Dynamic Tables in MatNWB", + "scratch": "Working with Scratch Space in MatNWB", + "behavior": "Behavior Data Processing", + "ecephys": "Electrophysiology", + "icephys": "Intracellular Electrophysiology", + "images": "Image Data", + "ogen": "Optogenetics", + "ophys": "Optical Physiology", + "dataPipe": "Advanced Writing Using DataPipes", + "dynamically_loaded_filters": "Implementing Dynamically Loaded Filters", + "remote_read": "Reading NWB Files from Remote Locations" +} diff --git a/docs/source/pages/tutorials/basicUsage.rst b/docs/source/pages/tutorials/basicUsage.rst index 5e1c8f10..b6d45886 100644 --- a/docs/source/pages/tutorials/basicUsage.rst +++ b/docs/source/pages/tutorials/basicUsage.rst @@ -1,6 +1,10 @@ -basicUsage -=============================== +Basic Usage of MatNWB +===================== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/basicUsage.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/behavior.rst b/docs/source/pages/tutorials/behavior.rst index c15fe726..ad0192c5 100644 --- a/docs/source/pages/tutorials/behavior.rst +++ b/docs/source/pages/tutorials/behavior.rst @@ -1,6 +1,10 @@ -behavior -=============================== +Behavior Data Processing +======================== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/behavior.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/convertTrials.rst b/docs/source/pages/tutorials/convertTrials.rst index 313e15ca..873d7023 100644 --- a/docs/source/pages/tutorials/convertTrials.rst +++ b/docs/source/pages/tutorials/convertTrials.rst @@ -1,6 +1,10 @@ -convertTrials +Converting Trials to NWB Format =============================== +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/convertTrials.mlx + :alt: Open in MATLAB Online + .. raw:: html - + diff --git a/docs/source/pages/tutorials/dataPipe.rst b/docs/source/pages/tutorials/dataPipe.rst index 8f7436a3..2376c019 100644 --- a/docs/source/pages/tutorials/dataPipe.rst +++ b/docs/source/pages/tutorials/dataPipe.rst @@ -1,6 +1,10 @@ -dataPipe -=============================== +Advanced Writing Using DataPipes +================================ + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dataPipe.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst index 8fb67911..3e88f879 100644 --- a/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst +++ b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst @@ -1,6 +1,10 @@ -dimensionMapNoDataPipes -=============================== +Mapping Dimensions without DataPipes +==================================== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dimensionMapNoDataPipes.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst index 27f0e4f6..3b8a1f64 100644 --- a/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst +++ b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst @@ -1,6 +1,10 @@ -dimensionMapWithDataPipes -=============================== +Mapping Dimensions with DataPipes +================================= + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dimensionMapWithDataPipes.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/dynamic_tables.rst b/docs/source/pages/tutorials/dynamic_tables.rst index a74dbe21..65094ae2 100644 --- a/docs/source/pages/tutorials/dynamic_tables.rst +++ b/docs/source/pages/tutorials/dynamic_tables.rst @@ -1,6 +1,10 @@ -dynamic_tables -=============================== +Using Dynamic Tables in MatNWB +============================== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dynamic_tables.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/dynamically_loaded_filters.rst b/docs/source/pages/tutorials/dynamically_loaded_filters.rst index e7600fea..a9791692 100644 --- a/docs/source/pages/tutorials/dynamically_loaded_filters.rst +++ b/docs/source/pages/tutorials/dynamically_loaded_filters.rst @@ -1,6 +1,10 @@ -dynamically_loaded_filters -=============================== +Implementing Dynamically Loaded Filters +======================================= + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dynamically_loaded_filters.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/ecephys.rst b/docs/source/pages/tutorials/ecephys.rst index 4e476bc2..39d7ca27 100644 --- a/docs/source/pages/tutorials/ecephys.rst +++ b/docs/source/pages/tutorials/ecephys.rst @@ -1,6 +1,10 @@ -ecephys -=============================== +Electrophysiology +================= + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/ecephys.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/icephys.rst b/docs/source/pages/tutorials/icephys.rst index a1f3eb78..3e851463 100644 --- a/docs/source/pages/tutorials/icephys.rst +++ b/docs/source/pages/tutorials/icephys.rst @@ -1,6 +1,10 @@ -icephys +Intracellular Electrophysiology =============================== +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/icephys.mlx + :alt: Open in MATLAB Online + .. raw:: html - + diff --git a/docs/source/pages/tutorials/images.rst b/docs/source/pages/tutorials/images.rst index bc088579..c1de5f9a 100644 --- a/docs/source/pages/tutorials/images.rst +++ b/docs/source/pages/tutorials/images.rst @@ -1,6 +1,10 @@ -images -=============================== +Image Data +========== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/images.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/index.rst b/docs/source/pages/tutorials/index.rst index 1b349ecf..a93692f9 100644 --- a/docs/source/pages/tutorials/index.rst +++ b/docs/source/pages/tutorials/index.rst @@ -1,24 +1,37 @@ Tutorials ========= +General Tutorials +----------------- .. toctree:: :maxdepth: 1 - :caption: Tutorials + intro + read_demo basicUsage - behavior - convertTrials - dataPipe dimensionMapNoDataPipes dimensionMapWithDataPipes + convertTrials dynamic_tables - dynamically_loaded_filters + scratch + +Domain-Specific Tutorials +------------------------- +.. toctree:: + :maxdepth: 1 + + behavior ecephys icephys images - intro ogen ophys - read_demo + +Advanced I/O +------------ +.. toctree:: + :maxdepth: 1 + + dataPipe + dynamically_loaded_filters remote_read - scratch \ No newline at end of file diff --git a/docs/source/pages/tutorials/intro.rst b/docs/source/pages/tutorials/intro.rst index 24aadfa2..5be8616d 100644 --- a/docs/source/pages/tutorials/intro.rst +++ b/docs/source/pages/tutorials/intro.rst @@ -1,6 +1,10 @@ -intro -=============================== +Getting Started with MatNWB +=========================== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/intro.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/ogen.rst b/docs/source/pages/tutorials/ogen.rst index ede496c7..20afd1ae 100644 --- a/docs/source/pages/tutorials/ogen.rst +++ b/docs/source/pages/tutorials/ogen.rst @@ -1,6 +1,10 @@ -ogen -=============================== +Optogenetics +============ + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/ogen.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/ophys.rst b/docs/source/pages/tutorials/ophys.rst index 511360ae..506b7b0f 100644 --- a/docs/source/pages/tutorials/ophys.rst +++ b/docs/source/pages/tutorials/ophys.rst @@ -1,6 +1,10 @@ -ophys -=============================== +Optical Physiology +================== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/ophys.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/read_demo.rst b/docs/source/pages/tutorials/read_demo.rst index 7cb5392c..a2e22c59 100644 --- a/docs/source/pages/tutorials/read_demo.rst +++ b/docs/source/pages/tutorials/read_demo.rst @@ -1,6 +1,10 @@ -read_demo -=============================== +Reading NWB Files with MatNWB +============================= + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/read_demo.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/remote_read.rst b/docs/source/pages/tutorials/remote_read.rst index 5b920117..259ac823 100644 --- a/docs/source/pages/tutorials/remote_read.rst +++ b/docs/source/pages/tutorials/remote_read.rst @@ -1,6 +1,10 @@ -remote_read -=============================== +Reading NWB Files from Remote Locations +======================================= + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/remote_read.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/docs/source/pages/tutorials/scratch.rst b/docs/source/pages/tutorials/scratch.rst index bcd17732..86292af4 100644 --- a/docs/source/pages/tutorials/scratch.rst +++ b/docs/source/pages/tutorials/scratch.rst @@ -1,6 +1,10 @@ -scratch -=============================== +Working with Scratch Space in MatNWB +==================================== + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/scratch.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/tools/documentation/_rst_templates/tutorial.rst.template b/tools/documentation/_rst_templates/tutorial.rst.template index 5c1b30fe..ecb9a0a3 100644 --- a/tools/documentation/_rst_templates/tutorial.rst.template +++ b/tools/documentation/_rst_templates/tutorial.rst.template @@ -1,6 +1,10 @@ -{{tutorial_name}} -=============================== +{{tutorial_title}} +{{tutorial_title_underline}} + +.. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg + :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/{{tutorial_name}}.mlx + :alt: Open in MATLAB Online .. raw:: html - + diff --git a/tools/documentation/private/generateRstForTutorials.m b/tools/documentation/private/generateRstForTutorials.m index 977d9a60..c1339cfa 100644 --- a/tools/documentation/private/generateRstForTutorials.m +++ b/tools/documentation/private/generateRstForTutorials.m @@ -7,6 +7,9 @@ function generateRstForTutorials() tutorialRstTargetDir = fullfile(docsSourceRootDir, 'pages', 'tutorials'); if ~isfolder(tutorialRstTargetDir); mkdir(tutorialRstTargetDir); end + tutorialConfigFilePath = fullfile(docsSourceRootDir, '_config', 'tutorial_config.json'); + S = jsondecode(fileread(tutorialConfigFilePath)); + rstTemplate = fileread( getRstTemplateFile('tutorial') ); % List all html files in source dir @@ -17,9 +20,12 @@ function generateRstForTutorials() relPath = strrep(thisFilePath, docsSourceRootDir, '../..'); [~, name] = fileparts(relPath); + title = S.(name); rstOutput = replace(rstTemplate, '{{static_html_path}}', relPath); rstOutput = replace(rstOutput, '{{tutorial_name}}', name); + rstOutput = replace(rstOutput, '{{tutorial_title}}', title); + rstOutput = replace(rstOutput, '{{tutorial_title_underline}}', repmat('=', 1, numel(title))); rstOutputFile = fullfile(tutorialRstTargetDir, [name, '.rst']); fid = fopen(rstOutputFile, 'wt'); @@ -34,5 +40,5 @@ function generateRstForTutorials() thisRst = fillTemplate(indexTemplate, data); rstFilePath = fullfile(tutorialRstTargetDir, ['index', '.rst']); - filewrite(rstFilePath, thisRst); + %filewrite(rstFilePath, thisRst); end \ No newline at end of file From 3cb5a3319ee4b3245d675e83cd0d0f1b0d52d708 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 21:48:12 +0100 Subject: [PATCH 10/30] Update main page and add installation instructions --- docs/source/conf.py | 16 ++++++++++++++++ docs/source/index.rst | 20 ++++++++++++++------ docs/source/pages/install_users.rst | 4 ---- docs/source/pages/installation_users.rst | 21 +++++++++++++++++++++ 4 files changed, 51 insertions(+), 10 deletions(-) delete mode 100644 docs/source/pages/install_users.rst create mode 100644 docs/source/pages/installation_users.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index b14ade56..b5da8423 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -30,6 +30,7 @@ def setup(app): 'sphinx.ext.autodoc', # autogenerate docs 'sphinx.ext.napoleon', # for parsing e.g google style parameter docstring 'sphinx.ext.viewcode', + 'sphinx.ext.extlinks', # For maintaining external links 'sphinx_copybutton', ] @@ -67,3 +68,18 @@ def setup(app): html_css_files = [ 'css/custom.css', ] + + +# External links used in the documentation +extlinks = { + 'incf_lesson': ('https://training.incf.org/lesson/%s', '%s'), + 'incf_collection': ('https://training.incf.org/collection/%s', '%s'), + 'nwb_extension': ('https://github.com/nwb-extensions/%s', '%s'), + 'pynwb': ('https://github.com/NeurodataWithoutBorders/pynwb/%s', '%s'), + 'nwb_overview': ('https://nwb-overview.readthedocs.io/en/latest/%s', '%s'), + 'hdmf-docs': ('https://hdmf.readthedocs.io/en/stable/%s', '%s'), + 'dandi': ('https://www.dandiarchive.org/%s', '%s'), + "nwbinspector": ("https://nwbinspector.readthedocs.io/en/dev/%s", "%s"), + 'hdmf-zarr': ('https://hdmf-zarr.readthedocs.io/en/latest/%s', '%s'), + 'matlab-online-tutorial': ('https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/%s.mlx', '%s') +} diff --git a/docs/source/index.rst b/docs/source/index.rst index 327d56ad..822f21a6 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,15 +1,23 @@ -MatNWB documentation -==================== +############## +NWB for MATLAB +############## -Add your content using ``reStructuredText`` syntax. See the -`reStructuredText `_ -documentation for details. +MatNWB is a MATLAB package for working with NWB files. It provides a high-level +API for efficiently working with neurodata stored in the NWB format. If you are +new to NWB and would like to learn more, then please also visit the +:nwb_overview:`NWB Overview <>` website, which provides an entry point for +researchers and developers interested in using NWB. + + +******** +Contents +******** .. toctree:: :maxdepth: 2 :caption: Getting Started - pages/install_users + pages/installation_users pages/tutorials/index pages/overview_citing diff --git a/docs/source/pages/install_users.rst b/docs/source/pages/install_users.rst deleted file mode 100644 index 20156dbf..00000000 --- a/docs/source/pages/install_users.rst +++ /dev/null @@ -1,4 +0,0 @@ -Install MatNWB -============== - -Todo: installation \ No newline at end of file diff --git a/docs/source/pages/installation_users.rst b/docs/source/pages/installation_users.rst new file mode 100644 index 00000000..25a5dd99 --- /dev/null +++ b/docs/source/pages/installation_users.rst @@ -0,0 +1,21 @@ +Install MatNWB +============== + +Download the current release of MatNWB from the +`MatNWB releases page `_ +or from the `MATLAB's FileExchange `_. +You can also check out the latest development version via:: + + git clone https://github.com/NeurodataWithoutBorders/matnwb.git + +After downloading MatNWB, make sure to add it to MATLAB's search path: + +.. code-block:: matlab + + addpath("path/to/matnwb") + savepath() % Permanently add to search path + +Requirements +------------ + +MATLAB R2016 or later? From ace3a4cc3719f9d541e890e34fb46b5714a784ae Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 6 Dec 2024 22:05:37 +0100 Subject: [PATCH 11/30] Add view full page badge for tutorials --- docs/source/pages/tutorials/basicUsage.rst | 3 +++ docs/source/pages/tutorials/behavior.rst | 3 +++ docs/source/pages/tutorials/convertTrials.rst | 3 +++ docs/source/pages/tutorials/dataPipe.rst | 3 +++ docs/source/pages/tutorials/dimensionMapNoDataPipes.rst | 3 +++ docs/source/pages/tutorials/dimensionMapWithDataPipes.rst | 3 +++ docs/source/pages/tutorials/dynamic_tables.rst | 3 +++ docs/source/pages/tutorials/dynamically_loaded_filters.rst | 3 +++ docs/source/pages/tutorials/ecephys.rst | 3 +++ docs/source/pages/tutorials/icephys.rst | 3 +++ docs/source/pages/tutorials/images.rst | 3 +++ docs/source/pages/tutorials/intro.rst | 3 +++ docs/source/pages/tutorials/ogen.rst | 3 +++ docs/source/pages/tutorials/ophys.rst | 3 +++ docs/source/pages/tutorials/read_demo.rst | 3 +++ docs/source/pages/tutorials/remote_read.rst | 3 +++ docs/source/pages/tutorials/scratch.rst | 3 +++ tools/documentation/_rst_templates/tutorial.rst.template | 3 +++ 18 files changed, 54 insertions(+) diff --git a/docs/source/pages/tutorials/basicUsage.rst b/docs/source/pages/tutorials/basicUsage.rst index b6d45886..5fa8e6c3 100644 --- a/docs/source/pages/tutorials/basicUsage.rst +++ b/docs/source/pages/tutorials/basicUsage.rst @@ -4,6 +4,9 @@ Basic Usage of MatNWB .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/basicUsage.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/basicUsage.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/behavior.rst b/docs/source/pages/tutorials/behavior.rst index ad0192c5..68266a78 100644 --- a/docs/source/pages/tutorials/behavior.rst +++ b/docs/source/pages/tutorials/behavior.rst @@ -4,6 +4,9 @@ Behavior Data Processing .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/behavior.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/behavior.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/convertTrials.rst b/docs/source/pages/tutorials/convertTrials.rst index 873d7023..e49ce35f 100644 --- a/docs/source/pages/tutorials/convertTrials.rst +++ b/docs/source/pages/tutorials/convertTrials.rst @@ -4,6 +4,9 @@ Converting Trials to NWB Format .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/convertTrials.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/convertTrials.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/dataPipe.rst b/docs/source/pages/tutorials/dataPipe.rst index 2376c019..9189c08d 100644 --- a/docs/source/pages/tutorials/dataPipe.rst +++ b/docs/source/pages/tutorials/dataPipe.rst @@ -4,6 +4,9 @@ Advanced Writing Using DataPipes .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dataPipe.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dataPipe.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst index 3e88f879..6aab39ab 100644 --- a/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst +++ b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst @@ -4,6 +4,9 @@ Mapping Dimensions without DataPipes .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dimensionMapNoDataPipes.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dimensionMapNoDataPipes.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst index 3b8a1f64..dc1d7b77 100644 --- a/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst +++ b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst @@ -4,6 +4,9 @@ Mapping Dimensions with DataPipes .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dimensionMapWithDataPipes.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dimensionMapWithDataPipes.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/dynamic_tables.rst b/docs/source/pages/tutorials/dynamic_tables.rst index 65094ae2..0917d800 100644 --- a/docs/source/pages/tutorials/dynamic_tables.rst +++ b/docs/source/pages/tutorials/dynamic_tables.rst @@ -4,6 +4,9 @@ Using Dynamic Tables in MatNWB .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dynamic_tables.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dynamic_tables.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/dynamically_loaded_filters.rst b/docs/source/pages/tutorials/dynamically_loaded_filters.rst index a9791692..b669f3c2 100644 --- a/docs/source/pages/tutorials/dynamically_loaded_filters.rst +++ b/docs/source/pages/tutorials/dynamically_loaded_filters.rst @@ -4,6 +4,9 @@ Implementing Dynamically Loaded Filters .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/dynamically_loaded_filters.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dynamically_loaded_filters.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/ecephys.rst b/docs/source/pages/tutorials/ecephys.rst index 39d7ca27..c957549c 100644 --- a/docs/source/pages/tutorials/ecephys.rst +++ b/docs/source/pages/tutorials/ecephys.rst @@ -4,6 +4,9 @@ Electrophysiology .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/ecephys.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/ecephys.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/icephys.rst b/docs/source/pages/tutorials/icephys.rst index 3e851463..e53ea910 100644 --- a/docs/source/pages/tutorials/icephys.rst +++ b/docs/source/pages/tutorials/icephys.rst @@ -4,6 +4,9 @@ Intracellular Electrophysiology .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/icephys.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/icephys.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/images.rst b/docs/source/pages/tutorials/images.rst index c1de5f9a..bdc9156c 100644 --- a/docs/source/pages/tutorials/images.rst +++ b/docs/source/pages/tutorials/images.rst @@ -4,6 +4,9 @@ Image Data .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/images.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/images.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/intro.rst b/docs/source/pages/tutorials/intro.rst index 5be8616d..423b61e8 100644 --- a/docs/source/pages/tutorials/intro.rst +++ b/docs/source/pages/tutorials/intro.rst @@ -4,6 +4,9 @@ Getting Started with MatNWB .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/intro.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/intro.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/ogen.rst b/docs/source/pages/tutorials/ogen.rst index 20afd1ae..9be344f4 100644 --- a/docs/source/pages/tutorials/ogen.rst +++ b/docs/source/pages/tutorials/ogen.rst @@ -4,6 +4,9 @@ Optogenetics .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/ogen.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/ogen.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/ophys.rst b/docs/source/pages/tutorials/ophys.rst index 506b7b0f..e39b6f1a 100644 --- a/docs/source/pages/tutorials/ophys.rst +++ b/docs/source/pages/tutorials/ophys.rst @@ -4,6 +4,9 @@ Optical Physiology .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/ophys.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/ophys.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/read_demo.rst b/docs/source/pages/tutorials/read_demo.rst index a2e22c59..7d83abc6 100644 --- a/docs/source/pages/tutorials/read_demo.rst +++ b/docs/source/pages/tutorials/read_demo.rst @@ -4,6 +4,9 @@ Reading NWB Files with MatNWB .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/read_demo.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/read_demo.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/remote_read.rst b/docs/source/pages/tutorials/remote_read.rst index 259ac823..7e487725 100644 --- a/docs/source/pages/tutorials/remote_read.rst +++ b/docs/source/pages/tutorials/remote_read.rst @@ -4,6 +4,9 @@ Reading NWB Files from Remote Locations .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/remote_read.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/remote_read.html + :alt: View full page .. raw:: html diff --git a/docs/source/pages/tutorials/scratch.rst b/docs/source/pages/tutorials/scratch.rst index 86292af4..92ae4888 100644 --- a/docs/source/pages/tutorials/scratch.rst +++ b/docs/source/pages/tutorials/scratch.rst @@ -4,6 +4,9 @@ Working with Scratch Space in MatNWB .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/scratch.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/scratch.html + :alt: View full page .. raw:: html diff --git a/tools/documentation/_rst_templates/tutorial.rst.template b/tools/documentation/_rst_templates/tutorial.rst.template index ecb9a0a3..b00dcd15 100644 --- a/tools/documentation/_rst_templates/tutorial.rst.template +++ b/tools/documentation/_rst_templates/tutorial.rst.template @@ -4,6 +4,9 @@ .. image:: https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg :target: https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/{{tutorial_name}}.mlx :alt: Open in MATLAB Online +.. image:: https://img.shields.io/badge/View-Full_Page-blue + :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/{{tutorial_name}}.html + :alt: View full page .. raw:: html From 4e65fce0cf0d6fdc7358f196106d3bfb8b900aec Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Dec 2024 14:10:12 +0100 Subject: [PATCH 12/30] Add hdmf_common types plus smaller changes Fix: Make sure neurodata types specfied with "short names" in the documentation for properties of generated classes are properly linked to their corresponding class pages in the docs i.e (TimeSeries) Add links to the nwb format specification for each type --- docs/source/conf.py | 9 +- docs/source/index.rst | 1 + .../core/AbstractFeatureSeries.rst | 4 + .../neurodata_types/core/AnnotationSeries.rst | 4 + .../neurodata_types/core/BehavioralEpochs.rst | 4 + .../neurodata_types/core/BehavioralEvents.rst | 4 + .../core/BehavioralTimeSeries.rst | 4 + .../neurodata_types/core/ClusterWaveforms.rst | 4 + .../pages/neurodata_types/core/Clustering.rst | 4 + .../neurodata_types/core/CompassDirection.rst | 4 + .../core/CorrectedImageStack.rst | 4 + .../core/CurrentClampSeries.rst | 4 + .../core/CurrentClampStimulusSeries.rst | 4 + .../core/DecompositionSeries.rst | 4 + .../pages/neurodata_types/core/Device.rst | 4 + .../pages/neurodata_types/core/DfOverF.rst | 4 + .../neurodata_types/core/ElectricalSeries.rst | 4 + .../neurodata_types/core/ElectrodeGroup.rst | 4 + .../neurodata_types/core/EventDetection.rst | 4 + .../neurodata_types/core/EventWaveform.rst | 4 + .../core/ExperimentalConditionsTable.rst | 4 + .../neurodata_types/core/EyeTracking.rst | 4 + .../core/FeatureExtraction.rst | 4 + .../neurodata_types/core/FilteredEphys.rst | 4 + .../neurodata_types/core/Fluorescence.rst | 4 + .../neurodata_types/core/GrayscaleImage.rst | 4 + .../neurodata_types/core/IZeroClampSeries.rst | 4 + .../pages/neurodata_types/core/Image.rst | 4 + .../neurodata_types/core/ImageMaskSeries.rst | 4 + .../neurodata_types/core/ImageReferences.rst | 4 + .../core/ImageSegmentation.rst | 4 + .../neurodata_types/core/ImageSeries.rst | 4 + .../pages/neurodata_types/core/Images.rst | 4 + .../neurodata_types/core/ImagingPlane.rst | 4 + .../core/ImagingRetinotopy.rst | 4 + .../neurodata_types/core/IndexSeries.rst | 4 + .../neurodata_types/core/IntervalSeries.rst | 4 + .../core/IntracellularElectrode.rst | 4 + .../core/IntracellularElectrodesTable.rst | 4 + .../core/IntracellularRecordingsTable.rst | 4 + .../core/IntracellularResponsesTable.rst | 4 + .../core/IntracellularStimuliTable.rst | 4 + .../source/pages/neurodata_types/core/LFP.rst | 4 + .../neurodata_types/core/LabMetaData.rst | 4 + .../neurodata_types/core/MotionCorrection.rst | 4 + .../neurodata_types/core/NWBContainer.rst | 4 + .../pages/neurodata_types/core/NWBData.rst | 4 + .../neurodata_types/core/NWBDataInterface.rst | 4 + .../pages/neurodata_types/core/NWBFile.rst | 4 + .../neurodata_types/core/OnePhotonSeries.rst | 4 + .../neurodata_types/core/OpticalChannel.rst | 4 + .../neurodata_types/core/OpticalSeries.rst | 4 + .../core/OptogeneticSeries.rst | 4 + .../core/OptogeneticStimulusSite.rst | 4 + .../neurodata_types/core/PatchClampSeries.rst | 4 + .../core/PlaneSegmentation.rst | 4 + .../pages/neurodata_types/core/Position.rst | 4 + .../neurodata_types/core/ProcessingModule.rst | 4 + .../neurodata_types/core/PupilTracking.rst | 4 + .../pages/neurodata_types/core/RGBAImage.rst | 4 + .../pages/neurodata_types/core/RGBImage.rst | 4 + .../neurodata_types/core/RepetitionsTable.rst | 4 + .../core/RoiResponseSeries.rst | 4 + .../neurodata_types/core/ScratchData.rst | 4 + .../core/SequentialRecordingsTable.rst | 4 + .../core/SimultaneousRecordingsTable.rst | 4 + .../neurodata_types/core/SpatialSeries.rst | 4 + .../neurodata_types/core/SpikeEventSeries.rst | 4 + .../pages/neurodata_types/core/Subject.rst | 4 + .../pages/neurodata_types/core/SweepTable.rst | 4 + .../neurodata_types/core/TimeIntervals.rst | 4 + .../pages/neurodata_types/core/TimeSeries.rst | 4 + .../core/TimeSeriesReferenceVectorData.rst | 4 + .../neurodata_types/core/TwoPhotonSeries.rst | 4 + .../pages/neurodata_types/core/Units.rst | 4 + .../core/VoltageClampSeries.rst | 4 + .../core/VoltageClampStimulusSeries.rst | 4 + .../pages/neurodata_types/core/index.rst | 4 +- .../hdmf_common/AlignedDynamicTable.rst | 11 +++ .../neurodata_types/hdmf_common/CSRMatrix.rst | 11 +++ .../neurodata_types/hdmf_common/Container.rst | 11 +++ .../neurodata_types/hdmf_common/Data.rst | 11 +++ .../hdmf_common/DynamicTable.rst | 11 +++ .../hdmf_common/DynamicTableRegion.rst | 11 +++ .../hdmf_common/ElementIdentifiers.rst | 11 +++ .../hdmf_common/SimpleMultiContainer.rst | 11 +++ .../hdmf_common/VectorData.rst | 11 +++ .../hdmf_common/VectorIndex.rst | 11 +++ .../neurodata_types/hdmf_common/index.rst | 19 ++++ .../docstring_processors.cpython-311.pyc | Bin 6843 -> 8558 bytes docs/source/sphinx_extensions/_util.py | 24 +++++ docs/source/sphinx_extensions/custom_roles.py | 87 ++++++++++++++++++ .../sphinx_extensions/docstring_processors.py | 44 ++++++++- .../index_core_types.rst.template | 10 -- .../index_functions.rst.template | 2 +- .../index_nwb_types.rst.template | 10 ++ .../index_tutorials.rst.template | 2 +- .../neurodata_class.rst.template | 11 +++ .../matnwb_generateRstFilesFromCode.m | 4 +- .../generateRstForNeurodataTypeClasses.m | 58 ++++++++++++ .../private/generateRstForNwbTypeClasses.m | 43 --------- 101 files changed, 674 insertions(+), 64 deletions(-) create mode 100644 docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/Container.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/Data.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/VectorData.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_common/index.rst create mode 100644 docs/source/sphinx_extensions/_util.py create mode 100644 docs/source/sphinx_extensions/custom_roles.py delete mode 100644 tools/documentation/_rst_templates/index_core_types.rst.template create mode 100644 tools/documentation/_rst_templates/index_nwb_types.rst.template create mode 100644 tools/documentation/_rst_templates/neurodata_class.rst.template create mode 100644 tools/documentation/private/generateRstForNeurodataTypeClasses.m delete mode 100644 tools/documentation/private/generateRstForNwbTypeClasses.m diff --git a/docs/source/conf.py b/docs/source/conf.py index b5da8423..af3ace12 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -11,9 +11,14 @@ sys.path.append('sphinx_extensions') from docstring_processors import process_matlab_docstring +from custom_roles import MatClassRole, register_matlab_types, register_type_short_names def setup(app): app.connect("autodoc-process-docstring", process_matlab_docstring) + app.connect("env-purge-doc", register_matlab_types) + app.connect("env-purge-doc", register_type_short_names) + app.add_role('matclass', MatClassRole()) + project = 'MatNWB' copyright = '2024, Neurodata Without Borders' # Todo: compute year @@ -81,5 +86,7 @@ def setup(app): 'dandi': ('https://www.dandiarchive.org/%s', '%s'), "nwbinspector": ("https://nwbinspector.readthedocs.io/en/dev/%s", "%s"), 'hdmf-zarr': ('https://hdmf-zarr.readthedocs.io/en/latest/%s', '%s'), - 'matlab-online-tutorial': ('https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/%s.mlx', '%s') + 'matlab-online-tutorial': ('https://matlab.mathworks.com/open/github/v1?repo=NeurodataWithoutBorders/matnwb&file=tutorials/%s.mlx', '%s'), + 'nwb-core-type-schema': ('https://nwb-schema.readthedocs.io/en/latest/format.html#%s', '%s'), + 'nwb-hdmf_common-type-schema': ('https://hdmf-common-schema.readthedocs.io/en/stable/format.html#%s', '%s') } diff --git a/docs/source/index.rst b/docs/source/index.rst index 822f21a6..ceb92a9a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -27,6 +27,7 @@ Contents pages/functions/index pages/neurodata_types/core/index + pages/neurodata_types/hdmf_common/index .. toctree:: :maxdepth: 2 diff --git a/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst b/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst index c4eebc7b..a2cdd5c6 100644 --- a/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst +++ b/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst @@ -1,6 +1,10 @@ AbstractFeatureSeries ===================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for AbstractFeatureSeries. + .. mat:module:: types.core .. autoclass:: types.core.AbstractFeatureSeries :members: diff --git a/docs/source/pages/neurodata_types/core/AnnotationSeries.rst b/docs/source/pages/neurodata_types/core/AnnotationSeries.rst index 7bd34497..479c2acb 100644 --- a/docs/source/pages/neurodata_types/core/AnnotationSeries.rst +++ b/docs/source/pages/neurodata_types/core/AnnotationSeries.rst @@ -1,6 +1,10 @@ AnnotationSeries ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for AnnotationSeries. + .. mat:module:: types.core .. autoclass:: types.core.AnnotationSeries :members: diff --git a/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst b/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst index a30da28d..23a28d1f 100644 --- a/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst +++ b/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst @@ -1,6 +1,10 @@ BehavioralEpochs ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for BehavioralEpochs. + .. mat:module:: types.core .. autoclass:: types.core.BehavioralEpochs :members: diff --git a/docs/source/pages/neurodata_types/core/BehavioralEvents.rst b/docs/source/pages/neurodata_types/core/BehavioralEvents.rst index 73b76ec7..99379bb2 100644 --- a/docs/source/pages/neurodata_types/core/BehavioralEvents.rst +++ b/docs/source/pages/neurodata_types/core/BehavioralEvents.rst @@ -1,6 +1,10 @@ BehavioralEvents ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for BehavioralEvents. + .. mat:module:: types.core .. autoclass:: types.core.BehavioralEvents :members: diff --git a/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst b/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst index 28729a31..03774dd6 100644 --- a/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst +++ b/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst @@ -1,6 +1,10 @@ BehavioralTimeSeries ==================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for BehavioralTimeSeries. + .. mat:module:: types.core .. autoclass:: types.core.BehavioralTimeSeries :members: diff --git a/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst b/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst index 326078d5..9b70c299 100644 --- a/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst +++ b/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst @@ -1,6 +1,10 @@ ClusterWaveforms ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ClusterWaveforms. + .. mat:module:: types.core .. autoclass:: types.core.ClusterWaveforms :members: diff --git a/docs/source/pages/neurodata_types/core/Clustering.rst b/docs/source/pages/neurodata_types/core/Clustering.rst index f4d71e48..3089328f 100644 --- a/docs/source/pages/neurodata_types/core/Clustering.rst +++ b/docs/source/pages/neurodata_types/core/Clustering.rst @@ -1,6 +1,10 @@ Clustering ========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Clustering. + .. mat:module:: types.core .. autoclass:: types.core.Clustering :members: diff --git a/docs/source/pages/neurodata_types/core/CompassDirection.rst b/docs/source/pages/neurodata_types/core/CompassDirection.rst index 741510f6..10b72ccc 100644 --- a/docs/source/pages/neurodata_types/core/CompassDirection.rst +++ b/docs/source/pages/neurodata_types/core/CompassDirection.rst @@ -1,6 +1,10 @@ CompassDirection ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for CompassDirection. + .. mat:module:: types.core .. autoclass:: types.core.CompassDirection :members: diff --git a/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst b/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst index 5aaa228a..b00dd099 100644 --- a/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst +++ b/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst @@ -1,6 +1,10 @@ CorrectedImageStack =================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for CorrectedImageStack. + .. mat:module:: types.core .. autoclass:: types.core.CorrectedImageStack :members: diff --git a/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst b/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst index 499c5716..189e4cdd 100644 --- a/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst @@ -1,6 +1,10 @@ CurrentClampSeries ================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for CurrentClampSeries. + .. mat:module:: types.core .. autoclass:: types.core.CurrentClampSeries :members: diff --git a/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst b/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst index 5d8c5f1b..4ec2a688 100644 --- a/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst +++ b/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst @@ -1,6 +1,10 @@ CurrentClampStimulusSeries ========================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for CurrentClampStimulusSeries. + .. mat:module:: types.core .. autoclass:: types.core.CurrentClampStimulusSeries :members: diff --git a/docs/source/pages/neurodata_types/core/DecompositionSeries.rst b/docs/source/pages/neurodata_types/core/DecompositionSeries.rst index d187f416..9037a541 100644 --- a/docs/source/pages/neurodata_types/core/DecompositionSeries.rst +++ b/docs/source/pages/neurodata_types/core/DecompositionSeries.rst @@ -1,6 +1,10 @@ DecompositionSeries =================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for DecompositionSeries. + .. mat:module:: types.core .. autoclass:: types.core.DecompositionSeries :members: diff --git a/docs/source/pages/neurodata_types/core/Device.rst b/docs/source/pages/neurodata_types/core/Device.rst index ce022bed..2891951e 100644 --- a/docs/source/pages/neurodata_types/core/Device.rst +++ b/docs/source/pages/neurodata_types/core/Device.rst @@ -1,6 +1,10 @@ Device ====== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Device. + .. mat:module:: types.core .. autoclass:: types.core.Device :members: diff --git a/docs/source/pages/neurodata_types/core/DfOverF.rst b/docs/source/pages/neurodata_types/core/DfOverF.rst index 35c40613..58baaf2b 100644 --- a/docs/source/pages/neurodata_types/core/DfOverF.rst +++ b/docs/source/pages/neurodata_types/core/DfOverF.rst @@ -1,6 +1,10 @@ DfOverF ======= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for DfOverF. + .. mat:module:: types.core .. autoclass:: types.core.DfOverF :members: diff --git a/docs/source/pages/neurodata_types/core/ElectricalSeries.rst b/docs/source/pages/neurodata_types/core/ElectricalSeries.rst index 15972a49..a8994f3e 100644 --- a/docs/source/pages/neurodata_types/core/ElectricalSeries.rst +++ b/docs/source/pages/neurodata_types/core/ElectricalSeries.rst @@ -1,6 +1,10 @@ ElectricalSeries ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ElectricalSeries. + .. mat:module:: types.core .. autoclass:: types.core.ElectricalSeries :members: diff --git a/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst b/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst index 4ba91041..7bef1742 100644 --- a/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst +++ b/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst @@ -1,6 +1,10 @@ ElectrodeGroup ============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ElectrodeGroup. + .. mat:module:: types.core .. autoclass:: types.core.ElectrodeGroup :members: diff --git a/docs/source/pages/neurodata_types/core/EventDetection.rst b/docs/source/pages/neurodata_types/core/EventDetection.rst index 5633b916..d3c41de3 100644 --- a/docs/source/pages/neurodata_types/core/EventDetection.rst +++ b/docs/source/pages/neurodata_types/core/EventDetection.rst @@ -1,6 +1,10 @@ EventDetection ============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for EventDetection. + .. mat:module:: types.core .. autoclass:: types.core.EventDetection :members: diff --git a/docs/source/pages/neurodata_types/core/EventWaveform.rst b/docs/source/pages/neurodata_types/core/EventWaveform.rst index 3550c82a..8887e86b 100644 --- a/docs/source/pages/neurodata_types/core/EventWaveform.rst +++ b/docs/source/pages/neurodata_types/core/EventWaveform.rst @@ -1,6 +1,10 @@ EventWaveform ============= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for EventWaveform. + .. mat:module:: types.core .. autoclass:: types.core.EventWaveform :members: diff --git a/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst b/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst index ad31451b..ab542823 100644 --- a/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst +++ b/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst @@ -1,6 +1,10 @@ ExperimentalConditionsTable =========================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ExperimentalConditionsTable. + .. mat:module:: types.core .. autoclass:: types.core.ExperimentalConditionsTable :members: diff --git a/docs/source/pages/neurodata_types/core/EyeTracking.rst b/docs/source/pages/neurodata_types/core/EyeTracking.rst index 124d9ebc..5c14a7f4 100644 --- a/docs/source/pages/neurodata_types/core/EyeTracking.rst +++ b/docs/source/pages/neurodata_types/core/EyeTracking.rst @@ -1,6 +1,10 @@ EyeTracking =========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for EyeTracking. + .. mat:module:: types.core .. autoclass:: types.core.EyeTracking :members: diff --git a/docs/source/pages/neurodata_types/core/FeatureExtraction.rst b/docs/source/pages/neurodata_types/core/FeatureExtraction.rst index 31c673cd..2e82a010 100644 --- a/docs/source/pages/neurodata_types/core/FeatureExtraction.rst +++ b/docs/source/pages/neurodata_types/core/FeatureExtraction.rst @@ -1,6 +1,10 @@ FeatureExtraction ================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for FeatureExtraction. + .. mat:module:: types.core .. autoclass:: types.core.FeatureExtraction :members: diff --git a/docs/source/pages/neurodata_types/core/FilteredEphys.rst b/docs/source/pages/neurodata_types/core/FilteredEphys.rst index 380d26d2..3a41d7ea 100644 --- a/docs/source/pages/neurodata_types/core/FilteredEphys.rst +++ b/docs/source/pages/neurodata_types/core/FilteredEphys.rst @@ -1,6 +1,10 @@ FilteredEphys ============= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for FilteredEphys. + .. mat:module:: types.core .. autoclass:: types.core.FilteredEphys :members: diff --git a/docs/source/pages/neurodata_types/core/Fluorescence.rst b/docs/source/pages/neurodata_types/core/Fluorescence.rst index 98568955..5446ecd9 100644 --- a/docs/source/pages/neurodata_types/core/Fluorescence.rst +++ b/docs/source/pages/neurodata_types/core/Fluorescence.rst @@ -1,6 +1,10 @@ Fluorescence ============ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Fluorescence. + .. mat:module:: types.core .. autoclass:: types.core.Fluorescence :members: diff --git a/docs/source/pages/neurodata_types/core/GrayscaleImage.rst b/docs/source/pages/neurodata_types/core/GrayscaleImage.rst index 19ea6a07..4943c4af 100644 --- a/docs/source/pages/neurodata_types/core/GrayscaleImage.rst +++ b/docs/source/pages/neurodata_types/core/GrayscaleImage.rst @@ -1,6 +1,10 @@ GrayscaleImage ============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for GrayscaleImage. + .. mat:module:: types.core .. autoclass:: types.core.GrayscaleImage :members: diff --git a/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst b/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst index 933c36bd..df82ead2 100644 --- a/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst @@ -1,6 +1,10 @@ IZeroClampSeries ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IZeroClampSeries. + .. mat:module:: types.core .. autoclass:: types.core.IZeroClampSeries :members: diff --git a/docs/source/pages/neurodata_types/core/Image.rst b/docs/source/pages/neurodata_types/core/Image.rst index f68e19c8..0c2b602a 100644 --- a/docs/source/pages/neurodata_types/core/Image.rst +++ b/docs/source/pages/neurodata_types/core/Image.rst @@ -1,6 +1,10 @@ Image ===== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Image. + .. mat:module:: types.core .. autoclass:: types.core.Image :members: diff --git a/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst b/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst index a64fce4c..824c9eb3 100644 --- a/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst +++ b/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst @@ -1,6 +1,10 @@ ImageMaskSeries =============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ImageMaskSeries. + .. mat:module:: types.core .. autoclass:: types.core.ImageMaskSeries :members: diff --git a/docs/source/pages/neurodata_types/core/ImageReferences.rst b/docs/source/pages/neurodata_types/core/ImageReferences.rst index f25c1dde..9553e3c6 100644 --- a/docs/source/pages/neurodata_types/core/ImageReferences.rst +++ b/docs/source/pages/neurodata_types/core/ImageReferences.rst @@ -1,6 +1,10 @@ ImageReferences =============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ImageReferences. + .. mat:module:: types.core .. autoclass:: types.core.ImageReferences :members: diff --git a/docs/source/pages/neurodata_types/core/ImageSegmentation.rst b/docs/source/pages/neurodata_types/core/ImageSegmentation.rst index dbd7bf12..ef348750 100644 --- a/docs/source/pages/neurodata_types/core/ImageSegmentation.rst +++ b/docs/source/pages/neurodata_types/core/ImageSegmentation.rst @@ -1,6 +1,10 @@ ImageSegmentation ================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ImageSegmentation. + .. mat:module:: types.core .. autoclass:: types.core.ImageSegmentation :members: diff --git a/docs/source/pages/neurodata_types/core/ImageSeries.rst b/docs/source/pages/neurodata_types/core/ImageSeries.rst index 2ca8ddaa..c71044d7 100644 --- a/docs/source/pages/neurodata_types/core/ImageSeries.rst +++ b/docs/source/pages/neurodata_types/core/ImageSeries.rst @@ -1,6 +1,10 @@ ImageSeries =========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ImageSeries. + .. mat:module:: types.core .. autoclass:: types.core.ImageSeries :members: diff --git a/docs/source/pages/neurodata_types/core/Images.rst b/docs/source/pages/neurodata_types/core/Images.rst index 59aa8822..c724a7e0 100644 --- a/docs/source/pages/neurodata_types/core/Images.rst +++ b/docs/source/pages/neurodata_types/core/Images.rst @@ -1,6 +1,10 @@ Images ====== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Images. + .. mat:module:: types.core .. autoclass:: types.core.Images :members: diff --git a/docs/source/pages/neurodata_types/core/ImagingPlane.rst b/docs/source/pages/neurodata_types/core/ImagingPlane.rst index 513c21db..ca57aa2a 100644 --- a/docs/source/pages/neurodata_types/core/ImagingPlane.rst +++ b/docs/source/pages/neurodata_types/core/ImagingPlane.rst @@ -1,6 +1,10 @@ ImagingPlane ============ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ImagingPlane. + .. mat:module:: types.core .. autoclass:: types.core.ImagingPlane :members: diff --git a/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst b/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst index 47edb9b3..3c8484cc 100644 --- a/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst +++ b/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst @@ -1,6 +1,10 @@ ImagingRetinotopy ================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ImagingRetinotopy. + .. mat:module:: types.core .. autoclass:: types.core.ImagingRetinotopy :members: diff --git a/docs/source/pages/neurodata_types/core/IndexSeries.rst b/docs/source/pages/neurodata_types/core/IndexSeries.rst index 5b2a7f40..f3915c21 100644 --- a/docs/source/pages/neurodata_types/core/IndexSeries.rst +++ b/docs/source/pages/neurodata_types/core/IndexSeries.rst @@ -1,6 +1,10 @@ IndexSeries =========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IndexSeries. + .. mat:module:: types.core .. autoclass:: types.core.IndexSeries :members: diff --git a/docs/source/pages/neurodata_types/core/IntervalSeries.rst b/docs/source/pages/neurodata_types/core/IntervalSeries.rst index d316f0fe..f6edf3f0 100644 --- a/docs/source/pages/neurodata_types/core/IntervalSeries.rst +++ b/docs/source/pages/neurodata_types/core/IntervalSeries.rst @@ -1,6 +1,10 @@ IntervalSeries ============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IntervalSeries. + .. mat:module:: types.core .. autoclass:: types.core.IntervalSeries :members: diff --git a/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst b/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst index ab0f254b..9e05309c 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst @@ -1,6 +1,10 @@ IntracellularElectrode ====================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IntracellularElectrode. + .. mat:module:: types.core .. autoclass:: types.core.IntracellularElectrode :members: diff --git a/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst b/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst index d1903fcc..cdf4c5e3 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst @@ -1,6 +1,10 @@ IntracellularElectrodesTable ============================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IntracellularElectrodesTable. + .. mat:module:: types.core .. autoclass:: types.core.IntracellularElectrodesTable :members: diff --git a/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst b/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst index 83af79be..ae0f7aa6 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst @@ -1,6 +1,10 @@ IntracellularRecordingsTable ============================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IntracellularRecordingsTable. + .. mat:module:: types.core .. autoclass:: types.core.IntracellularRecordingsTable :members: diff --git a/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst b/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst index e1be1bc1..f9e1e264 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst @@ -1,6 +1,10 @@ IntracellularResponsesTable =========================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IntracellularResponsesTable. + .. mat:module:: types.core .. autoclass:: types.core.IntracellularResponsesTable :members: diff --git a/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst b/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst index b01d0116..630f653c 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst @@ -1,6 +1,10 @@ IntracellularStimuliTable ========================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for IntracellularStimuliTable. + .. mat:module:: types.core .. autoclass:: types.core.IntracellularStimuliTable :members: diff --git a/docs/source/pages/neurodata_types/core/LFP.rst b/docs/source/pages/neurodata_types/core/LFP.rst index 62c80788..5170ef78 100644 --- a/docs/source/pages/neurodata_types/core/LFP.rst +++ b/docs/source/pages/neurodata_types/core/LFP.rst @@ -1,6 +1,10 @@ LFP === +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for LFP. + .. mat:module:: types.core .. autoclass:: types.core.LFP :members: diff --git a/docs/source/pages/neurodata_types/core/LabMetaData.rst b/docs/source/pages/neurodata_types/core/LabMetaData.rst index 7dd71b67..c5247ece 100644 --- a/docs/source/pages/neurodata_types/core/LabMetaData.rst +++ b/docs/source/pages/neurodata_types/core/LabMetaData.rst @@ -1,6 +1,10 @@ LabMetaData =========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for LabMetaData. + .. mat:module:: types.core .. autoclass:: types.core.LabMetaData :members: diff --git a/docs/source/pages/neurodata_types/core/MotionCorrection.rst b/docs/source/pages/neurodata_types/core/MotionCorrection.rst index 4aa78836..48aa0819 100644 --- a/docs/source/pages/neurodata_types/core/MotionCorrection.rst +++ b/docs/source/pages/neurodata_types/core/MotionCorrection.rst @@ -1,6 +1,10 @@ MotionCorrection ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for MotionCorrection. + .. mat:module:: types.core .. autoclass:: types.core.MotionCorrection :members: diff --git a/docs/source/pages/neurodata_types/core/NWBContainer.rst b/docs/source/pages/neurodata_types/core/NWBContainer.rst index 4761b9c2..39042d51 100644 --- a/docs/source/pages/neurodata_types/core/NWBContainer.rst +++ b/docs/source/pages/neurodata_types/core/NWBContainer.rst @@ -1,6 +1,10 @@ NWBContainer ============ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for NWBContainer. + .. mat:module:: types.core .. autoclass:: types.core.NWBContainer :members: diff --git a/docs/source/pages/neurodata_types/core/NWBData.rst b/docs/source/pages/neurodata_types/core/NWBData.rst index c376686e..3c7a0d88 100644 --- a/docs/source/pages/neurodata_types/core/NWBData.rst +++ b/docs/source/pages/neurodata_types/core/NWBData.rst @@ -1,6 +1,10 @@ NWBData ======= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for NWBData. + .. mat:module:: types.core .. autoclass:: types.core.NWBData :members: diff --git a/docs/source/pages/neurodata_types/core/NWBDataInterface.rst b/docs/source/pages/neurodata_types/core/NWBDataInterface.rst index dd29dca1..a49858ac 100644 --- a/docs/source/pages/neurodata_types/core/NWBDataInterface.rst +++ b/docs/source/pages/neurodata_types/core/NWBDataInterface.rst @@ -1,6 +1,10 @@ NWBDataInterface ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for NWBDataInterface. + .. mat:module:: types.core .. autoclass:: types.core.NWBDataInterface :members: diff --git a/docs/source/pages/neurodata_types/core/NWBFile.rst b/docs/source/pages/neurodata_types/core/NWBFile.rst index 9f390e5e..04f46379 100644 --- a/docs/source/pages/neurodata_types/core/NWBFile.rst +++ b/docs/source/pages/neurodata_types/core/NWBFile.rst @@ -1,6 +1,10 @@ NWBFile ======= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for NWBFile. + .. mat:module:: types.core .. autoclass:: types.core.NWBFile :members: diff --git a/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst b/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst index 93695fcd..5abde688 100644 --- a/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst +++ b/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst @@ -1,6 +1,10 @@ OnePhotonSeries =============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for OnePhotonSeries. + .. mat:module:: types.core .. autoclass:: types.core.OnePhotonSeries :members: diff --git a/docs/source/pages/neurodata_types/core/OpticalChannel.rst b/docs/source/pages/neurodata_types/core/OpticalChannel.rst index 7f3e711a..e32556cf 100644 --- a/docs/source/pages/neurodata_types/core/OpticalChannel.rst +++ b/docs/source/pages/neurodata_types/core/OpticalChannel.rst @@ -1,6 +1,10 @@ OpticalChannel ============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for OpticalChannel. + .. mat:module:: types.core .. autoclass:: types.core.OpticalChannel :members: diff --git a/docs/source/pages/neurodata_types/core/OpticalSeries.rst b/docs/source/pages/neurodata_types/core/OpticalSeries.rst index a9fc655f..3d0a6073 100644 --- a/docs/source/pages/neurodata_types/core/OpticalSeries.rst +++ b/docs/source/pages/neurodata_types/core/OpticalSeries.rst @@ -1,6 +1,10 @@ OpticalSeries ============= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for OpticalSeries. + .. mat:module:: types.core .. autoclass:: types.core.OpticalSeries :members: diff --git a/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst b/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst index 33071d10..36bcb78d 100644 --- a/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst +++ b/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst @@ -1,6 +1,10 @@ OptogeneticSeries ================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for OptogeneticSeries. + .. mat:module:: types.core .. autoclass:: types.core.OptogeneticSeries :members: diff --git a/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst b/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst index bb8b9ace..df2c34a8 100644 --- a/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst +++ b/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst @@ -1,6 +1,10 @@ OptogeneticStimulusSite ======================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for OptogeneticStimulusSite. + .. mat:module:: types.core .. autoclass:: types.core.OptogeneticStimulusSite :members: diff --git a/docs/source/pages/neurodata_types/core/PatchClampSeries.rst b/docs/source/pages/neurodata_types/core/PatchClampSeries.rst index 67d1bbc9..37c81695 100644 --- a/docs/source/pages/neurodata_types/core/PatchClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/PatchClampSeries.rst @@ -1,6 +1,10 @@ PatchClampSeries ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for PatchClampSeries. + .. mat:module:: types.core .. autoclass:: types.core.PatchClampSeries :members: diff --git a/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst b/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst index fd984fc6..2f0efdd9 100644 --- a/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst +++ b/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst @@ -1,6 +1,10 @@ PlaneSegmentation ================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for PlaneSegmentation. + .. mat:module:: types.core .. autoclass:: types.core.PlaneSegmentation :members: diff --git a/docs/source/pages/neurodata_types/core/Position.rst b/docs/source/pages/neurodata_types/core/Position.rst index 0b49fec6..3cb94c84 100644 --- a/docs/source/pages/neurodata_types/core/Position.rst +++ b/docs/source/pages/neurodata_types/core/Position.rst @@ -1,6 +1,10 @@ Position ======== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Position. + .. mat:module:: types.core .. autoclass:: types.core.Position :members: diff --git a/docs/source/pages/neurodata_types/core/ProcessingModule.rst b/docs/source/pages/neurodata_types/core/ProcessingModule.rst index 55bbc315..d32cd9b7 100644 --- a/docs/source/pages/neurodata_types/core/ProcessingModule.rst +++ b/docs/source/pages/neurodata_types/core/ProcessingModule.rst @@ -1,6 +1,10 @@ ProcessingModule ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ProcessingModule. + .. mat:module:: types.core .. autoclass:: types.core.ProcessingModule :members: diff --git a/docs/source/pages/neurodata_types/core/PupilTracking.rst b/docs/source/pages/neurodata_types/core/PupilTracking.rst index e45a9981..87d6d958 100644 --- a/docs/source/pages/neurodata_types/core/PupilTracking.rst +++ b/docs/source/pages/neurodata_types/core/PupilTracking.rst @@ -1,6 +1,10 @@ PupilTracking ============= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for PupilTracking. + .. mat:module:: types.core .. autoclass:: types.core.PupilTracking :members: diff --git a/docs/source/pages/neurodata_types/core/RGBAImage.rst b/docs/source/pages/neurodata_types/core/RGBAImage.rst index 420307c2..79879cb3 100644 --- a/docs/source/pages/neurodata_types/core/RGBAImage.rst +++ b/docs/source/pages/neurodata_types/core/RGBAImage.rst @@ -1,6 +1,10 @@ RGBAImage ========= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for RGBAImage. + .. mat:module:: types.core .. autoclass:: types.core.RGBAImage :members: diff --git a/docs/source/pages/neurodata_types/core/RGBImage.rst b/docs/source/pages/neurodata_types/core/RGBImage.rst index 3dfc9870..bacbeca6 100644 --- a/docs/source/pages/neurodata_types/core/RGBImage.rst +++ b/docs/source/pages/neurodata_types/core/RGBImage.rst @@ -1,6 +1,10 @@ RGBImage ======== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for RGBImage. + .. mat:module:: types.core .. autoclass:: types.core.RGBImage :members: diff --git a/docs/source/pages/neurodata_types/core/RepetitionsTable.rst b/docs/source/pages/neurodata_types/core/RepetitionsTable.rst index 71c33b5a..be7b4538 100644 --- a/docs/source/pages/neurodata_types/core/RepetitionsTable.rst +++ b/docs/source/pages/neurodata_types/core/RepetitionsTable.rst @@ -1,6 +1,10 @@ RepetitionsTable ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for RepetitionsTable. + .. mat:module:: types.core .. autoclass:: types.core.RepetitionsTable :members: diff --git a/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst b/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst index c48db5d9..c89359c3 100644 --- a/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst +++ b/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst @@ -1,6 +1,10 @@ RoiResponseSeries ================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for RoiResponseSeries. + .. mat:module:: types.core .. autoclass:: types.core.RoiResponseSeries :members: diff --git a/docs/source/pages/neurodata_types/core/ScratchData.rst b/docs/source/pages/neurodata_types/core/ScratchData.rst index d121abdd..73583d00 100644 --- a/docs/source/pages/neurodata_types/core/ScratchData.rst +++ b/docs/source/pages/neurodata_types/core/ScratchData.rst @@ -1,6 +1,10 @@ ScratchData =========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for ScratchData. + .. mat:module:: types.core .. autoclass:: types.core.ScratchData :members: diff --git a/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst b/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst index 8a343944..bd733cdb 100644 --- a/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst +++ b/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst @@ -1,6 +1,10 @@ SequentialRecordingsTable ========================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for SequentialRecordingsTable. + .. mat:module:: types.core .. autoclass:: types.core.SequentialRecordingsTable :members: diff --git a/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst b/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst index 0598bcb6..a9cee13f 100644 --- a/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst +++ b/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst @@ -1,6 +1,10 @@ SimultaneousRecordingsTable =========================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for SimultaneousRecordingsTable. + .. mat:module:: types.core .. autoclass:: types.core.SimultaneousRecordingsTable :members: diff --git a/docs/source/pages/neurodata_types/core/SpatialSeries.rst b/docs/source/pages/neurodata_types/core/SpatialSeries.rst index 662c9c46..da64d25e 100644 --- a/docs/source/pages/neurodata_types/core/SpatialSeries.rst +++ b/docs/source/pages/neurodata_types/core/SpatialSeries.rst @@ -1,6 +1,10 @@ SpatialSeries ============= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for SpatialSeries. + .. mat:module:: types.core .. autoclass:: types.core.SpatialSeries :members: diff --git a/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst b/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst index 4cb93f2d..67d73868 100644 --- a/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst +++ b/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst @@ -1,6 +1,10 @@ SpikeEventSeries ================ +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for SpikeEventSeries. + .. mat:module:: types.core .. autoclass:: types.core.SpikeEventSeries :members: diff --git a/docs/source/pages/neurodata_types/core/Subject.rst b/docs/source/pages/neurodata_types/core/Subject.rst index a227a712..97c8bdcb 100644 --- a/docs/source/pages/neurodata_types/core/Subject.rst +++ b/docs/source/pages/neurodata_types/core/Subject.rst @@ -1,6 +1,10 @@ Subject ======= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Subject. + .. mat:module:: types.core .. autoclass:: types.core.Subject :members: diff --git a/docs/source/pages/neurodata_types/core/SweepTable.rst b/docs/source/pages/neurodata_types/core/SweepTable.rst index 0088de4f..21aa040f 100644 --- a/docs/source/pages/neurodata_types/core/SweepTable.rst +++ b/docs/source/pages/neurodata_types/core/SweepTable.rst @@ -1,6 +1,10 @@ SweepTable ========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for SweepTable. + .. mat:module:: types.core .. autoclass:: types.core.SweepTable :members: diff --git a/docs/source/pages/neurodata_types/core/TimeIntervals.rst b/docs/source/pages/neurodata_types/core/TimeIntervals.rst index 7385bf7f..92aa48a6 100644 --- a/docs/source/pages/neurodata_types/core/TimeIntervals.rst +++ b/docs/source/pages/neurodata_types/core/TimeIntervals.rst @@ -1,6 +1,10 @@ TimeIntervals ============= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for TimeIntervals. + .. mat:module:: types.core .. autoclass:: types.core.TimeIntervals :members: diff --git a/docs/source/pages/neurodata_types/core/TimeSeries.rst b/docs/source/pages/neurodata_types/core/TimeSeries.rst index fbc71723..bb98563e 100644 --- a/docs/source/pages/neurodata_types/core/TimeSeries.rst +++ b/docs/source/pages/neurodata_types/core/TimeSeries.rst @@ -1,6 +1,10 @@ TimeSeries ========== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for TimeSeries. + .. mat:module:: types.core .. autoclass:: types.core.TimeSeries :members: diff --git a/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst b/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst index 93594259..874d2dee 100644 --- a/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst +++ b/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst @@ -1,6 +1,10 @@ TimeSeriesReferenceVectorData ============================= +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for TimeSeriesReferenceVectorData. + .. mat:module:: types.core .. autoclass:: types.core.TimeSeriesReferenceVectorData :members: diff --git a/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst b/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst index e71db63d..fe93fcb7 100644 --- a/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst +++ b/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst @@ -1,6 +1,10 @@ TwoPhotonSeries =============== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for TwoPhotonSeries. + .. mat:module:: types.core .. autoclass:: types.core.TwoPhotonSeries :members: diff --git a/docs/source/pages/neurodata_types/core/Units.rst b/docs/source/pages/neurodata_types/core/Units.rst index 54a4a71f..246dc4b2 100644 --- a/docs/source/pages/neurodata_types/core/Units.rst +++ b/docs/source/pages/neurodata_types/core/Units.rst @@ -1,6 +1,10 @@ Units ===== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for Units. + .. mat:module:: types.core .. autoclass:: types.core.Units :members: diff --git a/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst b/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst index b4ede53f..b64d01f7 100644 --- a/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst @@ -1,6 +1,10 @@ VoltageClampSeries ================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for VoltageClampSeries. + .. mat:module:: types.core .. autoclass:: types.core.VoltageClampSeries :members: diff --git a/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst b/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst index d1cc01a3..aecef25a 100644 --- a/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst +++ b/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst @@ -1,6 +1,10 @@ VoltageClampStimulusSeries ========================== +See also: + :nwb-core-type-schema:`Format Specification` + or :nwb-core-type-schema:`Source Specification` for VoltageClampStimulusSeries. + .. mat:module:: types.core .. autoclass:: types.core.VoltageClampStimulusSeries :members: diff --git a/docs/source/pages/neurodata_types/core/index.rst b/docs/source/pages/neurodata_types/core/index.rst index 34437913..463ecb17 100644 --- a/docs/source/pages/neurodata_types/core/index.rst +++ b/docs/source/pages/neurodata_types/core/index.rst @@ -1,5 +1,5 @@ -Neurodata Types -=============== +Core Neurodata Types +==================== These are the MatNWB neurodata types from the core schema specification. diff --git a/docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst b/docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst new file mode 100644 index 00000000..c548f96c --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst @@ -0,0 +1,11 @@ +AlignedDynamicTable +=================== + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for AlignedDynamicTable. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.AlignedDynamicTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst b/docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst new file mode 100644 index 00000000..fd666f0e --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst @@ -0,0 +1,11 @@ +CSRMatrix +========= + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for CSRMatrix. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.CSRMatrix + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/Container.rst b/docs/source/pages/neurodata_types/hdmf_common/Container.rst new file mode 100644 index 00000000..18aebc27 --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/Container.rst @@ -0,0 +1,11 @@ +Container +========= + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for Container. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.Container + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/Data.rst b/docs/source/pages/neurodata_types/hdmf_common/Data.rst new file mode 100644 index 00000000..a6082311 --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/Data.rst @@ -0,0 +1,11 @@ +Data +==== + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for Data. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.Data + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst b/docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst new file mode 100644 index 00000000..36e15275 --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst @@ -0,0 +1,11 @@ +DynamicTable +============ + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for DynamicTable. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.DynamicTable + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst b/docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst new file mode 100644 index 00000000..c36e282f --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst @@ -0,0 +1,11 @@ +DynamicTableRegion +================== + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for DynamicTableRegion. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.DynamicTableRegion + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst b/docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst new file mode 100644 index 00000000..30a5b243 --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst @@ -0,0 +1,11 @@ +ElementIdentifiers +================== + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for ElementIdentifiers. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.ElementIdentifiers + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst b/docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst new file mode 100644 index 00000000..bd08da3d --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst @@ -0,0 +1,11 @@ +SimpleMultiContainer +==================== + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for SimpleMultiContainer. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.SimpleMultiContainer + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/VectorData.rst b/docs/source/pages/neurodata_types/hdmf_common/VectorData.rst new file mode 100644 index 00000000..73f54ba7 --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/VectorData.rst @@ -0,0 +1,11 @@ +VectorData +========== + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for VectorData. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.VectorData + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst b/docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst new file mode 100644 index 00000000..ad12c4c3 --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst @@ -0,0 +1,11 @@ +VectorIndex +=========== + +See also: + :nwb-hdmf_common-type-schema:`Format Specification` + or :nwb-hdmf_common-type-schema:`Source Specification` for VectorIndex. + +.. mat:module:: types.hdmf_common +.. autoclass:: types.hdmf_common.VectorIndex + :members: + :show-inheritance: diff --git a/docs/source/pages/neurodata_types/hdmf_common/index.rst b/docs/source/pages/neurodata_types/hdmf_common/index.rst new file mode 100644 index 00000000..01b7409a --- /dev/null +++ b/docs/source/pages/neurodata_types/hdmf_common/index.rst @@ -0,0 +1,19 @@ +HDMF-Common Data Types +====================== + +These are the MatNWB neurodata types from the hdmf_common schema specification. + +.. toctree:: + :maxdepth: 2 + :caption: Functions + + AlignedDynamicTable + CSRMatrix + Container + Data + DynamicTable + DynamicTableRegion + ElementIdentifiers + SimpleMultiContainer + VectorData + VectorIndex \ No newline at end of file diff --git a/docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc b/docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc index 2fc5ab6f1cee68e3a9a9df0bc4813545bf29d35c..ca46ba0146dfecb420736c4d068eed1a4b313803 100644 GIT binary patch delta 2584 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 N+g1)Ka{C|ZzTeao>+Jvl diff --git a/docs/source/sphinx_extensions/_util.py b/docs/source/sphinx_extensions/_util.py new file mode 100644 index 00000000..eca81a2f --- /dev/null +++ b/docs/source/sphinx_extensions/_util.py @@ -0,0 +1,24 @@ +import os + +def list_neurodata_types(namespace_name): + # Get the absolute path of the script's directory + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Compute the absolute path two levels up from the script's directory + matnwb_src_dir = os.path.abspath(os.path.join(script_dir, '..', '..', '..')) + + # Construct the path to the namespace's types directory + types_dir = os.path.join(matnwb_src_dir, '+types', f"+{namespace_name}") + + # List to store the file names without extension + neurodata_types = [] + + # Check if the directory exists + if os.path.isdir(types_dir): + # Iterate through all .m files in the directory + for file_name in os.listdir(types_dir): + if file_name.endswith('.m'): + # Remove the file extension and add to the list + neurodata_types.append(os.path.splitext(file_name)[0]) + + return neurodata_types diff --git a/docs/source/sphinx_extensions/custom_roles.py b/docs/source/sphinx_extensions/custom_roles.py new file mode 100644 index 00000000..4a482961 --- /dev/null +++ b/docs/source/sphinx_extensions/custom_roles.py @@ -0,0 +1,87 @@ +import os +from sphinx.roles import XRefRole +from docutils import nodes, utils +from pprint import pprint +from _util import list_neurodata_types + + +class MatClassRole(XRefRole): + def process_link(self, env, refnode, has_explicit_title, title, target): + """ + Process the link for fundamental matlab classes. + """ + + # External class handling + base_url = "https://www.mathworks.com/help/matlab/ref/" + refnode["refuri"] = f"{base_url}{target}.html" + #refnode["mat:class"] = env.temp_data.get("mat:class") + #refnode["reftype"] = "class" + #print(refnode["mat:class"]) + #print(refnode["refuri"]) + if not has_explicit_title: + title = target + return title, target + + # Could this method be a way to fix the transformation to a file:// uri for the link target? + #def result_nodes(self, document, env, node, is_ref): + # pprint(vars(node)) + # return [node], [] + + +def register_matlab_types(app, env, docname): + """ + Register MATLAB types in the 'mat' domain with external links. + """ + + # MATLAB types and their corresponding external URLs + matlab_types = { + "double": "https://www.mathworks.com/help/matlab/ref/double.html", + "single": "https://www.mathworks.com/help/matlab/ref/single.html", + "int8": "https://www.mathworks.com/help/matlab/ref/int8.html", + "uint8": "https://www.mathworks.com/help/matlab/ref/uint8.html", + "int16": "https://www.mathworks.com/help/matlab/ref/int16.html", + "uint16": "https://www.mathworks.com/help/matlab/ref/uint16.html", + "int32": "https://www.mathworks.com/help/matlab/ref/int32.html", + "uint32": "https://www.mathworks.com/help/matlab/ref/uint32.html", + "int64": "https://www.mathworks.com/help/matlab/ref/int64.html", + "uint64": "https://www.mathworks.com/help/matlab/ref/uint64.html", + "logical": "https://www.mathworks.com/help/matlab/ref/logical.html", + "char": "https://www.mathworks.com/help/matlab/ref/char.html", + "cell": "https://www.mathworks.com/help/matlab/ref/cell.html", + "struct": "https://www.mathworks.com/help/matlab/ref/struct.html", + "table": "https://www.mathworks.com/help/matlab/ref/table.html", + "categorical": "https://www.mathworks.com/help/matlab/ref/categorical.html", + "datetime": "https://www.mathworks.com/help/matlab/ref/datetime.html", + "duration": "https://www.mathworks.com/help/matlab/ref/duration.html", + "calendarDuration": "https://www.mathworks.com/help/matlab/ref/calendarduration.html", + "function_handle": "https://www.mathworks.com/help/matlab/ref/function_handle.html", + "string": "https://www.mathworks.com/help/matlab/ref/string.html", + "complex": "https://www.mathworks.com/help/matlab/ref/complex.html", + } + + # Add MATLAB types to the mat domain + if "objects" not in env.domaindata["mat"]: + env.domaindata["mat"]["objects"] = {} + + for type_name, url in matlab_types.items(): + # Register the type with a special 'external' object type + env.domaindata["mat"]["objects"][type_name] = (url, "matclass") + + +def register_type_short_names(app, env, docname): +# register_type_short_names - Register short names for neurodata types as classes + + if "objects" not in env.domaindata["mat"]: + env.domaindata["mat"]["objects"] = {} + + core_nwb_types = list_neurodata_types('core') + for type_name in core_nwb_types: + # Register the type with a special 'external' object type + docname = f"pages/neurodata_types/core/{type_name}" + env.domaindata["mat"]["objects"][type_name] = (docname, "class") + + hdmf_data_types = list_neurodata_types('hdmf_common') + for type_name in hdmf_data_types: + # Register the type with a special 'external' object type + docname = f"pages/neurodata_types/hdmf_common/{type_name}" + env.domaindata["mat"]["objects"][type_name] = (docname, "class") diff --git a/docs/source/sphinx_extensions/docstring_processors.py b/docs/source/sphinx_extensions/docstring_processors.py index 2b3c9d51..6e17f80b 100644 --- a/docs/source/sphinx_extensions/docstring_processors.py +++ b/docs/source/sphinx_extensions/docstring_processors.py @@ -1,8 +1,11 @@ +import os import re +from _util import list_neurodata_types def process_matlab_docstring(app, what, name, obj, options, lines): _format_matlab_type_as_code_literal(lines) + _format_nwbtype_shortnames(lines) _make_syntax_examples_code_literals(lines) _format_input_arguments(lines) _split_and_format_example_lines(lines) @@ -18,19 +21,26 @@ def _format_matlab_type_as_code_literal(lines): "string", "complex" } - # Regex pattern to match MATLAB types as whole words, optionally wrapped in parentheses type_pattern = re.compile( - rf"(?\(?)" + rf"(?P\()" rf"(?P{'|'.join(re.escape(t) for t in matlab_types)})" - rf"(?P\)?)(?!\w)" + rf"(?P\))" ) - + for i, line in enumerate(lines): # Replace matches with inline code formatting, preserving parentheses + # lines[i] = type_pattern.sub( + # lambda match: ( + # f"{match.group('before') or ''}" + # f"``{match.group('type')}``" + # f"{match.group('after') or ''}" + # ), + # line + # ) lines[i] = type_pattern.sub( lambda match: ( f"{match.group('before') or ''}" - f"``{match.group('type')}``" + f":matclass:`{match.group('type')}`" f"{match.group('after') or ''}" ), line @@ -142,6 +152,30 @@ def _split_and_format_example_lines(lines): i += 1 # Move to the next line if no match +def _format_nwbtype_shortnames(lines): + """ + Preprocesses a list of docstring lines to replace occurrences of patterns + with their respective :class:`pattern` references. + + Modifies the list of lines in place. + + Parameters: + lines (list of str): The docstring lines to preprocess. + patterns (list of str): A list of patterns (e.g., ["TimeSeries", "DataArray"]). + """ + + patterns = list_neurodata_types('core') + list_neurodata_types('hdmf_common') + # Create a dictionary for replacements + replacements = {pattern: f"(:class:`{pattern}`)" for pattern in patterns} + + # Compile a regex that matches any of the patterns + regex = re.compile(r'\((' + '|'.join(map(re.escape, patterns)) + r')\)') + + # Iterate over the lines and replace matches in place + for i in range(len(lines)): + lines[i] = regex.sub(lambda match: replacements[match.group(1)], lines[i]) + + def _is_section_header(line): # Regex to identify section headers section_header_pattern = re.compile(r"^\s*%?\s*[A-Za-z ]+:") diff --git a/tools/documentation/_rst_templates/index_core_types.rst.template b/tools/documentation/_rst_templates/index_core_types.rst.template deleted file mode 100644 index ecf43f5e..00000000 --- a/tools/documentation/_rst_templates/index_core_types.rst.template +++ /dev/null @@ -1,10 +0,0 @@ -Neurodata Types -=============== - -These are the MatNWB neurodata types from the core schema specification. - -.. toctree:: - :maxdepth: 2 - :caption: Functions - -{{ function_list }} \ No newline at end of file diff --git a/tools/documentation/_rst_templates/index_functions.rst.template b/tools/documentation/_rst_templates/index_functions.rst.template index 9412c5da..102aa5f4 100644 --- a/tools/documentation/_rst_templates/index_functions.rst.template +++ b/tools/documentation/_rst_templates/index_functions.rst.template @@ -7,4 +7,4 @@ These are the main functions of the MatNWB API :maxdepth: 2 :caption: Functions -{{ function_list }} \ No newline at end of file +{{ function_list }} diff --git a/tools/documentation/_rst_templates/index_nwb_types.rst.template b/tools/documentation/_rst_templates/index_nwb_types.rst.template new file mode 100644 index 00000000..3248802b --- /dev/null +++ b/tools/documentation/_rst_templates/index_nwb_types.rst.template @@ -0,0 +1,10 @@ +{{ section_title }} +{{ section_title_underline }} + +These are the MatNWB neurodata types from the {{ namespace_name }} schema specification. + +.. toctree:: + :maxdepth: 2 + :caption: Functions + +{{ function_list }} diff --git a/tools/documentation/_rst_templates/index_tutorials.rst.template b/tools/documentation/_rst_templates/index_tutorials.rst.template index ca293976..987ebf45 100644 --- a/tools/documentation/_rst_templates/index_tutorials.rst.template +++ b/tools/documentation/_rst_templates/index_tutorials.rst.template @@ -5,4 +5,4 @@ Tutorials :maxdepth: 1 :caption: Tutorials -{{ file_list }} \ No newline at end of file +{{ file_list }} diff --git a/tools/documentation/_rst_templates/neurodata_class.rst.template b/tools/documentation/_rst_templates/neurodata_class.rst.template new file mode 100644 index 00000000..d23c3797 --- /dev/null +++ b/tools/documentation/_rst_templates/neurodata_class.rst.template @@ -0,0 +1,11 @@ +{{ class_name }} +{{ class_name_header_underline }} + +See also: + :nwb-{{ namespace_name }}-type-schema:`Format Specification<{{ lower_class_name }}>` + or :nwb-{{ namespace_name }}-type-schema:`Source Specification` for {{ class_name }}. + +.. mat:module:: {{ module_name }} +.. autoclass:: {{ full_class_name }} + :members: + :show-inheritance: diff --git a/tools/documentation/matnwb_generateRstFilesFromCode.m b/tools/documentation/matnwb_generateRstFilesFromCode.m index bb5492b7..5bc49733 100644 --- a/tools/documentation/matnwb_generateRstFilesFromCode.m +++ b/tools/documentation/matnwb_generateRstFilesFromCode.m @@ -1,5 +1,7 @@ function matnwb_generateRstFilesFromCode() generateRstForTutorials() generateRstForNwbFunctions() - generateRstForNwbTypeClasses() + generateRstForNeurodataTypeClasses('core') + generateRstForNeurodataTypeClasses('hdmf_common') + %generateRstForNeurodataTypeClasses('hdmf_experimental') end \ No newline at end of file diff --git a/tools/documentation/private/generateRstForNeurodataTypeClasses.m b/tools/documentation/private/generateRstForNeurodataTypeClasses.m new file mode 100644 index 00000000..0c26f9bf --- /dev/null +++ b/tools/documentation/private/generateRstForNeurodataTypeClasses.m @@ -0,0 +1,58 @@ +function generateRstForNeurodataTypeClasses(namespaceName) +% generateRstForNeurodataTypeClasses Generate rst files for each Neurodata matnwb class + + arguments + namespaceName (1,1) string + end + namespaceName = char(namespaceName); + + rootDir = misc.getMatnwbDir(); + classFiles = dir(fullfile(rootDir, '+types', ['+', namespaceName], '*.m')); + + docsSourceRootDir = fullfile(misc.getMatnwbDir, 'docs', 'source'); + exportDir = fullfile(docsSourceRootDir, 'pages', 'neurodata_types', namespaceName); + if ~isfolder(exportDir); mkdir(exportDir); end + + functionTemplate = fileread( getRstTemplateFile('function') ); + classTemplate = fileread( getRstTemplateFile('neurodata_class') ); + + for i = 1:numel(classFiles) + iFile = fullfile(classFiles(i).folder, classFiles(i).name); + [~, fileName] = fileparts(iFile); + + data.module_name = sprintf('types.%s', namespaceName); + data.namespace_name = namespaceName; + data.class_name = fileName; + data.lower_class_name = lower(data.class_name); + data.class_name_header_underline = repmat('=', 1, numel(data.class_name)); + data.full_class_name = sprintf('types.%s.%s', namespaceName, fileName); + + mc = meta.class.fromName(data.full_class_name); + if isempty(mc) + currentTemplate = functionTemplate; + else + currentTemplate = classTemplate; + end + + thisRst = fillTemplate(currentTemplate, data); + rstFilePath = fullfile(exportDir, [fileName, '.rst']); + filewrite(rstFilePath, thisRst); + end + + % Create index + indexTemplate = fileread( getRstTemplateFile('index_nwb_types') ); + [~, functionNames] = fileparts(string({classFiles.name})); + data.function_list = strjoin(" "+functionNames, newline); + + switch namespaceName + case 'core' + data.section_title = "Core Neurodata Types"; + case 'hdmf_common' + data.section_title = "HDMF-Common Data Types"; + end + data.section_title_underline = repmat('=', 1, strlength(data.section_title)); + + thisRst = fillTemplate(indexTemplate, data); + rstFilePath = fullfile(exportDir, ['index', '.rst']); + filewrite(rstFilePath, thisRst); +end diff --git a/tools/documentation/private/generateRstForNwbTypeClasses.m b/tools/documentation/private/generateRstForNwbTypeClasses.m deleted file mode 100644 index cb224225..00000000 --- a/tools/documentation/private/generateRstForNwbTypeClasses.m +++ /dev/null @@ -1,43 +0,0 @@ -function generateRstForNwbTypeClasses() -% generateRstForNwbTypeClasses Generate rst files for each Neurodata matnwb class - - rootDir = misc.getMatnwbDir(); - coreTypeFiles = dir(fullfile(rootDir, '+types', '+core', '*.m')); - - docsSourceRootDir = fullfile(misc.getMatnwbDir, 'docs', 'source'); - exportDir = fullfile(docsSourceRootDir, 'pages', 'neurodata_types', 'core'); - if ~isfolder(exportDir); mkdir(exportDir); end - - functionTemplate = fileread( getRstTemplateFile('function') ); - classTemplate = fileread( getRstTemplateFile('class') ); - - for i = 1:numel(coreTypeFiles) - iFile = fullfile(coreTypeFiles(i).folder, coreTypeFiles(i).name); - [~, functionName] = fileparts(iFile); - - data.function_name = functionName; - data.module_name = 'types.core'; - data.function_header_underline = repmat('=', 1, numel(functionName)); - data.full_function_name = sprintf('types.core.%s', functionName); - - mc = meta.class.fromName(data.full_function_name); - if isempty(mc) - currentTemplate = functionTemplate; - else - currentTemplate = classTemplate; - end - - thisRst = fillTemplate(currentTemplate, data); - rstFilePath = fullfile(exportDir, [functionName, '.rst']); - filewrite(rstFilePath, thisRst); - end - - % Create index - indexTemplate = fileread( getRstTemplateFile('index_core_types') ); - [~, functionNames] = fileparts(string({coreTypeFiles.name})); - data.function_list = strjoin(" "+functionNames, newline); - - thisRst = fillTemplate(indexTemplate, data); - rstFilePath = fullfile(exportDir, ['index', '.rst']); - filewrite(rstFilePath, thisRst); -end From ab1f8cdb87d4cc69ff8b8810abb2a87f4d40e098 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Dec 2024 16:27:10 +0100 Subject: [PATCH 13/30] Delete docstring_processors.cpython-311.pyc --- .../docstring_processors.cpython-311.pyc | Bin 8558 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc diff --git a/docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc b/docs/source/sphinx_extensions/__pycache__/docstring_processors.cpython-311.pyc deleted file mode 100644 index ca46ba0146dfecb420736c4d068eed1a4b313803..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8558 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 Vm#JgWK#Jtc920`j*%m Date: Sun, 8 Dec 2024 16:31:39 +0100 Subject: [PATCH 14/30] Update neurdata class rst template --- docs/source/pages/functions/index.rst | 2 +- .../neurodata_types/core/AbstractFeatureSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/AnnotationSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/BehavioralEpochs.rst | 12 ++++++++++++ .../pages/neurodata_types/core/BehavioralEvents.rst | 12 ++++++++++++ .../neurodata_types/core/BehavioralTimeSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ClusterWaveforms.rst | 12 ++++++++++++ .../source/pages/neurodata_types/core/Clustering.rst | 12 ++++++++++++ .../pages/neurodata_types/core/CompassDirection.rst | 12 ++++++++++++ .../neurodata_types/core/CorrectedImageStack.rst | 12 ++++++++++++ .../neurodata_types/core/CurrentClampSeries.rst | 12 ++++++++++++ .../core/CurrentClampStimulusSeries.rst | 12 ++++++++++++ .../neurodata_types/core/DecompositionSeries.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/Device.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/DfOverF.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ElectricalSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ElectrodeGroup.rst | 12 ++++++++++++ .../pages/neurodata_types/core/EventDetection.rst | 12 ++++++++++++ .../pages/neurodata_types/core/EventWaveform.rst | 12 ++++++++++++ .../core/ExperimentalConditionsTable.rst | 12 ++++++++++++ .../pages/neurodata_types/core/EyeTracking.rst | 12 ++++++++++++ .../pages/neurodata_types/core/FeatureExtraction.rst | 12 ++++++++++++ .../pages/neurodata_types/core/FilteredEphys.rst | 12 ++++++++++++ .../pages/neurodata_types/core/Fluorescence.rst | 12 ++++++++++++ .../pages/neurodata_types/core/GrayscaleImage.rst | 12 ++++++++++++ .../pages/neurodata_types/core/IZeroClampSeries.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/Image.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ImageMaskSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ImageReferences.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ImageSegmentation.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ImageSeries.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/Images.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ImagingPlane.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ImagingRetinotopy.rst | 12 ++++++++++++ .../pages/neurodata_types/core/IndexSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/IntervalSeries.rst | 12 ++++++++++++ .../neurodata_types/core/IntracellularElectrode.rst | 12 ++++++++++++ .../core/IntracellularElectrodesTable.rst | 12 ++++++++++++ .../core/IntracellularRecordingsTable.rst | 12 ++++++++++++ .../core/IntracellularResponsesTable.rst | 12 ++++++++++++ .../core/IntracellularStimuliTable.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/LFP.rst | 12 ++++++++++++ .../pages/neurodata_types/core/LabMetaData.rst | 12 ++++++++++++ .../pages/neurodata_types/core/MotionCorrection.rst | 12 ++++++++++++ .../pages/neurodata_types/core/NWBContainer.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/NWBData.rst | 12 ++++++++++++ .../pages/neurodata_types/core/NWBDataInterface.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/NWBFile.rst | 12 ++++++++++++ .../pages/neurodata_types/core/OnePhotonSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/OpticalChannel.rst | 12 ++++++++++++ .../pages/neurodata_types/core/OpticalSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/OptogeneticSeries.rst | 12 ++++++++++++ .../neurodata_types/core/OptogeneticStimulusSite.rst | 12 ++++++++++++ .../pages/neurodata_types/core/PatchClampSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/PlaneSegmentation.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/Position.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ProcessingModule.rst | 12 ++++++++++++ .../pages/neurodata_types/core/PupilTracking.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/RGBAImage.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/RGBImage.rst | 12 ++++++++++++ .../pages/neurodata_types/core/RepetitionsTable.rst | 12 ++++++++++++ .../pages/neurodata_types/core/RoiResponseSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/ScratchData.rst | 12 ++++++++++++ .../core/SequentialRecordingsTable.rst | 12 ++++++++++++ .../core/SimultaneousRecordingsTable.rst | 12 ++++++++++++ .../pages/neurodata_types/core/SpatialSeries.rst | 12 ++++++++++++ .../pages/neurodata_types/core/SpikeEventSeries.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/Subject.rst | 12 ++++++++++++ .../source/pages/neurodata_types/core/SweepTable.rst | 12 ++++++++++++ .../pages/neurodata_types/core/TimeIntervals.rst | 12 ++++++++++++ .../source/pages/neurodata_types/core/TimeSeries.rst | 12 ++++++++++++ .../core/TimeSeriesReferenceVectorData.rst | 12 ++++++++++++ .../pages/neurodata_types/core/TwoPhotonSeries.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/Units.rst | 12 ++++++++++++ .../neurodata_types/core/VoltageClampSeries.rst | 12 ++++++++++++ .../core/VoltageClampStimulusSeries.rst | 12 ++++++++++++ docs/source/pages/neurodata_types/core/index.rst | 2 +- .../hdmf_common/AlignedDynamicTable.rst | 12 ++++++++++++ .../pages/neurodata_types/hdmf_common/CSRMatrix.rst | 12 ++++++++++++ .../pages/neurodata_types/hdmf_common/Container.rst | 12 ++++++++++++ .../pages/neurodata_types/hdmf_common/Data.rst | 12 ++++++++++++ .../neurodata_types/hdmf_common/DynamicTable.rst | 12 ++++++++++++ .../hdmf_common/DynamicTableRegion.rst | 12 ++++++++++++ .../hdmf_common/ElementIdentifiers.rst | 12 ++++++++++++ .../hdmf_common/SimpleMultiContainer.rst | 12 ++++++++++++ .../pages/neurodata_types/hdmf_common/VectorData.rst | 12 ++++++++++++ .../neurodata_types/hdmf_common/VectorIndex.rst | 12 ++++++++++++ .../pages/neurodata_types/hdmf_common/index.rst | 2 +- .../_rst_templates/neurodata_class.rst.template | 12 ++++++++++++ 89 files changed, 1035 insertions(+), 3 deletions(-) diff --git a/docs/source/pages/functions/index.rst b/docs/source/pages/functions/index.rst index fae4ae4d..35497918 100644 --- a/docs/source/pages/functions/index.rst +++ b/docs/source/pages/functions/index.rst @@ -12,4 +12,4 @@ These are the main functions of the MatNWB API nwbExport generateCore generateExtension - nwbClearGenerated \ No newline at end of file + nwbClearGenerated diff --git a/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst b/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst index a2cdd5c6..07e4f992 100644 --- a/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst +++ b/docs/source/pages/neurodata_types/core/AbstractFeatureSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.AbstractFeatureSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/AnnotationSeries.rst b/docs/source/pages/neurodata_types/core/AnnotationSeries.rst index 479c2acb..b1c9b0d2 100644 --- a/docs/source/pages/neurodata_types/core/AnnotationSeries.rst +++ b/docs/source/pages/neurodata_types/core/AnnotationSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.AnnotationSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst b/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst index 23a28d1f..0e2117ad 100644 --- a/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst +++ b/docs/source/pages/neurodata_types/core/BehavioralEpochs.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.BehavioralEpochs :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/BehavioralEvents.rst b/docs/source/pages/neurodata_types/core/BehavioralEvents.rst index 99379bb2..e63b194f 100644 --- a/docs/source/pages/neurodata_types/core/BehavioralEvents.rst +++ b/docs/source/pages/neurodata_types/core/BehavioralEvents.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.BehavioralEvents :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst b/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst index 03774dd6..d9e0d546 100644 --- a/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst +++ b/docs/source/pages/neurodata_types/core/BehavioralTimeSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.BehavioralTimeSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst b/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst index 9b70c299..f425b563 100644 --- a/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst +++ b/docs/source/pages/neurodata_types/core/ClusterWaveforms.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ClusterWaveforms :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Clustering.rst b/docs/source/pages/neurodata_types/core/Clustering.rst index 3089328f..a3851633 100644 --- a/docs/source/pages/neurodata_types/core/Clustering.rst +++ b/docs/source/pages/neurodata_types/core/Clustering.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Clustering :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/CompassDirection.rst b/docs/source/pages/neurodata_types/core/CompassDirection.rst index 10b72ccc..bc8da3f0 100644 --- a/docs/source/pages/neurodata_types/core/CompassDirection.rst +++ b/docs/source/pages/neurodata_types/core/CompassDirection.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.CompassDirection :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst b/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst index b00dd099..5e3c9151 100644 --- a/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst +++ b/docs/source/pages/neurodata_types/core/CorrectedImageStack.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.CorrectedImageStack :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst b/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst index 189e4cdd..313a119f 100644 --- a/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/CurrentClampSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.CurrentClampSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst b/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst index 4ec2a688..5dce9ba9 100644 --- a/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst +++ b/docs/source/pages/neurodata_types/core/CurrentClampStimulusSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.CurrentClampStimulusSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/DecompositionSeries.rst b/docs/source/pages/neurodata_types/core/DecompositionSeries.rst index 9037a541..7796c0f1 100644 --- a/docs/source/pages/neurodata_types/core/DecompositionSeries.rst +++ b/docs/source/pages/neurodata_types/core/DecompositionSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.DecompositionSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Device.rst b/docs/source/pages/neurodata_types/core/Device.rst index 2891951e..d0ffa727 100644 --- a/docs/source/pages/neurodata_types/core/Device.rst +++ b/docs/source/pages/neurodata_types/core/Device.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Device :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/DfOverF.rst b/docs/source/pages/neurodata_types/core/DfOverF.rst index 58baaf2b..77720c0f 100644 --- a/docs/source/pages/neurodata_types/core/DfOverF.rst +++ b/docs/source/pages/neurodata_types/core/DfOverF.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.DfOverF :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ElectricalSeries.rst b/docs/source/pages/neurodata_types/core/ElectricalSeries.rst index a8994f3e..c3b7cfbc 100644 --- a/docs/source/pages/neurodata_types/core/ElectricalSeries.rst +++ b/docs/source/pages/neurodata_types/core/ElectricalSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ElectricalSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst b/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst index 7bef1742..ae2a2c37 100644 --- a/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst +++ b/docs/source/pages/neurodata_types/core/ElectrodeGroup.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ElectrodeGroup :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/EventDetection.rst b/docs/source/pages/neurodata_types/core/EventDetection.rst index d3c41de3..dc2ae86b 100644 --- a/docs/source/pages/neurodata_types/core/EventDetection.rst +++ b/docs/source/pages/neurodata_types/core/EventDetection.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.EventDetection :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/EventWaveform.rst b/docs/source/pages/neurodata_types/core/EventWaveform.rst index 8887e86b..a0c3caae 100644 --- a/docs/source/pages/neurodata_types/core/EventWaveform.rst +++ b/docs/source/pages/neurodata_types/core/EventWaveform.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.EventWaveform :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst b/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst index ab542823..106ae041 100644 --- a/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst +++ b/docs/source/pages/neurodata_types/core/ExperimentalConditionsTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ExperimentalConditionsTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/EyeTracking.rst b/docs/source/pages/neurodata_types/core/EyeTracking.rst index 5c14a7f4..f8f03482 100644 --- a/docs/source/pages/neurodata_types/core/EyeTracking.rst +++ b/docs/source/pages/neurodata_types/core/EyeTracking.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.EyeTracking :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/FeatureExtraction.rst b/docs/source/pages/neurodata_types/core/FeatureExtraction.rst index 2e82a010..f9fe65c0 100644 --- a/docs/source/pages/neurodata_types/core/FeatureExtraction.rst +++ b/docs/source/pages/neurodata_types/core/FeatureExtraction.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.FeatureExtraction :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/FilteredEphys.rst b/docs/source/pages/neurodata_types/core/FilteredEphys.rst index 3a41d7ea..9a8218e5 100644 --- a/docs/source/pages/neurodata_types/core/FilteredEphys.rst +++ b/docs/source/pages/neurodata_types/core/FilteredEphys.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.FilteredEphys :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Fluorescence.rst b/docs/source/pages/neurodata_types/core/Fluorescence.rst index 5446ecd9..227e7162 100644 --- a/docs/source/pages/neurodata_types/core/Fluorescence.rst +++ b/docs/source/pages/neurodata_types/core/Fluorescence.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Fluorescence :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/GrayscaleImage.rst b/docs/source/pages/neurodata_types/core/GrayscaleImage.rst index 4943c4af..c6cf392c 100644 --- a/docs/source/pages/neurodata_types/core/GrayscaleImage.rst +++ b/docs/source/pages/neurodata_types/core/GrayscaleImage.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.GrayscaleImage :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst b/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst index df82ead2..0dd8c742 100644 --- a/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/IZeroClampSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IZeroClampSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Image.rst b/docs/source/pages/neurodata_types/core/Image.rst index 0c2b602a..71d5d11b 100644 --- a/docs/source/pages/neurodata_types/core/Image.rst +++ b/docs/source/pages/neurodata_types/core/Image.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Image :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst b/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst index 824c9eb3..cf817773 100644 --- a/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst +++ b/docs/source/pages/neurodata_types/core/ImageMaskSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ImageMaskSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ImageReferences.rst b/docs/source/pages/neurodata_types/core/ImageReferences.rst index 9553e3c6..7b468c61 100644 --- a/docs/source/pages/neurodata_types/core/ImageReferences.rst +++ b/docs/source/pages/neurodata_types/core/ImageReferences.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ImageReferences :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ImageSegmentation.rst b/docs/source/pages/neurodata_types/core/ImageSegmentation.rst index ef348750..32355372 100644 --- a/docs/source/pages/neurodata_types/core/ImageSegmentation.rst +++ b/docs/source/pages/neurodata_types/core/ImageSegmentation.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ImageSegmentation :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ImageSeries.rst b/docs/source/pages/neurodata_types/core/ImageSeries.rst index c71044d7..9e6c987d 100644 --- a/docs/source/pages/neurodata_types/core/ImageSeries.rst +++ b/docs/source/pages/neurodata_types/core/ImageSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ImageSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Images.rst b/docs/source/pages/neurodata_types/core/Images.rst index c724a7e0..fab43149 100644 --- a/docs/source/pages/neurodata_types/core/Images.rst +++ b/docs/source/pages/neurodata_types/core/Images.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Images :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ImagingPlane.rst b/docs/source/pages/neurodata_types/core/ImagingPlane.rst index ca57aa2a..57e6e6a2 100644 --- a/docs/source/pages/neurodata_types/core/ImagingPlane.rst +++ b/docs/source/pages/neurodata_types/core/ImagingPlane.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ImagingPlane :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst b/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst index 3c8484cc..22884930 100644 --- a/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst +++ b/docs/source/pages/neurodata_types/core/ImagingRetinotopy.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ImagingRetinotopy :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IndexSeries.rst b/docs/source/pages/neurodata_types/core/IndexSeries.rst index f3915c21..a2996e15 100644 --- a/docs/source/pages/neurodata_types/core/IndexSeries.rst +++ b/docs/source/pages/neurodata_types/core/IndexSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IndexSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IntervalSeries.rst b/docs/source/pages/neurodata_types/core/IntervalSeries.rst index f6edf3f0..7b188a7e 100644 --- a/docs/source/pages/neurodata_types/core/IntervalSeries.rst +++ b/docs/source/pages/neurodata_types/core/IntervalSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IntervalSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst b/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst index 9e05309c..a6808e52 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularElectrode.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IntracellularElectrode :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst b/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst index cdf4c5e3..1f2ba25a 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularElectrodesTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IntracellularElectrodesTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst b/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst index ae0f7aa6..a14f1ff8 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularRecordingsTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IntracellularRecordingsTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst b/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst index f9e1e264..b44495e9 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularResponsesTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IntracellularResponsesTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst b/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst index 630f653c..fc65405b 100644 --- a/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst +++ b/docs/source/pages/neurodata_types/core/IntracellularStimuliTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.IntracellularStimuliTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/LFP.rst b/docs/source/pages/neurodata_types/core/LFP.rst index 5170ef78..a38d4277 100644 --- a/docs/source/pages/neurodata_types/core/LFP.rst +++ b/docs/source/pages/neurodata_types/core/LFP.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.LFP :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/LabMetaData.rst b/docs/source/pages/neurodata_types/core/LabMetaData.rst index c5247ece..91aaf6ae 100644 --- a/docs/source/pages/neurodata_types/core/LabMetaData.rst +++ b/docs/source/pages/neurodata_types/core/LabMetaData.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.LabMetaData :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/MotionCorrection.rst b/docs/source/pages/neurodata_types/core/MotionCorrection.rst index 48aa0819..d7553e5e 100644 --- a/docs/source/pages/neurodata_types/core/MotionCorrection.rst +++ b/docs/source/pages/neurodata_types/core/MotionCorrection.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.MotionCorrection :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/NWBContainer.rst b/docs/source/pages/neurodata_types/core/NWBContainer.rst index 39042d51..0f76f1bc 100644 --- a/docs/source/pages/neurodata_types/core/NWBContainer.rst +++ b/docs/source/pages/neurodata_types/core/NWBContainer.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.NWBContainer :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/NWBData.rst b/docs/source/pages/neurodata_types/core/NWBData.rst index 3c7a0d88..e9f91de3 100644 --- a/docs/source/pages/neurodata_types/core/NWBData.rst +++ b/docs/source/pages/neurodata_types/core/NWBData.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.NWBData :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/NWBDataInterface.rst b/docs/source/pages/neurodata_types/core/NWBDataInterface.rst index a49858ac..bd071390 100644 --- a/docs/source/pages/neurodata_types/core/NWBDataInterface.rst +++ b/docs/source/pages/neurodata_types/core/NWBDataInterface.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.NWBDataInterface :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/NWBFile.rst b/docs/source/pages/neurodata_types/core/NWBFile.rst index 04f46379..497cfda0 100644 --- a/docs/source/pages/neurodata_types/core/NWBFile.rst +++ b/docs/source/pages/neurodata_types/core/NWBFile.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.NWBFile :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst b/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst index 5abde688..2125676f 100644 --- a/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst +++ b/docs/source/pages/neurodata_types/core/OnePhotonSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.OnePhotonSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/OpticalChannel.rst b/docs/source/pages/neurodata_types/core/OpticalChannel.rst index e32556cf..90a1609c 100644 --- a/docs/source/pages/neurodata_types/core/OpticalChannel.rst +++ b/docs/source/pages/neurodata_types/core/OpticalChannel.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.OpticalChannel :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/OpticalSeries.rst b/docs/source/pages/neurodata_types/core/OpticalSeries.rst index 3d0a6073..9253a1bc 100644 --- a/docs/source/pages/neurodata_types/core/OpticalSeries.rst +++ b/docs/source/pages/neurodata_types/core/OpticalSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.OpticalSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst b/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst index 36bcb78d..4db93ef4 100644 --- a/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst +++ b/docs/source/pages/neurodata_types/core/OptogeneticSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.OptogeneticSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst b/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst index df2c34a8..1128fa1a 100644 --- a/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst +++ b/docs/source/pages/neurodata_types/core/OptogeneticStimulusSite.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.OptogeneticStimulusSite :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/PatchClampSeries.rst b/docs/source/pages/neurodata_types/core/PatchClampSeries.rst index 37c81695..db4819eb 100644 --- a/docs/source/pages/neurodata_types/core/PatchClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/PatchClampSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.PatchClampSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst b/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst index 2f0efdd9..3c5cb1eb 100644 --- a/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst +++ b/docs/source/pages/neurodata_types/core/PlaneSegmentation.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.PlaneSegmentation :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Position.rst b/docs/source/pages/neurodata_types/core/Position.rst index 3cb94c84..38a70ea5 100644 --- a/docs/source/pages/neurodata_types/core/Position.rst +++ b/docs/source/pages/neurodata_types/core/Position.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Position :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ProcessingModule.rst b/docs/source/pages/neurodata_types/core/ProcessingModule.rst index d32cd9b7..557fb5fb 100644 --- a/docs/source/pages/neurodata_types/core/ProcessingModule.rst +++ b/docs/source/pages/neurodata_types/core/ProcessingModule.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ProcessingModule :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/PupilTracking.rst b/docs/source/pages/neurodata_types/core/PupilTracking.rst index 87d6d958..445c7c26 100644 --- a/docs/source/pages/neurodata_types/core/PupilTracking.rst +++ b/docs/source/pages/neurodata_types/core/PupilTracking.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.PupilTracking :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/RGBAImage.rst b/docs/source/pages/neurodata_types/core/RGBAImage.rst index 79879cb3..4560ff55 100644 --- a/docs/source/pages/neurodata_types/core/RGBAImage.rst +++ b/docs/source/pages/neurodata_types/core/RGBAImage.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.RGBAImage :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/RGBImage.rst b/docs/source/pages/neurodata_types/core/RGBImage.rst index bacbeca6..34d4786a 100644 --- a/docs/source/pages/neurodata_types/core/RGBImage.rst +++ b/docs/source/pages/neurodata_types/core/RGBImage.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.RGBImage :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/RepetitionsTable.rst b/docs/source/pages/neurodata_types/core/RepetitionsTable.rst index be7b4538..5457d172 100644 --- a/docs/source/pages/neurodata_types/core/RepetitionsTable.rst +++ b/docs/source/pages/neurodata_types/core/RepetitionsTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.RepetitionsTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst b/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst index c89359c3..f90e5a86 100644 --- a/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst +++ b/docs/source/pages/neurodata_types/core/RoiResponseSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.RoiResponseSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/ScratchData.rst b/docs/source/pages/neurodata_types/core/ScratchData.rst index 73583d00..adba6c51 100644 --- a/docs/source/pages/neurodata_types/core/ScratchData.rst +++ b/docs/source/pages/neurodata_types/core/ScratchData.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.ScratchData :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst b/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst index bd733cdb..cf645af2 100644 --- a/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst +++ b/docs/source/pages/neurodata_types/core/SequentialRecordingsTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.SequentialRecordingsTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst b/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst index a9cee13f..ff8807aa 100644 --- a/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst +++ b/docs/source/pages/neurodata_types/core/SimultaneousRecordingsTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.SimultaneousRecordingsTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/SpatialSeries.rst b/docs/source/pages/neurodata_types/core/SpatialSeries.rst index da64d25e..6c2d0bb9 100644 --- a/docs/source/pages/neurodata_types/core/SpatialSeries.rst +++ b/docs/source/pages/neurodata_types/core/SpatialSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.SpatialSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst b/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst index 67d73868..6e6187b4 100644 --- a/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst +++ b/docs/source/pages/neurodata_types/core/SpikeEventSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.SpikeEventSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Subject.rst b/docs/source/pages/neurodata_types/core/Subject.rst index 97c8bdcb..2c1520c6 100644 --- a/docs/source/pages/neurodata_types/core/Subject.rst +++ b/docs/source/pages/neurodata_types/core/Subject.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Subject :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/SweepTable.rst b/docs/source/pages/neurodata_types/core/SweepTable.rst index 21aa040f..ddcaa975 100644 --- a/docs/source/pages/neurodata_types/core/SweepTable.rst +++ b/docs/source/pages/neurodata_types/core/SweepTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.SweepTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/TimeIntervals.rst b/docs/source/pages/neurodata_types/core/TimeIntervals.rst index 92aa48a6..681b1b4b 100644 --- a/docs/source/pages/neurodata_types/core/TimeIntervals.rst +++ b/docs/source/pages/neurodata_types/core/TimeIntervals.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.TimeIntervals :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/TimeSeries.rst b/docs/source/pages/neurodata_types/core/TimeSeries.rst index bb98563e..d6df57c0 100644 --- a/docs/source/pages/neurodata_types/core/TimeSeries.rst +++ b/docs/source/pages/neurodata_types/core/TimeSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.TimeSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst b/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst index 874d2dee..59e348b3 100644 --- a/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst +++ b/docs/source/pages/neurodata_types/core/TimeSeriesReferenceVectorData.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.TimeSeriesReferenceVectorData :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst b/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst index fe93fcb7..45c3d66f 100644 --- a/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst +++ b/docs/source/pages/neurodata_types/core/TwoPhotonSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.TwoPhotonSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/Units.rst b/docs/source/pages/neurodata_types/core/Units.rst index 246dc4b2..56f89ea1 100644 --- a/docs/source/pages/neurodata_types/core/Units.rst +++ b/docs/source/pages/neurodata_types/core/Units.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.Units :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst b/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst index b64d01f7..6df7ef95 100644 --- a/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst +++ b/docs/source/pages/neurodata_types/core/VoltageClampSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.VoltageClampSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst b/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst index aecef25a..66adddef 100644 --- a/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst +++ b/docs/source/pages/neurodata_types/core/VoltageClampStimulusSeries.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.core.VoltageClampStimulusSeries :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/core/index.rst b/docs/source/pages/neurodata_types/core/index.rst index 463ecb17..e084a566 100644 --- a/docs/source/pages/neurodata_types/core/index.rst +++ b/docs/source/pages/neurodata_types/core/index.rst @@ -81,4 +81,4 @@ These are the MatNWB neurodata types from the core schema specification. TwoPhotonSeries Units VoltageClampSeries - VoltageClampStimulusSeries \ No newline at end of file + VoltageClampStimulusSeries diff --git a/docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst b/docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst index c548f96c..39928c83 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/AlignedDynamicTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.AlignedDynamicTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst b/docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst index fd666f0e..6c6df90d 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/CSRMatrix.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.CSRMatrix :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/Container.rst b/docs/source/pages/neurodata_types/hdmf_common/Container.rst index 18aebc27..97493f3c 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/Container.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/Container.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.Container :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/Data.rst b/docs/source/pages/neurodata_types/hdmf_common/Data.rst index a6082311..722d18ad 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/Data.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/Data.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.Data :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst b/docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst index 36e15275..2bb92720 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/DynamicTable.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.DynamicTable :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst b/docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst index c36e282f..d553ec16 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/DynamicTableRegion.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.DynamicTableRegion :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst b/docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst index 30a5b243..fb340e1c 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/ElementIdentifiers.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.ElementIdentifiers :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst b/docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst index bd08da3d..28fb6335 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/SimpleMultiContainer.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.SimpleMultiContainer :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/VectorData.rst b/docs/source/pages/neurodata_types/hdmf_common/VectorData.rst index 73f54ba7..5c82d2b3 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/VectorData.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/VectorData.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.VectorData :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst b/docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst index ad12c4c3..4d7abc1f 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/VectorIndex.rst @@ -9,3 +9,15 @@ See also: .. autoclass:: types.hdmf_common.VectorIndex :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. diff --git a/docs/source/pages/neurodata_types/hdmf_common/index.rst b/docs/source/pages/neurodata_types/hdmf_common/index.rst index 01b7409a..b746d1be 100644 --- a/docs/source/pages/neurodata_types/hdmf_common/index.rst +++ b/docs/source/pages/neurodata_types/hdmf_common/index.rst @@ -16,4 +16,4 @@ These are the MatNWB neurodata types from the hdmf_common schema specification. ElementIdentifiers SimpleMultiContainer VectorData - VectorIndex \ No newline at end of file + VectorIndex diff --git a/tools/documentation/_rst_templates/neurodata_class.rst.template b/tools/documentation/_rst_templates/neurodata_class.rst.template index d23c3797..1697b13a 100644 --- a/tools/documentation/_rst_templates/neurodata_class.rst.template +++ b/tools/documentation/_rst_templates/neurodata_class.rst.template @@ -9,3 +9,15 @@ See also: .. autoclass:: {{ full_class_name }} :members: :show-inheritance: + + +.. + Add an anonymous reference target which should be linked to from the + Required Properties section + +__ + +.. tip:: + + **\*** If a required property link is not functional, the property may be + defined in a superclass. Please refer to the superclass documentation. From 4c891e8475b5a776dbcd3a52e2ee82f7fed64714 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Dec 2024 16:36:12 +0100 Subject: [PATCH 15/30] Update docstring_processors.py - Add docstring processors for adding role to class properties and change formatting for sphinx-autodetected class constructor from reference to constructor method to reference for class --- .../sphinx_extensions/docstring_processors.py | 73 +++++++++++++++++-- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/docs/source/sphinx_extensions/docstring_processors.py b/docs/source/sphinx_extensions/docstring_processors.py index 6e17f80b..4bf0221a 100644 --- a/docs/source/sphinx_extensions/docstring_processors.py +++ b/docs/source/sphinx_extensions/docstring_processors.py @@ -6,6 +6,8 @@ def process_matlab_docstring(app, what, name, obj, options, lines): _format_matlab_type_as_code_literal(lines) _format_nwbtype_shortnames(lines) + _format_required_properties(lines) + _replace_class_contructor_method_role_with_class_role(lines) _make_syntax_examples_code_literals(lines) _format_input_arguments(lines) _split_and_format_example_lines(lines) @@ -47,6 +49,68 @@ def _format_matlab_type_as_code_literal(lines): ) +def _format_required_properties(lines): + """ + Process lines to find the 'Required Properties' section and format its values. + + Args: + lines (list of str): Lines from a docstring to process. + """ + + try: + # Find the index of the "Required Properties:" line + required_idx = next(i for i, line in enumerate(lines) if "Required Properties:" in line) + + if not required_idx: + return + + lines[required_idx] = lines[required_idx].replace("Required Properties:", "Required Properties\ `*`__:") + + # Process the line following "Required Properties:" + required_line = lines[required_idx + 1] + + values = required_line.strip().split(", ") + + # Format the values + formatted_values = [ + f":attr:`{value.strip()}`" if value.lower() != "none" else f"``{value.strip()}``" + for value in values + ] + # Update the line with required properties. Add single preceding space + # for proper indentation + lines[required_idx + 1] = " " + ", ".join(formatted_values) + + except (StopIteration, IndexError): + # If "Required Properties:" or the following line is not found, return the original lines + pass + + +def _replace_class_contructor_method_role_with_class_role(lines): + """ + Process docstrings to replace `:meth:` expressions with `:class:`. + + Args: + lines: List of lines in the docstring. + """ + + # Regular expression to match the `:meth:` pattern + pattern = re.compile( + r":meth:`([^`]+)\s*<([a-zA-Z0-9_.]+)\.([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)>`" + ) + + # Replacement function for re.sub + def replace_meth_with_class(match): + display_name = match.group(1).replace("()", "").strip() # The displayed name, e.g., "AbstractFeatureSeries" + namespace_prefix = match.group(2) # The module path, e.g., "types.core" + class_name = match.group(3) # The class name, e.g., "AbstractFeatureSeries" + # Construct the new :class: pattern + return f":class:`{display_name} <{namespace_prefix}.{class_name}>`" + + # Update lines in place + for i, line in enumerate(lines): + lines[i] = pattern.sub(replace_meth_with_class, line) + + def _make_syntax_examples_code_literals(lines): """ Process a MATLAB docstring to wrap expressions in the Syntax section with double backticks. @@ -59,7 +123,7 @@ def _make_syntax_examples_code_literals(lines): # Regex to match MATLAB expressions matlab_expr_pattern = re.compile( - r"^\s*((?:\[[\w,\s]*\]\s*=\s*|[\w]+\s*=\s*)?[A-Za-z]\w*\([^)]*\))" + r"^\s*((?:\[[\w,\s]*\]\s*=\s*|[\w]+\s*=\s*)?[A-Za-z][\w\.]*\([^)]*\))" ) for i, line in enumerate(lines): @@ -89,10 +153,8 @@ def _format_input_arguments(lines): Args: lines (list of str): List of lines in the Input Arguments section. - - Returns: - list of str: Formatted lines. """ + # Regex pattern for list item names with optional types in parentheses input_arg_pattern = re.compile( r"(?P^\s*)-\s*(?P\w+)" # Match the name of the argument @@ -116,8 +178,6 @@ def _format_input_arguments(lines): line ) - return lines - def _split_and_format_example_lines(lines): """ @@ -161,7 +221,6 @@ def _format_nwbtype_shortnames(lines): Parameters: lines (list of str): The docstring lines to preprocess. - patterns (list of str): A list of patterns (e.g., ["TimeSeries", "DataArray"]). """ patterns = list_neurodata_types('core') + list_neurodata_types('hdmf_common') From a61884e40af7a7d1a664499a04980a8119f9e740 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Dec 2024 19:50:44 +0100 Subject: [PATCH 16/30] Update generator to add more information in class docstrings --- +file/+internal/getRequiredPropertyNames.m | 26 +++++ +file/+internal/mergeProps.m | 18 +++ +file/fillClass.m | 20 +++- +file/fillConstructor.m | 122 ++++++++++++++++++++- +file/writeNamespace.m | 7 +- 5 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 +file/+internal/getRequiredPropertyNames.m create mode 100644 +file/+internal/mergeProps.m diff --git a/+file/+internal/getRequiredPropertyNames.m b/+file/+internal/getRequiredPropertyNames.m new file mode 100644 index 00000000..a57c0a13 --- /dev/null +++ b/+file/+internal/getRequiredPropertyNames.m @@ -0,0 +1,26 @@ +function requiredPropertyNames = getRequiredPropertyNames(classprops) +% getRequiredPropertyNames - Get name of required properties from props info + + allProperties = keys(classprops); + requiredPropertyNames = {}; + + for iProp = 1:length(allProperties) + + propertyName = allProperties{iProp}; + prop = classprops(propertyName); + + isRequired = ischar(prop) || isa(prop, 'containers.Map') || isstruct(prop); + isPropertyRequired = false; + if isa(prop, 'file.interface.HasProps') + isPropertyRequired = false(size(prop)); + for iSubProp = 1:length(prop) + p = prop(iSubProp); + isPropertyRequired(iSubProp) = p.required; + end + end + + if isRequired || all(isPropertyRequired) + requiredPropertyNames = [requiredPropertyNames {propertyName}]; %#ok + end + end +end diff --git a/+file/+internal/mergeProps.m b/+file/+internal/mergeProps.m new file mode 100644 index 00000000..6344121a --- /dev/null +++ b/+file/+internal/mergeProps.m @@ -0,0 +1,18 @@ +function allProps = mergeProps(props, superClassProps) +% merge_props - Merge maps containing props info for class and it's superclasses + + allPropsCell = [{props}, superClassProps]; + allProps = containers.Map(); + + % Start from most remote ancestor and work towards current class. + % Important to go in this order because subclasses can override + % properties, and we need to keep the property definition for the superclass + % that is closest to the current class or the property definition for the + % class itself in the final map + for i = numel(allPropsCell):-1:1 + superPropNames = allPropsCell{i}.keys; + for jProp = 1:numel(superPropNames) + allProps(superPropNames{jProp}) = allPropsCell{i}(superPropNames{jProp}); + end + end +end diff --git a/+file/fillClass.m b/+file/fillClass.m index 58723bfd..de5d09e5 100644 --- a/+file/fillClass.m +++ b/+file/fillClass.m @@ -1,4 +1,4 @@ -function template = fillClass(name, namespace, processed, classprops, inherited) +function template = fillClass(name, namespace, processed, classprops, inherited, superClassProps) %name is the name of the scheme %namespace is the namespace context for this class @@ -87,7 +87,20 @@ %% return classfile string classDefinitionHeader = [... 'classdef ' name ' < ' depnm ' & ' classTag newline... %header, dependencies - '% ' upper(name) ' ' class.doc]; %name, docstr + '% ' upper(name) ' - ' class.doc]; %name, docstr + + allClassProps = file.internal.mergeProps(classprops, superClassProps); + allRequiredPropertyNames = file.internal.getRequiredPropertyNames(allClassProps); + if isempty(allRequiredPropertyNames) + allRequiredPropertyNames = {'None'}; + end + + % Add list of required properties in class docstring + classDefinitionHeader = [classDefinitionHeader, newline... + '%', newline, ... + '% Required Properties:', newline, ... + sprintf('%% %s', strjoin(allRequiredPropertyNames, ', '))]; + hiddenAndReadonly = intersect(hidden, readonly); hidden = setdiff(hidden, hiddenAndReadonly); readonly = setdiff(readonly, hiddenAndReadonly); @@ -126,7 +139,8 @@ depnm,... defaults,... %all defaults, regardless of inheritance classprops,... - namespace); + namespace, ... + superClassProps); setterFcns = file.fillSetters(setdiff(nonInherited, union(readonly, hiddenAndReadonly))); validatorFcns = file.fillValidators(allProperties, classprops, namespace, namespace.getFullClassName(name), inherited); exporterFcns = file.fillExport(nonInherited, class, depnm); diff --git a/+file/fillConstructor.m b/+file/fillConstructor.m index 269829d2..7c8d36d3 100644 --- a/+file/fillConstructor.m +++ b/+file/fillConstructor.m @@ -1,6 +1,11 @@ -function functionString = fillConstructor(name, parentname, defaults, props, namespace) +function functionString = fillConstructor(name, parentname, defaults, props, namespace, superClassProps) caps = upper(name); - functionBody = ['% ' caps ' Constructor for ' name]; + functionBody = ['% ' caps ' - Constructor for ' name]; + + docString = fillConstructorDocString(name, props, namespace, superClassProps); + if ~isempty(docString) + functionBody = [functionBody newline() docString]; + end bodyString = fillBody(parentname, defaults, props, namespace); if ~isempty(bodyString) @@ -23,7 +28,6 @@ ['function obj = ' name '(varargin)']... file.addSpaces(functionBody, 4)... 'end'}, newline()); - end function bodystr = fillBody(parentName, defaults, props, namespace) @@ -198,4 +202,116 @@ ' types.util.dynamictable.checkConfig(obj);', ... 'end',... }, newline); +end + +function docString = fillConstructorDocString(name, props, namespace, superClassProps) + + classVarName = name; classVarName(1) = lower(classVarName(1)); + fullClassName = sprintf('types.%s.%s', namespace.name, name); + fullClassNameUpper = sprintf('types.%s.%s', namespace.name, upper(name)); + + props = file.internal.mergeProps(props, superClassProps); + names = props.keys(); + + docString = [... + "%", ... + "% Syntax:", ... + sprintf("%% %s = %s() creates a %s object with unset property values.", classVarName, fullClassNameUpper, name), ... + "%", ... + ]; + + if ~isempty(names) + docString = [docString, ... + sprintf("%% %s = %s(Name, Value) creates a %s object where one or more property values are specified using name-value pairs.", classVarName, fullClassNameUpper, name), ... + "%", ... + "% Input Arguments (Name-Value Arguments):", ... + ]; + end + + for i = 1:numel(names) + propName = names{i}; + thisProp = props(propName); + try + if isprop(thisProp, 'readonly') && thisProp.readonly + continue + end + catch + % pass + end + + valueType = getTypeStr(thisProp); + try + description = thisProp.doc; + catch + description = 'No description'; + end + + docString = [docString, ... + sprintf("%% - %s (%s) - %s", propName, valueType, description), ... + "%"]; %#ok + end + + docString = [docString, ... + "% Output Arguments:", ... + sprintf("%% - %s (%s) - A %s object", classVarName, fullClassName, name), ... + "" + ]; + + docString = char( strjoin(docString, newline) ); +end + +% Todo: Mostly duplicate code from file.fillProps. Should consolidate +function typeStr = getTypeStr(prop) + if ischar(prop) + typeStr = prop; + elseif isstruct(prop) + columnNames = fieldnames(prop); + columnDocStr = cell(size(columnNames)); + for i=1:length(columnNames) + name = columnNames{i}; + columnDocStr{i} = getTypeStr(prop.(name)); + end + typeStr = ['Table with columns: (', strjoin(columnDocStr, ', '), ')']; + elseif isa(prop, 'file.Attribute') + if isa(prop.dtype, 'containers.Map') + assertValidRefType(prop.dtype('reftype')) + typeStr = sprintf('%s reference to %s', capitalize(prop.dtype('reftype')), prop.dtype('target_type')); + else + typeStr = prop.dtype; + end + elseif isa(prop, 'containers.Map') + assertValidRefType(prop('reftype')) + typeStr = sprintf('%s reference to %s', capitalize(prop('reftype')), prop('target_type')); + elseif isa(prop, 'file.interface.HasProps') + typeStrCell = cell(size(prop)); + for iProp = 1:length(typeStrCell) + anonProp = prop(iProp); + if isa(anonProp, 'file.Dataset') && isempty(anonProp.type) + typeStrCell{iProp} = getTypeStr(anonProp.dtype); + elseif isempty(anonProp.type) + typeStrCell{iProp} = 'types.untyped.Set'; + else + typeStrCell{iProp} = anonProp.type; + end + end + typeStr = strjoin(typeStrCell, '|'); + else + typeStr = prop.type; + end +end + +function assertValidRefType(referenceType) + arguments + referenceType (1,1) string + end + assert( ismember(referenceType, ["region", "object"]), ... + 'NWB:ClassGenerator:InvalidRefType', ... + 'Invalid reftype found while filling description for class properties.') +end + +function word = capitalize(word) + arguments + word (1,:) char + end + word(1) = upper(word(1)); end \ No newline at end of file diff --git a/+file/writeNamespace.m b/+file/writeNamespace.m index 00ab7aa9..77d0da7c 100644 --- a/+file/writeNamespace.m +++ b/+file/writeNamespace.m @@ -15,11 +15,16 @@ function writeNamespace(namespaceName, saveDir) [processed, classprops, inherited] = file.processClass(className, Namespace, pregenerated); if ~isempty(processed) + superClassProps = cell(1, numel(processed)-1); + for iSuper = 2:numel(processed) + [~, superClassProps{iSuper-1}, ~] = file.processClass(processed(iSuper).type, Namespace, pregenerated); + end + fid = fopen(fullfile(classFileDir, [className '.m']), 'W'); % Create cleanup object to close to file in case the write operation fails. fileCleanupObj = onCleanup(@(id) fclose(fid)); fwrite(fid, file.fillClass(className, Namespace, processed, ... - classprops, inherited), 'char'); + classprops, inherited, superClassProps), 'char'); else % pass end From 5c5400fadfab556bd1108d562074bcfbd1fcf325 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Dec 2024 19:51:07 +0100 Subject: [PATCH 17/30] Rerun generateCore (update all classes) --- +types/+core/AbstractFeatureSeries.m | 47 +++++++- +types/+core/AnnotationSeries.m | 39 ++++++- +types/+core/BehavioralEpochs.m | 19 ++- +types/+core/BehavioralEvents.m | 19 ++- +types/+core/BehavioralTimeSeries.m | 19 ++- +types/+core/ClusterWaveforms.m | 25 +++- +types/+core/Clustering.m | 25 +++- +types/+core/CompassDirection.m | 19 ++- +types/+core/CorrectedImageStack.m | 23 +++- +types/+core/CurrentClampSeries.m | 55 ++++++++- +types/+core/CurrentClampStimulusSeries.m | 49 +++++++- +types/+core/DecompositionSeries.m | 51 +++++++- +types/+core/Device.m | 27 ++++- +types/+core/DfOverF.m | 19 ++- +types/+core/ElectricalSeries.m | 47 +++++++- +types/+core/ElectrodeGroup.m | 25 +++- +types/+core/EventDetection.m | 25 +++- +types/+core/EventWaveform.m | 19 ++- +types/+core/ExperimentalConditionsTable.m | 29 ++++- +types/+core/EyeTracking.m | 19 ++- +types/+core/FeatureExtraction.m | 25 +++- +types/+core/FilteredEphys.m | 19 ++- +types/+core/Fluorescence.m | 19 ++- +types/+core/GrayscaleImage.m | 23 +++- +types/+core/IZeroClampSeries.m | 47 +++++++- +types/+core/Image.m | 23 +++- +types/+core/ImageMaskSeries.m | 55 ++++++++- +types/+core/ImageReferences.m | 19 ++- +types/+core/ImageSegmentation.m | 19 ++- +types/+core/ImageSeries.m | 53 ++++++++- +types/+core/Images.m | 23 +++- +types/+core/ImagingPlane.m | 47 +++++++- +types/+core/ImagingRetinotopy.m | 79 ++++++++++++- +types/+core/IndexSeries.m | 45 +++++++- +types/+core/IntervalSeries.m | 39 ++++++- +types/+core/IntracellularElectrode.m | 35 +++++- +types/+core/IntracellularElectrodesTable.m | 25 +++- +types/+core/IntracellularRecordingsTable.m | 33 +++++- +types/+core/IntracellularResponsesTable.m | 25 +++- +types/+core/IntracellularStimuliTable.m | 27 ++++- +types/+core/LFP.m | 19 ++- +types/+core/LabMetaData.m | 14 ++- +types/+core/MotionCorrection.m | 19 ++- +types/+core/NWBContainer.m | 14 ++- +types/+core/NWBData.m | 19 ++- +types/+core/NWBDataInterface.m | 14 ++- +types/+core/NWBFile.m | 115 ++++++++++++++++++- +types/+core/OnePhotonSeries.m | 67 ++++++++++- +types/+core/OpticalChannel.m | 21 +++- +types/+core/OpticalSeries.m | 59 +++++++++- +types/+core/OptogeneticSeries.m | 43 ++++++- +types/+core/OptogeneticStimulusSite.m | 25 +++- +types/+core/PatchClampSeries.m | 51 +++++++- +types/+core/PlaneSegmentation.m | 39 ++++++- +types/+core/Position.m | 19 ++- +types/+core/ProcessingModule.m | 23 +++- +types/+core/PupilTracking.m | 19 ++- +types/+core/RGBAImage.m | 23 +++- +types/+core/RGBImage.m | 23 +++- +types/+core/RepetitionsTable.m | 29 ++++- +types/+core/RoiResponseSeries.m | 45 +++++++- +types/+core/ScratchData.m | 21 +++- +types/+core/SequentialRecordingsTable.m | 31 ++++- +types/+core/SimultaneousRecordingsTable.m | 29 ++++- +types/+core/SpatialSeries.m | 45 +++++++- +types/+core/SpikeEventSeries.m | 47 +++++++- +types/+core/Subject.m | 37 +++++- +types/+core/SweepTable.m | 31 ++++- +types/+core/TimeIntervals.m | 37 +++++- +types/+core/TimeSeries.m | 43 ++++++- +types/+core/TimeSeriesReferenceVectorData.m | 21 +++- +types/+core/TwoPhotonSeries.m | 61 +++++++++- +types/+core/Units.m | 49 +++++++- +types/+core/VoltageClampSeries.m | 63 +++++++++- +types/+core/VoltageClampStimulusSeries.m | 49 +++++++- +types/+hdmf_common/AlignedDynamicTable.m | 29 ++++- +types/+hdmf_common/CSRMatrix.m | 25 +++- +types/+hdmf_common/Container.m | 14 ++- +types/+hdmf_common/Data.m | 19 ++- +types/+hdmf_common/DynamicTable.m | 25 +++- +types/+hdmf_common/DynamicTableRegion.m | 27 ++++- +types/+hdmf_common/ElementIdentifiers.m | 19 ++- +types/+hdmf_common/SimpleMultiContainer.m | 21 +++- +types/+hdmf_common/VectorData.m | 25 +++- +types/+hdmf_common/VectorIndex.m | 27 ++++- +types/+hdmf_experimental/EnumData.m | 23 +++- +types/+hdmf_experimental/HERD.m | 29 ++++- 87 files changed, 2653 insertions(+), 174 deletions(-) diff --git a/+types/+core/AbstractFeatureSeries.m b/+types/+core/AbstractFeatureSeries.m index 27e6aaf3..5cae4d44 100644 --- a/+types/+core/AbstractFeatureSeries.m +++ b/+types/+core/AbstractFeatureSeries.m @@ -1,5 +1,8 @@ classdef AbstractFeatureSeries < types.core.TimeSeries & types.untyped.GroupClass -% ABSTRACTFEATURESERIES Abstract features, such as quantitative descriptions of sensory stimuli. The TimeSeries::data field is a 2D array, storing those features (e.g., for visual grating stimulus this might be orientation, spatial frequency and contrast). Null stimuli (eg, uniform gray) can be marked as being an independent feature (eg, 1.0 for gray, 0.0 for actual stimulus) or by storing NaNs for feature values, or through use of the TimeSeries::control fields. A set of features is considered to persist until the next set of features is defined. The final set of features stored should be the null set. This is useful when storing the raw stimulus is impractical. +% ABSTRACTFEATURESERIES - Abstract features, such as quantitative descriptions of sensory stimuli. The TimeSeries::data field is a 2D array, storing those features (e.g., for visual grating stimulus this might be orientation, spatial frequency and contrast). Null stimuli (eg, uniform gray) can be marked as being an independent feature (eg, 1.0 for gray, 0.0 for actual stimulus) or by storing NaNs for feature values, or through use of the TimeSeries::control fields. A set of features is considered to persist until the next set of features is defined. The final set of features stored should be the null set. This is useful when storing the raw stimulus is impractical. +% +% Required Properties: +% data, features % REQUIRED PROPERTIES @@ -13,7 +16,47 @@ methods function obj = AbstractFeatureSeries(varargin) - % ABSTRACTFEATURESERIES Constructor for AbstractFeatureSeries + % ABSTRACTFEATURESERIES - Constructor for AbstractFeatureSeries + % + % Syntax: + % abstractFeatureSeries = types.core.ABSTRACTFEATURESERIES() creates a AbstractFeatureSeries object with unset property values. + % + % abstractFeatureSeries = types.core.ABSTRACTFEATURESERIES(Name, Value) creates a AbstractFeatureSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Values of each feature at each time. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Since there can be different units for different features, store the units in 'feature_units'. The default value for this attribute is "see 'feature_units'". + % + % - description (char) - Description of the time series. + % + % - feature_units (char) - Units of each feature. + % + % - features (char) - Description of the features represented in TimeSeries::data. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - abstractFeatureSeries (types.core.AbstractFeatureSeries) - A AbstractFeatureSeries object + varargin = [{'data_unit' 'see `feature_units`'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/AnnotationSeries.m b/+types/+core/AnnotationSeries.m index c821b8d6..dd44fbb3 100644 --- a/+types/+core/AnnotationSeries.m +++ b/+types/+core/AnnotationSeries.m @@ -1,11 +1,46 @@ classdef AnnotationSeries < types.core.TimeSeries & types.untyped.GroupClass -% ANNOTATIONSERIES Stores user annotations made during an experiment. The data[] field stores a text array, and timestamps are stored for each annotation (ie, interval=1). This is largely an alias to a standard TimeSeries storing a text array but that is identifiable as storing annotations in a machine-readable way. +% ANNOTATIONSERIES - Stores user annotations made during an experiment. The data[] field stores a text array, and timestamps are stored for each annotation (ie, interval=1). This is largely an alias to a standard TimeSeries storing a text array but that is identifiable as storing annotations in a machine-readable way. +% +% Required Properties: +% data methods function obj = AnnotationSeries(varargin) - % ANNOTATIONSERIES Constructor for AnnotationSeries + % ANNOTATIONSERIES - Constructor for AnnotationSeries + % + % Syntax: + % annotationSeries = types.core.ANNOTATIONSERIES() creates a AnnotationSeries object with unset property values. + % + % annotationSeries = types.core.ANNOTATIONSERIES(Name, Value) creates a AnnotationSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (char) - Annotations made during an experiment. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - description (char) - Description of the time series. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - annotationSeries (types.core.AnnotationSeries) - A AnnotationSeries object + varargin = [{'data_resolution' types.util.correctType(-1, 'single') 'data_unit' 'n/a'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/BehavioralEpochs.m b/+types/+core/BehavioralEpochs.m index c697b625..93239c91 100644 --- a/+types/+core/BehavioralEpochs.m +++ b/+types/+core/BehavioralEpochs.m @@ -1,5 +1,8 @@ classdef BehavioralEpochs < types.core.NWBDataInterface & types.untyped.GroupClass -% BEHAVIORALEPOCHS TimeSeries for storing behavioral epochs. The objective of this and the other two Behavioral interfaces (e.g. BehavioralEvents and BehavioralTimeSeries) is to provide generic hooks for software tools/scripts. This allows a tool/script to take the output one specific interface (e.g., UnitTimes) and plot that data relative to another data modality (e.g., behavioral events) without having to define all possible modalities in advance. Declaring one of these interfaces means that one or more TimeSeries of the specified type is published. These TimeSeries should reside in a group having the same name as the interface. For example, if a BehavioralTimeSeries interface is declared, the module will have one or more TimeSeries defined in the module sub-group 'BehavioralTimeSeries'. BehavioralEpochs should use IntervalSeries. BehavioralEvents is used for irregular events. BehavioralTimeSeries is for continuous data. +% BEHAVIORALEPOCHS - TimeSeries for storing behavioral epochs. The objective of this and the other two Behavioral interfaces (e.g. BehavioralEvents and BehavioralTimeSeries) is to provide generic hooks for software tools/scripts. This allows a tool/script to take the output one specific interface (e.g., UnitTimes) and plot that data relative to another data modality (e.g., behavioral events) without having to define all possible modalities in advance. Declaring one of these interfaces means that one or more TimeSeries of the specified type is published. These TimeSeries should reside in a group having the same name as the interface. For example, if a BehavioralTimeSeries interface is declared, the module will have one or more TimeSeries defined in the module sub-group 'BehavioralTimeSeries'. BehavioralEpochs should use IntervalSeries. BehavioralEvents is used for irregular events. BehavioralTimeSeries is for continuous data. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -9,7 +12,19 @@ methods function obj = BehavioralEpochs(varargin) - % BEHAVIORALEPOCHS Constructor for BehavioralEpochs + % BEHAVIORALEPOCHS - Constructor for BehavioralEpochs + % + % Syntax: + % behavioralEpochs = types.core.BEHAVIORALEPOCHS() creates a BehavioralEpochs object with unset property values. + % + % behavioralEpochs = types.core.BEHAVIORALEPOCHS(Name, Value) creates a BehavioralEpochs object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - intervalseries (IntervalSeries) - IntervalSeries object containing start and stop times of epochs. + % + % Output Arguments: + % - behavioralEpochs (types.core.BehavioralEpochs) - A BehavioralEpochs object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.intervalseries, ivarargin] = types.util.parseConstrained(obj,'intervalseries', 'types.core.IntervalSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/BehavioralEvents.m b/+types/+core/BehavioralEvents.m index a5d94e28..1536944c 100644 --- a/+types/+core/BehavioralEvents.m +++ b/+types/+core/BehavioralEvents.m @@ -1,5 +1,8 @@ classdef BehavioralEvents < types.core.NWBDataInterface & types.untyped.GroupClass -% BEHAVIORALEVENTS TimeSeries for storing behavioral events. See description of BehavioralEpochs for more details. +% BEHAVIORALEVENTS - TimeSeries for storing behavioral events. See description of BehavioralEpochs for more details. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -9,7 +12,19 @@ methods function obj = BehavioralEvents(varargin) - % BEHAVIORALEVENTS Constructor for BehavioralEvents + % BEHAVIORALEVENTS - Constructor for BehavioralEvents + % + % Syntax: + % behavioralEvents = types.core.BEHAVIORALEVENTS() creates a BehavioralEvents object with unset property values. + % + % behavioralEvents = types.core.BEHAVIORALEVENTS(Name, Value) creates a BehavioralEvents object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - timeseries (TimeSeries) - TimeSeries object containing behavioral events. + % + % Output Arguments: + % - behavioralEvents (types.core.BehavioralEvents) - A BehavioralEvents object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.timeseries, ivarargin] = types.util.parseConstrained(obj,'timeseries', 'types.core.TimeSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/BehavioralTimeSeries.m b/+types/+core/BehavioralTimeSeries.m index 4f697bae..c5689f52 100644 --- a/+types/+core/BehavioralTimeSeries.m +++ b/+types/+core/BehavioralTimeSeries.m @@ -1,5 +1,8 @@ classdef BehavioralTimeSeries < types.core.NWBDataInterface & types.untyped.GroupClass -% BEHAVIORALTIMESERIES TimeSeries for storing Behavoioral time series data. See description of BehavioralEpochs for more details. +% BEHAVIORALTIMESERIES - TimeSeries for storing Behavoioral time series data. See description of BehavioralEpochs for more details. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -9,7 +12,19 @@ methods function obj = BehavioralTimeSeries(varargin) - % BEHAVIORALTIMESERIES Constructor for BehavioralTimeSeries + % BEHAVIORALTIMESERIES - Constructor for BehavioralTimeSeries + % + % Syntax: + % behavioralTimeSeries = types.core.BEHAVIORALTIMESERIES() creates a BehavioralTimeSeries object with unset property values. + % + % behavioralTimeSeries = types.core.BEHAVIORALTIMESERIES(Name, Value) creates a BehavioralTimeSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - timeseries (TimeSeries) - TimeSeries object containing continuous behavioral data. + % + % Output Arguments: + % - behavioralTimeSeries (types.core.BehavioralTimeSeries) - A BehavioralTimeSeries object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.timeseries, ivarargin] = types.util.parseConstrained(obj,'timeseries', 'types.core.TimeSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/ClusterWaveforms.m b/+types/+core/ClusterWaveforms.m index be7285f1..751f4a9f 100644 --- a/+types/+core/ClusterWaveforms.m +++ b/+types/+core/ClusterWaveforms.m @@ -1,5 +1,8 @@ classdef ClusterWaveforms < types.core.NWBDataInterface & types.untyped.GroupClass -% CLUSTERWAVEFORMS DEPRECATED The mean waveform shape, including standard deviation, of the different clusters. Ideally, the waveform analysis should be performed on data that is only high-pass filtered. This is a separate module because it is expected to require updating. For example, IMEC probes may require different storage requirements to store/display mean waveforms, requiring a new interface or an extension of this one. +% CLUSTERWAVEFORMS - DEPRECATED The mean waveform shape, including standard deviation, of the different clusters. Ideally, the waveform analysis should be performed on data that is only high-pass filtered. This is a separate module because it is expected to require updating. For example, IMEC probes may require different storage requirements to store/display mean waveforms, requiring a new interface or an extension of this one. +% +% Required Properties: +% waveform_filtering, waveform_mean, waveform_sd % REQUIRED PROPERTIES @@ -15,7 +18,25 @@ methods function obj = ClusterWaveforms(varargin) - % CLUSTERWAVEFORMS Constructor for ClusterWaveforms + % CLUSTERWAVEFORMS - Constructor for ClusterWaveforms + % + % Syntax: + % clusterWaveforms = types.core.CLUSTERWAVEFORMS() creates a ClusterWaveforms object with unset property values. + % + % clusterWaveforms = types.core.CLUSTERWAVEFORMS(Name, Value) creates a ClusterWaveforms object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - clustering_interface (Clustering) - Link to Clustering interface that was the source of the clustered data + % + % - waveform_filtering (char) - Filtering applied to data before generating mean/sd + % + % - waveform_mean (single) - The mean waveform for each cluster, using the same indices for each wave as cluster numbers in the associated Clustering module (i.e, cluster 3 is in array slot [3]). Waveforms corresponding to gaps in cluster sequence should be empty (e.g., zero- filled) + % + % - waveform_sd (single) - Stdev of waveforms for each cluster, using the same indices as in mean + % + % Output Arguments: + % - clusterWaveforms (types.core.ClusterWaveforms) - A ClusterWaveforms object + obj = obj@types.core.NWBDataInterface(varargin{:}); diff --git a/+types/+core/Clustering.m b/+types/+core/Clustering.m index ab8dc279..8a18b0a6 100644 --- a/+types/+core/Clustering.m +++ b/+types/+core/Clustering.m @@ -1,5 +1,8 @@ classdef Clustering < types.core.NWBDataInterface & types.untyped.GroupClass -% CLUSTERING DEPRECATED Clustered spike data, whether from automatic clustering tools (e.g., klustakwik) or as a result of manual sorting. +% CLUSTERING - DEPRECATED Clustered spike data, whether from automatic clustering tools (e.g., klustakwik) or as a result of manual sorting. +% +% Required Properties: +% description, num, peak_over_rms, times % REQUIRED PROPERTIES @@ -12,7 +15,25 @@ methods function obj = Clustering(varargin) - % CLUSTERING Constructor for Clustering + % CLUSTERING - Constructor for Clustering + % + % Syntax: + % clustering = types.core.CLUSTERING() creates a Clustering object with unset property values. + % + % clustering = types.core.CLUSTERING(Name, Value) creates a Clustering object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of clusters or clustering, (e.g. cluster 0 is noise, clusters curated using Klusters, etc) + % + % - num (int32) - Cluster number of each event + % + % - peak_over_rms (single) - Maximum ratio of waveform peak to RMS on any channel in the cluster (provides a basic clustering metric). + % + % - times (double) - Times of clustered events, in seconds. This may be a link to times field in associated FeatureExtraction module. + % + % Output Arguments: + % - clustering (types.core.Clustering) - A Clustering object + obj = obj@types.core.NWBDataInterface(varargin{:}); diff --git a/+types/+core/CompassDirection.m b/+types/+core/CompassDirection.m index fb301492..9df7227b 100644 --- a/+types/+core/CompassDirection.m +++ b/+types/+core/CompassDirection.m @@ -1,5 +1,8 @@ classdef CompassDirection < types.core.NWBDataInterface & types.untyped.GroupClass -% COMPASSDIRECTION With a CompassDirection interface, a module publishes a SpatialSeries object representing a floating point value for theta. The SpatialSeries::reference_frame field should indicate what direction corresponds to 0 and which is the direction of rotation (this should be clockwise). The si_unit for the SpatialSeries should be radians or degrees. +% COMPASSDIRECTION - With a CompassDirection interface, a module publishes a SpatialSeries object representing a floating point value for theta. The SpatialSeries::reference_frame field should indicate what direction corresponds to 0 and which is the direction of rotation (this should be clockwise). The si_unit for the SpatialSeries should be radians or degrees. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -9,7 +12,19 @@ methods function obj = CompassDirection(varargin) - % COMPASSDIRECTION Constructor for CompassDirection + % COMPASSDIRECTION - Constructor for CompassDirection + % + % Syntax: + % compassDirection = types.core.COMPASSDIRECTION() creates a CompassDirection object with unset property values. + % + % compassDirection = types.core.COMPASSDIRECTION(Name, Value) creates a CompassDirection object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - spatialseries (SpatialSeries) - SpatialSeries object containing direction of gaze travel. + % + % Output Arguments: + % - compassDirection (types.core.CompassDirection) - A CompassDirection object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.spatialseries, ivarargin] = types.util.parseConstrained(obj,'spatialseries', 'types.core.SpatialSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/CorrectedImageStack.m b/+types/+core/CorrectedImageStack.m index 072b7f57..559497fb 100644 --- a/+types/+core/CorrectedImageStack.m +++ b/+types/+core/CorrectedImageStack.m @@ -1,5 +1,8 @@ classdef CorrectedImageStack < types.core.NWBDataInterface & types.untyped.GroupClass -% CORRECTEDIMAGESTACK Results from motion correction of an image stack. +% CORRECTEDIMAGESTACK - Results from motion correction of an image stack. +% +% Required Properties: +% corrected, xy_translation % REQUIRED PROPERTIES @@ -14,7 +17,23 @@ methods function obj = CorrectedImageStack(varargin) - % CORRECTEDIMAGESTACK Constructor for CorrectedImageStack + % CORRECTEDIMAGESTACK - Constructor for CorrectedImageStack + % + % Syntax: + % correctedImageStack = types.core.CORRECTEDIMAGESTACK() creates a CorrectedImageStack object with unset property values. + % + % correctedImageStack = types.core.CORRECTEDIMAGESTACK(Name, Value) creates a CorrectedImageStack object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - corrected (ImageSeries) - Image stack with frames shifted to the common coordinates. + % + % - original (ImageSeries) - Link to ImageSeries object that is being registered. + % + % - xy_translation (TimeSeries) - Stores the x,y delta necessary to align each frame to the common coordinates, for example, to align each frame to a reference image. + % + % Output Arguments: + % - correctedImageStack (types.core.CorrectedImageStack) - A CorrectedImageStack object + obj = obj@types.core.NWBDataInterface(varargin{:}); diff --git a/+types/+core/CurrentClampSeries.m b/+types/+core/CurrentClampSeries.m index fe27e0cb..47578edf 100644 --- a/+types/+core/CurrentClampSeries.m +++ b/+types/+core/CurrentClampSeries.m @@ -1,5 +1,8 @@ classdef CurrentClampSeries < types.core.PatchClampSeries & types.untyped.GroupClass -% CURRENTCLAMPSERIES Voltage data from an intracellular current-clamp recording. A corresponding CurrentClampStimulusSeries (stored separately as a stimulus) is used to store the current injected. +% CURRENTCLAMPSERIES - Voltage data from an intracellular current-clamp recording. A corresponding CurrentClampStimulusSeries (stored separately as a stimulus) is used to store the current injected. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -11,7 +14,55 @@ methods function obj = CurrentClampSeries(varargin) - % CURRENTCLAMPSERIES Constructor for CurrentClampSeries + % CURRENTCLAMPSERIES - Constructor for CurrentClampSeries + % + % Syntax: + % currentClampSeries = types.core.CURRENTCLAMPSERIES() creates a CurrentClampSeries object with unset property values. + % + % currentClampSeries = types.core.CURRENTCLAMPSERIES(Name, Value) creates a CurrentClampSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - bias_current (single) - Bias current, in amps. + % + % - bridge_balance (single) - Bridge balance, in ohms. + % + % - capacitance_compensation (single) - Capacitance compensation, in farads. + % + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (any) - Recorded voltage. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - electrode (IntracellularElectrode) - Link to IntracellularElectrode object that describes the electrode that was used to apply or record this data. + % + % - gain (single) - Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - stimulus_description (char) - Protocol/stimulus name for this patch-clamp dataset. + % + % - sweep_number (uint32) - Sweep number, allows to group different PatchClampSeries together. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - currentClampSeries (types.core.CurrentClampSeries) - A CurrentClampSeries object + varargin = [{'data_unit' 'volts'} varargin]; obj = obj@types.core.PatchClampSeries(varargin{:}); diff --git a/+types/+core/CurrentClampStimulusSeries.m b/+types/+core/CurrentClampStimulusSeries.m index ab8d7a4d..adffb47f 100644 --- a/+types/+core/CurrentClampStimulusSeries.m +++ b/+types/+core/CurrentClampStimulusSeries.m @@ -1,11 +1,56 @@ classdef CurrentClampStimulusSeries < types.core.PatchClampSeries & types.untyped.GroupClass -% CURRENTCLAMPSTIMULUSSERIES Stimulus current applied during current clamp recording. +% CURRENTCLAMPSTIMULUSSERIES - Stimulus current applied during current clamp recording. +% +% Required Properties: +% data methods function obj = CurrentClampStimulusSeries(varargin) - % CURRENTCLAMPSTIMULUSSERIES Constructor for CurrentClampStimulusSeries + % CURRENTCLAMPSTIMULUSSERIES - Constructor for CurrentClampStimulusSeries + % + % Syntax: + % currentClampStimulusSeries = types.core.CURRENTCLAMPSTIMULUSSERIES() creates a CurrentClampStimulusSeries object with unset property values. + % + % currentClampStimulusSeries = types.core.CURRENTCLAMPSTIMULUSSERIES(Name, Value) creates a CurrentClampStimulusSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (any) - Stimulus current applied. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - electrode (IntracellularElectrode) - Link to IntracellularElectrode object that describes the electrode that was used to apply or record this data. + % + % - gain (single) - Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - stimulus_description (char) - Protocol/stimulus name for this patch-clamp dataset. + % + % - sweep_number (uint32) - Sweep number, allows to group different PatchClampSeries together. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - currentClampStimulusSeries (types.core.CurrentClampStimulusSeries) - A CurrentClampStimulusSeries object + varargin = [{'data_unit' 'amperes'} varargin]; obj = obj@types.core.PatchClampSeries(varargin{:}); diff --git a/+types/+core/DecompositionSeries.m b/+types/+core/DecompositionSeries.m index 118c7e5c..49e97d80 100644 --- a/+types/+core/DecompositionSeries.m +++ b/+types/+core/DecompositionSeries.m @@ -1,5 +1,8 @@ classdef DecompositionSeries < types.core.TimeSeries & types.untyped.GroupClass -% DECOMPOSITIONSERIES Spectral analysis of a time series, e.g. of an LFP or a speech signal. +% DECOMPOSITIONSERIES - Spectral analysis of a time series, e.g. of an LFP or a speech signal. +% +% Required Properties: +% bands, data, metric % REQUIRED PROPERTIES @@ -15,7 +18,51 @@ methods function obj = DecompositionSeries(varargin) - % DECOMPOSITIONSERIES Constructor for DecompositionSeries + % DECOMPOSITIONSERIES - Constructor for DecompositionSeries + % + % Syntax: + % decompositionSeries = types.core.DECOMPOSITIONSERIES() creates a DecompositionSeries object with unset property values. + % + % decompositionSeries = types.core.DECOMPOSITIONSERIES(Name, Value) creates a DecompositionSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - bands (DynamicTable) - Table for describing the bands that this series was generated from. There should be one row in this table for each band. + % + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Data decomposed into frequency bands. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion'. + % + % - description (char) - Description of the time series. + % + % - metric (char) - The metric used, e.g. phase, amplitude, power. + % + % - source_channels (DynamicTableRegion) - DynamicTableRegion pointer to the channels that this decomposition series was generated from. + % + % - source_timeseries (TimeSeries) - Link to TimeSeries object that this data was calculated from. Metadata about electrodes and their position can be read from that ElectricalSeries so it is not necessary to store that information here. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - decompositionSeries (types.core.DecompositionSeries) - A DecompositionSeries object + varargin = [{'data_unit' 'no unit'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/Device.m b/+types/+core/Device.m index 426a9bd5..243bb76a 100644 --- a/+types/+core/Device.m +++ b/+types/+core/Device.m @@ -1,5 +1,8 @@ classdef Device < types.core.NWBContainer & types.untyped.GroupClass -% DEVICE Metadata about a data acquisition device, e.g., recording system, electrode, microscope. +% DEVICE - Metadata about a data acquisition device, e.g., recording system, electrode, microscope. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -13,7 +16,27 @@ methods function obj = Device(varargin) - % DEVICE Constructor for Device + % DEVICE - Constructor for Device + % + % Syntax: + % device = types.core.DEVICE() creates a Device object with unset property values. + % + % device = types.core.DEVICE(Name, Value) creates a Device object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of the device as free-form text. If there is any software/firmware associated with the device, the names and versions of those can be added to NWBFile.was_generated_by. + % + % - manufacturer (char) - The name of the manufacturer of the device, e.g., Imec, Plexon, Thorlabs. + % + % - model_name (char) - The model name of the device, e.g., Neuropixels 1.0, V-Probe, Bergamo III. + % + % - model_number (char) - The model number (or part/product number) of the device, e.g., PRB_1_4_0480_1, PLX-VP-32-15SE(75)-(260-80)(460-10)-300-(1)CON/32m-V, BERGAMO. + % + % - serial_number (char) - The serial number of the device. + % + % Output Arguments: + % - device (types.core.Device) - A Device object + obj = obj@types.core.NWBContainer(varargin{:}); diff --git a/+types/+core/DfOverF.m b/+types/+core/DfOverF.m index 50748605..0aa24714 100644 --- a/+types/+core/DfOverF.m +++ b/+types/+core/DfOverF.m @@ -1,5 +1,8 @@ classdef DfOverF < types.core.NWBDataInterface & types.untyped.GroupClass -% DFOVERF dF/F information about a region of interest (ROI). Storage hierarchy of dF/F should be the same as for segmentation (i.e., same names for ROIs and for image planes). +% DFOVERF - dF/F information about a region of interest (ROI). Storage hierarchy of dF/F should be the same as for segmentation (i.e., same names for ROIs and for image planes). +% +% Required Properties: +% roiresponseseries % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = DfOverF(varargin) - % DFOVERF Constructor for DfOverF + % DFOVERF - Constructor for DfOverF + % + % Syntax: + % dfOverF = types.core.DFOVERF() creates a DfOverF object with unset property values. + % + % dfOverF = types.core.DFOVERF(Name, Value) creates a DfOverF object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - roiresponseseries (RoiResponseSeries) - RoiResponseSeries object(s) containing dF/F for a ROI. + % + % Output Arguments: + % - dfOverF (types.core.DfOverF) - A DfOverF object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.roiresponseseries, ivarargin] = types.util.parseConstrained(obj,'roiresponseseries', 'types.core.RoiResponseSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/ElectricalSeries.m b/+types/+core/ElectricalSeries.m index 9a81aa87..6c27bfeb 100644 --- a/+types/+core/ElectricalSeries.m +++ b/+types/+core/ElectricalSeries.m @@ -1,5 +1,8 @@ classdef ElectricalSeries < types.core.TimeSeries & types.untyped.GroupClass -% ELECTRICALSERIES A time series of acquired voltage data from extracellular recordings. The data field is an int or float array storing data in volts. The first dimension should always represent time. The second dimension, if present, should represent channels. +% ELECTRICALSERIES - A time series of acquired voltage data from extracellular recordings. The data field is an int or float array storing data in volts. The first dimension should always represent time. The second dimension, if present, should represent channels. +% +% Required Properties: +% data, electrodes % READONLY PROPERTIES @@ -18,7 +21,47 @@ methods function obj = ElectricalSeries(varargin) - % ELECTRICALSERIES Constructor for ElectricalSeries + % ELECTRICALSERIES - Constructor for ElectricalSeries + % + % Syntax: + % electricalSeries = types.core.ELECTRICALSERIES() creates a ElectricalSeries object with unset property values. + % + % electricalSeries = types.core.ELECTRICALSERIES(Name, Value) creates a ElectricalSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - channel_conversion (single) - Channel-specific conversion factor. Multiply the data in the 'data' dataset by these values along the channel axis (as indicated by axis attribute) AND by the global conversion factor in the 'conversion' attribute of 'data' to get the data values in Volts, i.e, data in Volts = data * data.conversion * channel_conversion. This approach allows for both global and per-channel data conversion factors needed to support the storage of electrical recordings as native values generated by data acquisition systems. If this dataset is not present, then there is no channel-specific conversion factor, i.e. it is 1 for all channels. + % + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Recorded voltage data. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - electrodes (DynamicTableRegion) - DynamicTableRegion pointer to the electrodes that this time series was generated from. + % + % - filtering (char) - Filtering applied to all channels of the data. For example, if this ElectricalSeries represents high-pass-filtered data (also known as AP Band), then this value could be "High-pass 4-pole Bessel filter at 500 Hz". If this ElectricalSeries represents low-pass-filtered LFP data and the type of filter is unknown, then this value could be "Low-pass filter at 300 Hz". If a non-standard filter type is used, provide as much detail about the filter properties as possible. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - electricalSeries (types.core.ElectricalSeries) - A ElectricalSeries object + varargin = [{'channel_conversion_axis' types.util.correctType(1, 'int32') 'data_unit' 'volts'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/ElectrodeGroup.m b/+types/+core/ElectrodeGroup.m index d44737b8..9795bafc 100644 --- a/+types/+core/ElectrodeGroup.m +++ b/+types/+core/ElectrodeGroup.m @@ -1,5 +1,8 @@ classdef ElectrodeGroup < types.core.NWBContainer & types.untyped.GroupClass -% ELECTRODEGROUP A physical grouping of electrodes, e.g. a shank of an array. +% ELECTRODEGROUP - A physical grouping of electrodes, e.g. a shank of an array. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -12,7 +15,25 @@ methods function obj = ElectrodeGroup(varargin) - % ELECTRODEGROUP Constructor for ElectrodeGroup + % ELECTRODEGROUP - Constructor for ElectrodeGroup + % + % Syntax: + % electrodeGroup = types.core.ELECTRODEGROUP() creates a ElectrodeGroup object with unset property values. + % + % electrodeGroup = types.core.ELECTRODEGROUP(Name, Value) creates a ElectrodeGroup object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of this electrode group. + % + % - device (Device) - Link to the device that was used to record from this electrode group. + % + % - location (char) - Location of electrode group. Specify the area, layer, comments on estimation of area/layer, etc. Use standard atlas names for anatomical regions when possible. + % + % - position (Table with columns: (single, single, single)) - stereotaxic or common framework coordinates + % + % Output Arguments: + % - electrodeGroup (types.core.ElectrodeGroup) - A ElectrodeGroup object + obj = obj@types.core.NWBContainer(varargin{:}); diff --git a/+types/+core/EventDetection.m b/+types/+core/EventDetection.m index 6a7cfe46..b03f3aab 100644 --- a/+types/+core/EventDetection.m +++ b/+types/+core/EventDetection.m @@ -1,5 +1,8 @@ classdef EventDetection < types.core.NWBDataInterface & types.untyped.GroupClass -% EVENTDETECTION Detected spike events from voltage trace(s). +% EVENTDETECTION - Detected spike events from voltage trace(s). +% +% Required Properties: +% detection_method, source_idx, times % READONLY PROPERTIES @@ -19,7 +22,25 @@ methods function obj = EventDetection(varargin) - % EVENTDETECTION Constructor for EventDetection + % EVENTDETECTION - Constructor for EventDetection + % + % Syntax: + % eventDetection = types.core.EVENTDETECTION() creates a EventDetection object with unset property values. + % + % eventDetection = types.core.EVENTDETECTION(Name, Value) creates a EventDetection object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - detection_method (char) - Description of how events were detected, such as voltage threshold, or dV/dT threshold, as well as relevant values. + % + % - source_electricalseries (ElectricalSeries) - Link to the ElectricalSeries that this data was calculated from. Metadata about electrodes and their position can be read from that ElectricalSeries so it's not necessary to include that information here. + % + % - source_idx (int32) - Indices (zero-based) into source ElectricalSeries::data array corresponding to time of event. ''description'' should define what is meant by time of event (e.g., .25 ms before action potential peak, zero-crossing time, etc). The index points to each event from the raw data. + % + % - times (double) - Timestamps of events, in seconds. + % + % Output Arguments: + % - eventDetection (types.core.EventDetection) - A EventDetection object + varargin = [{'times_unit' 'seconds'} varargin]; obj = obj@types.core.NWBDataInterface(varargin{:}); diff --git a/+types/+core/EventWaveform.m b/+types/+core/EventWaveform.m index 3915db7c..3847832f 100644 --- a/+types/+core/EventWaveform.m +++ b/+types/+core/EventWaveform.m @@ -1,5 +1,8 @@ classdef EventWaveform < types.core.NWBDataInterface & types.untyped.GroupClass -% EVENTWAVEFORM DEPRECATED. Represents either the waveforms of detected events, as extracted from a raw data trace in /acquisition, or the event waveforms that were stored during experiment acquisition. +% EVENTWAVEFORM - DEPRECATED. Represents either the waveforms of detected events, as extracted from a raw data trace in /acquisition, or the event waveforms that were stored during experiment acquisition. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -9,7 +12,19 @@ methods function obj = EventWaveform(varargin) - % EVENTWAVEFORM Constructor for EventWaveform + % EVENTWAVEFORM - Constructor for EventWaveform + % + % Syntax: + % eventWaveform = types.core.EVENTWAVEFORM() creates a EventWaveform object with unset property values. + % + % eventWaveform = types.core.EVENTWAVEFORM(Name, Value) creates a EventWaveform object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - spikeeventseries (SpikeEventSeries) - SpikeEventSeries object(s) containing detected spike event waveforms. + % + % Output Arguments: + % - eventWaveform (types.core.EventWaveform) - A EventWaveform object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.spikeeventseries, ivarargin] = types.util.parseConstrained(obj,'spikeeventseries', 'types.core.SpikeEventSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/ExperimentalConditionsTable.m b/+types/+core/ExperimentalConditionsTable.m index 5dbc082f..ff3b62e0 100644 --- a/+types/+core/ExperimentalConditionsTable.m +++ b/+types/+core/ExperimentalConditionsTable.m @@ -1,5 +1,8 @@ classdef ExperimentalConditionsTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% EXPERIMENTALCONDITIONSTABLE A table for grouping different intracellular recording repetitions together that belong to the same experimental condition. +% EXPERIMENTALCONDITIONSTABLE - A table for grouping different intracellular recording repetitions together that belong to the same experimental condition. +% +% Required Properties: +% id, repetitions, repetitions_index % REQUIRED PROPERTIES @@ -10,7 +13,29 @@ methods function obj = ExperimentalConditionsTable(varargin) - % EXPERIMENTALCONDITIONSTABLE Constructor for ExperimentalConditionsTable + % EXPERIMENTALCONDITIONSTABLE - Constructor for ExperimentalConditionsTable + % + % Syntax: + % experimentalConditionsTable = types.core.EXPERIMENTALCONDITIONSTABLE() creates a ExperimentalConditionsTable object with unset property values. + % + % experimentalConditionsTable = types.core.EXPERIMENTALCONDITIONSTABLE(Name, Value) creates a ExperimentalConditionsTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - repetitions (DynamicTableRegion) - A reference to one or more rows in the RepetitionsTable table. + % + % - repetitions_index (VectorIndex) - Index dataset for the repetitions column. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - experimentalConditionsTable (types.core.ExperimentalConditionsTable) - A ExperimentalConditionsTable object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/EyeTracking.m b/+types/+core/EyeTracking.m index b27553a5..5fb4cb3f 100644 --- a/+types/+core/EyeTracking.m +++ b/+types/+core/EyeTracking.m @@ -1,5 +1,8 @@ classdef EyeTracking < types.core.NWBDataInterface & types.untyped.GroupClass -% EYETRACKING Eye-tracking data, representing direction of gaze. +% EYETRACKING - Eye-tracking data, representing direction of gaze. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -9,7 +12,19 @@ methods function obj = EyeTracking(varargin) - % EYETRACKING Constructor for EyeTracking + % EYETRACKING - Constructor for EyeTracking + % + % Syntax: + % eyeTracking = types.core.EYETRACKING() creates a EyeTracking object with unset property values. + % + % eyeTracking = types.core.EYETRACKING(Name, Value) creates a EyeTracking object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - spatialseries (SpatialSeries) - SpatialSeries object containing data measuring direction of gaze. + % + % Output Arguments: + % - eyeTracking (types.core.EyeTracking) - A EyeTracking object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.spatialseries, ivarargin] = types.util.parseConstrained(obj,'spatialseries', 'types.core.SpatialSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/FeatureExtraction.m b/+types/+core/FeatureExtraction.m index cab26ebd..13342b54 100644 --- a/+types/+core/FeatureExtraction.m +++ b/+types/+core/FeatureExtraction.m @@ -1,5 +1,8 @@ classdef FeatureExtraction < types.core.NWBDataInterface & types.untyped.GroupClass -% FEATUREEXTRACTION Features, such as PC1 and PC2, that are extracted from signals stored in a SpikeEventSeries or other source. +% FEATUREEXTRACTION - Features, such as PC1 and PC2, that are extracted from signals stored in a SpikeEventSeries or other source. +% +% Required Properties: +% description, electrodes, features, times % REQUIRED PROPERTIES @@ -12,7 +15,25 @@ methods function obj = FeatureExtraction(varargin) - % FEATUREEXTRACTION Constructor for FeatureExtraction + % FEATUREEXTRACTION - Constructor for FeatureExtraction + % + % Syntax: + % featureExtraction = types.core.FEATUREEXTRACTION() creates a FeatureExtraction object with unset property values. + % + % featureExtraction = types.core.FEATUREEXTRACTION(Name, Value) creates a FeatureExtraction object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of features (eg, ''PC1'') for each of the extracted features. + % + % - electrodes (DynamicTableRegion) - DynamicTableRegion pointer to the electrodes that this time series was generated from. + % + % - features (single) - Multi-dimensional array of features extracted from each event. + % + % - times (double) - Times of events that features correspond to (can be a link). + % + % Output Arguments: + % - featureExtraction (types.core.FeatureExtraction) - A FeatureExtraction object + obj = obj@types.core.NWBDataInterface(varargin{:}); diff --git a/+types/+core/FilteredEphys.m b/+types/+core/FilteredEphys.m index 53c81128..02cd1e5e 100644 --- a/+types/+core/FilteredEphys.m +++ b/+types/+core/FilteredEphys.m @@ -1,5 +1,8 @@ classdef FilteredEphys < types.core.NWBDataInterface & types.untyped.GroupClass -% FILTEREDEPHYS Electrophysiology data from one or more channels that has been subjected to filtering. Examples of filtered data include Theta and Gamma (LFP has its own interface). FilteredEphys modules publish an ElectricalSeries for each filtered channel or set of channels. The name of each ElectricalSeries is arbitrary but should be informative. The source of the filtered data, whether this is from analysis of another time series or as acquired by hardware, should be noted in each's TimeSeries::description field. There is no assumed 1::1 correspondence between filtered ephys signals and electrodes, as a single signal can apply to many nearby electrodes, and one electrode may have different filtered (e.g., theta and/or gamma) signals represented. Filter properties should be noted in the ElectricalSeries 'filtering' attribute. +% FILTEREDEPHYS - Electrophysiology data from one or more channels that has been subjected to filtering. Examples of filtered data include Theta and Gamma (LFP has its own interface). FilteredEphys modules publish an ElectricalSeries for each filtered channel or set of channels. The name of each ElectricalSeries is arbitrary but should be informative. The source of the filtered data, whether this is from analysis of another time series or as acquired by hardware, should be noted in each's TimeSeries::description field. There is no assumed 1::1 correspondence between filtered ephys signals and electrodes, as a single signal can apply to many nearby electrodes, and one electrode may have different filtered (e.g., theta and/or gamma) signals represented. Filter properties should be noted in the ElectricalSeries 'filtering' attribute. +% +% Required Properties: +% electricalseries % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = FilteredEphys(varargin) - % FILTEREDEPHYS Constructor for FilteredEphys + % FILTEREDEPHYS - Constructor for FilteredEphys + % + % Syntax: + % filteredEphys = types.core.FILTEREDEPHYS() creates a FilteredEphys object with unset property values. + % + % filteredEphys = types.core.FILTEREDEPHYS(Name, Value) creates a FilteredEphys object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - electricalseries (ElectricalSeries) - ElectricalSeries object(s) containing filtered electrophysiology data. + % + % Output Arguments: + % - filteredEphys (types.core.FilteredEphys) - A FilteredEphys object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.electricalseries, ivarargin] = types.util.parseConstrained(obj,'electricalseries', 'types.core.ElectricalSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/Fluorescence.m b/+types/+core/Fluorescence.m index 904cb778..2c6f372f 100644 --- a/+types/+core/Fluorescence.m +++ b/+types/+core/Fluorescence.m @@ -1,5 +1,8 @@ classdef Fluorescence < types.core.NWBDataInterface & types.untyped.GroupClass -% FLUORESCENCE Fluorescence information about a region of interest (ROI). Storage hierarchy of fluorescence should be the same as for segmentation (ie, same names for ROIs and for image planes). +% FLUORESCENCE - Fluorescence information about a region of interest (ROI). Storage hierarchy of fluorescence should be the same as for segmentation (ie, same names for ROIs and for image planes). +% +% Required Properties: +% roiresponseseries % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = Fluorescence(varargin) - % FLUORESCENCE Constructor for Fluorescence + % FLUORESCENCE - Constructor for Fluorescence + % + % Syntax: + % fluorescence = types.core.FLUORESCENCE() creates a Fluorescence object with unset property values. + % + % fluorescence = types.core.FLUORESCENCE(Name, Value) creates a Fluorescence object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - roiresponseseries (RoiResponseSeries) - RoiResponseSeries object(s) containing fluorescence data for a ROI. + % + % Output Arguments: + % - fluorescence (types.core.Fluorescence) - A Fluorescence object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.roiresponseseries, ivarargin] = types.util.parseConstrained(obj,'roiresponseseries', 'types.core.RoiResponseSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/GrayscaleImage.m b/+types/+core/GrayscaleImage.m index 0f773147..ee85bdf1 100644 --- a/+types/+core/GrayscaleImage.m +++ b/+types/+core/GrayscaleImage.m @@ -1,11 +1,30 @@ classdef GrayscaleImage < types.core.Image & types.untyped.DatasetClass -% GRAYSCALEIMAGE A grayscale image. +% GRAYSCALEIMAGE - A grayscale image. +% +% Required Properties: +% data methods function obj = GrayscaleImage(varargin) - % GRAYSCALEIMAGE Constructor for GrayscaleImage + % GRAYSCALEIMAGE - Constructor for GrayscaleImage + % + % Syntax: + % grayscaleImage = types.core.GRAYSCALEIMAGE() creates a GrayscaleImage object with unset property values. + % + % grayscaleImage = types.core.GRAYSCALEIMAGE(Name, Value) creates a GrayscaleImage object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (numeric) - No description + % + % - description (char) - Description of the image. + % + % - resolution (single) - Pixel resolution of the image, in pixels per centimeter. + % + % Output Arguments: + % - grayscaleImage (types.core.GrayscaleImage) - A GrayscaleImage object + obj = obj@types.core.Image(varargin{:}); diff --git a/+types/+core/IZeroClampSeries.m b/+types/+core/IZeroClampSeries.m index 880a25c2..1002c42d 100644 --- a/+types/+core/IZeroClampSeries.m +++ b/+types/+core/IZeroClampSeries.m @@ -1,11 +1,54 @@ classdef IZeroClampSeries < types.core.CurrentClampSeries & types.untyped.GroupClass -% IZEROCLAMPSERIES Voltage data from an intracellular recording when all current and amplifier settings are off (i.e., CurrentClampSeries fields will be zero). There is no CurrentClampStimulusSeries associated with an IZero series because the amplifier is disconnected and no stimulus can reach the cell. +% IZEROCLAMPSERIES - Voltage data from an intracellular recording when all current and amplifier settings are off (i.e., CurrentClampSeries fields will be zero). There is no CurrentClampStimulusSeries associated with an IZero series because the amplifier is disconnected and no stimulus can reach the cell. +% +% Required Properties: +% bias_current, bridge_balance, capacitance_compensation, data methods function obj = IZeroClampSeries(varargin) - % IZEROCLAMPSERIES Constructor for IZeroClampSeries + % IZEROCLAMPSERIES - Constructor for IZeroClampSeries + % + % Syntax: + % iZeroClampSeries = types.core.IZEROCLAMPSERIES() creates a IZeroClampSeries object with unset property values. + % + % iZeroClampSeries = types.core.IZEROCLAMPSERIES(Name, Value) creates a IZeroClampSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (any) - Recorded voltage. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - electrode (IntracellularElectrode) - Link to IntracellularElectrode object that describes the electrode that was used to apply or record this data. + % + % - gain (single) - Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - sweep_number (uint32) - Sweep number, allows to group different PatchClampSeries together. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - iZeroClampSeries (types.core.IZeroClampSeries) - A IZeroClampSeries object + varargin = [{'bias_current' types.util.correctType(0, 'single') 'bridge_balance' types.util.correctType(0, 'single') 'capacitance_compensation' types.util.correctType(0, 'single') 'stimulus_description' 'N/A'} varargin]; obj = obj@types.core.CurrentClampSeries(varargin{:}); diff --git a/+types/+core/Image.m b/+types/+core/Image.m index 93a0ab7d..f46bc05c 100644 --- a/+types/+core/Image.m +++ b/+types/+core/Image.m @@ -1,5 +1,8 @@ classdef Image < types.core.NWBData & types.untyped.DatasetClass -% IMAGE An abstract data type for an image. Shape can be 2-D (x, y), or 3-D where the third dimension can have three or four elements, e.g. (x, y, (r, g, b)) or (x, y, (r, g, b, a)). +% IMAGE - An abstract data type for an image. Shape can be 2-D (x, y), or 3-D where the third dimension can have three or four elements, e.g. (x, y, (r, g, b)) or (x, y, (r, g, b, a)). +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -10,7 +13,23 @@ methods function obj = Image(varargin) - % IMAGE Constructor for Image + % IMAGE - Constructor for Image + % + % Syntax: + % image = types.core.IMAGE() creates a Image object with unset property values. + % + % image = types.core.IMAGE(Name, Value) creates a Image object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (numeric) - No description + % + % - description (char) - Description of the image. + % + % - resolution (single) - Pixel resolution of the image, in pixels per centimeter. + % + % Output Arguments: + % - image (types.core.Image) - A Image object + obj = obj@types.core.NWBData(varargin{:}); diff --git a/+types/+core/ImageMaskSeries.m b/+types/+core/ImageMaskSeries.m index ae2c1710..8ee1dfc5 100644 --- a/+types/+core/ImageMaskSeries.m +++ b/+types/+core/ImageMaskSeries.m @@ -1,5 +1,8 @@ classdef ImageMaskSeries < types.core.ImageSeries & types.untyped.GroupClass -% IMAGEMASKSERIES DEPRECATED. An alpha mask that is applied to a presented visual stimulus. The 'data' array contains an array of mask values that are applied to the displayed image. Mask values are stored as RGBA. Mask can vary with time. The timestamps array indicates the starting time of a mask, and that mask pattern continues until it's explicitly changed. +% IMAGEMASKSERIES - DEPRECATED. An alpha mask that is applied to a presented visual stimulus. The 'data' array contains an array of mask values that are applied to the displayed image. Mask values are stored as RGBA. Mask can vary with time. The timestamps array indicates the starting time of a mask, and that mask pattern continues until it's explicitly changed. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -9,7 +12,55 @@ methods function obj = ImageMaskSeries(varargin) - % IMAGEMASKSERIES Constructor for ImageMaskSeries + % IMAGEMASKSERIES - Constructor for ImageMaskSeries + % + % Syntax: + % imageMaskSeries = types.core.IMAGEMASKSERIES() creates a ImageMaskSeries object with unset property values. + % + % imageMaskSeries = types.core.IMAGEMASKSERIES(Name, Value) creates a ImageMaskSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Binary data representing images across frames. If data are stored in an external file, this should be an empty 3D array. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - device (Device) - Link to the Device object that was used to capture these images. + % + % - dimension (int32) - Number of pixels on x, y, (and z) axes. + % + % - external_file (char) - Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored in another NWB file and that file is linked to this file. + % + % - external_file_starting_frame (int32) - Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an index to indicate which frames each file contains, to facilitate random access. The 'starting_frame' attribute, hence, contains a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0]. + % + % - format (char) - Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not present, then the default format='raw' case is assumed. + % + % - masked_imageseries (ImageSeries) - Link to ImageSeries object that this image mask is applied to. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - imageMaskSeries (types.core.ImageMaskSeries) - A ImageMaskSeries object + obj = obj@types.core.ImageSeries(varargin{:}); diff --git a/+types/+core/ImageReferences.m b/+types/+core/ImageReferences.m index e5139fbf..fc0cfa16 100644 --- a/+types/+core/ImageReferences.m +++ b/+types/+core/ImageReferences.m @@ -1,11 +1,26 @@ classdef ImageReferences < types.core.NWBData & types.untyped.DatasetClass -% IMAGEREFERENCES Ordered dataset of references to Image objects. +% IMAGEREFERENCES - Ordered dataset of references to Image objects. +% +% Required Properties: +% data methods function obj = ImageReferences(varargin) - % IMAGEREFERENCES Constructor for ImageReferences + % IMAGEREFERENCES - Constructor for ImageReferences + % + % Syntax: + % imageReferences = types.core.IMAGEREFERENCES() creates a ImageReferences object with unset property values. + % + % imageReferences = types.core.IMAGEREFERENCES(Name, Value) creates a ImageReferences object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (Object reference to Image) - No description + % + % Output Arguments: + % - imageReferences (types.core.ImageReferences) - A ImageReferences object + obj = obj@types.core.NWBData(varargin{:}); diff --git a/+types/+core/ImageSegmentation.m b/+types/+core/ImageSegmentation.m index 75d2c312..e4873310 100644 --- a/+types/+core/ImageSegmentation.m +++ b/+types/+core/ImageSegmentation.m @@ -1,5 +1,8 @@ classdef ImageSegmentation < types.core.NWBDataInterface & types.untyped.GroupClass -% IMAGESEGMENTATION Stores pixels in an image that represent different regions of interest (ROIs) or masks. All segmentation for a given imaging plane is stored together, with storage for multiple imaging planes (masks) supported. Each ROI is stored in its own subgroup, with the ROI group containing both a 2D mask and a list of pixels that make up this mask. Segments can also be used for masking neuropil. If segmentation is allowed to change with time, a new imaging plane (or module) is required and ROI names should remain consistent between them. +% IMAGESEGMENTATION - Stores pixels in an image that represent different regions of interest (ROIs) or masks. All segmentation for a given imaging plane is stored together, with storage for multiple imaging planes (masks) supported. Each ROI is stored in its own subgroup, with the ROI group containing both a 2D mask and a list of pixels that make up this mask. Segments can also be used for masking neuropil. If segmentation is allowed to change with time, a new imaging plane (or module) is required and ROI names should remain consistent between them. +% +% Required Properties: +% planesegmentation % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = ImageSegmentation(varargin) - % IMAGESEGMENTATION Constructor for ImageSegmentation + % IMAGESEGMENTATION - Constructor for ImageSegmentation + % + % Syntax: + % imageSegmentation = types.core.IMAGESEGMENTATION() creates a ImageSegmentation object with unset property values. + % + % imageSegmentation = types.core.IMAGESEGMENTATION(Name, Value) creates a ImageSegmentation object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - planesegmentation (PlaneSegmentation) - Results from image segmentation of a specific imaging plane. + % + % Output Arguments: + % - imageSegmentation (types.core.ImageSegmentation) - A ImageSegmentation object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.planesegmentation, ivarargin] = types.util.parseConstrained(obj,'planesegmentation', 'types.core.PlaneSegmentation', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/ImageSeries.m b/+types/+core/ImageSeries.m index 1fbdd219..0a66dfc2 100644 --- a/+types/+core/ImageSeries.m +++ b/+types/+core/ImageSeries.m @@ -1,5 +1,8 @@ classdef ImageSeries < types.core.TimeSeries & types.untyped.GroupClass -% IMAGESERIES General image data that is common between acquisition and stimulus time series. Sometimes the image data is stored in the file in a raw format while other times it will be stored as a series of external image files in the host file system. The data field will either be binary data, if the data is stored in the NWB file, or empty, if the data is stored in an external image stack. [frame][x][y] or [frame][x][y][z]. +% IMAGESERIES - General image data that is common between acquisition and stimulus time series. Sometimes the image data is stored in the file in a raw format while other times it will be stored as a series of external image files in the host file system. The data field will either be binary data, if the data is stored in the NWB file, or empty, if the data is stored in an external image stack. [frame][x][y] or [frame][x][y][z]. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -13,7 +16,53 @@ methods function obj = ImageSeries(varargin) - % IMAGESERIES Constructor for ImageSeries + % IMAGESERIES - Constructor for ImageSeries + % + % Syntax: + % imageSeries = types.core.IMAGESERIES() creates a ImageSeries object with unset property values. + % + % imageSeries = types.core.IMAGESERIES(Name, Value) creates a ImageSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Binary data representing images across frames. If data are stored in an external file, this should be an empty 3D array. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - device (Device) - Link to the Device object that was used to capture these images. + % + % - dimension (int32) - Number of pixels on x, y, (and z) axes. + % + % - external_file (char) - Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored in another NWB file and that file is linked to this file. + % + % - external_file_starting_frame (int32) - Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an index to indicate which frames each file contains, to facilitate random access. The 'starting_frame' attribute, hence, contains a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0]. + % + % - format (char) - Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not present, then the default format='raw' case is assumed. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - imageSeries (types.core.ImageSeries) - A ImageSeries object + obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/Images.m b/+types/+core/Images.m index b07572ad..6d88c6e5 100644 --- a/+types/+core/Images.m +++ b/+types/+core/Images.m @@ -1,5 +1,8 @@ classdef Images < types.core.NWBDataInterface & types.untyped.GroupClass -% IMAGES A collection of images with an optional way to specify the order of the images using the "order_of_images" dataset. An order must be specified if the images are referenced by index, e.g., from an IndexSeries. +% IMAGES - A collection of images with an optional way to specify the order of the images using the "order_of_images" dataset. An order must be specified if the images are referenced by index, e.g., from an IndexSeries. +% +% Required Properties: +% image % REQUIRED PROPERTIES @@ -14,7 +17,23 @@ methods function obj = Images(varargin) - % IMAGES Constructor for Images + % IMAGES - Constructor for Images + % + % Syntax: + % images = types.core.IMAGES() creates a Images object with unset property values. + % + % images = types.core.IMAGES(Name, Value) creates a Images object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of this collection of images. + % + % - image (Image) - Images stored in this collection. + % + % - order_of_images (ImageReferences) - Ordered dataset of references to Image objects stored in the parent group. Each Image object in the Images group should be stored once and only once, so the dataset should have the same length as the number of images. + % + % Output Arguments: + % - images (types.core.Images) - A Images object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.image, ivarargin] = types.util.parseConstrained(obj,'image', 'types.core.Image', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/ImagingPlane.m b/+types/+core/ImagingPlane.m index c47afe39..779dc16f 100644 --- a/+types/+core/ImagingPlane.m +++ b/+types/+core/ImagingPlane.m @@ -1,5 +1,8 @@ classdef ImagingPlane < types.core.NWBContainer & types.untyped.GroupClass -% IMAGINGPLANE An imaging plane and its metadata. +% IMAGINGPLANE - An imaging plane and its metadata. +% +% Required Properties: +% excitation_lambda, indicator, location, opticalchannel % REQUIRED PROPERTIES @@ -26,7 +29,47 @@ methods function obj = ImagingPlane(varargin) - % IMAGINGPLANE Constructor for ImagingPlane + % IMAGINGPLANE - Constructor for ImagingPlane + % + % Syntax: + % imagingPlane = types.core.IMAGINGPLANE() creates a ImagingPlane object with unset property values. + % + % imagingPlane = types.core.IMAGINGPLANE(Name, Value) creates a ImagingPlane object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of the imaging plane. + % + % - device (Device) - Link to the Device object that was used to record from this electrode. + % + % - excitation_lambda (single) - Excitation wavelength, in nm. + % + % - grid_spacing (single) - Space between pixels in (x, y) or voxels in (x, y, z) directions, in the specified unit. Assumes imaging plane is a regular grid. See also reference_frame to interpret the grid. + % + % - grid_spacing_unit (char) - Measurement units for grid_spacing. The default value is 'meters'. + % + % - imaging_rate (single) - Rate that images are acquired, in Hz. If the corresponding TimeSeries is present, the rate should be stored there instead. + % + % - indicator (char) - Calcium indicator. + % + % - location (char) - Location of the imaging plane. Specify the area, layer, comments on estimation of area/layer, stereotaxic coordinates if in vivo, etc. Use standard atlas names for anatomical regions when possible. + % + % - manifold (single) - DEPRECATED Physical position of each pixel. 'xyz' represents the position of the pixel relative to the defined coordinate space. Deprecated in favor of origin_coords and grid_spacing. + % + % - manifold_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as pixels from x = -500 to 499, y = -500 to 499 that correspond to a 2 m x 2 m range, then the 'conversion' multiplier to get from raw data acquisition pixel units to meters is 2/1000. + % + % - manifold_unit (char) - Base unit of measurement for working with the data. The default value is 'meters'. + % + % - opticalchannel (OpticalChannel) - An optical channel used to record from an imaging plane. + % + % - origin_coords (single) - Physical location of the first element of the imaging plane (0, 0) for 2-D data or (0, 0, 0) for 3-D data. See also reference_frame for what the physical location is relative to (e.g., bregma). + % + % - origin_coords_unit (char) - Measurement units for origin_coords. The default value is 'meters'. + % + % - reference_frame (char) - Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = "Origin coordinates are relative to bregma. First dimension corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral)." + % + % Output Arguments: + % - imagingPlane (types.core.ImagingPlane) - A ImagingPlane object + varargin = [{'grid_spacing_unit' 'meters' 'manifold_conversion' types.util.correctType(1, 'single') 'manifold_unit' 'meters' 'origin_coords_unit' 'meters'} varargin]; obj = obj@types.core.NWBContainer(varargin{:}); [obj.opticalchannel, ivarargin] = types.util.parseConstrained(obj,'opticalchannel', 'types.core.OpticalChannel', varargin{:}); diff --git a/+types/+core/ImagingRetinotopy.m b/+types/+core/ImagingRetinotopy.m index 24dbf26d..68bddb5d 100644 --- a/+types/+core/ImagingRetinotopy.m +++ b/+types/+core/ImagingRetinotopy.m @@ -1,5 +1,8 @@ classdef ImagingRetinotopy < types.core.NWBDataInterface & types.untyped.GroupClass -% IMAGINGRETINOTOPY DEPRECATED. Intrinsic signal optical imaging or widefield imaging for measuring retinotopy. Stores orthogonal maps (e.g., altitude/azimuth; radius/theta) of responses to specific stimuli and a combined polarity map from which to identify visual areas. This group does not store the raw responses imaged during retinotopic mapping or the stimuli presented, but rather the resulting phase and power maps after applying a Fourier transform on the averaged responses. Note: for data consistency, all images and arrays are stored in the format [row][column] and [row, col], which equates to [y][x]. Field of view and dimension arrays may appear backward (i.e., y before x). +% IMAGINGRETINOTOPY - DEPRECATED. Intrinsic signal optical imaging or widefield imaging for measuring retinotopy. Stores orthogonal maps (e.g., altitude/azimuth; radius/theta) of responses to specific stimuli and a combined polarity map from which to identify visual areas. This group does not store the raw responses imaged during retinotopic mapping or the stimuli presented, but rather the resulting phase and power maps after applying a Fourier transform on the averaged responses. Note: for data consistency, all images and arrays are stored in the format [row][column] and [row, col], which equates to [y][x]. Field of view and dimension arrays may appear backward (i.e., y before x). +% +% Required Properties: +% axis_1_phase_map, axis_2_phase_map, axis_descriptions, vasculature_image % REQUIRED PROPERTIES @@ -42,7 +45,79 @@ methods function obj = ImagingRetinotopy(varargin) - % IMAGINGRETINOTOPY Constructor for ImagingRetinotopy + % IMAGINGRETINOTOPY - Constructor for ImagingRetinotopy + % + % Syntax: + % imagingRetinotopy = types.core.IMAGINGRETINOTOPY() creates a ImagingRetinotopy object with unset property values. + % + % imagingRetinotopy = types.core.IMAGINGRETINOTOPY(Name, Value) creates a ImagingRetinotopy object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - axis_1_phase_map (single) - Phase response to stimulus on the first measured axis. + % + % - axis_1_phase_map_dimension (int32) - Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. + % + % - axis_1_phase_map_field_of_view (single) - Size of viewing area, in meters. + % + % - axis_1_phase_map_unit (char) - Unit that axis data is stored in (e.g., degrees). + % + % - axis_1_power_map (single) - Power response on the first measured axis. Response is scaled so 0.0 is no power in the response and 1.0 is maximum relative power. + % + % - axis_1_power_map_dimension (int32) - Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. + % + % - axis_1_power_map_field_of_view (single) - Size of viewing area, in meters. + % + % - axis_1_power_map_unit (char) - Unit that axis data is stored in (e.g., degrees). + % + % - axis_2_phase_map (single) - Phase response to stimulus on the second measured axis. + % + % - axis_2_phase_map_dimension (int32) - Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. + % + % - axis_2_phase_map_field_of_view (single) - Size of viewing area, in meters. + % + % - axis_2_phase_map_unit (char) - Unit that axis data is stored in (e.g., degrees). + % + % - axis_2_power_map (single) - Power response on the second measured axis. Response is scaled so 0.0 is no power in the response and 1.0 is maximum relative power. + % + % - axis_2_power_map_dimension (int32) - Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. + % + % - axis_2_power_map_field_of_view (single) - Size of viewing area, in meters. + % + % - axis_2_power_map_unit (char) - Unit that axis data is stored in (e.g., degrees). + % + % - axis_descriptions (char) - Two-element array describing the contents of the two response axis fields. Description should be something like ['altitude', 'azimuth'] or '['radius', 'theta']. + % + % - focal_depth_image (uint16) - Gray-scale image taken with same settings/parameters (e.g., focal depth, wavelength) as data collection. Array format: [rows][columns]. + % + % - focal_depth_image_bits_per_pixel (int32) - Number of bits used to represent each value. This is necessary to determine maximum (white) pixel value. + % + % - focal_depth_image_dimension (int32) - Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. + % + % - focal_depth_image_field_of_view (single) - Size of viewing area, in meters. + % + % - focal_depth_image_focal_depth (single) - Focal depth offset, in meters. + % + % - focal_depth_image_format (char) - Format of image. Right now only 'raw' is supported. + % + % - sign_map (single) - Sine of the angle between the direction of the gradient in axis_1 and axis_2. + % + % - sign_map_dimension (int32) - Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. + % + % - sign_map_field_of_view (single) - Size of viewing area, in meters. + % + % - vasculature_image (uint16) - Gray-scale anatomical image of cortical surface. Array structure: [rows][columns] + % + % - vasculature_image_bits_per_pixel (int32) - Number of bits used to represent each value. This is necessary to determine maximum (white) pixel value + % + % - vasculature_image_dimension (int32) - Number of rows and columns in the image. NOTE: row, column representation is equivalent to height, width. + % + % - vasculature_image_field_of_view (single) - Size of viewing area, in meters. + % + % - vasculature_image_format (char) - Format of image. Right now only 'raw' is supported. + % + % Output Arguments: + % - imagingRetinotopy (types.core.ImagingRetinotopy) - A ImagingRetinotopy object + obj = obj@types.core.NWBDataInterface(varargin{:}); diff --git a/+types/+core/IndexSeries.m b/+types/+core/IndexSeries.m index 8ba26b10..39a63924 100644 --- a/+types/+core/IndexSeries.m +++ b/+types/+core/IndexSeries.m @@ -1,5 +1,8 @@ classdef IndexSeries < types.core.TimeSeries & types.untyped.GroupClass -% INDEXSERIES Stores indices to image frames stored in an ImageSeries. The purpose of the IndexSeries is to allow a static image stack to be stored in an Images object, and the images in the stack to be referenced out-of-order. This can be for the display of individual images, or of movie segments (as a movie is simply a series of images). The data field stores the index of the frame in the referenced Images object, and the timestamps array indicates when that image was displayed. +% INDEXSERIES - Stores indices to image frames stored in an ImageSeries. The purpose of the IndexSeries is to allow a static image stack to be stored in an Images object, and the images in the stack to be referenced out-of-order. This can be for the display of individual images, or of movie segments (as a movie is simply a series of images). The data field stores the index of the frame in the referenced Images object, and the timestamps array indicates when that image was displayed. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -10,7 +13,45 @@ methods function obj = IndexSeries(varargin) - % INDEXSERIES Constructor for IndexSeries + % INDEXSERIES - Constructor for IndexSeries + % + % Syntax: + % indexSeries = types.core.INDEXSERIES() creates a IndexSeries object with unset property values. + % + % indexSeries = types.core.INDEXSERIES(Name, Value) creates a IndexSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (uint32) - Index of the image (using zero-indexing) in the linked Images object. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - This field is unused by IndexSeries. + % + % - data_offset (single) - This field is unused by IndexSeries. + % + % - data_resolution (single) - This field is unused by IndexSeries. + % + % - description (char) - Description of the time series. + % + % - indexed_images (Images) - Link to Images object containing an ordered set of images that are indexed. The Images object must contain a 'ordered_images' dataset specifying the order of the images in the Images type. + % + % - indexed_timeseries (ImageSeries) - Link to ImageSeries object containing images that are indexed. Use of this link is discouraged and will be deprecated. Link to an Images type instead. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - indexSeries (types.core.IndexSeries) - A IndexSeries object + varargin = [{'data_unit' 'N/A'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/IntervalSeries.m b/+types/+core/IntervalSeries.m index 27fd745a..93cea135 100644 --- a/+types/+core/IntervalSeries.m +++ b/+types/+core/IntervalSeries.m @@ -1,11 +1,46 @@ classdef IntervalSeries < types.core.TimeSeries & types.untyped.GroupClass -% INTERVALSERIES Stores intervals of data. The timestamps field stores the beginning and end of intervals. The data field stores whether the interval just started (>0 value) or ended (<0 value). Different interval types can be represented in the same series by using multiple key values (eg, 1 for feature A, 2 for feature B, 3 for feature C, etc). The field data stores an 8-bit integer. This is largely an alias of a standard TimeSeries but that is identifiable as representing time intervals in a machine-readable way. +% INTERVALSERIES - Stores intervals of data. The timestamps field stores the beginning and end of intervals. The data field stores whether the interval just started (>0 value) or ended (<0 value). Different interval types can be represented in the same series by using multiple key values (eg, 1 for feature A, 2 for feature B, 3 for feature C, etc). The field data stores an 8-bit integer. This is largely an alias of a standard TimeSeries but that is identifiable as representing time intervals in a machine-readable way. +% +% Required Properties: +% data methods function obj = IntervalSeries(varargin) - % INTERVALSERIES Constructor for IntervalSeries + % INTERVALSERIES - Constructor for IntervalSeries + % + % Syntax: + % intervalSeries = types.core.INTERVALSERIES() creates a IntervalSeries object with unset property values. + % + % intervalSeries = types.core.INTERVALSERIES(Name, Value) creates a IntervalSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (int8) - Use values >0 if interval started, <0 if interval ended. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - description (char) - Description of the time series. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - intervalSeries (types.core.IntervalSeries) - A IntervalSeries object + varargin = [{'data_resolution' types.util.correctType(-1, 'single') 'data_unit' 'n/a'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/IntracellularElectrode.m b/+types/+core/IntracellularElectrode.m index 6b1231e0..0cdbf388 100644 --- a/+types/+core/IntracellularElectrode.m +++ b/+types/+core/IntracellularElectrode.m @@ -1,5 +1,8 @@ classdef IntracellularElectrode < types.core.NWBContainer & types.untyped.GroupClass -% INTRACELLULARELECTRODE An intracellular electrode and its metadata. +% INTRACELLULARELECTRODE - An intracellular electrode and its metadata. +% +% Required Properties: +% description % REQUIRED PROPERTIES @@ -20,7 +23,35 @@ methods function obj = IntracellularElectrode(varargin) - % INTRACELLULARELECTRODE Constructor for IntracellularElectrode + % INTRACELLULARELECTRODE - Constructor for IntracellularElectrode + % + % Syntax: + % intracellularElectrode = types.core.INTRACELLULARELECTRODE() creates a IntracellularElectrode object with unset property values. + % + % intracellularElectrode = types.core.INTRACELLULARELECTRODE(Name, Value) creates a IntracellularElectrode object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - cell_id (char) - unique ID of the cell + % + % - description (char) - Description of electrode (e.g., whole-cell, sharp, etc.). + % + % - device (Device) - Device that was used to record from this electrode. + % + % - filtering (char) - Electrode specific filtering. + % + % - initial_access_resistance (char) - Initial access resistance. + % + % - location (char) - Location of the electrode. Specify the area, layer, comments on estimation of area/layer, stereotaxic coordinates if in vivo, etc. Use standard atlas names for anatomical regions when possible. + % + % - resistance (char) - Electrode resistance, in ohms. + % + % - seal (char) - Information about seal used for recording. + % + % - slice (char) - Information about slice used for recording. + % + % Output Arguments: + % - intracellularElectrode (types.core.IntracellularElectrode) - A IntracellularElectrode object + obj = obj@types.core.NWBContainer(varargin{:}); diff --git a/+types/+core/IntracellularElectrodesTable.m b/+types/+core/IntracellularElectrodesTable.m index ce75faef..037f7a0f 100644 --- a/+types/+core/IntracellularElectrodesTable.m +++ b/+types/+core/IntracellularElectrodesTable.m @@ -1,5 +1,8 @@ classdef IntracellularElectrodesTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% INTRACELLULARELECTRODESTABLE Table for storing intracellular electrode related metadata. +% INTRACELLULARELECTRODESTABLE - Table for storing intracellular electrode related metadata. +% +% Required Properties: +% electrode, id % REQUIRED PROPERTIES @@ -9,7 +12,25 @@ methods function obj = IntracellularElectrodesTable(varargin) - % INTRACELLULARELECTRODESTABLE Constructor for IntracellularElectrodesTable + % INTRACELLULARELECTRODESTABLE - Constructor for IntracellularElectrodesTable + % + % Syntax: + % intracellularElectrodesTable = types.core.INTRACELLULARELECTRODESTABLE() creates a IntracellularElectrodesTable object with unset property values. + % + % intracellularElectrodesTable = types.core.INTRACELLULARELECTRODESTABLE(Name, Value) creates a IntracellularElectrodesTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - electrode (VectorData) - Column for storing the reference to the intracellular electrode. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - intracellularElectrodesTable (types.core.IntracellularElectrodesTable) - A IntracellularElectrodesTable object + varargin = [{'description' 'Table for storing intracellular electrode related metadata.'} varargin]; obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/IntracellularRecordingsTable.m b/+types/+core/IntracellularRecordingsTable.m index 1fc8d5c9..fd36d444 100644 --- a/+types/+core/IntracellularRecordingsTable.m +++ b/+types/+core/IntracellularRecordingsTable.m @@ -1,5 +1,8 @@ classdef IntracellularRecordingsTable < types.hdmf_common.AlignedDynamicTable & types.untyped.GroupClass -% INTRACELLULARRECORDINGSTABLE A table to group together a stimulus and response from a single electrode and a single simultaneous recording. Each row in the table represents a single recording consisting typically of a stimulus and a corresponding response. In some cases, however, only a stimulus or a response is recorded as part of an experiment. In this case, both the stimulus and response will point to the same TimeSeries while the idx_start and count of the invalid column will be set to -1, thus, indicating that no values have been recorded for the stimulus or response, respectively. Note, a recording MUST contain at least a stimulus or a response. Typically the stimulus and response are PatchClampSeries. However, the use of AD/DA channels that are not associated to an electrode is also common in intracellular electrophysiology, in which case other TimeSeries may be used. +% INTRACELLULARRECORDINGSTABLE - A table to group together a stimulus and response from a single electrode and a single simultaneous recording. Each row in the table represents a single recording consisting typically of a stimulus and a corresponding response. In some cases, however, only a stimulus or a response is recorded as part of an experiment. In this case, both the stimulus and response will point to the same TimeSeries while the idx_start and count of the invalid column will be set to -1, thus, indicating that no values have been recorded for the stimulus or response, respectively. Note, a recording MUST contain at least a stimulus or a response. Typically the stimulus and response are PatchClampSeries. However, the use of AD/DA channels that are not associated to an electrode is also common in intracellular electrophysiology, in which case other TimeSeries may be used. +% +% Required Properties: +% electrodes, id, responses, stimuli % REQUIRED PROPERTIES @@ -11,7 +14,33 @@ methods function obj = IntracellularRecordingsTable(varargin) - % INTRACELLULARRECORDINGSTABLE Constructor for IntracellularRecordingsTable + % INTRACELLULARRECORDINGSTABLE - Constructor for IntracellularRecordingsTable + % + % Syntax: + % intracellularRecordingsTable = types.core.INTRACELLULARRECORDINGSTABLE() creates a IntracellularRecordingsTable object with unset property values. + % + % intracellularRecordingsTable = types.core.INTRACELLULARRECORDINGSTABLE(Name, Value) creates a IntracellularRecordingsTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - categories (char) - The names of the categories in this AlignedDynamicTable. Each category is represented by one DynamicTable stored in the parent group. This attribute should be used to specify an order of categories and the category names must match the names of the corresponding DynamicTable in the group. + % + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - dynamictable (DynamicTable) - A DynamicTable representing a particular category for columns in the AlignedDynamicTable parent container. The table MUST be aligned with (i.e., have the same number of rows) as all other DynamicTables stored in the AlignedDynamicTable parent container. The name of the category is given by the name of the DynamicTable and its description by the description attribute of the DynamicTable. + % + % - electrodes (IntracellularElectrodesTable) - Table for storing intracellular electrode related metadata. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - responses (IntracellularResponsesTable) - Table for storing intracellular response related metadata. + % + % - stimuli (IntracellularStimuliTable) - Table for storing intracellular stimulus related metadata. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - intracellularRecordingsTable (types.core.IntracellularRecordingsTable) - A IntracellularRecordingsTable object + varargin = [{'description' 'A table to group together a stimulus and response from a single electrode and a single simultaneous recording and for storing metadata about the intracellular recording.'} varargin]; obj = obj@types.hdmf_common.AlignedDynamicTable(varargin{:}); diff --git a/+types/+core/IntracellularResponsesTable.m b/+types/+core/IntracellularResponsesTable.m index 1b4979b7..b85e938f 100644 --- a/+types/+core/IntracellularResponsesTable.m +++ b/+types/+core/IntracellularResponsesTable.m @@ -1,5 +1,8 @@ classdef IntracellularResponsesTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% INTRACELLULARRESPONSESTABLE Table for storing intracellular response related metadata. +% INTRACELLULARRESPONSESTABLE - Table for storing intracellular response related metadata. +% +% Required Properties: +% id, response % REQUIRED PROPERTIES @@ -9,7 +12,25 @@ methods function obj = IntracellularResponsesTable(varargin) - % INTRACELLULARRESPONSESTABLE Constructor for IntracellularResponsesTable + % INTRACELLULARRESPONSESTABLE - Constructor for IntracellularResponsesTable + % + % Syntax: + % intracellularResponsesTable = types.core.INTRACELLULARRESPONSESTABLE() creates a IntracellularResponsesTable object with unset property values. + % + % intracellularResponsesTable = types.core.INTRACELLULARRESPONSESTABLE(Name, Value) creates a IntracellularResponsesTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - response (TimeSeriesReferenceVectorData) - Column storing the reference to the recorded response for the recording (rows) + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - intracellularResponsesTable (types.core.IntracellularResponsesTable) - A IntracellularResponsesTable object + varargin = [{'description' 'Table for storing intracellular response related metadata.'} varargin]; obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/IntracellularStimuliTable.m b/+types/+core/IntracellularStimuliTable.m index b0cf39e9..ab1d83e1 100644 --- a/+types/+core/IntracellularStimuliTable.m +++ b/+types/+core/IntracellularStimuliTable.m @@ -1,5 +1,8 @@ classdef IntracellularStimuliTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% INTRACELLULARSTIMULITABLE Table for storing intracellular stimulus related metadata. +% INTRACELLULARSTIMULITABLE - Table for storing intracellular stimulus related metadata. +% +% Required Properties: +% id, stimulus % REQUIRED PROPERTIES @@ -13,7 +16,27 @@ methods function obj = IntracellularStimuliTable(varargin) - % INTRACELLULARSTIMULITABLE Constructor for IntracellularStimuliTable + % INTRACELLULARSTIMULITABLE - Constructor for IntracellularStimuliTable + % + % Syntax: + % intracellularStimuliTable = types.core.INTRACELLULARSTIMULITABLE() creates a IntracellularStimuliTable object with unset property values. + % + % intracellularStimuliTable = types.core.INTRACELLULARSTIMULITABLE(Name, Value) creates a IntracellularStimuliTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - stimulus (TimeSeriesReferenceVectorData) - Column storing the reference to the recorded stimulus for the recording (rows). + % + % - stimulus_template (TimeSeriesReferenceVectorData) - Column storing the reference to the stimulus template for the recording (rows). + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - intracellularStimuliTable (types.core.IntracellularStimuliTable) - A IntracellularStimuliTable object + varargin = [{'description' 'Table for storing intracellular stimulus related metadata.'} varargin]; obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/LFP.m b/+types/+core/LFP.m index 56eaf392..e0e3c19b 100644 --- a/+types/+core/LFP.m +++ b/+types/+core/LFP.m @@ -1,5 +1,8 @@ classdef LFP < types.core.NWBDataInterface & types.untyped.GroupClass -% LFP LFP data from one or more channels. The electrode map in each published ElectricalSeries will identify which channels are providing LFP data. Filter properties should be noted in the ElectricalSeries 'filtering' attribute. +% LFP - LFP data from one or more channels. The electrode map in each published ElectricalSeries will identify which channels are providing LFP data. Filter properties should be noted in the ElectricalSeries 'filtering' attribute. +% +% Required Properties: +% electricalseries % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = LFP(varargin) - % LFP Constructor for LFP + % LFP - Constructor for LFP + % + % Syntax: + % lFP = types.core.LFP() creates a LFP object with unset property values. + % + % lFP = types.core.LFP(Name, Value) creates a LFP object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - electricalseries (ElectricalSeries) - ElectricalSeries object(s) containing LFP data for one or more channels. + % + % Output Arguments: + % - lFP (types.core.LFP) - A LFP object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.electricalseries, ivarargin] = types.util.parseConstrained(obj,'electricalseries', 'types.core.ElectricalSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/LabMetaData.m b/+types/+core/LabMetaData.m index 80933fc3..6f9cb0ae 100644 --- a/+types/+core/LabMetaData.m +++ b/+types/+core/LabMetaData.m @@ -1,11 +1,21 @@ classdef LabMetaData < types.core.NWBContainer & types.untyped.GroupClass -% LABMETADATA Lab-specific meta-data. +% LABMETADATA - Lab-specific meta-data. +% +% Required Properties: +% None methods function obj = LabMetaData(varargin) - % LABMETADATA Constructor for LabMetaData + % LABMETADATA - Constructor for LabMetaData + % + % Syntax: + % labMetaData = types.core.LABMETADATA() creates a LabMetaData object with unset property values. + % + % Output Arguments: + % - labMetaData (types.core.LabMetaData) - A LabMetaData object + obj = obj@types.core.NWBContainer(varargin{:}); if strcmp(class(obj), 'types.core.LabMetaData') cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); diff --git a/+types/+core/MotionCorrection.m b/+types/+core/MotionCorrection.m index 75b428da..9a67b4b7 100644 --- a/+types/+core/MotionCorrection.m +++ b/+types/+core/MotionCorrection.m @@ -1,5 +1,8 @@ classdef MotionCorrection < types.core.NWBDataInterface & types.untyped.GroupClass -% MOTIONCORRECTION An image stack where all frames are shifted (registered) to a common coordinate system, to account for movement and drift between frames. Note: each frame at each point in time is assumed to be 2-D (has only x & y dimensions). +% MOTIONCORRECTION - An image stack where all frames are shifted (registered) to a common coordinate system, to account for movement and drift between frames. Note: each frame at each point in time is assumed to be 2-D (has only x & y dimensions). +% +% Required Properties: +% correctedimagestack % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = MotionCorrection(varargin) - % MOTIONCORRECTION Constructor for MotionCorrection + % MOTIONCORRECTION - Constructor for MotionCorrection + % + % Syntax: + % motionCorrection = types.core.MOTIONCORRECTION() creates a MotionCorrection object with unset property values. + % + % motionCorrection = types.core.MOTIONCORRECTION(Name, Value) creates a MotionCorrection object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - correctedimagestack (CorrectedImageStack) - Results from motion correction of an image stack. + % + % Output Arguments: + % - motionCorrection (types.core.MotionCorrection) - A MotionCorrection object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.correctedimagestack, ivarargin] = types.util.parseConstrained(obj,'correctedimagestack', 'types.core.CorrectedImageStack', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/NWBContainer.m b/+types/+core/NWBContainer.m index 7d53229c..02b6d2b1 100644 --- a/+types/+core/NWBContainer.m +++ b/+types/+core/NWBContainer.m @@ -1,11 +1,21 @@ classdef NWBContainer < types.hdmf_common.Container & types.untyped.GroupClass -% NWBCONTAINER An abstract data type for a generic container storing collections of data and metadata. Base type for all data and metadata containers. +% NWBCONTAINER - An abstract data type for a generic container storing collections of data and metadata. Base type for all data and metadata containers. +% +% Required Properties: +% None methods function obj = NWBContainer(varargin) - % NWBCONTAINER Constructor for NWBContainer + % NWBCONTAINER - Constructor for NWBContainer + % + % Syntax: + % nWBContainer = types.core.NWBCONTAINER() creates a NWBContainer object with unset property values. + % + % Output Arguments: + % - nWBContainer (types.core.NWBContainer) - A NWBContainer object + obj = obj@types.hdmf_common.Container(varargin{:}); if strcmp(class(obj), 'types.core.NWBContainer') cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); diff --git a/+types/+core/NWBData.m b/+types/+core/NWBData.m index 8a1ef897..8a01aa52 100644 --- a/+types/+core/NWBData.m +++ b/+types/+core/NWBData.m @@ -1,11 +1,26 @@ classdef NWBData < types.hdmf_common.Data & types.untyped.DatasetClass -% NWBDATA An abstract data type for a dataset. +% NWBDATA - An abstract data type for a dataset. +% +% Required Properties: +% data methods function obj = NWBData(varargin) - % NWBDATA Constructor for NWBData + % NWBDATA - Constructor for NWBData + % + % Syntax: + % nWBData = types.core.NWBDATA() creates a NWBData object with unset property values. + % + % nWBData = types.core.NWBDATA(Name, Value) creates a NWBData object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (any) - No description + % + % Output Arguments: + % - nWBData (types.core.NWBData) - A NWBData object + obj = obj@types.hdmf_common.Data(varargin{:}); diff --git a/+types/+core/NWBDataInterface.m b/+types/+core/NWBDataInterface.m index 182ffe34..1012dc86 100644 --- a/+types/+core/NWBDataInterface.m +++ b/+types/+core/NWBDataInterface.m @@ -1,11 +1,21 @@ classdef NWBDataInterface < types.core.NWBContainer & types.untyped.GroupClass -% NWBDATAINTERFACE An abstract data type for a generic container storing collections of data, as opposed to metadata. +% NWBDATAINTERFACE - An abstract data type for a generic container storing collections of data, as opposed to metadata. +% +% Required Properties: +% None methods function obj = NWBDataInterface(varargin) - % NWBDATAINTERFACE Constructor for NWBDataInterface + % NWBDATAINTERFACE - Constructor for NWBDataInterface + % + % Syntax: + % nWBDataInterface = types.core.NWBDATAINTERFACE() creates a NWBDataInterface object with unset property values. + % + % Output Arguments: + % - nWBDataInterface (types.core.NWBDataInterface) - A NWBDataInterface object + obj = obj@types.core.NWBContainer(varargin{:}); if strcmp(class(obj), 'types.core.NWBDataInterface') cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); diff --git a/+types/+core/NWBFile.m b/+types/+core/NWBFile.m index 2b05d58e..5896f6e8 100644 --- a/+types/+core/NWBFile.m +++ b/+types/+core/NWBFile.m @@ -1,5 +1,8 @@ classdef NWBFile < types.core.NWBContainer & types.untyped.GroupClass -% NWBFILE An NWB file storing cellular-based neurophysiology data from a single experimental session. +% NWBFILE - An NWB file storing cellular-based neurophysiology data from a single experimental session. +% +% Required Properties: +% file_create_date, identifier, session_description, session_start_time, timestamps_reference_time % READONLY PROPERTIES @@ -64,7 +67,115 @@ methods function obj = NWBFile(varargin) - % NWBFILE Constructor for NWBFile + % NWBFILE - Constructor for NWBFile + % + % Syntax: + % nWBFile = types.core.NWBFILE() creates a NWBFile object with unset property values. + % + % nWBFile = types.core.NWBFILE(Name, Value) creates a NWBFile object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - acquisition (DynamicTable|NWBDataInterface) - Tabular data that is relevant to acquisition + % + % - analysis (DynamicTable|NWBContainer) - Tabular data that is relevant to data stored in analysis + % + % - file_create_date (datetime) - A record of the date the file was created and of subsequent modifications. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted strings: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" with no timezone offset. Date accuracy is up to milliseconds. The file can be created after the experiment was run, so this may differ from the experiment start time. Each modification to the nwb file adds a new entry to the array. + % + % - general (LabMetaData) - Place-holder than can be extended so that lab-specific meta-data can be placed in /general. + % + % - general_data_collection (char) - Notes about data collection and analysis. + % + % - general_devices (Device) - Data acquisition devices. + % + % - general_experiment_description (char) - General description of the experiment. + % + % - general_experimenter (char) - Name of person(s) who performed the experiment. Can also specify roles of different people involved. + % + % - general_extracellular_ephys (ElectrodeGroup) - Physical group of electrodes. + % + % - general_extracellular_ephys_electrodes (DynamicTable) - A table of all electrodes (i.e. channels) used for recording. + % + % - general_institution (char) - Institution(s) where experiment was performed. + % + % - general_intracellular_ephys (IntracellularElectrode) - An intracellular electrode. + % + % - general_intracellular_ephys_experimental_conditions (ExperimentalConditionsTable) - A table for grouping different intracellular recording repetitions together that belong to the same experimental experimental_conditions. + % + % - general_intracellular_ephys_filtering (char) - [DEPRECATED] Use IntracellularElectrode.filtering instead. Description of filtering used. Includes filtering type and parameters, frequency fall-off, etc. If this changes between TimeSeries, filter description should be stored as a text attribute for each TimeSeries. + % + % - general_intracellular_ephys_intracellular_recordings (IntracellularRecordingsTable) - A table to group together a stimulus and response from a single electrode and a single simultaneous recording. Each row in the table represents a single recording consisting typically of a stimulus and a corresponding response. In some cases, however, only a stimulus or a response are recorded as as part of an experiment. In this case both, the stimulus and response will point to the same TimeSeries while the idx_start and count of the invalid column will be set to -1, thus, indicating that no values have been recorded for the stimulus or response, respectively. Note, a recording MUST contain at least a stimulus or a response. Typically the stimulus and response are PatchClampSeries. However, the use of AD/DA channels that are not associated to an electrode is also common in intracellular electrophysiology, in which case other TimeSeries may be used. + % + % - general_intracellular_ephys_repetitions (RepetitionsTable) - A table for grouping different sequential intracellular recordings together. With each SequentialRecording typically representing a particular type of stimulus, the RepetitionsTable table is typically used to group sets of stimuli applied in sequence. + % + % - general_intracellular_ephys_sequential_recordings (SequentialRecordingsTable) - A table for grouping different sequential recordings from the SimultaneousRecordingsTable table together. This is typically used to group together sequential recordings where the a sequence of stimuli of the same type with varying parameters have been presented in a sequence. + % + % - general_intracellular_ephys_simultaneous_recordings (SimultaneousRecordingsTable) - A table for grouping different intracellular recordings from the IntracellularRecordingsTable table together that were recorded simultaneously from different electrodes + % + % - general_intracellular_ephys_sweep_table (SweepTable) - [DEPRECATED] Table used to group different PatchClampSeries. SweepTable is being replaced by IntracellularRecordingsTable and SimultaneousRecordingsTable tables. Additional SequentialRecordingsTable, RepetitionsTable and ExperimentalConditions tables provide enhanced support for experiment metadata. + % + % - general_keywords (char) - Terms to search over. + % + % - general_lab (char) - Laboratory where experiment was performed. + % + % - general_notes (char) - Notes about the experiment. + % + % - general_optogenetics (OptogeneticStimulusSite) - An optogenetic stimulation site. + % + % - general_optophysiology (ImagingPlane) - An imaging plane. + % + % - general_pharmacology (char) - Description of drugs used, including how and when they were administered. Anesthesia(s), painkiller(s), etc., plus dosage, concentration, etc. + % + % - general_protocol (char) - Experimental protocol, if applicable. e.g., include IACUC protocol number. + % + % - general_related_publications (char) - Publication information. PMID, DOI, URL, etc. + % + % - general_session_id (char) - Lab-specific ID for the session. + % + % - general_slices (char) - Description of slices, including information about preparation thickness, orientation, temperature, and bath solution. + % + % - general_source_script (char) - Script file or link to public source code used to create this NWB file. + % + % - general_source_script_file_name (char) - Name of script file. + % + % - general_stimulus (char) - Notes about stimuli, such as how and where they were presented. + % + % - general_subject (Subject) - Information about the animal or person from which the data was measured. + % + % - general_surgery (char) - Narrative description about surgery/surgeries, including date(s) and who performed surgery. + % + % - general_virus (char) - Information about virus(es) used in experiments, including virus ID, source, date made, injection location, volume, etc. + % + % - general_was_generated_by (char) - Name and version of software package(s) used to generate data contained in this NWB File. For each software package or library, include the name of the software as the first value and the version as the second value. + % + % - identifier (char) - A unique text identifier for the file. For example, concatenated lab name, file creation date/time and experimentalist, or a hash of these and/or other values. The goal is that the string should be unique to all other files. + % + % - intervals (TimeIntervals) - Optional additional table(s) for describing other experimental time intervals. + % + % - intervals_epochs (TimeIntervals) - Divisions in time marking experimental stages or sub-divisions of a single recording session. + % + % - intervals_invalid_times (TimeIntervals) - Time intervals that should be removed from analysis. + % + % - intervals_trials (TimeIntervals) - Repeated experimental events that have a logical grouping. + % + % - processing (ProcessingModule) - Intermediate analysis of acquired data. + % + % - scratch (DynamicTable|NWBContainer|ScratchData) - No description + % + % - session_description (char) - A description of the experimental session and data in the file. + % + % - session_start_time (datetime) - Date and time of the experiment/session start. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" with no timezone offset. Date accuracy is up to milliseconds. + % + % - stimulus_presentation (DynamicTable|NWBDataInterface|TimeSeries) - DynamicTable objects containing data of presented stimuli. + % + % - stimulus_templates (Images|TimeSeries) - Images objects containing images of presented stimuli. + % + % - timestamps_reference_time (datetime) - Date and time corresponding to time zero of all timestamps. The date is stored in UTC with local timezone offset as ISO 8601 extended formatted string: 2018-09-28T14:43:54.123+02:00. Dates stored in UTC end in "Z" with no timezone offset. Date accuracy is up to milliseconds. All times stored in the file use this time as reference (i.e., time zero). + % + % - units (Units) - Data about sorted spike units. + % + % Output Arguments: + % - nWBFile (types.core.NWBFile) - A NWBFile object + varargin = [{'nwb_version' '2.8.0'} varargin]; obj = obj@types.core.NWBContainer(varargin{:}); diff --git a/+types/+core/OnePhotonSeries.m b/+types/+core/OnePhotonSeries.m index ed410612..b6c3e09e 100644 --- a/+types/+core/OnePhotonSeries.m +++ b/+types/+core/OnePhotonSeries.m @@ -1,5 +1,8 @@ classdef OnePhotonSeries < types.core.ImageSeries & types.untyped.GroupClass -% ONEPHOTONSERIES Image stack recorded over time from 1-photon microscope. +% ONEPHOTONSERIES - Image stack recorded over time from 1-photon microscope. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -15,7 +18,67 @@ methods function obj = OnePhotonSeries(varargin) - % ONEPHOTONSERIES Constructor for OnePhotonSeries + % ONEPHOTONSERIES - Constructor for OnePhotonSeries + % + % Syntax: + % onePhotonSeries = types.core.ONEPHOTONSERIES() creates a OnePhotonSeries object with unset property values. + % + % onePhotonSeries = types.core.ONEPHOTONSERIES(Name, Value) creates a OnePhotonSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - binning (uint8) - Amount of pixels combined into 'bins'; could be 1, 2, 4, 8, etc. + % + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Binary data representing images across frames. If data are stored in an external file, this should be an empty 3D array. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - device (Device) - Link to the Device object that was used to capture these images. + % + % - dimension (int32) - Number of pixels on x, y, (and z) axes. + % + % - exposure_time (single) - Exposure time of the sample; often the inverse of the frequency. + % + % - external_file (char) - Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored in another NWB file and that file is linked to this file. + % + % - external_file_starting_frame (int32) - Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an index to indicate which frames each file contains, to facilitate random access. The 'starting_frame' attribute, hence, contains a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0]. + % + % - format (char) - Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not present, then the default format='raw' case is assumed. + % + % - imaging_plane (ImagingPlane) - Link to ImagingPlane object from which this TimeSeries data was generated. + % + % - intensity (single) - Intensity of the excitation in mW/mm^2, if known. + % + % - pmt_gain (single) - Photomultiplier gain. + % + % - power (single) - Power of the excitation in mW, if known. + % + % - scan_line_rate (single) - Lines imaged per second. This is also stored in /general/optophysiology but is kept here as it is useful information for analysis, and so good to be stored w/ the actual data. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - onePhotonSeries (types.core.OnePhotonSeries) - A OnePhotonSeries object + obj = obj@types.core.ImageSeries(varargin{:}); diff --git a/+types/+core/OpticalChannel.m b/+types/+core/OpticalChannel.m index 038b43a0..92496701 100644 --- a/+types/+core/OpticalChannel.m +++ b/+types/+core/OpticalChannel.m @@ -1,5 +1,8 @@ classdef OpticalChannel < types.core.NWBContainer & types.untyped.GroupClass -% OPTICALCHANNEL An optical channel used to record from an imaging plane. +% OPTICALCHANNEL - An optical channel used to record from an imaging plane. +% +% Required Properties: +% description, emission_lambda % REQUIRED PROPERTIES @@ -10,7 +13,21 @@ methods function obj = OpticalChannel(varargin) - % OPTICALCHANNEL Constructor for OpticalChannel + % OPTICALCHANNEL - Constructor for OpticalChannel + % + % Syntax: + % opticalChannel = types.core.OPTICALCHANNEL() creates a OpticalChannel object with unset property values. + % + % opticalChannel = types.core.OPTICALCHANNEL(Name, Value) creates a OpticalChannel object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description or other notes about the channel. + % + % - emission_lambda (single) - Emission wavelength for channel, in nm. + % + % Output Arguments: + % - opticalChannel (types.core.OpticalChannel) - A OpticalChannel object + obj = obj@types.core.NWBContainer(varargin{:}); diff --git a/+types/+core/OpticalSeries.m b/+types/+core/OpticalSeries.m index 62a1fe82..fc441c1b 100644 --- a/+types/+core/OpticalSeries.m +++ b/+types/+core/OpticalSeries.m @@ -1,5 +1,8 @@ classdef OpticalSeries < types.core.ImageSeries & types.untyped.GroupClass -% OPTICALSERIES Image data that is presented or recorded. A stimulus template movie will be stored only as an image. When the image is presented as stimulus, additional data is required, such as field of view (e.g., how much of the visual field the image covers, or how what is the area of the target being imaged). If the OpticalSeries represents acquired imaging data, orientation is also important. +% OPTICALSERIES - Image data that is presented or recorded. A stimulus template movie will be stored only as an image. When the image is presented as stimulus, additional data is required, such as field of view (e.g., how much of the visual field the image covers, or how what is the area of the target being imaged). If the OpticalSeries represents acquired imaging data, orientation is also important. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -11,7 +14,59 @@ methods function obj = OpticalSeries(varargin) - % OPTICALSERIES Constructor for OpticalSeries + % OPTICALSERIES - Constructor for OpticalSeries + % + % Syntax: + % opticalSeries = types.core.OPTICALSERIES() creates a OpticalSeries object with unset property values. + % + % opticalSeries = types.core.OPTICALSERIES(Name, Value) creates a OpticalSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Images presented to subject, either grayscale or RGB + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - device (Device) - Link to the Device object that was used to capture these images. + % + % - dimension (int32) - Number of pixels on x, y, (and z) axes. + % + % - distance (single) - Distance from camera/monitor to target/eye. + % + % - external_file (char) - Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored in another NWB file and that file is linked to this file. + % + % - external_file_starting_frame (int32) - Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an index to indicate which frames each file contains, to facilitate random access. The 'starting_frame' attribute, hence, contains a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0]. + % + % - field_of_view (single) - Width, height and depth of image, or imaged area, in meters. + % + % - format (char) - Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not present, then the default format='raw' case is assumed. + % + % - orientation (char) - Description of image relative to some reference frame (e.g., which way is up). Must also specify frame of reference. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - opticalSeries (types.core.OpticalSeries) - A OpticalSeries object + obj = obj@types.core.ImageSeries(varargin{:}); diff --git a/+types/+core/OptogeneticSeries.m b/+types/+core/OptogeneticSeries.m index 92176f50..e00fca82 100644 --- a/+types/+core/OptogeneticSeries.m +++ b/+types/+core/OptogeneticSeries.m @@ -1,5 +1,8 @@ classdef OptogeneticSeries < types.core.TimeSeries & types.untyped.GroupClass -% OPTOGENETICSERIES An optogenetic stimulus. +% OPTOGENETICSERIES - An optogenetic stimulus. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -9,7 +12,43 @@ methods function obj = OptogeneticSeries(varargin) - % OPTOGENETICSERIES Constructor for OptogeneticSeries + % OPTOGENETICSERIES - Constructor for OptogeneticSeries + % + % Syntax: + % optogeneticSeries = types.core.OPTOGENETICSERIES() creates a OptogeneticSeries object with unset property values. + % + % optogeneticSeries = types.core.OPTOGENETICSERIES(Name, Value) creates a OptogeneticSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Applied power for optogenetic stimulus, in watts. Shape can be 1D or 2D. 2D data is meant to be used in an extension of OptogeneticSeries that defines what the second dimension represents. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - site (OptogeneticStimulusSite) - Link to OptogeneticStimulusSite object that describes the site to which this stimulus was applied. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - optogeneticSeries (types.core.OptogeneticSeries) - A OptogeneticSeries object + varargin = [{'data_unit' 'watts'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/OptogeneticStimulusSite.m b/+types/+core/OptogeneticStimulusSite.m index f463c067..fb648df1 100644 --- a/+types/+core/OptogeneticStimulusSite.m +++ b/+types/+core/OptogeneticStimulusSite.m @@ -1,5 +1,8 @@ classdef OptogeneticStimulusSite < types.core.NWBContainer & types.untyped.GroupClass -% OPTOGENETICSTIMULUSSITE A site of optogenetic stimulation. +% OPTOGENETICSTIMULUSSITE - A site of optogenetic stimulation. +% +% Required Properties: +% description, excitation_lambda, location % REQUIRED PROPERTIES @@ -15,7 +18,25 @@ methods function obj = OptogeneticStimulusSite(varargin) - % OPTOGENETICSTIMULUSSITE Constructor for OptogeneticStimulusSite + % OPTOGENETICSTIMULUSSITE - Constructor for OptogeneticStimulusSite + % + % Syntax: + % optogeneticStimulusSite = types.core.OPTOGENETICSTIMULUSSITE() creates a OptogeneticStimulusSite object with unset property values. + % + % optogeneticStimulusSite = types.core.OPTOGENETICSTIMULUSSITE(Name, Value) creates a OptogeneticStimulusSite object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of stimulation site. + % + % - device (Device) - Device that generated the stimulus. + % + % - excitation_lambda (single) - Excitation wavelength, in nm. + % + % - location (char) - Location of the stimulation site. Specify the area, layer, comments on estimation of area/layer, stereotaxic coordinates if in vivo, etc. Use standard atlas names for anatomical regions when possible. + % + % Output Arguments: + % - optogeneticStimulusSite (types.core.OptogeneticStimulusSite) - A OptogeneticStimulusSite object + obj = obj@types.core.NWBContainer(varargin{:}); diff --git a/+types/+core/PatchClampSeries.m b/+types/+core/PatchClampSeries.m index 28cc4446..98676e8c 100644 --- a/+types/+core/PatchClampSeries.m +++ b/+types/+core/PatchClampSeries.m @@ -1,5 +1,8 @@ classdef PatchClampSeries < types.core.TimeSeries & types.untyped.GroupClass -% PATCHCLAMPSERIES An abstract base class for patch-clamp data - stimulus or response, current or voltage. +% PATCHCLAMPSERIES - An abstract base class for patch-clamp data - stimulus or response, current or voltage. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -12,7 +15,51 @@ methods function obj = PatchClampSeries(varargin) - % PATCHCLAMPSERIES Constructor for PatchClampSeries + % PATCHCLAMPSERIES - Constructor for PatchClampSeries + % + % Syntax: + % patchClampSeries = types.core.PATCHCLAMPSERIES() creates a PatchClampSeries object with unset property values. + % + % patchClampSeries = types.core.PATCHCLAMPSERIES(Name, Value) creates a PatchClampSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Recorded voltage or current. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - electrode (IntracellularElectrode) - Link to IntracellularElectrode object that describes the electrode that was used to apply or record this data. + % + % - gain (single) - Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - stimulus_description (char) - Protocol/stimulus name for this patch-clamp dataset. + % + % - sweep_number (uint32) - Sweep number, allows to group different PatchClampSeries together. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - patchClampSeries (types.core.PatchClampSeries) - A PatchClampSeries object + obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/PlaneSegmentation.m b/+types/+core/PlaneSegmentation.m index 5fd95183..bc3289e4 100644 --- a/+types/+core/PlaneSegmentation.m +++ b/+types/+core/PlaneSegmentation.m @@ -1,5 +1,8 @@ classdef PlaneSegmentation < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% PLANESEGMENTATION Results from image segmentation of a specific imaging plane. +% PLANESEGMENTATION - Results from image segmentation of a specific imaging plane. +% +% Required Properties: +% id % OPTIONAL PROPERTIES @@ -15,7 +18,39 @@ methods function obj = PlaneSegmentation(varargin) - % PLANESEGMENTATION Constructor for PlaneSegmentation + % PLANESEGMENTATION - Constructor for PlaneSegmentation + % + % Syntax: + % planeSegmentation = types.core.PLANESEGMENTATION() creates a PlaneSegmentation object with unset property values. + % + % planeSegmentation = types.core.PLANESEGMENTATION(Name, Value) creates a PlaneSegmentation object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - image_mask (VectorData) - ROI masks for each ROI. Each image mask is the size of the original imaging plane (or volume) and members of the ROI are finite non-zero. + % + % - imaging_plane (ImagingPlane) - Link to ImagingPlane object from which this data was generated. + % + % - pixel_mask (VectorData) - Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation + % + % - pixel_mask_index (VectorIndex) - Index into pixel_mask. + % + % - reference_images (ImageSeries) - One or more image stacks that the masks apply to (can be one-element stack). + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % - voxel_mask (VectorData) - Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation + % + % - voxel_mask_index (VectorIndex) - Index into voxel_mask. + % + % Output Arguments: + % - planeSegmentation (types.core.PlaneSegmentation) - A PlaneSegmentation object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/Position.m b/+types/+core/Position.m index 58b4ebaf..389ca1ef 100644 --- a/+types/+core/Position.m +++ b/+types/+core/Position.m @@ -1,5 +1,8 @@ classdef Position < types.core.NWBDataInterface & types.untyped.GroupClass -% POSITION Position data, whether along the x, x/y or x/y/z axis. +% POSITION - Position data, whether along the x, x/y or x/y/z axis. +% +% Required Properties: +% spatialseries % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = Position(varargin) - % POSITION Constructor for Position + % POSITION - Constructor for Position + % + % Syntax: + % position = types.core.POSITION() creates a Position object with unset property values. + % + % position = types.core.POSITION(Name, Value) creates a Position object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - spatialseries (SpatialSeries) - SpatialSeries object containing position data. + % + % Output Arguments: + % - position (types.core.Position) - A Position object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.spatialseries, ivarargin] = types.util.parseConstrained(obj,'spatialseries', 'types.core.SpatialSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/ProcessingModule.m b/+types/+core/ProcessingModule.m index c925acde..accc72fe 100644 --- a/+types/+core/ProcessingModule.m +++ b/+types/+core/ProcessingModule.m @@ -1,5 +1,8 @@ classdef ProcessingModule < types.core.NWBContainer & types.untyped.GroupClass -% PROCESSINGMODULE A collection of processed data. +% PROCESSINGMODULE - A collection of processed data. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -11,7 +14,23 @@ methods function obj = ProcessingModule(varargin) - % PROCESSINGMODULE Constructor for ProcessingModule + % PROCESSINGMODULE - Constructor for ProcessingModule + % + % Syntax: + % processingModule = types.core.PROCESSINGMODULE() creates a ProcessingModule object with unset property values. + % + % processingModule = types.core.PROCESSINGMODULE(Name, Value) creates a ProcessingModule object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - description (char) - Description of this collection of processed data. + % + % - dynamictable (DynamicTable) - Tables stored in this collection. + % + % - nwbdatainterface (NWBDataInterface) - Data objects stored in this collection. + % + % Output Arguments: + % - processingModule (types.core.ProcessingModule) - A ProcessingModule object + obj = obj@types.core.NWBContainer(varargin{:}); [obj.dynamictable, ivarargin] = types.util.parseConstrained(obj,'dynamictable', 'types.hdmf_common.DynamicTable', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/PupilTracking.m b/+types/+core/PupilTracking.m index f7018341..0a4f149a 100644 --- a/+types/+core/PupilTracking.m +++ b/+types/+core/PupilTracking.m @@ -1,5 +1,8 @@ classdef PupilTracking < types.core.NWBDataInterface & types.untyped.GroupClass -% PUPILTRACKING Eye-tracking data, representing pupil size. +% PUPILTRACKING - Eye-tracking data, representing pupil size. +% +% Required Properties: +% timeseries % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = PupilTracking(varargin) - % PUPILTRACKING Constructor for PupilTracking + % PUPILTRACKING - Constructor for PupilTracking + % + % Syntax: + % pupilTracking = types.core.PUPILTRACKING() creates a PupilTracking object with unset property values. + % + % pupilTracking = types.core.PUPILTRACKING(Name, Value) creates a PupilTracking object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - timeseries (TimeSeries) - TimeSeries object containing time series data on pupil size. + % + % Output Arguments: + % - pupilTracking (types.core.PupilTracking) - A PupilTracking object + obj = obj@types.core.NWBDataInterface(varargin{:}); [obj.timeseries, ivarargin] = types.util.parseConstrained(obj,'timeseries', 'types.core.TimeSeries', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+core/RGBAImage.m b/+types/+core/RGBAImage.m index 3ae47ea7..4d1953ba 100644 --- a/+types/+core/RGBAImage.m +++ b/+types/+core/RGBAImage.m @@ -1,11 +1,30 @@ classdef RGBAImage < types.core.Image & types.untyped.DatasetClass -% RGBAIMAGE A color image with transparency. +% RGBAIMAGE - A color image with transparency. +% +% Required Properties: +% data methods function obj = RGBAImage(varargin) - % RGBAIMAGE Constructor for RGBAImage + % RGBAIMAGE - Constructor for RGBAImage + % + % Syntax: + % rGBAImage = types.core.RGBAIMAGE() creates a RGBAImage object with unset property values. + % + % rGBAImage = types.core.RGBAIMAGE(Name, Value) creates a RGBAImage object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (numeric) - No description + % + % - description (char) - Description of the image. + % + % - resolution (single) - Pixel resolution of the image, in pixels per centimeter. + % + % Output Arguments: + % - rGBAImage (types.core.RGBAImage) - A RGBAImage object + obj = obj@types.core.Image(varargin{:}); diff --git a/+types/+core/RGBImage.m b/+types/+core/RGBImage.m index 5aa72807..d5c5da6e 100644 --- a/+types/+core/RGBImage.m +++ b/+types/+core/RGBImage.m @@ -1,11 +1,30 @@ classdef RGBImage < types.core.Image & types.untyped.DatasetClass -% RGBIMAGE A color image. +% RGBIMAGE - A color image. +% +% Required Properties: +% data methods function obj = RGBImage(varargin) - % RGBIMAGE Constructor for RGBImage + % RGBIMAGE - Constructor for RGBImage + % + % Syntax: + % rGBImage = types.core.RGBIMAGE() creates a RGBImage object with unset property values. + % + % rGBImage = types.core.RGBIMAGE(Name, Value) creates a RGBImage object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (numeric) - No description + % + % - description (char) - Description of the image. + % + % - resolution (single) - Pixel resolution of the image, in pixels per centimeter. + % + % Output Arguments: + % - rGBImage (types.core.RGBImage) - A RGBImage object + obj = obj@types.core.Image(varargin{:}); diff --git a/+types/+core/RepetitionsTable.m b/+types/+core/RepetitionsTable.m index a7d3b163..814ec47f 100644 --- a/+types/+core/RepetitionsTable.m +++ b/+types/+core/RepetitionsTable.m @@ -1,5 +1,8 @@ classdef RepetitionsTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% REPETITIONSTABLE A table for grouping different sequential intracellular recordings together. With each SequentialRecording typically representing a particular type of stimulus, the RepetitionsTable table is typically used to group sets of stimuli applied in sequence. +% REPETITIONSTABLE - A table for grouping different sequential intracellular recordings together. With each SequentialRecording typically representing a particular type of stimulus, the RepetitionsTable table is typically used to group sets of stimuli applied in sequence. +% +% Required Properties: +% id, sequential_recordings, sequential_recordings_index % REQUIRED PROPERTIES @@ -10,7 +13,29 @@ methods function obj = RepetitionsTable(varargin) - % REPETITIONSTABLE Constructor for RepetitionsTable + % REPETITIONSTABLE - Constructor for RepetitionsTable + % + % Syntax: + % repetitionsTable = types.core.REPETITIONSTABLE() creates a RepetitionsTable object with unset property values. + % + % repetitionsTable = types.core.REPETITIONSTABLE(Name, Value) creates a RepetitionsTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - sequential_recordings (DynamicTableRegion) - A reference to one or more rows in the SequentialRecordingsTable table. + % + % - sequential_recordings_index (VectorIndex) - Index dataset for the sequential_recordings column. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - repetitionsTable (types.core.RepetitionsTable) - A RepetitionsTable object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/RoiResponseSeries.m b/+types/+core/RoiResponseSeries.m index 452d27b1..b76999f9 100644 --- a/+types/+core/RoiResponseSeries.m +++ b/+types/+core/RoiResponseSeries.m @@ -1,5 +1,8 @@ classdef RoiResponseSeries < types.core.TimeSeries & types.untyped.GroupClass -% ROIRESPONSESERIES ROI responses over an imaging plane. The first dimension represents time. The second dimension, if present, represents ROIs. +% ROIRESPONSESERIES - ROI responses over an imaging plane. The first dimension represents time. The second dimension, if present, represents ROIs. +% +% Required Properties: +% data, rois % REQUIRED PROPERTIES @@ -9,7 +12,45 @@ methods function obj = RoiResponseSeries(varargin) - % ROIRESPONSESERIES Constructor for RoiResponseSeries + % ROIRESPONSESERIES - Constructor for RoiResponseSeries + % + % Syntax: + % roiResponseSeries = types.core.ROIRESPONSESERIES() creates a RoiResponseSeries object with unset property values. + % + % roiResponseSeries = types.core.ROIRESPONSESERIES(Name, Value) creates a RoiResponseSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Signals from ROIs. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - rois (DynamicTableRegion) - DynamicTableRegion referencing into an ROITable containing information on the ROIs stored in this timeseries. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - roiResponseSeries (types.core.RoiResponseSeries) - A RoiResponseSeries object + obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/ScratchData.m b/+types/+core/ScratchData.m index 0d7923b3..c2bd4715 100644 --- a/+types/+core/ScratchData.m +++ b/+types/+core/ScratchData.m @@ -1,5 +1,8 @@ classdef ScratchData < types.core.NWBData & types.untyped.DatasetClass -% SCRATCHDATA Any one-off datasets +% SCRATCHDATA - Any one-off datasets +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -9,7 +12,21 @@ methods function obj = ScratchData(varargin) - % SCRATCHDATA Constructor for ScratchData + % SCRATCHDATA - Constructor for ScratchData + % + % Syntax: + % scratchData = types.core.SCRATCHDATA() creates a ScratchData object with unset property values. + % + % scratchData = types.core.SCRATCHDATA(Name, Value) creates a ScratchData object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (any) - No description + % + % - notes (char) - Any notes the user has about the dataset being stored + % + % Output Arguments: + % - scratchData (types.core.ScratchData) - A ScratchData object + obj = obj@types.core.NWBData(varargin{:}); diff --git a/+types/+core/SequentialRecordingsTable.m b/+types/+core/SequentialRecordingsTable.m index edcb85ab..96e3c970 100644 --- a/+types/+core/SequentialRecordingsTable.m +++ b/+types/+core/SequentialRecordingsTable.m @@ -1,5 +1,8 @@ classdef SequentialRecordingsTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% SEQUENTIALRECORDINGSTABLE A table for grouping different sequential recordings from the SimultaneousRecordingsTable table together. This is typically used to group together sequential recordings where a sequence of stimuli of the same type with varying parameters have been presented in a sequence. +% SEQUENTIALRECORDINGSTABLE - A table for grouping different sequential recordings from the SimultaneousRecordingsTable table together. This is typically used to group together sequential recordings where a sequence of stimuli of the same type with varying parameters have been presented in a sequence. +% +% Required Properties: +% id, simultaneous_recordings, simultaneous_recordings_index, stimulus_type % REQUIRED PROPERTIES @@ -11,7 +14,31 @@ methods function obj = SequentialRecordingsTable(varargin) - % SEQUENTIALRECORDINGSTABLE Constructor for SequentialRecordingsTable + % SEQUENTIALRECORDINGSTABLE - Constructor for SequentialRecordingsTable + % + % Syntax: + % sequentialRecordingsTable = types.core.SEQUENTIALRECORDINGSTABLE() creates a SequentialRecordingsTable object with unset property values. + % + % sequentialRecordingsTable = types.core.SEQUENTIALRECORDINGSTABLE(Name, Value) creates a SequentialRecordingsTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - simultaneous_recordings (DynamicTableRegion) - A reference to one or more rows in the SimultaneousRecordingsTable table. + % + % - simultaneous_recordings_index (VectorIndex) - Index dataset for the simultaneous_recordings column. + % + % - stimulus_type (VectorData) - The type of stimulus used for the sequential recording. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - sequentialRecordingsTable (types.core.SequentialRecordingsTable) - A SequentialRecordingsTable object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/SimultaneousRecordingsTable.m b/+types/+core/SimultaneousRecordingsTable.m index c4c1fddc..9b1e325d 100644 --- a/+types/+core/SimultaneousRecordingsTable.m +++ b/+types/+core/SimultaneousRecordingsTable.m @@ -1,5 +1,8 @@ classdef SimultaneousRecordingsTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% SIMULTANEOUSRECORDINGSTABLE A table for grouping different intracellular recordings from the IntracellularRecordingsTable table together that were recorded simultaneously from different electrodes. +% SIMULTANEOUSRECORDINGSTABLE - A table for grouping different intracellular recordings from the IntracellularRecordingsTable table together that were recorded simultaneously from different electrodes. +% +% Required Properties: +% id, recordings, recordings_index % REQUIRED PROPERTIES @@ -10,7 +13,29 @@ methods function obj = SimultaneousRecordingsTable(varargin) - % SIMULTANEOUSRECORDINGSTABLE Constructor for SimultaneousRecordingsTable + % SIMULTANEOUSRECORDINGSTABLE - Constructor for SimultaneousRecordingsTable + % + % Syntax: + % simultaneousRecordingsTable = types.core.SIMULTANEOUSRECORDINGSTABLE() creates a SimultaneousRecordingsTable object with unset property values. + % + % simultaneousRecordingsTable = types.core.SIMULTANEOUSRECORDINGSTABLE(Name, Value) creates a SimultaneousRecordingsTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - recordings (DynamicTableRegion) - A reference to one or more rows in the IntracellularRecordingsTable table. + % + % - recordings_index (VectorIndex) - Index dataset for the recordings column. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - simultaneousRecordingsTable (types.core.SimultaneousRecordingsTable) - A SimultaneousRecordingsTable object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/SpatialSeries.m b/+types/+core/SpatialSeries.m index ce027498..25dd6b5b 100644 --- a/+types/+core/SpatialSeries.m +++ b/+types/+core/SpatialSeries.m @@ -1,5 +1,8 @@ classdef SpatialSeries < types.core.TimeSeries & types.untyped.GroupClass -% SPATIALSERIES Direction, e.g., of gaze or travel, or position. The TimeSeries::data field is a 2D array storing position or direction relative to some reference frame. Array structure: [num measurements] [num dimensions]. Each SpatialSeries has a text dataset reference_frame that indicates the zero-position, or the zero-axes for direction. For example, if representing gaze direction, 'straight-ahead' might be a specific pixel on the monitor, or some other point in space. For position data, the 0,0 point might be the top-left corner of an enclosure, as viewed from the tracking camera. The unit of data will indicate how to interpret SpatialSeries values. +% SPATIALSERIES - Direction, e.g., of gaze or travel, or position. The TimeSeries::data field is a 2D array storing position or direction relative to some reference frame. Array structure: [num measurements] [num dimensions]. Each SpatialSeries has a text dataset reference_frame that indicates the zero-position, or the zero-axes for direction. For example, if representing gaze direction, 'straight-ahead' might be a specific pixel on the monitor, or some other point in space. For position data, the 0,0 point might be the top-left corner of an enclosure, as viewed from the tracking camera. The unit of data will indicate how to interpret SpatialSeries values. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -9,7 +12,45 @@ methods function obj = SpatialSeries(varargin) - % SPATIALSERIES Constructor for SpatialSeries + % SPATIALSERIES - Constructor for SpatialSeries + % + % Syntax: + % spatialSeries = types.core.SPATIALSERIES() creates a SpatialSeries object with unset property values. + % + % spatialSeries = types.core.SPATIALSERIES(Name, Value) creates a SpatialSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - 1-D or 2-D array storing position or direction relative to some reference frame. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. The default value is 'meters'. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - reference_frame (char) - Description defining what exactly 'straight-ahead' means. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - spatialSeries (types.core.SpatialSeries) - A SpatialSeries object + varargin = [{'data_unit' 'meters'} varargin]; obj = obj@types.core.TimeSeries(varargin{:}); diff --git a/+types/+core/SpikeEventSeries.m b/+types/+core/SpikeEventSeries.m index 95b53c18..44e8a075 100644 --- a/+types/+core/SpikeEventSeries.m +++ b/+types/+core/SpikeEventSeries.m @@ -1,11 +1,54 @@ classdef SpikeEventSeries < types.core.ElectricalSeries & types.untyped.GroupClass -% SPIKEEVENTSERIES Stores snapshots/snippets of recorded spike events (i.e., threshold crossings). This may also be raw data, as reported by ephys hardware. If so, the TimeSeries::description field should describe how events were detected. All events span the same recording channels and store snapshots of equal duration. TimeSeries::data array structure: [num events] [num channels] [num samples] (or [num events] [num samples] for single electrode). +% SPIKEEVENTSERIES - Stores snapshots/snippets of recorded spike events (i.e., threshold crossings). This may also be raw data, as reported by ephys hardware. If so, the TimeSeries::description field should describe how events were detected. All events span the same recording channels and store snapshots of equal duration. TimeSeries::data array structure: [num events] [num channels] [num samples] (or [num events] [num samples] for single electrode). +% +% Required Properties: +% data, electrodes, timestamps methods function obj = SpikeEventSeries(varargin) - % SPIKEEVENTSERIES Constructor for SpikeEventSeries + % SPIKEEVENTSERIES - Constructor for SpikeEventSeries + % + % Syntax: + % spikeEventSeries = types.core.SPIKEEVENTSERIES() creates a SpikeEventSeries object with unset property values. + % + % spikeEventSeries = types.core.SPIKEEVENTSERIES(Name, Value) creates a SpikeEventSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - channel_conversion (single) - Channel-specific conversion factor. Multiply the data in the 'data' dataset by these values along the channel axis (as indicated by axis attribute) AND by the global conversion factor in the 'conversion' attribute of 'data' to get the data values in Volts, i.e, data in Volts = data * data.conversion * channel_conversion. This approach allows for both global and per-channel data conversion factors needed to support the storage of electrical recordings as native values generated by data acquisition systems. If this dataset is not present, then there is no channel-specific conversion factor, i.e. it is 1 for all channels. + % + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Spike waveforms. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - electrodes (DynamicTableRegion) - DynamicTableRegion pointer to the electrodes that this time series was generated from. + % + % - filtering (char) - Filtering applied to all channels of the data. For example, if this ElectricalSeries represents high-pass-filtered data (also known as AP Band), then this value could be "High-pass 4-pole Bessel filter at 500 Hz". If this ElectricalSeries represents low-pass-filtered LFP data and the type of filter is unknown, then this value could be "Low-pass filter at 300 Hz". If a non-standard filter type is used, provide as much detail about the filter properties as possible. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. Timestamps are required for the events. Unlike for TimeSeries, timestamps are required for SpikeEventSeries and are thus re-specified here. + % + % Output Arguments: + % - spikeEventSeries (types.core.SpikeEventSeries) - A SpikeEventSeries object + varargin = [{'data_unit' 'volts' 'timestamps_interval' types.util.correctType(1, 'int32') 'timestamps_unit' 'seconds'} varargin]; obj = obj@types.core.ElectricalSeries(varargin{:}); diff --git a/+types/+core/Subject.m b/+types/+core/Subject.m index bfd4e1ae..4d6927c9 100644 --- a/+types/+core/Subject.m +++ b/+types/+core/Subject.m @@ -1,5 +1,8 @@ classdef Subject < types.core.NWBContainer & types.untyped.GroupClass -% SUBJECT Information about the animal or person from which the data was measured. +% SUBJECT - Information about the animal or person from which the data was measured. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -18,7 +21,37 @@ methods function obj = Subject(varargin) - % SUBJECT Constructor for Subject + % SUBJECT - Constructor for Subject + % + % Syntax: + % subject = types.core.SUBJECT() creates a Subject object with unset property values. + % + % subject = types.core.SUBJECT(Name, Value) creates a Subject object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - age (char) - Age of subject. Can be supplied instead of 'date_of_birth'. + % + % - age_reference (char) - Age is with reference to this event. Can be 'birth' or 'gestational'. If reference is omitted, 'birth' is implied. + % + % - date_of_birth (datetime) - Date of birth of subject. Can be supplied instead of 'age'. + % + % - description (char) - Description of subject and where subject came from (e.g., breeder, if animal). + % + % - genotype (char) - Genetic strain. If absent, assume Wild Type (WT). + % + % - sex (char) - Gender of subject. + % + % - species (char) - Species of subject. + % + % - strain (char) - Strain of subject. + % + % - subject_id (char) - ID of animal/person used/participating in experiment (lab convention). + % + % - weight (char) - Weight at time of experiment, at time of surgery and at other important times. + % + % Output Arguments: + % - subject (types.core.Subject) - A Subject object + varargin = [{'age_reference' 'birth'} varargin]; obj = obj@types.core.NWBContainer(varargin{:}); diff --git a/+types/+core/SweepTable.m b/+types/+core/SweepTable.m index be7b8b81..4746b27c 100644 --- a/+types/+core/SweepTable.m +++ b/+types/+core/SweepTable.m @@ -1,5 +1,8 @@ classdef SweepTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% SWEEPTABLE [DEPRECATED] Table used to group different PatchClampSeries. SweepTable is being replaced by IntracellularRecordingsTable and SimultaneousRecordingsTable tables. Additional SequentialRecordingsTable, RepetitionsTable, and ExperimentalConditions tables provide enhanced support for experiment metadata. +% SWEEPTABLE - [DEPRECATED] Table used to group different PatchClampSeries. SweepTable is being replaced by IntracellularRecordingsTable and SimultaneousRecordingsTable tables. Additional SequentialRecordingsTable, RepetitionsTable, and ExperimentalConditions tables provide enhanced support for experiment metadata. +% +% Required Properties: +% id, series, series_index, sweep_number % REQUIRED PROPERTIES @@ -11,7 +14,31 @@ methods function obj = SweepTable(varargin) - % SWEEPTABLE Constructor for SweepTable + % SWEEPTABLE - Constructor for SweepTable + % + % Syntax: + % sweepTable = types.core.SWEEPTABLE() creates a SweepTable object with unset property values. + % + % sweepTable = types.core.SWEEPTABLE(Name, Value) creates a SweepTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - series (VectorData) - The PatchClampSeries with the sweep number in that row. + % + % - series_index (VectorIndex) - Index for series. + % + % - sweep_number (VectorData) - Sweep number of the PatchClampSeries in that row. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - sweepTable (types.core.SweepTable) - A SweepTable object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/TimeIntervals.m b/+types/+core/TimeIntervals.m index 74555b63..49a50c5d 100644 --- a/+types/+core/TimeIntervals.m +++ b/+types/+core/TimeIntervals.m @@ -1,5 +1,8 @@ classdef TimeIntervals < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% TIMEINTERVALS A container for aggregating epoch data and the TimeSeries that each epoch applies to. +% TIMEINTERVALS - A container for aggregating epoch data and the TimeSeries that each epoch applies to. +% +% Required Properties: +% id, start_time, stop_time % REQUIRED PROPERTIES @@ -17,7 +20,37 @@ methods function obj = TimeIntervals(varargin) - % TIMEINTERVALS Constructor for TimeIntervals + % TIMEINTERVALS - Constructor for TimeIntervals + % + % Syntax: + % timeIntervals = types.core.TIMEINTERVALS() creates a TimeIntervals object with unset property values. + % + % timeIntervals = types.core.TIMEINTERVALS(Name, Value) creates a TimeIntervals object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - start_time (VectorData) - Start time of epoch, in seconds. + % + % - stop_time (VectorData) - Stop time of epoch, in seconds. + % + % - tags (VectorData) - User-defined tags that identify or categorize events. + % + % - tags_index (VectorIndex) - Index for tags. + % + % - timeseries (TimeSeriesReferenceVectorData) - An index into a TimeSeries object. + % + % - timeseries_index (VectorIndex) - Index for timeseries. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - timeIntervals (types.core.TimeIntervals) - A TimeIntervals object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/TimeSeries.m b/+types/+core/TimeSeries.m index 2fad2f65..f1f369b3 100644 --- a/+types/+core/TimeSeries.m +++ b/+types/+core/TimeSeries.m @@ -1,5 +1,8 @@ classdef TimeSeries < types.core.NWBDataInterface & types.untyped.GroupClass -% TIMESERIES General purpose time series. +% TIMESERIES - General purpose time series. +% +% Required Properties: +% data % READONLY PROPERTIES @@ -30,7 +33,43 @@ methods function obj = TimeSeries(varargin) - % TIMESERIES Constructor for TimeSeries + % TIMESERIES - Constructor for TimeSeries + % + % Syntax: + % timeSeries = types.core.TIMESERIES() creates a TimeSeries object with unset property values. + % + % timeSeries = types.core.TIMESERIES(Name, Value) creates a TimeSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (any) - Data values. Data can be in 1-D, 2-D, 3-D, or 4-D. The first dimension should always represent time. This can also be used to store binary data (e.g., image frames). This can also be a link to data stored in an external file. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - timeSeries (types.core.TimeSeries) - A TimeSeries object + varargin = [{'comments' 'no comments' 'data_conversion' types.util.correctType(1, 'single') 'data_offset' types.util.correctType(0, 'single') 'data_resolution' types.util.correctType(-1, 'single') 'description' 'no description' 'starting_time_unit' 'seconds' 'timestamps_interval' types.util.correctType(1, 'int32') 'timestamps_unit' 'seconds'} varargin]; obj = obj@types.core.NWBDataInterface(varargin{:}); diff --git a/+types/+core/TimeSeriesReferenceVectorData.m b/+types/+core/TimeSeriesReferenceVectorData.m index 719c12d2..04cb7695 100644 --- a/+types/+core/TimeSeriesReferenceVectorData.m +++ b/+types/+core/TimeSeriesReferenceVectorData.m @@ -1,11 +1,28 @@ classdef TimeSeriesReferenceVectorData < types.hdmf_common.VectorData & types.untyped.DatasetClass -% TIMESERIESREFERENCEVECTORDATA Column storing references to a TimeSeries (rows). For each TimeSeries this VectorData column stores the start_index and count to indicate the range in time to be selected as well as an object reference to the TimeSeries. +% TIMESERIESREFERENCEVECTORDATA - Column storing references to a TimeSeries (rows). For each TimeSeries this VectorData column stores the start_index and count to indicate the range in time to be selected as well as an object reference to the TimeSeries. +% +% Required Properties: +% data methods function obj = TimeSeriesReferenceVectorData(varargin) - % TIMESERIESREFERENCEVECTORDATA Constructor for TimeSeriesReferenceVectorData + % TIMESERIESREFERENCEVECTORDATA - Constructor for TimeSeriesReferenceVectorData + % + % Syntax: + % timeSeriesReferenceVectorData = types.core.TIMESERIESREFERENCEVECTORDATA() creates a TimeSeriesReferenceVectorData object with unset property values. + % + % timeSeriesReferenceVectorData = types.core.TIMESERIESREFERENCEVECTORDATA(Name, Value) creates a TimeSeriesReferenceVectorData object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (Table with columns: (int32, int32, Object reference to TimeSeries)) - No description + % + % - description (char) - Description of what these vectors represent. + % + % Output Arguments: + % - timeSeriesReferenceVectorData (types.core.TimeSeriesReferenceVectorData) - A TimeSeriesReferenceVectorData object + obj = obj@types.hdmf_common.VectorData(varargin{:}); diff --git a/+types/+core/TwoPhotonSeries.m b/+types/+core/TwoPhotonSeries.m index e49afe47..5fe676f7 100644 --- a/+types/+core/TwoPhotonSeries.m +++ b/+types/+core/TwoPhotonSeries.m @@ -1,5 +1,8 @@ classdef TwoPhotonSeries < types.core.ImageSeries & types.untyped.GroupClass -% TWOPHOTONSERIES Image stack recorded over time from 2-photon microscope. +% TWOPHOTONSERIES - Image stack recorded over time from 2-photon microscope. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -12,7 +15,61 @@ methods function obj = TwoPhotonSeries(varargin) - % TWOPHOTONSERIES Constructor for TwoPhotonSeries + % TWOPHOTONSERIES - Constructor for TwoPhotonSeries + % + % Syntax: + % twoPhotonSeries = types.core.TWOPHOTONSERIES() creates a TwoPhotonSeries object with unset property values. + % + % twoPhotonSeries = types.core.TWOPHOTONSERIES(Name, Value) creates a TwoPhotonSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (numeric) - Binary data representing images across frames. If data are stored in an external file, this should be an empty 3D array. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - data_unit (char) - Base unit of measurement for working with the data. Actual stored values are not necessarily stored in these units. To access the data in these units, multiply 'data' by 'conversion' and add 'offset'. + % + % - description (char) - Description of the time series. + % + % - device (Device) - Link to the Device object that was used to capture these images. + % + % - dimension (int32) - Number of pixels on x, y, (and z) axes. + % + % - external_file (char) - Paths to one or more external file(s). The field is only present if format='external'. This is only relevant if the image series is stored in the file system as one or more image file(s). This field should NOT be used if the image is stored in another NWB file and that file is linked to this file. + % + % - external_file_starting_frame (int32) - Each external image may contain one or more consecutive frames of the full ImageSeries. This attribute serves as an index to indicate which frames each file contains, to facilitate random access. The 'starting_frame' attribute, hence, contains a list of frame numbers within the full ImageSeries of the first frame of each file listed in the parent 'external_file' dataset. Zero-based indexing is used (hence, the first element will always be zero). For example, if the 'external_file' dataset has three paths to files and the first file has 5 frames, the second file has 10 frames, and the third file has 20 frames, then this attribute will have values [0, 5, 15]. If there is a single external file that holds all of the frames of the ImageSeries (and so there is a single element in the 'external_file' dataset), then this attribute should have value [0]. + % + % - field_of_view (single) - Width, height and depth of image, or imaged area, in meters. + % + % - format (char) - Format of image. If this is 'external', then the attribute 'external_file' contains the path information to the image files. If this is 'raw', then the raw (single-channel) binary data is stored in the 'data' dataset. If this attribute is not present, then the default format='raw' case is assumed. + % + % - imaging_plane (ImagingPlane) - Link to ImagingPlane object from which this TimeSeries data was generated. + % + % - pmt_gain (single) - Photomultiplier gain. + % + % - scan_line_rate (single) - Lines imaged per second. This is also stored in /general/optophysiology but is kept here as it is useful information for analysis, and so good to be stored w/ the actual data. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - twoPhotonSeries (types.core.TwoPhotonSeries) - A TwoPhotonSeries object + obj = obj@types.core.ImageSeries(varargin{:}); diff --git a/+types/+core/Units.m b/+types/+core/Units.m index 75f2f708..e42631d0 100644 --- a/+types/+core/Units.m +++ b/+types/+core/Units.m @@ -1,5 +1,8 @@ classdef Units < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% UNITS Data about spiking units. Event times of observed units (e.g. cell, synapse, etc.) should be concatenated and stored in spike_times. +% UNITS - Data about spiking units. Event times of observed units (e.g. cell, synapse, etc.) should be concatenated and stored in spike_times. +% +% Required Properties: +% id % OPTIONAL PROPERTIES @@ -20,7 +23,49 @@ methods function obj = Units(varargin) - % UNITS Constructor for Units + % UNITS - Constructor for Units + % + % Syntax: + % units = types.core.UNITS() creates a Units object with unset property values. + % + % units = types.core.UNITS(Name, Value) creates a Units object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - electrode_group (VectorData) - Electrode group that each spike unit came from. + % + % - electrodes (DynamicTableRegion) - Electrode that each spike unit came from, specified using a DynamicTableRegion. + % + % - electrodes_index (VectorIndex) - Index into electrodes. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - obs_intervals (VectorData) - Observation intervals for each unit. + % + % - obs_intervals_index (VectorIndex) - Index into the obs_intervals dataset. + % + % - spike_times (VectorData) - Spike times for each unit in seconds. + % + % - spike_times_index (VectorIndex) - Index into the spike_times dataset. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % - waveform_mean (VectorData) - Spike waveform mean for each spike unit. + % + % - waveform_sd (VectorData) - Spike waveform standard deviation for each spike unit. + % + % - waveforms (VectorData) - Individual waveforms for each spike on each electrode. This is a doubly indexed column. The 'waveforms_index' column indexes which waveforms in this column belong to the same spike event for a given unit, where each waveform was recorded from a different electrode. The 'waveforms_index_index' column indexes the 'waveforms_index' column to indicate which spike events belong to a given unit. For example, if the 'waveforms_index_index' column has values [2, 5, 6], then the first 2 elements of the 'waveforms_index' column correspond to the 2 spike events of the first unit, the next 3 elements of the 'waveforms_index' column correspond to the 3 spike events of the second unit, and the next 1 element of the 'waveforms_index' column corresponds to the 1 spike event of the third unit. If the 'waveforms_index' column has values [3, 6, 8, 10, 12, 13], then the first 3 elements of the 'waveforms' column contain the 3 spike waveforms that were recorded from 3 different electrodes for the first spike time of the first unit. See https://nwb-schema.readthedocs.io/en/stable/format_description.html#doubly-ragged-arrays for a graphical representation of this example. When there is only one electrode for each unit (i.e., each spike time is associated with a single waveform), then the 'waveforms_index' column will have values 1, 2, ..., N, where N is the number of spike events. The number of electrodes for each spike event should be the same within a given unit. The 'electrodes' column should be used to indicate which electrodes are associated with each unit, and the order of the waveforms within a given unit x spike event should be the same as the order of the electrodes referenced in the 'electrodes' column of this table. The number of samples for each waveform must be the same. + % + % - waveforms_index (VectorIndex) - Index into the 'waveforms' dataset. One value for every spike event. See 'waveforms' for more detail. + % + % - waveforms_index_index (VectorIndex) - Index into the 'waveforms_index' dataset. One value for every unit (row in the table). See 'waveforms' for more detail. + % + % Output Arguments: + % - units (types.core.Units) - A Units object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); diff --git a/+types/+core/VoltageClampSeries.m b/+types/+core/VoltageClampSeries.m index 50984fc5..2d2e3f50 100644 --- a/+types/+core/VoltageClampSeries.m +++ b/+types/+core/VoltageClampSeries.m @@ -1,5 +1,8 @@ classdef VoltageClampSeries < types.core.PatchClampSeries & types.untyped.GroupClass -% VOLTAGECLAMPSERIES Current data from an intracellular voltage-clamp recording. A corresponding VoltageClampStimulusSeries (stored separately as a stimulus) is used to store the voltage injected. +% VOLTAGECLAMPSERIES - Current data from an intracellular voltage-clamp recording. A corresponding VoltageClampStimulusSeries (stored separately as a stimulus) is used to store the voltage injected. +% +% Required Properties: +% data % READONLY PROPERTIES @@ -25,7 +28,63 @@ methods function obj = VoltageClampSeries(varargin) - % VOLTAGECLAMPSERIES Constructor for VoltageClampSeries + % VOLTAGECLAMPSERIES - Constructor for VoltageClampSeries + % + % Syntax: + % voltageClampSeries = types.core.VOLTAGECLAMPSERIES() creates a VoltageClampSeries object with unset property values. + % + % voltageClampSeries = types.core.VOLTAGECLAMPSERIES(Name, Value) creates a VoltageClampSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - capacitance_fast (single) - Fast capacitance, in farads. + % + % - capacitance_slow (single) - Slow capacitance, in farads. + % + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (any) - Recorded current. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - electrode (IntracellularElectrode) - Link to IntracellularElectrode object that describes the electrode that was used to apply or record this data. + % + % - gain (single) - Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). + % + % - resistance_comp_bandwidth (single) - Resistance compensation bandwidth, in hertz. + % + % - resistance_comp_correction (single) - Resistance compensation correction, in percent. + % + % - resistance_comp_prediction (single) - Resistance compensation prediction, in percent. + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - stimulus_description (char) - Protocol/stimulus name for this patch-clamp dataset. + % + % - sweep_number (uint32) - Sweep number, allows to group different PatchClampSeries together. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % - whole_cell_capacitance_comp (single) - Whole cell capacitance compensation, in farads. + % + % - whole_cell_series_resistance_comp (single) - Whole cell series resistance compensation, in ohms. + % + % Output Arguments: + % - voltageClampSeries (types.core.VoltageClampSeries) - A VoltageClampSeries object + varargin = [{'capacitance_fast_unit' 'farads' 'capacitance_slow_unit' 'farads' 'data_unit' 'amperes' 'resistance_comp_bandwidth_unit' 'hertz' 'resistance_comp_correction_unit' 'percent' 'resistance_comp_prediction_unit' 'percent' 'whole_cell_capacitance_comp_unit' 'farads' 'whole_cell_series_resistance_comp_unit' 'ohms'} varargin]; obj = obj@types.core.PatchClampSeries(varargin{:}); diff --git a/+types/+core/VoltageClampStimulusSeries.m b/+types/+core/VoltageClampStimulusSeries.m index 2df68b7e..cf0925a7 100644 --- a/+types/+core/VoltageClampStimulusSeries.m +++ b/+types/+core/VoltageClampStimulusSeries.m @@ -1,11 +1,56 @@ classdef VoltageClampStimulusSeries < types.core.PatchClampSeries & types.untyped.GroupClass -% VOLTAGECLAMPSTIMULUSSERIES Stimulus voltage applied during a voltage clamp recording. +% VOLTAGECLAMPSTIMULUSSERIES - Stimulus voltage applied during a voltage clamp recording. +% +% Required Properties: +% data methods function obj = VoltageClampStimulusSeries(varargin) - % VOLTAGECLAMPSTIMULUSSERIES Constructor for VoltageClampStimulusSeries + % VOLTAGECLAMPSTIMULUSSERIES - Constructor for VoltageClampStimulusSeries + % + % Syntax: + % voltageClampStimulusSeries = types.core.VOLTAGECLAMPSTIMULUSSERIES() creates a VoltageClampStimulusSeries object with unset property values. + % + % voltageClampStimulusSeries = types.core.VOLTAGECLAMPSTIMULUSSERIES(Name, Value) creates a VoltageClampStimulusSeries object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - comments (char) - Human-readable comments about the TimeSeries. This second descriptive field can be used to store additional information, or descriptive information if the primary description field is populated with a computer-readable string. + % + % - control (uint8) - Numerical labels that apply to each time point in data for the purpose of querying and slicing data by these values. If present, the length of this array should be the same size as the first dimension of data. + % + % - control_description (char) - Description of each control value. Must be present if control is present. If present, control_description[0] should describe time points where control == 0. + % + % - data (any) - Stimulus voltage applied. + % + % - data_continuity (char) - Optionally describe the continuity of the data. Can be "continuous", "instantaneous", or "step". For example, a voltage trace would be "continuous", because samples are recorded from a continuous process. An array of lick times would be "instantaneous", because the data represents distinct moments in time. Times of image presentations would be "step" because the picture remains the same until the next timepoint. This field is optional, but is useful in providing information about the underlying data. It may inform the way this data is interpreted, the way it is visualized, and what analysis methods are applicable. + % + % - data_conversion (single) - Scalar to multiply each element in data to convert it to the specified 'unit'. If the data are stored in acquisition system units or other units that require a conversion to be interpretable, multiply the data by 'conversion' to convert the data to the specified 'unit'. e.g. if the data acquisition system stores values in this object as signed 16-bit integers (int16 range -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data acquisition system gain is 8000X, then the 'conversion' multiplier to get from raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + % + % - data_offset (single) - Scalar to add to the data after scaling by 'conversion' to finalize its coercion to the specified 'unit'. Two common examples of this include (a) data stored in an unsigned type that requires a shift after scaling to re-center the data, and (b) specialized recording devices that naturally cause a scalar offset with respect to the true units. + % + % - data_resolution (single) - Smallest meaningful difference between values in data, stored in the specified by unit, e.g., the change in value of the least significant bit, or a larger number if signal noise is known to be present. If unknown, use -1.0. + % + % - description (char) - Description of the time series. + % + % - electrode (IntracellularElectrode) - Link to IntracellularElectrode object that describes the electrode that was used to apply or record this data. + % + % - gain (single) - Gain of the recording, in units Volt/Amp (v-clamp) or Volt/Volt (c-clamp). + % + % - starting_time (double) - Timestamp of the first sample in seconds. When timestamps are uniformly spaced, the timestamp of the first sample can be specified and all subsequent ones calculated from the sampling rate attribute. + % + % - starting_time_rate (single) - Sampling rate, in Hz. + % + % - stimulus_description (char) - Protocol/stimulus name for this patch-clamp dataset. + % + % - sweep_number (uint32) - Sweep number, allows to group different PatchClampSeries together. + % + % - timestamps (double) - Timestamps for samples stored in data, in seconds, relative to the common experiment master-clock stored in NWBFile.timestamps_reference_time. + % + % Output Arguments: + % - voltageClampStimulusSeries (types.core.VoltageClampStimulusSeries) - A VoltageClampStimulusSeries object + varargin = [{'data_unit' 'volts'} varargin]; obj = obj@types.core.PatchClampSeries(varargin{:}); diff --git a/+types/+hdmf_common/AlignedDynamicTable.m b/+types/+hdmf_common/AlignedDynamicTable.m index 1754bee8..f3232e15 100644 --- a/+types/+hdmf_common/AlignedDynamicTable.m +++ b/+types/+hdmf_common/AlignedDynamicTable.m @@ -1,5 +1,8 @@ classdef AlignedDynamicTable < types.hdmf_common.DynamicTable & types.untyped.GroupClass -% ALIGNEDDYNAMICTABLE DynamicTable container that supports storing a collection of sub-tables. Each sub-table is a DynamicTable itself that is aligned with the main table by row index. I.e., all DynamicTables stored in this group MUST have the same number of rows. This type effectively defines a 2-level table in which the main data is stored in the main table implemented by this type and additional columns of the table are grouped into categories, with each category being represented by a separate DynamicTable stored within the group. +% ALIGNEDDYNAMICTABLE - DynamicTable container that supports storing a collection of sub-tables. Each sub-table is a DynamicTable itself that is aligned with the main table by row index. I.e., all DynamicTables stored in this group MUST have the same number of rows. This type effectively defines a 2-level table in which the main data is stored in the main table implemented by this type and additional columns of the table are grouped into categories, with each category being represented by a separate DynamicTable stored within the group. +% +% Required Properties: +% id % OPTIONAL PROPERTIES @@ -10,7 +13,29 @@ methods function obj = AlignedDynamicTable(varargin) - % ALIGNEDDYNAMICTABLE Constructor for AlignedDynamicTable + % ALIGNEDDYNAMICTABLE - Constructor for AlignedDynamicTable + % + % Syntax: + % alignedDynamicTable = types.hdmf_common.ALIGNEDDYNAMICTABLE() creates a AlignedDynamicTable object with unset property values. + % + % alignedDynamicTable = types.hdmf_common.ALIGNEDDYNAMICTABLE(Name, Value) creates a AlignedDynamicTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - categories (char) - The names of the categories in this AlignedDynamicTable. Each category is represented by one DynamicTable stored in the parent group. This attribute should be used to specify an order of categories and the category names must match the names of the corresponding DynamicTable in the group. + % + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - dynamictable (DynamicTable) - A DynamicTable representing a particular category for columns in the AlignedDynamicTable parent container. The table MUST be aligned with (i.e., have the same number of rows) as all other DynamicTables stored in the AlignedDynamicTable parent container. The name of the category is given by the name of the DynamicTable and its description by the description attribute of the DynamicTable. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - alignedDynamicTable (types.hdmf_common.AlignedDynamicTable) - A AlignedDynamicTable object + obj = obj@types.hdmf_common.DynamicTable(varargin{:}); [obj.dynamictable, ivarargin] = types.util.parseConstrained(obj,'dynamictable', 'types.hdmf_common.DynamicTable', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+hdmf_common/CSRMatrix.m b/+types/+hdmf_common/CSRMatrix.m index 64e9b4d5..3b8e1c6c 100644 --- a/+types/+hdmf_common/CSRMatrix.m +++ b/+types/+hdmf_common/CSRMatrix.m @@ -1,5 +1,8 @@ classdef CSRMatrix < types.hdmf_common.Container & types.untyped.GroupClass -% CSRMATRIX A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. +% CSRMATRIX - A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. +% +% Required Properties: +% data, indices, indptr % REQUIRED PROPERTIES @@ -15,7 +18,25 @@ methods function obj = CSRMatrix(varargin) - % CSRMATRIX Constructor for CSRMatrix + % CSRMATRIX - Constructor for CSRMatrix + % + % Syntax: + % cSRMatrix = types.hdmf_common.CSRMATRIX() creates a CSRMatrix object with unset property values. + % + % cSRMatrix = types.hdmf_common.CSRMATRIX(Name, Value) creates a CSRMatrix object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (any) - The non-zero values in the matrix. + % + % - indices (uint) - The column indices. + % + % - indptr (uint) - The row index pointer. + % + % - shape (uint) - The shape (number of rows, number of columns) of this sparse matrix. + % + % Output Arguments: + % - cSRMatrix (types.hdmf_common.CSRMatrix) - A CSRMatrix object + obj = obj@types.hdmf_common.Container(varargin{:}); diff --git a/+types/+hdmf_common/Container.m b/+types/+hdmf_common/Container.m index b0075373..80468457 100644 --- a/+types/+hdmf_common/Container.m +++ b/+types/+hdmf_common/Container.m @@ -1,11 +1,21 @@ classdef Container < types.untyped.MetaClass & types.untyped.GroupClass -% CONTAINER An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. +% CONTAINER - An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. +% +% Required Properties: +% None methods function obj = Container(varargin) - % CONTAINER Constructor for Container + % CONTAINER - Constructor for Container + % + % Syntax: + % container = types.hdmf_common.CONTAINER() creates a Container object with unset property values. + % + % Output Arguments: + % - container (types.hdmf_common.Container) - A Container object + obj = obj@types.untyped.MetaClass(varargin{:}); if strcmp(class(obj), 'types.hdmf_common.Container') cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); diff --git a/+types/+hdmf_common/Data.m b/+types/+hdmf_common/Data.m index a53a8e72..40c20f04 100644 --- a/+types/+hdmf_common/Data.m +++ b/+types/+hdmf_common/Data.m @@ -1,5 +1,8 @@ classdef Data < types.untyped.MetaClass & types.untyped.DatasetClass -% DATA An abstract data type for a dataset. +% DATA - An abstract data type for a dataset. +% +% Required Properties: +% data % REQUIRED PROPERTIES @@ -9,7 +12,19 @@ methods function obj = Data(varargin) - % DATA Constructor for Data + % DATA - Constructor for Data + % + % Syntax: + % data = types.hdmf_common.DATA() creates a Data object with unset property values. + % + % data = types.hdmf_common.DATA(Name, Value) creates a Data object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (any) - No description + % + % Output Arguments: + % - data (types.hdmf_common.Data) - A Data object + obj = obj@types.untyped.MetaClass(varargin{:}); diff --git a/+types/+hdmf_common/DynamicTable.m b/+types/+hdmf_common/DynamicTable.m index 80bc4d01..6abca75a 100644 --- a/+types/+hdmf_common/DynamicTable.m +++ b/+types/+hdmf_common/DynamicTable.m @@ -1,5 +1,8 @@ classdef DynamicTable < types.hdmf_common.Container & types.untyped.GroupClass -% DYNAMICTABLE A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. +% DYNAMICTABLE - A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. +% +% Required Properties: +% id % REQUIRED PROPERTIES @@ -15,7 +18,25 @@ methods function obj = DynamicTable(varargin) - % DYNAMICTABLE Constructor for DynamicTable + % DYNAMICTABLE - Constructor for DynamicTable + % + % Syntax: + % dynamicTable = types.hdmf_common.DYNAMICTABLE() creates a DynamicTable object with unset property values. + % + % dynamicTable = types.hdmf_common.DYNAMICTABLE(Name, Value) creates a DynamicTable object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - colnames (char) - The names of the columns in this table. This should be used to specify an order to the columns. + % + % - description (char) - Description of what is in this dynamic table. + % + % - id (ElementIdentifiers) - Array of unique identifiers for the rows of this dynamic table. + % + % - vectordata (VectorData) - Vector columns, including index columns, of this dynamic table. + % + % Output Arguments: + % - dynamicTable (types.hdmf_common.DynamicTable) - A DynamicTable object + obj = obj@types.hdmf_common.Container(varargin{:}); [obj.vectordata, ivarargin] = types.util.parseConstrained(obj,'vectordata', 'types.hdmf_common.VectorData', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+hdmf_common/DynamicTableRegion.m b/+types/+hdmf_common/DynamicTableRegion.m index 506b6b02..6a20ed2a 100644 --- a/+types/+hdmf_common/DynamicTableRegion.m +++ b/+types/+hdmf_common/DynamicTableRegion.m @@ -1,5 +1,8 @@ classdef DynamicTableRegion < types.hdmf_common.VectorData & types.untyped.DatasetClass -% DYNAMICTABLEREGION DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. +% DYNAMICTABLEREGION - DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -9,7 +12,27 @@ methods function obj = DynamicTableRegion(varargin) - % DYNAMICTABLEREGION Constructor for DynamicTableRegion + % DYNAMICTABLEREGION - Constructor for DynamicTableRegion + % + % Syntax: + % dynamicTableRegion = types.hdmf_common.DYNAMICTABLEREGION() creates a DynamicTableRegion object with unset property values. + % + % dynamicTableRegion = types.hdmf_common.DYNAMICTABLEREGION(Name, Value) creates a DynamicTableRegion object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (int8) - No description + % + % - description (char) - Description of what this table region points to. + % + % - resolution (double) - NOTE: this is a special value for compatibility with the Units table and is only written to file when detected to be in that specific HDF5 Group. The smallest possible difference between two spike times. Usually 1 divided by the acquisition sampling rate from which spike times were extracted, but could be larger if the acquisition time series was downsampled or smaller if the acquisition time series was smoothed/interpolated and it is possible for the spike time to be between samples. + % + % - sampling_rate (single) - NOTE: this is a special value for compatibility with the Units table and is only written to file when detected to be in that specific HDF5 Group. Must be Hertz + % + % - table (Object reference to DynamicTable) - Reference to the DynamicTable object that this region applies to. + % + % Output Arguments: + % - dynamicTableRegion (types.hdmf_common.DynamicTableRegion) - A DynamicTableRegion object + obj = obj@types.hdmf_common.VectorData(varargin{:}); diff --git a/+types/+hdmf_common/ElementIdentifiers.m b/+types/+hdmf_common/ElementIdentifiers.m index d9bd22b0..e819ad66 100644 --- a/+types/+hdmf_common/ElementIdentifiers.m +++ b/+types/+hdmf_common/ElementIdentifiers.m @@ -1,11 +1,26 @@ classdef ElementIdentifiers < types.hdmf_common.Data & types.untyped.DatasetClass -% ELEMENTIDENTIFIERS A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. +% ELEMENTIDENTIFIERS - A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. +% +% Required Properties: +% data methods function obj = ElementIdentifiers(varargin) - % ELEMENTIDENTIFIERS Constructor for ElementIdentifiers + % ELEMENTIDENTIFIERS - Constructor for ElementIdentifiers + % + % Syntax: + % elementIdentifiers = types.hdmf_common.ELEMENTIDENTIFIERS() creates a ElementIdentifiers object with unset property values. + % + % elementIdentifiers = types.hdmf_common.ELEMENTIDENTIFIERS(Name, Value) creates a ElementIdentifiers object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (int8) - No description + % + % Output Arguments: + % - elementIdentifiers (types.hdmf_common.ElementIdentifiers) - A ElementIdentifiers object + obj = obj@types.hdmf_common.Data(varargin{:}); diff --git a/+types/+hdmf_common/SimpleMultiContainer.m b/+types/+hdmf_common/SimpleMultiContainer.m index fbc4746b..49945024 100644 --- a/+types/+hdmf_common/SimpleMultiContainer.m +++ b/+types/+hdmf_common/SimpleMultiContainer.m @@ -1,5 +1,8 @@ classdef SimpleMultiContainer < types.hdmf_common.Container & types.untyped.GroupClass -% SIMPLEMULTICONTAINER A simple Container for holding onto multiple containers. +% SIMPLEMULTICONTAINER - A simple Container for holding onto multiple containers. +% +% Required Properties: +% None % OPTIONAL PROPERTIES @@ -10,7 +13,21 @@ methods function obj = SimpleMultiContainer(varargin) - % SIMPLEMULTICONTAINER Constructor for SimpleMultiContainer + % SIMPLEMULTICONTAINER - Constructor for SimpleMultiContainer + % + % Syntax: + % simpleMultiContainer = types.hdmf_common.SIMPLEMULTICONTAINER() creates a SimpleMultiContainer object with unset property values. + % + % simpleMultiContainer = types.hdmf_common.SIMPLEMULTICONTAINER(Name, Value) creates a SimpleMultiContainer object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - container (Container) - Container objects held within this SimpleMultiContainer. + % + % - data (Data) - Data objects held within this SimpleMultiContainer. + % + % Output Arguments: + % - simpleMultiContainer (types.hdmf_common.SimpleMultiContainer) - A SimpleMultiContainer object + obj = obj@types.hdmf_common.Container(varargin{:}); [obj.container, ivarargin] = types.util.parseConstrained(obj,'container', 'types.hdmf_common.Container', varargin{:}); varargin(ivarargin) = []; diff --git a/+types/+hdmf_common/VectorData.m b/+types/+hdmf_common/VectorData.m index cc269845..0e6f22d9 100644 --- a/+types/+hdmf_common/VectorData.m +++ b/+types/+hdmf_common/VectorData.m @@ -1,5 +1,8 @@ classdef VectorData < types.hdmf_common.Data & types.untyped.DatasetClass -% VECTORDATA An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. +% VECTORDATA - An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. +% +% Required Properties: +% data % HIDDEN READONLY PROPERTIES @@ -18,7 +21,25 @@ methods function obj = VectorData(varargin) - % VECTORDATA Constructor for VectorData + % VECTORDATA - Constructor for VectorData + % + % Syntax: + % vectorData = types.hdmf_common.VECTORDATA() creates a VectorData object with unset property values. + % + % vectorData = types.hdmf_common.VECTORDATA(Name, Value) creates a VectorData object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (any) - No description + % + % - description (char) - Description of what these vectors represent. + % + % - resolution (double) - NOTE: this is a special value for compatibility with the Units table and is only written to file when detected to be in that specific HDF5 Group. The smallest possible difference between two spike times. Usually 1 divided by the acquisition sampling rate from which spike times were extracted, but could be larger if the acquisition time series was downsampled or smaller if the acquisition time series was smoothed/interpolated and it is possible for the spike time to be between samples. + % + % - sampling_rate (single) - NOTE: this is a special value for compatibility with the Units table and is only written to file when detected to be in that specific HDF5 Group. Must be Hertz + % + % Output Arguments: + % - vectorData (types.hdmf_common.VectorData) - A VectorData object + varargin = [{'unit' 'volts'} varargin]; obj = obj@types.hdmf_common.Data(varargin{:}); diff --git a/+types/+hdmf_common/VectorIndex.m b/+types/+hdmf_common/VectorIndex.m index 4a40a936..a875dcb9 100644 --- a/+types/+hdmf_common/VectorIndex.m +++ b/+types/+hdmf_common/VectorIndex.m @@ -1,5 +1,8 @@ classdef VectorIndex < types.hdmf_common.VectorData & types.untyped.DatasetClass -% VECTORINDEX Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by "_index". +% VECTORINDEX - Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by "_index". +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -9,7 +12,27 @@ methods function obj = VectorIndex(varargin) - % VECTORINDEX Constructor for VectorIndex + % VECTORINDEX - Constructor for VectorIndex + % + % Syntax: + % vectorIndex = types.hdmf_common.VECTORINDEX() creates a VectorIndex object with unset property values. + % + % vectorIndex = types.hdmf_common.VECTORINDEX(Name, Value) creates a VectorIndex object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (uint8) - No description + % + % - description (char) - Description of what these vectors represent. + % + % - resolution (double) - NOTE: this is a special value for compatibility with the Units table and is only written to file when detected to be in that specific HDF5 Group. The smallest possible difference between two spike times. Usually 1 divided by the acquisition sampling rate from which spike times were extracted, but could be larger if the acquisition time series was downsampled or smaller if the acquisition time series was smoothed/interpolated and it is possible for the spike time to be between samples. + % + % - sampling_rate (single) - NOTE: this is a special value for compatibility with the Units table and is only written to file when detected to be in that specific HDF5 Group. Must be Hertz + % + % - target (Object reference to VectorData) - Reference to the target dataset that this index applies to. + % + % Output Arguments: + % - vectorIndex (types.hdmf_common.VectorIndex) - A VectorIndex object + obj = obj@types.hdmf_common.VectorData(varargin{:}); diff --git a/+types/+hdmf_experimental/EnumData.m b/+types/+hdmf_experimental/EnumData.m index e22843e1..0c334e5f 100644 --- a/+types/+hdmf_experimental/EnumData.m +++ b/+types/+hdmf_experimental/EnumData.m @@ -1,5 +1,8 @@ classdef EnumData < types.hdmf_common.VectorData & types.untyped.DatasetClass -% ENUMDATA Data that come from a fixed set of values. A data value of i corresponds to the i-th value in the VectorData referenced by the 'elements' attribute. +% ENUMDATA - Data that come from a fixed set of values. A data value of i corresponds to the i-th value in the VectorData referenced by the 'elements' attribute. +% +% Required Properties: +% data % OPTIONAL PROPERTIES @@ -9,7 +12,23 @@ methods function obj = EnumData(varargin) - % ENUMDATA Constructor for EnumData + % ENUMDATA - Constructor for EnumData + % + % Syntax: + % enumData = types.hdmf_experimental.ENUMDATA() creates a EnumData object with unset property values. + % + % enumData = types.hdmf_experimental.ENUMDATA(Name, Value) creates a EnumData object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - data (uint8) - No description + % + % - description (char) - Description of what these vectors represent. + % + % - elements (Object reference to VectorData) - Reference to the VectorData object that contains the enumerable elements + % + % Output Arguments: + % - enumData (types.hdmf_experimental.EnumData) - A EnumData object + obj = obj@types.hdmf_common.VectorData(varargin{:}); diff --git a/+types/+hdmf_experimental/HERD.m b/+types/+hdmf_experimental/HERD.m index 38fe06d1..efa48a4d 100644 --- a/+types/+hdmf_experimental/HERD.m +++ b/+types/+hdmf_experimental/HERD.m @@ -1,5 +1,8 @@ classdef HERD < types.hdmf_common.Container & types.untyped.GroupClass -% HERD HDMF External Resources Data Structure. A set of six tables for tracking external resource references in a file or across multiple files. +% HERD - HDMF External Resources Data Structure. A set of six tables for tracking external resource references in a file or across multiple files. +% +% Required Properties: +% entities, entity_keys, files, keys, object_keys, objects % REQUIRED PROPERTIES @@ -14,7 +17,29 @@ methods function obj = HERD(varargin) - % HERD Constructor for HERD + % HERD - Constructor for HERD + % + % Syntax: + % hERD = types.hdmf_experimental.HERD() creates a HERD object with unset property values. + % + % hERD = types.hdmf_experimental.HERD(Name, Value) creates a HERD object where one or more property values are specified using name-value pairs. + % + % Input Arguments (Name-Value Arguments): + % - entities (Data) - A table for mapping user terms (i.e., keys) to resource entities. + % + % - entity_keys (Data) - A table for identifying which keys use which entity. + % + % - files (Data) - A table for storing object ids of files used in external resources. + % + % - keys (Data) - A table for storing user terms that are used to refer to external resources. + % + % - object_keys (Data) - A table for identifying which objects use which keys. + % + % - objects (Data) - A table for identifying which objects in a file contain references to external resources. + % + % Output Arguments: + % - hERD (types.hdmf_experimental.HERD) - A HERD object + obj = obj@types.hdmf_common.Container(varargin{:}); From 78c1f8da6bc9fd2dcc56a4050aa25956bbac39bf Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Dec 2024 21:42:19 +0100 Subject: [PATCH 18/30] Update link target attributes in livescript htmls --- .../_static/html/tutorials/basicUsage.html | 11 +++- .../_static/html/tutorials/behavior.html | 11 +++- .../tutorials/dimensionMapNoDataPipes.html | 11 +++- .../tutorials/dimensionMapWithDataPipes.html | 11 +++- .../html/tutorials/dynamic_tables.html | 11 +++- .../tutorials/dynamically_loaded_filters.html | 11 +++- .../_static/html/tutorials/ecephys.html | 11 +++- .../_static/html/tutorials/icephys.html | 11 +++- .../source/_static/html/tutorials/images.html | 11 +++- docs/source/_static/html/tutorials/intro.html | 11 +++- docs/source/_static/html/tutorials/ogen.html | 11 +++- docs/source/_static/html/tutorials/ophys.html | 11 +++- .../_static/html/tutorials/read_demo.html | 11 +++- .../_static/html/tutorials/remote_read.html | 11 +++- .../_static/html/tutorials/scratch.html | 11 +++- .../private/postProcessLivescriptHtml.m | 54 +++++++++++++++++++ 16 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 tools/documentation/private/postProcessLivescriptHtml.m diff --git a/docs/source/_static/html/tutorials/basicUsage.html b/docs/source/_static/html/tutorials/basicUsage.html index c9cab5d2..d5b51d3f 100644 --- a/docs/source/_static/html/tutorials/basicUsage.html +++ b/docs/source/_static/html/tutorials/basicUsage.html @@ -428,4 +428,13 @@ end ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/behavior.html b/docs/source/_static/html/tutorials/behavior.html index 4f70f592..b44de917 100644 --- a/docs/source/_static/html/tutorials/behavior.html +++ b/docs/source/_static/html/tutorials/behavior.html @@ -366,4 +366,13 @@ fprintf('Exported NWB file to "%s"\n', 'behavior_tutorial.nwb') ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html b/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html index 34e8d692..c5c619d0 100644 --- a/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html +++ b/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html @@ -89,4 +89,13 @@ % ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html b/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html index fb17536d..b503d8dd 100644 --- a/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html +++ b/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html @@ -107,4 +107,13 @@ % ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/dynamic_tables.html b/docs/source/_static/html/tutorials/dynamic_tables.html index af3f6c88..ffa50eda 100644 --- a/docs/source/_static/html/tutorials/dynamic_tables.html +++ b/docs/source/_static/html/tutorials/dynamic_tables.html @@ -574,4 +574,13 @@ % ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/dynamically_loaded_filters.html b/docs/source/_static/html/tutorials/dynamically_loaded_filters.html index 7250e315..e41ebef3 100644 --- a/docs/source/_static/html/tutorials/dynamically_loaded_filters.html +++ b/docs/source/_static/html/tutorials/dynamically_loaded_filters.html @@ -138,4 +138,13 @@ % ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ecephys.html b/docs/source/_static/html/tutorials/ecephys.html index 8d82796e..3e84eb39 100644 --- a/docs/source/_static/html/tutorials/ecephys.html +++ b/docs/source/_static/html/tutorials/ecephys.html @@ -1353,4 +1353,13 @@ %% ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/icephys.html b/docs/source/_static/html/tutorials/icephys.html index 5b095148..4901d2d7 100644 --- a/docs/source/_static/html/tutorials/icephys.html +++ b/docs/source/_static/html/tutorials/icephys.html @@ -506,4 +506,13 @@ % ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/images.html b/docs/source/_static/html/tutorials/images.html index bbdaecdf..50bf1797 100644 --- a/docs/source/_static/html/tutorials/images.html +++ b/docs/source/_static/html/tutorials/images.html @@ -368,4 +368,13 @@ nwbExport(nwb, "images_test.nwb"); ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/intro.html b/docs/source/_static/html/tutorials/intro.html index 63ebd0ce..89727651 100644 --- a/docs/source/_static/html/tutorials/intro.html +++ b/docs/source/_static/html/tutorials/intro.html @@ -304,4 +304,13 @@ % documentation> to learn what data types are available. ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ogen.html b/docs/source/_static/html/tutorials/ogen.html index 4e19478b..a0f2ac53 100644 --- a/docs/source/_static/html/tutorials/ogen.html +++ b/docs/source/_static/html/tutorials/ogen.html @@ -198,4 +198,13 @@ nwbExport(nwb, 'ogen_tutorial.nwb'); ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/ophys.html b/docs/source/_static/html/tutorials/ophys.html index c9cab142..72271094 100644 --- a/docs/source/_static/html/tutorials/ophys.html +++ b/docs/source/_static/html/tutorials/ophys.html @@ -476,4 +476,13 @@ %% ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/read_demo.html b/docs/source/_static/html/tutorials/read_demo.html index 46c9dafb..60d29ef1 100644 --- a/docs/source/_static/html/tutorials/read_demo.html +++ b/docs/source/_static/html/tutorials/read_demo.html @@ -353,4 +353,13 @@ % out the DANDI breakout session later in this event. ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/remote_read.html b/docs/source/_static/html/tutorials/remote_read.html index 9746120e..b549d092 100644 --- a/docs/source/_static/html/tutorials/remote_read.html +++ b/docs/source/_static/html/tutorials/remote_read.html @@ -55,4 +55,13 @@ % ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/source/_static/html/tutorials/scratch.html b/docs/source/_static/html/tutorials/scratch.html index fe467263..5efa82e6 100644 --- a/docs/source/_static/html/tutorials/scratch.html +++ b/docs/source/_static/html/tutorials/scratch.html @@ -161,4 +161,13 @@ % ##### SOURCE END ##### --> - \ No newline at end of file + + \ No newline at end of file diff --git a/tools/documentation/private/postProcessLivescriptHtml.m b/tools/documentation/private/postProcessLivescriptHtml.m new file mode 100644 index 00000000..14b7b138 --- /dev/null +++ b/tools/documentation/private/postProcessLivescriptHtml.m @@ -0,0 +1,54 @@ +function postProcessLivescriptHtml(htmlFile) + %POSTPROCESSLIVESCRIPHTML Update links in an HTML file to open in the top frame + % + % This function reads an HTML file and updates all tags with an + % href attribute starting with "https:" by adding or updating the + % target attribute to "top". The modified HTML content is written + % back to the same file. + % + % Syntax: + % postProcessLivescriptHtml(htmlFile) + % + % Input: + % htmlFile - (1,1) string: Path to the HTML file to process. + % + % Example: + % postProcessLivescriptHtml("example.html"); + % + % This will ensure that links in "example.html" with href="https:" + % open in the top frame when clicked. + + % The purpose of this function is to ensure links open in the top frame + % and not an iframe if tutorial htmls are embedded in an iframe. + + arguments + htmlFile (1,1) string {mustBeFile} + end + + % Read the content of the HTML file + htmlContent = fileread(htmlFile); + + % % Add target="top" to links with href starting with https + % updatedHtmlContent = regexprep(htmlContent, ... + % '", ... + sprintf("%s", str)); + + % Write the modified content back to the HTML file + try + fid = fopen(htmlFile, 'wt'); + if fid == -1 + error('Could not open the file for writing: %s', htmlFile); + end + fwrite(fid, updatedHtmlContent, 'char'); + fclose(fid); + catch + error('Could not write to the file: %s', htmlFile); + end +end From 9afb81cb4b7b34d770b2a475b70046c3d1531bb6 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Dec 2024 21:53:29 +0100 Subject: [PATCH 19/30] Fix previous commit - use correct target attribute value --- docs/source/_static/html/tutorials/basicUsage.html | 2 +- docs/source/_static/html/tutorials/behavior.html | 2 +- .../_static/html/tutorials/dimensionMapNoDataPipes.html | 2 +- .../html/tutorials/dimensionMapWithDataPipes.html | 2 +- docs/source/_static/html/tutorials/dynamic_tables.html | 2 +- .../html/tutorials/dynamically_loaded_filters.html | 2 +- docs/source/_static/html/tutorials/ecephys.html | 2 +- docs/source/_static/html/tutorials/icephys.html | 2 +- docs/source/_static/html/tutorials/images.html | 2 +- docs/source/_static/html/tutorials/intro.html | 2 +- docs/source/_static/html/tutorials/ogen.html | 2 +- docs/source/_static/html/tutorials/ophys.html | 2 +- docs/source/_static/html/tutorials/read_demo.html | 2 +- docs/source/_static/html/tutorials/remote_read.html | 2 +- docs/source/_static/html/tutorials/scratch.html | 2 +- tools/documentation/private/update_link_target_js.html | 9 +++++++++ 16 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 tools/documentation/private/update_link_target_js.html diff --git a/docs/source/_static/html/tutorials/basicUsage.html b/docs/source/_static/html/tutorials/basicUsage.html index d5b51d3f..e0436ed9 100644 --- a/docs/source/_static/html/tutorials/basicUsage.html +++ b/docs/source/_static/html/tutorials/basicUsage.html @@ -433,7 +433,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/behavior.html b/docs/source/_static/html/tutorials/behavior.html index b44de917..b1ae8884 100644 --- a/docs/source/_static/html/tutorials/behavior.html +++ b/docs/source/_static/html/tutorials/behavior.html @@ -371,7 +371,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html b/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html index c5c619d0..17bbc2be 100644 --- a/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html +++ b/docs/source/_static/html/tutorials/dimensionMapNoDataPipes.html @@ -94,7 +94,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html b/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html index b503d8dd..2caf2e2d 100644 --- a/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html +++ b/docs/source/_static/html/tutorials/dimensionMapWithDataPipes.html @@ -112,7 +112,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/dynamic_tables.html b/docs/source/_static/html/tutorials/dynamic_tables.html index ffa50eda..0de0b42e 100644 --- a/docs/source/_static/html/tutorials/dynamic_tables.html +++ b/docs/source/_static/html/tutorials/dynamic_tables.html @@ -579,7 +579,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/dynamically_loaded_filters.html b/docs/source/_static/html/tutorials/dynamically_loaded_filters.html index e41ebef3..ebf14bd3 100644 --- a/docs/source/_static/html/tutorials/dynamically_loaded_filters.html +++ b/docs/source/_static/html/tutorials/dynamically_loaded_filters.html @@ -143,7 +143,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/ecephys.html b/docs/source/_static/html/tutorials/ecephys.html index 3e84eb39..56be33d4 100644 --- a/docs/source/_static/html/tutorials/ecephys.html +++ b/docs/source/_static/html/tutorials/ecephys.html @@ -1358,7 +1358,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/icephys.html b/docs/source/_static/html/tutorials/icephys.html index 4901d2d7..61473dee 100644 --- a/docs/source/_static/html/tutorials/icephys.html +++ b/docs/source/_static/html/tutorials/icephys.html @@ -511,7 +511,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/images.html b/docs/source/_static/html/tutorials/images.html index 50bf1797..4c248f0a 100644 --- a/docs/source/_static/html/tutorials/images.html +++ b/docs/source/_static/html/tutorials/images.html @@ -373,7 +373,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/intro.html b/docs/source/_static/html/tutorials/intro.html index 89727651..a3725382 100644 --- a/docs/source/_static/html/tutorials/intro.html +++ b/docs/source/_static/html/tutorials/intro.html @@ -309,7 +309,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/ogen.html b/docs/source/_static/html/tutorials/ogen.html index a0f2ac53..c3c17c29 100644 --- a/docs/source/_static/html/tutorials/ogen.html +++ b/docs/source/_static/html/tutorials/ogen.html @@ -203,7 +203,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/ophys.html b/docs/source/_static/html/tutorials/ophys.html index 72271094..9a2befd2 100644 --- a/docs/source/_static/html/tutorials/ophys.html +++ b/docs/source/_static/html/tutorials/ophys.html @@ -481,7 +481,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/read_demo.html b/docs/source/_static/html/tutorials/read_demo.html index 60d29ef1..dce55448 100644 --- a/docs/source/_static/html/tutorials/read_demo.html +++ b/docs/source/_static/html/tutorials/read_demo.html @@ -358,7 +358,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/remote_read.html b/docs/source/_static/html/tutorials/remote_read.html index b549d092..10ca8819 100644 --- a/docs/source/_static/html/tutorials/remote_read.html +++ b/docs/source/_static/html/tutorials/remote_read.html @@ -60,7 +60,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/docs/source/_static/html/tutorials/scratch.html b/docs/source/_static/html/tutorials/scratch.html index 5efa82e6..25f996cf 100644 --- a/docs/source/_static/html/tutorials/scratch.html +++ b/docs/source/_static/html/tutorials/scratch.html @@ -166,7 +166,7 @@ document.addEventListener("DOMContentLoaded", function() { const links = document.querySelectorAll('a[href^="https:"]'); links.forEach(link => { - link.setAttribute("target", "top"); + link.setAttribute("target", "_top"); }); }); diff --git a/tools/documentation/private/update_link_target_js.html b/tools/documentation/private/update_link_target_js.html new file mode 100644 index 00000000..27f8b8c5 --- /dev/null +++ b/tools/documentation/private/update_link_target_js.html @@ -0,0 +1,9 @@ + From 11d96fc738c7c84e0a3edd8918b6ac662f8dea9d Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Dec 2024 22:02:30 +0100 Subject: [PATCH 20/30] Update links for types to point to readthedocs --- .../_static/html/tutorials/behavior.html | 64 +++++++-------- .../html/tutorials/dynamic_tables.html | 80 +++++++++---------- .../_static/html/tutorials/ecephys.html | 78 +++++++++--------- .../_static/html/tutorials/icephys.html | 70 ++++++++-------- .../source/_static/html/tutorials/images.html | 56 ++++++------- docs/source/_static/html/tutorials/intro.html | 60 +++++++------- docs/source/_static/html/tutorials/ogen.html | 14 ++-- docs/source/_static/html/tutorials/ophys.html | 48 +++++------ .../_static/html/tutorials/scratch.html | 4 +- .../private/postProcessLivescriptHtml.m | 9 +++ 10 files changed, 246 insertions(+), 237 deletions(-) diff --git a/docs/source/_static/html/tutorials/behavior.html b/docs/source/_static/html/tutorials/behavior.html index b1ae8884..0efb8b10 100644 --- a/docs/source/_static/html/tutorials/behavior.html +++ b/docs/source/_static/html/tutorials/behavior.html @@ -87,7 +87,7 @@ units: [] Warning: The following required properties are missing for instance for type "NwbFile": - timestamps_reference_time

SpatialSeries: Storing continuous spatial data

SpatialSeries is a subclass of TimeSeries that represents data in space, such as the spatial direction e.g., of gaze or travel or position of an animal over time.
Create data that corresponds to x, y position over time.
position_data = [linspace(0, 10, 50); linspace(0, 8, 50)]; % 2 x nT array
In SpatialSeries data, the first dimension is always time (in seconds), the second dimension represents the x, y position. However, as described in the dimensionMapNoDataPipes tutorial, when a MATLAB array is exported to HDF5, the array is transposed. Therefore, in order to correctly export the data, in MATLAB the last dimension of an array should be time. SpatialSeries data should be stored as one continuous stream as it is acquired, not by trials as is often reshaped for analysis. Data can be trial-aligned on-the-fly using the trials table. See the trials tutorial for further information.
For position data reference_frame indicates the zero-position, e.g. the 0,0 point might be the bottom-left corner of an enclosure, as viewed from the tracking camera.
timestamps = linspace(0, 50, 50)/ 200;
position_spatial_series = types.core.SpatialSeries( ...
'description', 'Postion (x, y) in an open field.', ...
'data', position_data, ...
'timestamps', timestamps, ...
'reference_frame', '(0,0) is the bottom left corner.' ...
)
position_spatial_series =
SpatialSeries with properties: + timestamps_reference_time

SpatialSeries: Storing continuous spatial data

SpatialSeries is a subclass of TimeSeries that represents data in space, such as the spatial direction e.g., of gaze or travel or position of an animal over time.
Create data that corresponds to x, y position over time.
position_data = [linspace(0, 10, 50); linspace(0, 8, 50)]; % 2 x nT array
In SpatialSeries data, the first dimension is always time (in seconds), the second dimension represents the x, y position. However, as described in the dimensionMapNoDataPipes tutorial, when a MATLAB array is exported to HDF5, the array is transposed. Therefore, in order to correctly export the data, in MATLAB the last dimension of an array should be time. SpatialSeries data should be stored as one continuous stream as it is acquired, not by trials as is often reshaped for analysis. Data can be trial-aligned on-the-fly using the trials table. See the trials tutorial for further information.
For position data reference_frame indicates the zero-position, e.g. the 0,0 point might be the bottom-left corner of an enclosure, as viewed from the tracking camera.
timestamps = linspace(0, 50, 50)/ 200;
position_spatial_series = types.core.SpatialSeries( ...
'description', 'Postion (x, y) in an open field.', ...
'data', position_data, ...
'timestamps', timestamps, ...
'reference_frame', '(0,0) is the bottom left corner.' ...
)
position_spatial_series =
SpatialSeries with properties: reference_frame: '(0,0) is the bottom left corner.' starting_time_unit: 'seconds' @@ -106,7 +106,7 @@ starting_time: [] starting_time_rate: [] timestamps: [0 0.0051 0.0102 0.0153 0.0204 0.0255 0.0306 0.0357 0.0408 0.0459 0.0510 0.0561 0.0612 0.0663 0.0714 0.0765 0.0816 0.0867 0.0918 0.0969 0.1020 0.1071 0.1122 0.1173 0.1224 0.1276 0.1327 0.1378 0.1429 0.1480 0.1531 … ] (1×50 double) -

Position: Storing position measured over time

To help data analysis and visualization tools know that this SpatialSeries object represents the position of the subject, store the SpatialSeries object inside a Position object, which can hold one or more SpatialSeries objects.
position = types.core.Position();
position.spatialseries.set('SpatialSeries', position_spatial_series);

Create a Behavior Processing Module

Create a processing module called "behavior" for storing behavioral data in the NWBFile, then add the Position object to the processing module.
behavior_processing_module = types.core.ProcessingModule('description', 'stores behavioral data.');
behavior_processing_module.nwbdatainterface.set("Position", position);
nwb.processing.set("behavior", behavior_processing_module);

CompassDirection: Storing view angle measured over time

Analogous to how position can be stored, we can create a SpatialSeries object for representing the view angle of the subject.
For direction data reference_frame indicates the zero direction, for instance in this case "straight ahead" is 0 radians.
view_angle_data = linspace(0, 4, 50);
direction_spatial_series = types.core.SpatialSeries( ...
'description', 'View angle of the subject measured in radians.', ...
'data', view_angle_data, ...
'timestamps', timestamps, ...
'reference_frame', 'straight ahead', ...
'data_unit', 'radians' ...
);
direction = types.core.CompassDirection();
direction.spatialseries.set('spatial_series', direction_spatial_series);
We can add a CompassDirection object to the behavior processing module the same way we have added the position data.
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('CompassDirection', direction);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehaviorTimeSeries: Storing continuous behavior data

BehavioralTimeSeries is an interface for storing continuous behavior data, such as the speed of a subject.
speed_data = linspace(0, 0.4, 50);
 
speed_time_series = types.core.TimeSeries( ...
'data', speed_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 10.0, ... % Hz
'description', 'he speed of the subject measured over time.', ...
'data_unit', 'm/s' ...
);
 
behavioral_time_series = types.core.BehavioralTimeSeries();
behavioral_time_series.timeseries.set('speed', speed_time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralTimeSeries', behavioral_time_series);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehavioralEvents: Storing behavioral events

BehavioralEvents is an interface for storing behavioral events. We can use it for storing the timing and amount of rewards (e.g. water amount) or lever press times.
reward_amount = [1.0, 1.5, 1.0, 1.5];
event_timestamps = [1.0, 2.0, 5.0, 6.0];
 
time_series = types.core.TimeSeries( ...
'data', reward_amount, ...
'timestamps', event_timestamps, ...
'description', 'The water amount the subject received as a reward.', ...
'data_unit', 'ml' ...
);
 
behavioral_events = types.core.BehavioralEvents();
behavioral_events.timeseries.set('lever_presses', time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it
Storing only the timestamps of the events is possible with the ndx-events NWB extension. You can also add labels associated with the events with this extension. You can find information about installation and example usage here.

BehavioralEpochs: Storing intervals of behavior data

BehavioralEpochs is for storing intervals of behavior data. BehavioralEpochs uses IntervalSeries to represent the time intervals. Create an IntervalSeries object that represents the time intervals when the animal was running. IntervalSeries uses 1 to indicate the beginning of an interval and -1 to indicate the end.
run_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was running.', ...
'data', [1, -1, 1, -1, 1, -1], ...
'timestamps', [0.5, 1.5, 3.5, 4.0, 7.0, 7.3] ...
);
 
behavioral_epochs = types.core.BehavioralEpochs();
behavioral_epochs.intervalseries.set('running', run_intervals);
You can add more than one IntervalSeries to a BehavioralEpochs object.
sleep_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was sleeping', ...
'data', [1, -1, 1, -1], ...
'timestamps', [15.0, 30.0, 60.0, 95.0] ...
);
behavioral_epochs.intervalseries.set('sleeping', sleep_intervals);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
% behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
% nwb.processing.set('behavior', behavior_processing_module);

Another approach: TimeIntervals

Using TimeIntervals to represent time intervals is often preferred over BehavioralEpochs and IntervalSeries. TimeIntervals is a subclass of DynamicTable, which offers flexibility for tabular data by allowing the addition of optional columns which are not defined in the standard DynamicTable class.
sleep_intervals = types.core.TimeIntervals( ...
'description', 'Intervals when the animal was sleeping.', ...
'colnames', {'start_time', 'stop_time', 'stage'} ...
);
 
sleep_intervals.addRow('start_time', 0.3, 'stop_time', 0.35, 'stage', 1);
sleep_intervals.addRow('start_time', 0.7, 'stop_time', 0.9, 'stage', 2);
sleep_intervals.addRow('start_time', 1.3, 'stop_time', 3.0, 'stage', 3);
 
nwb.intervals.set('sleep_intervals', sleep_intervals);

EyeTracking: Storing continuous eye-tracking data of gaze direction

EyeTracking is for storing eye-tracking data which represents direction of gaze as measured by an eye tracking algorithm. An EyeTracking object holds one or more SpatialSeries objects that represent the gaze direction over time extracted from a video.
eye_position_data = [linspace(-20, 30, 50); linspace(30, -20, 50)];
 
right_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
left_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
eye_tracking = types.core.EyeTracking();
eye_tracking.spatialseries.set('right_eye_position', right_eye_position);
eye_tracking.spatialseries.set('left_eye_position', left_eye_position);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('EyeTracking', eye_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

PupilTracking: Storing continuous eye-tracking data of pupil size

PupilTracking is for storing eye-tracking data which represents pupil size. PupilTracking holds one or more TimeSeries objects that can represent different features such as the dilation of the pupil measured over time by a pupil tracking algorithm.
pupil_diameter = types.core.TimeSeries( ...
'description', 'Pupil diameter extracted from the video of the right eye.', ...
'data', linspace(0.001, 0.002, 50), ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 20.0, ... % Hz
'data_unit', 'meters' ...
);
 
pupil_tracking = types.core.PupilTracking();
pupil_tracking.timeseries.set('pupil_diameter', pupil_diameter);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('PupilTracking', pupil_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

Writing the behavior data to an NWB file

All of the above commands build an NWBFile object in-memory. To write this file, use nwbExport.
% Save to tutorials/tutorial_nwb_files folder
nwbFilePath = misc.getTutorialNwbFilePath('behavior_tutorial.nwb');
nwbExport(nwb, nwbFilePath);
fprintf('Exported NWB file to "%s"\n', 'behavior_tutorial.nwb')
Exported NWB file to "behavior_tutorial.nwb"
+

Position: Storing position measured over time

To help data analysis and visualization tools know that this SpatialSeries object represents the position of the subject, store the SpatialSeries object inside a Position object, which can hold one or more SpatialSeries objects.
position = types.core.Position();
position.spatialseries.set('SpatialSeries', position_spatial_series);

Create a Behavior Processing Module

Create a processing module called "behavior" for storing behavioral data in the NWBFile, then add the Position object to the processing module.
behavior_processing_module = types.core.ProcessingModule('description', 'stores behavioral data.');
behavior_processing_module.nwbdatainterface.set("Position", position);
nwb.processing.set("behavior", behavior_processing_module);

CompassDirection: Storing view angle measured over time

Analogous to how position can be stored, we can create a SpatialSeries object for representing the view angle of the subject.
For direction data reference_frame indicates the zero direction, for instance in this case "straight ahead" is 0 radians.
view_angle_data = linspace(0, 4, 50);
direction_spatial_series = types.core.SpatialSeries( ...
'description', 'View angle of the subject measured in radians.', ...
'data', view_angle_data, ...
'timestamps', timestamps, ...
'reference_frame', 'straight ahead', ...
'data_unit', 'radians' ...
);
direction = types.core.CompassDirection();
direction.spatialseries.set('spatial_series', direction_spatial_series);
We can add a CompassDirection object to the behavior processing module the same way we have added the position data.
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('CompassDirection', direction);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehaviorTimeSeries: Storing continuous behavior data

BehavioralTimeSeries is an interface for storing continuous behavior data, such as the speed of a subject.
speed_data = linspace(0, 0.4, 50);
 
speed_time_series = types.core.TimeSeries( ...
'data', speed_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 10.0, ... % Hz
'description', 'he speed of the subject measured over time.', ...
'data_unit', 'm/s' ...
);
 
behavioral_time_series = types.core.BehavioralTimeSeries();
behavioral_time_series.timeseries.set('speed', speed_time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralTimeSeries', behavioral_time_series);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it

BehavioralEvents: Storing behavioral events

BehavioralEvents is an interface for storing behavioral events. We can use it for storing the timing and amount of rewards (e.g. water amount) or lever press times.
reward_amount = [1.0, 1.5, 1.0, 1.5];
event_timestamps = [1.0, 2.0, 5.0, 6.0];
 
time_series = types.core.TimeSeries( ...
'data', reward_amount, ...
'timestamps', event_timestamps, ...
'description', 'The water amount the subject received as a reward.', ...
'data_unit', 'ml' ...
);
 
behavioral_events = types.core.BehavioralEvents();
behavioral_events.timeseries.set('lever_presses', time_series);
 
%behavior_processing_module = types.core.ProcessingModule("stores behavioral data."); % if you have not already created it
behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
%nwb.processing.set('behavior', behavior_processing_module); % if you have not already added it
Storing only the timestamps of the events is possible with the ndx-events NWB extension. You can also add labels associated with the events with this extension. You can find information about installation and example usage here.

BehavioralEpochs: Storing intervals of behavior data

BehavioralEpochs is for storing intervals of behavior data. BehavioralEpochs uses IntervalSeries to represent the time intervals. Create an IntervalSeries object that represents the time intervals when the animal was running. IntervalSeries uses 1 to indicate the beginning of an interval and -1 to indicate the end.
run_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was running.', ...
'data', [1, -1, 1, -1, 1, -1], ...
'timestamps', [0.5, 1.5, 3.5, 4.0, 7.0, 7.3] ...
);
 
behavioral_epochs = types.core.BehavioralEpochs();
behavioral_epochs.intervalseries.set('running', run_intervals);
You can add more than one IntervalSeries to a BehavioralEpochs object.
sleep_intervals = types.core.IntervalSeries( ...
'description', 'Intervals when the animal was sleeping', ...
'data', [1, -1, 1, -1], ...
'timestamps', [15.0, 30.0, 60.0, 95.0] ...
);
behavioral_epochs.intervalseries.set('sleeping', sleep_intervals);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
% behavior_processing_module.nwbdatainterface.set('BehavioralEvents', behavioral_events);
% nwb.processing.set('behavior', behavior_processing_module);

Another approach: TimeIntervals

Using TimeIntervals to represent time intervals is often preferred over BehavioralEpochs and IntervalSeries. TimeIntervals is a subclass of DynamicTable, which offers flexibility for tabular data by allowing the addition of optional columns which are not defined in the standard DynamicTable class.
sleep_intervals = types.core.TimeIntervals( ...
'description', 'Intervals when the animal was sleeping.', ...
'colnames', {'start_time', 'stop_time', 'stage'} ...
);
 
sleep_intervals.addRow('start_time', 0.3, 'stop_time', 0.35, 'stage', 1);
sleep_intervals.addRow('start_time', 0.7, 'stop_time', 0.9, 'stage', 2);
sleep_intervals.addRow('start_time', 1.3, 'stop_time', 3.0, 'stage', 3);
 
nwb.intervals.set('sleep_intervals', sleep_intervals);

EyeTracking: Storing continuous eye-tracking data of gaze direction

EyeTracking is for storing eye-tracking data which represents direction of gaze as measured by an eye tracking algorithm. An EyeTracking object holds one or more SpatialSeries objects that represent the gaze direction over time extracted from a video.
eye_position_data = [linspace(-20, 30, 50); linspace(30, -20, 50)];
 
right_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
left_eye_position = types.core.SpatialSeries( ...
'description', 'The position of the right eye measured in degrees.', ...
'data', eye_position_data, ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 50.0, ... % Hz
'reference_frame', '(0,0) is middle', ...
'data_unit', 'degrees' ...
);
 
eye_tracking = types.core.EyeTracking();
eye_tracking.spatialseries.set('right_eye_position', right_eye_position);
eye_tracking.spatialseries.set('left_eye_position', left_eye_position);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('EyeTracking', eye_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

PupilTracking: Storing continuous eye-tracking data of pupil size

PupilTracking is for storing eye-tracking data which represents pupil size. PupilTracking holds one or more TimeSeries objects that can represent different features such as the dilation of the pupil measured over time by a pupil tracking algorithm.
pupil_diameter = types.core.TimeSeries( ...
'description', 'Pupil diameter extracted from the video of the right eye.', ...
'data', linspace(0.001, 0.002, 50), ...
'starting_time', 1.0, ... % NB: Important to set starting_time when using starting_time_rate
'starting_time_rate', 20.0, ... % Hz
'data_unit', 'meters' ...
);
 
pupil_tracking = types.core.PupilTracking();
pupil_tracking.timeseries.set('pupil_diameter', pupil_diameter);
 
% behavior_processing_module = types.core.ProcessingModule("stores behavioral data.");
behavior_processing_module.nwbdatainterface.set('PupilTracking', pupil_tracking);
% nwb.processing.set('behavior', behavior_processing_module);

Writing the behavior data to an NWB file

All of the above commands build an NWBFile object in-memory. To write this file, use nwbExport.
% Save to tutorials/tutorial_nwb_files folder
nwbFilePath = misc.getTutorialNwbFilePath('behavior_tutorial.nwb');
nwbExport(nwb, nwbFilePath);
fprintf('Exported NWB file to "%s"\n', 'behavior_tutorial.nwb')
Exported NWB file to "behavior_tutorial.nwb"

diff --git a/docs/source/pages/tutorials/behavior.rst b/docs/source/pages/tutorials/behavior.rst index 68266a78..a0552988 100644 --- a/docs/source/pages/tutorials/behavior.rst +++ b/docs/source/pages/tutorials/behavior.rst @@ -8,6 +8,13 @@ Behavior Data Processing :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/behavior.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/convertTrials.rst b/docs/source/pages/tutorials/convertTrials.rst index e49ce35f..e231e7b9 100644 --- a/docs/source/pages/tutorials/convertTrials.rst +++ b/docs/source/pages/tutorials/convertTrials.rst @@ -8,6 +8,13 @@ Converting Trials to NWB Format :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/convertTrials.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/dataPipe.rst b/docs/source/pages/tutorials/dataPipe.rst index 9189c08d..7f422605 100644 --- a/docs/source/pages/tutorials/dataPipe.rst +++ b/docs/source/pages/tutorials/dataPipe.rst @@ -8,6 +8,13 @@ Advanced Writing Using DataPipes :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dataPipe.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst index 6aab39ab..136c370b 100644 --- a/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst +++ b/docs/source/pages/tutorials/dimensionMapNoDataPipes.rst @@ -8,6 +8,13 @@ Mapping Dimensions without DataPipes :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dimensionMapNoDataPipes.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst index dc1d7b77..a05a9bff 100644 --- a/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst +++ b/docs/source/pages/tutorials/dimensionMapWithDataPipes.rst @@ -8,6 +8,13 @@ Mapping Dimensions with DataPipes :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dimensionMapWithDataPipes.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/dynamic_tables.rst b/docs/source/pages/tutorials/dynamic_tables.rst index 0917d800..13a8716f 100644 --- a/docs/source/pages/tutorials/dynamic_tables.rst +++ b/docs/source/pages/tutorials/dynamic_tables.rst @@ -8,6 +8,13 @@ Using Dynamic Tables in MatNWB :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dynamic_tables.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/dynamically_loaded_filters.rst b/docs/source/pages/tutorials/dynamically_loaded_filters.rst index b669f3c2..aecfc79a 100644 --- a/docs/source/pages/tutorials/dynamically_loaded_filters.rst +++ b/docs/source/pages/tutorials/dynamically_loaded_filters.rst @@ -8,6 +8,13 @@ Implementing Dynamically Loaded Filters :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dynamically_loaded_filters.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/ecephys.rst b/docs/source/pages/tutorials/ecephys.rst index c957549c..e2fb37b6 100644 --- a/docs/source/pages/tutorials/ecephys.rst +++ b/docs/source/pages/tutorials/ecephys.rst @@ -7,7 +7,16 @@ Electrophysiology .. image:: https://img.shields.io/badge/View-Full_Page-blue :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/ecephys.html :alt: View full page +.. image:: https://img.shields.io/badge/View-Youtube-red + :target: https://www.youtube.com/watch?v=W8t4_quIl1k&ab_channel=NeurodataWithoutBorders + :alt: View tutorial on YouTube .. raw:: html - + + diff --git a/docs/source/pages/tutorials/icephys.rst b/docs/source/pages/tutorials/icephys.rst index e53ea910..1e441266 100644 --- a/docs/source/pages/tutorials/icephys.rst +++ b/docs/source/pages/tutorials/icephys.rst @@ -8,6 +8,13 @@ Intracellular Electrophysiology :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/icephys.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/images.rst b/docs/source/pages/tutorials/images.rst index bdc9156c..3e62d8ee 100644 --- a/docs/source/pages/tutorials/images.rst +++ b/docs/source/pages/tutorials/images.rst @@ -8,6 +8,13 @@ Image Data :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/images.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/intro.rst b/docs/source/pages/tutorials/intro.rst index 423b61e8..5181d12f 100644 --- a/docs/source/pages/tutorials/intro.rst +++ b/docs/source/pages/tutorials/intro.rst @@ -8,6 +8,13 @@ Getting Started with MatNWB :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/intro.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/ogen.rst b/docs/source/pages/tutorials/ogen.rst index 9be344f4..b6cf079f 100644 --- a/docs/source/pages/tutorials/ogen.rst +++ b/docs/source/pages/tutorials/ogen.rst @@ -8,6 +8,13 @@ Optogenetics :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/ogen.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/ophys.rst b/docs/source/pages/tutorials/ophys.rst index e39b6f1a..b25b3391 100644 --- a/docs/source/pages/tutorials/ophys.rst +++ b/docs/source/pages/tutorials/ophys.rst @@ -7,7 +7,16 @@ Optical Physiology .. image:: https://img.shields.io/badge/View-Full_Page-blue :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/ophys.html :alt: View full page +.. image:: https://img.shields.io/badge/View-Youtube-red + :target: https://www.youtube.com/watch?v=OBidHdocnTc&ab_channel=NeurodataWithoutBorders + :alt: View tutorial on YouTube .. raw:: html - + + diff --git a/docs/source/pages/tutorials/read_demo.rst b/docs/source/pages/tutorials/read_demo.rst index 7d83abc6..462e0aaa 100644 --- a/docs/source/pages/tutorials/read_demo.rst +++ b/docs/source/pages/tutorials/read_demo.rst @@ -8,6 +8,13 @@ Reading NWB Files with MatNWB :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/read_demo.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/remote_read.rst b/docs/source/pages/tutorials/remote_read.rst index 7e487725..9dfa11b8 100644 --- a/docs/source/pages/tutorials/remote_read.rst +++ b/docs/source/pages/tutorials/remote_read.rst @@ -8,6 +8,13 @@ Reading NWB Files from Remote Locations :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/remote_read.html :alt: View full page + .. raw:: html - + + diff --git a/docs/source/pages/tutorials/scratch.rst b/docs/source/pages/tutorials/scratch.rst index 92ae4888..63adb931 100644 --- a/docs/source/pages/tutorials/scratch.rst +++ b/docs/source/pages/tutorials/scratch.rst @@ -8,6 +8,13 @@ Working with Scratch Space in MatNWB :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/scratch.html :alt: View full page + .. raw:: html - + + diff --git a/logo/logo_favicon.svg b/logo/logo_favicon.svg new file mode 100644 index 00000000..8e88e7c4 --- /dev/null +++ b/logo/logo_favicon.svg @@ -0,0 +1,584 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/documentation/_rst_templates/tutorial.rst.template b/tools/documentation/_rst_templates/tutorial.rst.template index b00dcd15..78f5f639 100644 --- a/tools/documentation/_rst_templates/tutorial.rst.template +++ b/tools/documentation/_rst_templates/tutorial.rst.template @@ -7,7 +7,14 @@ .. image:: https://img.shields.io/badge/View-Full_Page-blue :target: https://neurodatawithoutborders.github.io/matnwb/tutorials/html/{{tutorial_name}}.html :alt: View full page +{{youtube_badge_block}} .. raw:: html - + + diff --git a/tools/documentation/_rst_templates/youtube_badge.rst.template b/tools/documentation/_rst_templates/youtube_badge.rst.template new file mode 100644 index 00000000..2338c72a --- /dev/null +++ b/tools/documentation/_rst_templates/youtube_badge.rst.template @@ -0,0 +1,3 @@ +.. image:: https://img.shields.io/badge/View-Youtube-red + :target: {{youtube_url}} + :alt: View tutorial on YouTube \ No newline at end of file diff --git a/tools/documentation/private/generateRstForTutorials.m b/tools/documentation/private/generateRstForTutorials.m index c1339cfa..ea5fdc84 100644 --- a/tools/documentation/private/generateRstForTutorials.m +++ b/tools/documentation/private/generateRstForTutorials.m @@ -20,12 +20,21 @@ function generateRstForTutorials() relPath = strrep(thisFilePath, docsSourceRootDir, '../..'); [~, name] = fileparts(relPath); - title = S.(name); + title = S.titles.(name); rstOutput = replace(rstTemplate, '{{static_html_path}}', relPath); rstOutput = replace(rstOutput, '{{tutorial_name}}', name); rstOutput = replace(rstOutput, '{{tutorial_title}}', title); rstOutput = replace(rstOutput, '{{tutorial_title_underline}}', repmat('=', 1, numel(title))); + + % Add the youtube badge block if the tutorial has a corresponding youtube video + if isfield(S.youtube, name) + youtubeBadge = fileread( getRstTemplateFile('youtube_badge') ); + youtubeBadge = replace(youtubeBadge, '{{youtube_url}}', S.youtube.(name)); + else + youtubeBadge = ''; + end + rstOutput = replace(rstOutput, '{{youtube_badge_block}}', youtubeBadge); rstOutputFile = fullfile(tutorialRstTargetDir, [name, '.rst']); fid = fopen(rstOutputFile, 'wt'); From 7521754b6103fa5bf2dad8c26898d10d63be5de0 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Dec 2024 12:18:15 +0100 Subject: [PATCH 23/30] Update .codespellrc --- .codespellrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codespellrc b/.codespellrc index 65f2c912..5c4c3a73 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,3 +1,3 @@ [codespell] -skip = *.html,*logo_matnwb.svg,*fastsearch.m,*.yaml,*testResults.xml +skip = *.html,*.svg,*fastsearch.m,*.yaml,*testResults.xml ignore-words-list = DNE,nd,whos From 097114af1b479271e1e24cf602c400d11600337d Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Dec 2024 13:29:23 +0100 Subject: [PATCH 24/30] Fix links in tutorials, add hdmf_experimental types --- .../_static/html/tutorials/basicUsage.html | 4 +- .../_static/html/tutorials/convertTrials.html | 4 +- .../html/tutorials/dynamic_tables.html | 6 +-- .../_static/html/tutorials/ecephys.html | 8 +-- docs/source/_static/html/tutorials/intro.html | 51 +++++++++--------- docs/source/_static/html/tutorials/ophys.html | 4 +- .../hdmf_experimental/EnumData.rst | 23 ++++++++ .../hdmf_experimental/HERD.rst | 23 ++++++++ .../hdmf_experimental/index.rst | 11 ++++ docs/source/pages/tutorials/basicUsage.rst | 2 +- docs/source/pages/tutorials/behavior.rst | 2 +- docs/source/pages/tutorials/convertTrials.rst | 2 +- docs/source/pages/tutorials/dataPipe.rst | 2 +- .../tutorials/dimensionMapNoDataPipes.rst | 2 +- .../tutorials/dimensionMapWithDataPipes.rst | 2 +- .../source/pages/tutorials/dynamic_tables.rst | 2 +- .../tutorials/dynamically_loaded_filters.rst | 2 +- docs/source/pages/tutorials/ecephys.rst | 2 +- docs/source/pages/tutorials/icephys.rst | 2 +- docs/source/pages/tutorials/images.rst | 2 +- docs/source/pages/tutorials/intro.rst | 2 +- docs/source/pages/tutorials/ogen.rst | 2 +- docs/source/pages/tutorials/ophys.rst | 2 +- docs/source/pages/tutorials/read_demo.rst | 2 +- docs/source/pages/tutorials/remote_read.rst | 2 +- docs/source/pages/tutorials/scratch.rst | 2 +- .../_rst_templates/tutorial.rst.template | 2 +- .../matnwb_generateRstFilesFromCode.m | 2 +- .../generateRstForNeurodataTypeClasses.m | 2 + .../private/postProcessLivescriptHtml.m | 14 +++-- tutorials/intro.mlx | Bin 221807 -> 221827 bytes tutorials/private/mcode/intro.m | 9 ++-- 32 files changed, 129 insertions(+), 68 deletions(-) create mode 100644 docs/source/pages/neurodata_types/hdmf_experimental/EnumData.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_experimental/HERD.rst create mode 100644 docs/source/pages/neurodata_types/hdmf_experimental/index.rst diff --git a/docs/source/_static/html/tutorials/basicUsage.html b/docs/source/_static/html/tutorials/basicUsage.html index e0436ed9..60d0d32d 100644 --- a/docs/source/_static/html/tutorials/basicUsage.html +++ b/docs/source/_static/html/tutorials/basicUsage.html @@ -12,7 +12,7 @@ .S9 { margin-left: 56px; line-height: 21px; min-height: 0px; text-align: left; white-space: pre-wrap; } .S10 { margin: 10px 10px 9px 4px; padding: 0px; line-height: 21px; min-height: 0px; white-space: pre-wrap; color: rgb(0, 0, 0); font-family: Helvetica, Arial, sans-serif; font-style: normal; font-size: 14px; font-weight: 400; text-align: left; } .S11 { border-left: 1px solid rgb(233, 233, 233); border-right: 1px solid rgb(233, 233, 233); border-top: 0px none rgb(0, 0, 0); border-bottom: 1px solid rgb(233, 233, 233); border-radius: 0px; padding: 0px 45px 4px 13px; line-height: 17.234px; min-height: 18px; white-space: nowrap; color: rgb(0, 0, 0); font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; } -.S12 { color: rgb(64, 64, 64); padding: 10px 0px 6px 17px; background: rgb(255, 255, 255) none repeat scroll 0% 0% / auto padding-box border-box; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; overflow-x: hidden; line-height: 17.234px; }

Using NWB Data

last updated: February 9, 2021
In this tutorial, we demonstrate the reading and usage of the NWB file produced in the File Conversion Tutorial. The output is a near-reproduction of Figure 1e from the Li et al publication, showing raster and peristimulus time histogram (PSTH) plots for neural recordings from anterior lateral motor cortex (ALM). This figure illustrates the main finding of the publication, showing the robustness of motor planning behavior and neural dynamics following short unilateral network silencing via optogenetic inhibition.

Reading NWB Files

NWB files can be read in using the nwbRead() function. This function returns a nwbfile object which is the in-memory representation of the NWB file structure.
nwb = nwbRead('out\ANM255201_20141124.nwb');

Constrained Sets

Analyzed data in NWB is placed under the analysis property, which is a Constrained Set. A constrained set consists of an arbitrary amount of key-value pairs similar to Map containers in MATLAB or a dictionary in Python. However, constrained sets also have the ability to validate their own properties closer to how a typed Object would.
You can get/set values in constrained sets using their respective .get()/.set() methods and retrieve all Set properties using the keys() method, like in a containers.Map.
unit_names = keys(nwb.analysis);

Dynamic Tables

nwb.intervals_trials returns a unique type of table called a Dynamic Table. Dynamic tables inherit from the NWB type types.hdmf_common.DynamicTable and allow for a table-like interface in NWB. In the case below, we grab the special column start_time. Dynamic Tables allow adding your own vectors using the vectordata property, which are Constrained Sets. All columns are represented by either a types.hdmf_common.VectorData or a types.hdmf_common.VectorIndex type.

Data Stubs

The data property of the column id in nwb.units is a types.untyped.DataStub. This object is a representation of a dataset that is not loaded in memory, and is what allows MatNWB to lazily load its file data. To load the data into memory, use the .load() method which extracts all data from the NWB file. Alternatively, you can index into the DataStub directly using conventional MATLAB syntax.

Jagged Arrays in Dynamic Tables

With the new addition of addRow and getRow to Dynamic Tables, the concept of jagged arrays can be worked around and no longer require full understanding outside of specific data format concerns or low-level nwb tool development. The below paragraph is retained in its entirety from its original form as purely informational.
All data in a Dynamic Table must be aligned by row and column, but not all data fits into this paradigm neatly. In order to represent variable amounts of data that is localised to each row and column, NWB uses a concept called Jagged Arrays. These arrays consist of two column types: the familiar types.core.VectorData, and the new types.core.VectorIndex. A Vector Index holds no data, instead holding a reference to another Vector Data and a vector of indices that align to the Dynamic Table dimensions. The indices represent the last index boundary in the Vector Data object for the Vector Index row. As an example, an index of three in the first row of the Vector Index column points to the first three values in the referenced Vector Data column. Subsequently, if the next index were a five, it would indicate the fourth and fifth elements in the referenced Vector Data column.
The jagged arrays serve to represent multiple trials and spike times associated to each unit by id. A convenient way to represent these in MATLAB is to use Map containers where each unit's data is indexed directly by its unit id. Below, we utilize getRow in order to build the same Map.
unit_ids = nwb.units.id.data.load(); % array of unit ids represented within this
% Initialize trials & times Map containers indexed by unit_ids
unit_trials = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_times = containers.Map('KeyType',class(unit_ids),'ValueType','any');
last_idx = 0;
for i = 1:length(unit_ids)
unit_id = unit_ids(i);
row = nwb.units.getRow(unit_id, 'useId', true, 'columns', {'spike_times', 'trials'});
unit_trials(unit_id) = row.trials{1};
unit_times(unit_id) = row.spike_times{1};
end

Process Units

We now do the following for each Unit:
  • Filter out invalid trials
  • Separate datasets based on resulting mouse behavior (right/left licks).
  • Derive "sample", "delay", and "response" times for this analyzed neuron.
  • Compose a peristimulus time histogram from the data.
sorted_ids = sort(unit_ids);
Photostim = struct(...
'ind', true,... % mask into xs and ys for this photostim
'period', 'none',...
'duration', 0,... % in seconds
'ramp_offset', 0); % in seconds
% Initialize Map container of plotting data for each unit, stored as structure
Unit = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_struct = struct(...
'id', [],...
'xs', [],...
'ys', [],...
'xlim', [-Inf Inf],...
'sample', 0,...
'delay', 0,...
'response', 0,...
'left_scatter', false,...
'right_scatter', false,...
'photostim', Photostim); % can have multiple photostim
for unit_id = unit_ids'
We first extract trial IDs from the Unit IDs.
unit_trial_id = unit_trials(unit_id);
Then filter out outliers from the Sample, Delay, and Response time points with which we derive a "good enough" estimate.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'PoleInTime', 'PoleOutTime', 'CueTime', 'GoodTrials'});
unit_sample = trial.PoleInTime;
unit_delay = trial.PoleOutTime;
unit_response = trial.CueTime;
unit_good_trials = trial.GoodTrials;
% Subjective parameters
delay_threshold = 0.064;
response_threshold = 0.43;
expected_delay_offset = 1.3; % determined from figure 1a
expected_response_offset = 1.3;
expected_delay = unit_sample + expected_delay_offset;
expected_response = unit_delay + expected_response_offset;
good_delay = (unit_delay > expected_delay - delay_threshold) &...
(unit_delay < expected_delay + delay_threshold);
good_response = (unit_response > expected_response - response_threshold) &...
(unit_response < expected_response + response_threshold);
avg_sample = mean(unit_sample(good_delay & good_response));
avg_delay = mean(unit_delay(good_delay & good_response));
avg_response = mean(unit_response(good_delay & good_response));
Filter the rest of the data by "good" trials.
unit_good_trials = unit_good_trials & good_delay & good_response;
unit_trial_id = unit_trial_id(unit_good_trials);
unit_spike_time = unit_times(unit_id);
unit_spike_time = unit_spike_time(unit_good_trials);
Retrieve good trial data and organize by stimulation type.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'start_time', 'HitR', 'HitL', 'StimTrials', 'PhotostimulationType'});
unit_is_photostim = logical(trial.StimTrials);
unit_stim_type = trial.PhotostimulationType;
unit_no_stim = ~unit_is_photostim & 0 == unit_stim_type;
unit_sample_stim = unit_is_photostim & 1 == unit_stim_type;
unit_early_stim = unit_is_photostim & 2 == unit_stim_type;
unit_middle_stim = unit_is_photostim & 3 == unit_stim_type;
Compose Scatter Plots and the Peristimulus Time Histogram zeroed on the Response time.
xs = unit_spike_time - trial.start_time - avg_response;
ys = unit_trial_id;
curr_unit = unit_struct;
curr_unit.xs = xs;
curr_unit.ys = ys;
curr_unit.left_scatter = logical(trial.HitL);
curr_unit.right_scatter = logical(trial.HitR);
curr_unit.sample = avg_sample - avg_response;
curr_unit.delay = avg_delay - avg_response;
curr_unit.response = 0;
% Photostim periods
curr_unit.photostim.ind = unit_no_stim;
% Sample
if any(unit_sample_stim)
SampleStim = Photostim;
SampleStim.ind = unit_sample_stim;
SampleStim.period = 'Sample';
SampleStim.duration = 0.5;
SampleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = SampleStim;
end
% Early Delay
if any(unit_early_stim)
early_stim_types = unique(unit_stim_type(unit_early_stim));
for i_early_types=1:length(early_stim_types)
early_type = early_stim_types(i_early_types);
EarlyStim = Photostim;
EarlyStim.period = 'Early Delay';
EarlyStim.ind = early_type == unit_stim_type & unit_early_stim;
if early_type == 2
EarlyStim.duration = 0.5;
EarlyStim.ramp_offset = 0.1;
else
EarlyStim.duration = 0.8;
EarlyStim.ramp_offset = 0.2;
end
curr_unit.photostim(end+1) = EarlyStim;
end
end
% Middle Delay
if any(unit_middle_stim)
MiddleStim = Photostim;
MiddleStim.ind = unit_middle_stim;
MiddleStim.period = 'Middle Delay';
MiddleStim.duration = 0.5;
MiddleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = MiddleStim;
end
Unit(unit_id) = curr_unit;
end

Plot Example Neurons

neuron_labels = [2, 3]; % neuron labels from Figure 1e
neuron_ids = [11, 2]; % neuron unit IDs corresponding to the Fig 1e labels
num_conditions = 4; % photostim conditions: nostim, sample, early, middle if applicable
num_neurons = length(neuron_ids);
% Inititalize data structures for each summary plot of categorized neural spike data at specified stimulus condition
RasterPlot = struct(...
'xs', 0,...
'ys', 0);
ConditionPlot = struct(...
'label', '',...
'xlim', 0,...
'sample', 0,...
'delay', 0,...
'response', 0,...
'right_scatter', RasterPlot,...
'left_scatter', RasterPlot,...
'psth_bin_window', 0,...
'stim_type', '');
fig = figure;
% Plot neural spike data for each neuron and stimulus condition in a subplot array: num_neurons (rows) x num_conditions (columns)
for nn=1:num_neurons
Neuron = Unit(neuron_ids(nn));
% Initialize structure with neural + stimulus condition data
CurrPlot = ConditionPlot;
CurrPlot.xlim = [min(Neuron.xs) max(Neuron.xs)];
CurrPlot.sample = Neuron.sample;
CurrPlot.delay = Neuron.delay;
CurrPlot.response = Neuron.response;
% Plot each neuron/condition
plot_row = (nn - 1) * num_conditions;
for cc=1:num_conditions
ax = subplot(num_neurons, num_conditions, plot_row + cc, 'Parent', fig);
Stim = Neuron.photostim(cc);
CurrPlot.stim_type = Stim.period;
if strcmp(Stim.period, 'none')
CurrPlot.label = sprintf('Neuron %d', neuron_labels(nn));
CurrPlot.psth_bin_window = 9;
else
CurrPlot.label = Stim.period;
CurrPlot.psth_bin_window = 2;
end
stim_left_scatter_ind = Stim.ind & Neuron.left_scatter;
stim_left_scatter_trials = Neuron.ys(stim_left_scatter_ind);
CurrPlot.left_scatter.xs = Neuron.xs(stim_left_scatter_ind);
[~,CurrPlot.left_scatter.ys] = ismember(stim_left_scatter_trials,unique(stim_left_scatter_trials));
stim_right_scatter_ind = Stim.ind & Neuron.right_scatter;
stim_right_scatter_trials = Neuron.ys(stim_right_scatter_ind);
CurrPlot.right_scatter.xs = Neuron.xs(stim_right_scatter_ind);
[~,CurrPlot.right_scatter.ys] = ismember(stim_right_scatter_trials,unique(stim_right_scatter_trials));
plot_condition(ax, CurrPlot);
end
end
+.S12 { color: rgb(64, 64, 64); padding: 10px 0px 6px 17px; background: rgb(255, 255, 255) none repeat scroll 0% 0% / auto padding-box border-box; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; overflow-x: hidden; line-height: 17.234px; }

Using NWB Data

last updated: February 9, 2021
In this tutorial, we demonstrate the reading and usage of the NWB file produced in the File Conversion Tutorial. The output is a near-reproduction of Figure 1e from the Li et al publication, showing raster and peristimulus time histogram (PSTH) plots for neural recordings from anterior lateral motor cortex (ALM). This figure illustrates the main finding of the publication, showing the robustness of motor planning behavior and neural dynamics following short unilateral network silencing via optogenetic inhibition.

Reading NWB Files

NWB files can be read in using the nwbRead() function. This function returns a nwbfile object which is the in-memory representation of the NWB file structure.
nwb = nwbRead('out\ANM255201_20141124.nwb');

Constrained Sets

Analyzed data in NWB is placed under the analysis property, which is a Constrained Set. A constrained set consists of an arbitrary amount of key-value pairs similar to Map containers in MATLAB or a dictionary in Python. However, constrained sets also have the ability to validate their own properties closer to how a typed Object would.
You can get/set values in constrained sets using their respective .get()/.set() methods and retrieve all Set properties using the keys() method, like in a containers.Map.
unit_names = keys(nwb.analysis);

Dynamic Tables

nwb.intervals_trials returns a unique type of table called a Dynamic Table. Dynamic tables inherit from the NWB type types.hdmf_common.DynamicTable and allow for a table-like interface in NWB. In the case below, we grab the special column start_time. Dynamic Tables allow adding your own vectors using the vectordata property, which are Constrained Sets. All columns are represented by either a types.hdmf_common.VectorData or a types.hdmf_common.VectorIndex type.

Data Stubs

The data property of the column id in nwb.units is a types.untyped.DataStub. This object is a representation of a dataset that is not loaded in memory, and is what allows MatNWB to lazily load its file data. To load the data into memory, use the .load() method which extracts all data from the NWB file. Alternatively, you can index into the DataStub directly using conventional MATLAB syntax.

Jagged Arrays in Dynamic Tables

With the new addition of addRow and getRow to Dynamic Tables, the concept of jagged arrays can be worked around and no longer require full understanding outside of specific data format concerns or low-level nwb tool development. The below paragraph is retained in its entirety from its original form as purely informational.
All data in a Dynamic Table must be aligned by row and column, but not all data fits into this paradigm neatly. In order to represent variable amounts of data that is localised to each row and column, NWB uses a concept called Jagged Arrays. These arrays consist of two column types: the familiar types.core.VectorData, and the new types.core.VectorIndex. A Vector Index holds no data, instead holding a reference to another Vector Data and a vector of indices that align to the Dynamic Table dimensions. The indices represent the last index boundary in the Vector Data object for the Vector Index row. As an example, an index of three in the first row of the Vector Index column points to the first three values in the referenced Vector Data column. Subsequently, if the next index were a five, it would indicate the fourth and fifth elements in the referenced Vector Data column.
The jagged arrays serve to represent multiple trials and spike times associated to each unit by id. A convenient way to represent these in MATLAB is to use Map containers where each unit's data is indexed directly by its unit id. Below, we utilize getRow in order to build the same Map.
unit_ids = nwb.units.id.data.load(); % array of unit ids represented within this
% Initialize trials & times Map containers indexed by unit_ids
unit_trials = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_times = containers.Map('KeyType',class(unit_ids),'ValueType','any');
last_idx = 0;
for i = 1:length(unit_ids)
unit_id = unit_ids(i);
row = nwb.units.getRow(unit_id, 'useId', true, 'columns', {'spike_times', 'trials'});
unit_trials(unit_id) = row.trials{1};
unit_times(unit_id) = row.spike_times{1};
end

Process Units

We now do the following for each Unit:
  • Filter out invalid trials
  • Separate datasets based on resulting mouse behavior (right/left licks).
  • Derive "sample", "delay", and "response" times for this analyzed neuron.
  • Compose a peristimulus time histogram from the data.
sorted_ids = sort(unit_ids);
Photostim = struct(...
'ind', true,... % mask into xs and ys for this photostim
'period', 'none',...
'duration', 0,... % in seconds
'ramp_offset', 0); % in seconds
% Initialize Map container of plotting data for each unit, stored as structure
Unit = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_struct = struct(...
'id', [],...
'xs', [],...
'ys', [],...
'xlim', [-Inf Inf],...
'sample', 0,...
'delay', 0,...
'response', 0,...
'left_scatter', false,...
'right_scatter', false,...
'photostim', Photostim); % can have multiple photostim
for unit_id = unit_ids'
We first extract trial IDs from the Unit IDs.
unit_trial_id = unit_trials(unit_id);
Then filter out outliers from the Sample, Delay, and Response time points with which we derive a "good enough" estimate.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'PoleInTime', 'PoleOutTime', 'CueTime', 'GoodTrials'});
unit_sample = trial.PoleInTime;
unit_delay = trial.PoleOutTime;
unit_response = trial.CueTime;
unit_good_trials = trial.GoodTrials;
% Subjective parameters
delay_threshold = 0.064;
response_threshold = 0.43;
expected_delay_offset = 1.3; % determined from figure 1a
expected_response_offset = 1.3;
expected_delay = unit_sample + expected_delay_offset;
expected_response = unit_delay + expected_response_offset;
good_delay = (unit_delay > expected_delay - delay_threshold) &...
(unit_delay < expected_delay + delay_threshold);
good_response = (unit_response > expected_response - response_threshold) &...
(unit_response < expected_response + response_threshold);
avg_sample = mean(unit_sample(good_delay & good_response));
avg_delay = mean(unit_delay(good_delay & good_response));
avg_response = mean(unit_response(good_delay & good_response));
Filter the rest of the data by "good" trials.
unit_good_trials = unit_good_trials & good_delay & good_response;
unit_trial_id = unit_trial_id(unit_good_trials);
unit_spike_time = unit_times(unit_id);
unit_spike_time = unit_spike_time(unit_good_trials);
Retrieve good trial data and organize by stimulation type.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'start_time', 'HitR', 'HitL', 'StimTrials', 'PhotostimulationType'});
unit_is_photostim = logical(trial.StimTrials);
unit_stim_type = trial.PhotostimulationType;
unit_no_stim = ~unit_is_photostim & 0 == unit_stim_type;
unit_sample_stim = unit_is_photostim & 1 == unit_stim_type;
unit_early_stim = unit_is_photostim & 2 == unit_stim_type;
unit_middle_stim = unit_is_photostim & 3 == unit_stim_type;
Compose Scatter Plots and the Peristimulus Time Histogram zeroed on the Response time.
xs = unit_spike_time - trial.start_time - avg_response;
ys = unit_trial_id;
curr_unit = unit_struct;
curr_unit.xs = xs;
curr_unit.ys = ys;
curr_unit.left_scatter = logical(trial.HitL);
curr_unit.right_scatter = logical(trial.HitR);
curr_unit.sample = avg_sample - avg_response;
curr_unit.delay = avg_delay - avg_response;
curr_unit.response = 0;
% Photostim periods
curr_unit.photostim.ind = unit_no_stim;
% Sample
if any(unit_sample_stim)
SampleStim = Photostim;
SampleStim.ind = unit_sample_stim;
SampleStim.period = 'Sample';
SampleStim.duration = 0.5;
SampleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = SampleStim;
end
% Early Delay
if any(unit_early_stim)
early_stim_types = unique(unit_stim_type(unit_early_stim));
for i_early_types=1:length(early_stim_types)
early_type = early_stim_types(i_early_types);
EarlyStim = Photostim;
EarlyStim.period = 'Early Delay';
EarlyStim.ind = early_type == unit_stim_type & unit_early_stim;
if early_type == 2
EarlyStim.duration = 0.5;
EarlyStim.ramp_offset = 0.1;
else
EarlyStim.duration = 0.8;
EarlyStim.ramp_offset = 0.2;
end
curr_unit.photostim(end+1) = EarlyStim;
end
end
% Middle Delay
if any(unit_middle_stim)
MiddleStim = Photostim;
MiddleStim.ind = unit_middle_stim;
MiddleStim.period = 'Middle Delay';
MiddleStim.duration = 0.5;
MiddleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = MiddleStim;
end
Unit(unit_id) = curr_unit;
end

Plot Example Neurons

neuron_labels = [2, 3]; % neuron labels from Figure 1e
neuron_ids = [11, 2]; % neuron unit IDs corresponding to the Fig 1e labels
num_conditions = 4; % photostim conditions: nostim, sample, early, middle if applicable
num_neurons = length(neuron_ids);
% Inititalize data structures for each summary plot of categorized neural spike data at specified stimulus condition
RasterPlot = struct(...
'xs', 0,...
'ys', 0);
ConditionPlot = struct(...
'label', '',...
'xlim', 0,...
'sample', 0,...
'delay', 0,...
'response', 0,...
'right_scatter', RasterPlot,...
'left_scatter', RasterPlot,...
'psth_bin_window', 0,...
'stim_type', '');
fig = figure;
% Plot neural spike data for each neuron and stimulus condition in a subplot array: num_neurons (rows) x num_conditions (columns)
for nn=1:num_neurons
Neuron = Unit(neuron_ids(nn));
% Initialize structure with neural + stimulus condition data
CurrPlot = ConditionPlot;
CurrPlot.xlim = [min(Neuron.xs) max(Neuron.xs)];
CurrPlot.sample = Neuron.sample;
CurrPlot.delay = Neuron.delay;
CurrPlot.response = Neuron.response;
% Plot each neuron/condition
plot_row = (nn - 1) * num_conditions;
for cc=1:num_conditions
ax = subplot(num_neurons, num_conditions, plot_row + cc, 'Parent', fig);
Stim = Neuron.photostim(cc);
CurrPlot.stim_type = Stim.period;
if strcmp(Stim.period, 'none')
CurrPlot.label = sprintf('Neuron %d', neuron_labels(nn));
CurrPlot.psth_bin_window = 9;
else
CurrPlot.label = Stim.period;
CurrPlot.psth_bin_window = 2;
end
stim_left_scatter_ind = Stim.ind & Neuron.left_scatter;
stim_left_scatter_trials = Neuron.ys(stim_left_scatter_ind);
CurrPlot.left_scatter.xs = Neuron.xs(stim_left_scatter_ind);
[~,CurrPlot.left_scatter.ys] = ismember(stim_left_scatter_trials,unique(stim_left_scatter_trials));
stim_right_scatter_ind = Stim.ind & Neuron.right_scatter;
stim_right_scatter_trials = Neuron.ys(stim_right_scatter_ind);
CurrPlot.right_scatter.xs = Neuron.xs(stim_right_scatter_ind);
[~,CurrPlot.right_scatter.ys] = ismember(stim_right_scatter_trials,unique(stim_right_scatter_trials));
plot_condition(ax, CurrPlot);
end
end

Helper Functions

PSTH helper function
function [psth_xs, psth_ys] = calculate_psth(xs, bin_window, bin_width)
[bin_counts, edges] = histcounts(xs, 'BinWidth', bin_width);
psth_xs = edges(1:end-1) + (bin_width / 2);
moving_avg_b = (1/bin_window) * ones(1,bin_window);
psth_ys = filter(moving_avg_b, 1, bin_counts);
end
Plotter function for each stimulus condition
function plot_condition(ax, ConditionPlot)
left_cdata = [1 0 0]; % red
right_cdata = [0 0 1]; % blue
hist_margin = 50;
scatter_margin = 10;
% Calculate PSTH values
% moving average over 200 ms as per figure 1e
hist_bin_width = 0.2 / ConditionPlot.psth_bin_window;
[left_psth_xs, left_psth_ys] =...
calculate_psth(ConditionPlot.left_scatter.xs, ConditionPlot.psth_bin_window, hist_bin_width);
[right_psth_xs, right_psth_ys] =...
calculate_psth(ConditionPlot.right_scatter.xs, ConditionPlot.psth_bin_window, hist_bin_width);
right_scatter_offset = min(ConditionPlot.right_scatter.ys);
right_scatter_height = max(ConditionPlot.right_scatter.ys) - right_scatter_offset;
left_scatter_offset = min(ConditionPlot.left_scatter.ys);
left_scatter_height = max(ConditionPlot.left_scatter.ys) - left_scatter_offset;
psth_height = max([left_psth_ys right_psth_ys]);
left_y_offset = hist_margin...
+ psth_height...
- left_scatter_offset;
right_y_offset = scatter_margin...
+ left_y_offset...
+ left_scatter_offset...
+ left_scatter_height...
- right_scatter_offset;
subplot_height = right_y_offset...
+ right_scatter_offset...
+ right_scatter_height;
hold(ax, 'on');
% PSTH
plot(ax, left_psth_xs, left_psth_ys, 'Color', left_cdata);
plot(ax, right_psth_xs, right_psth_ys, 'Color', right_cdata);
% Scatter Plot
scatter(ax,...
ConditionPlot.left_scatter.xs,...
left_y_offset + ConditionPlot.left_scatter.ys,...
'Marker', '.',...
'CData', left_cdata,...
'SizeData', 1);
scatter(ax,...
ConditionPlot.right_scatter.xs,...
right_y_offset + ConditionPlot.right_scatter.ys,...
'Marker', '.',...
'CData', right_cdata,...
'SizeData', 1);
% sample, delay, response lines
line(ax, repmat(ConditionPlot.sample, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
line(ax, repmat(ConditionPlot.delay, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
line(ax, repmat(ConditionPlot.response, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
% blue bar for photoinhibition period
if ~strcmp(ConditionPlot.stim_type, 'none')
stim_height = subplot_height;
stim_width = 0.5; % seconds
% end time relative to 'go' cue as described in the paper.
switch ConditionPlot.stim_type
case 'Sample'
end_offset = 1.6;
case 'Early Delay'
end_offset = 0.8;
case 'Middle Delay'
end_offset = 0.3;
otherwise
error('Invalid photostim period `%s`', ConditionPlot.stim_type);
end
stim_offset = ConditionPlot.response - stim_width - end_offset;
patch_vertices = [...
stim_offset, 0;...
stim_offset, stim_height;...
stim_offset+stim_width, stim_height;...
stim_offset+stim_width, 0];
patch(ax,...
'Faces', 1:4,...
'Vertices', patch_vertices,...
'FaceColor', '#B3D3EC',... % light blue shading
'EdgeColor', 'none',...
'FaceAlpha', 0.8);
end
title(ax, ConditionPlot.label);
xlabel(ax, 'Time (Seconds)');
ylabel(ax, 'Spikes s^{-1}')
xticks(ax, [-2 0 2]);
yticks(ax, [0 max(10, round(psth_height, -1))]);
% legend(ax, [scatter_left_plot, scatter_right_plot], {'Left Lick', 'Right Lick'},...
% 'location', 'northwestoutside');
ax.TickDir = 'out';
ax.XLim = ConditionPlot.xlim;
ax.YLim = [0 subplot_height];
hold(ax, 'off');
end
@@ -23,7 +23,7 @@ % _last updated: February 9, 2021_ % % In this tutorial, we demonstrate the reading and usage of the NWB file produced -% in the . The output is a near-reproduction of Figure 1e from % the publication, showing % raster and peristimulus time histogram (PSTH) plots for neural recordings from diff --git a/docs/source/_static/html/tutorials/convertTrials.html b/docs/source/_static/html/tutorials/convertTrials.html index 1df73a43..d3528cb7 100644 --- a/docs/source/_static/html/tutorials/convertTrials.html +++ b/docs/source/_static/html/tutorials/convertTrials.html @@ -477,7 +477,7 @@

Hashes

end end -

To better understand how spike_times_index and spike_times map to each other, refer to this diagram from the Extracellular Electrophysiology Tutorial.

+

To better understand how spike_times_index and spike_times map to each other, refer to this diagram from the Extracellular Electrophysiology Tutorial.

Raw Acquisition Data

Each ALM-3 session is associated with a large number of raw voltage data grouped by trial ID. To map this data to NWB, each trial is created as its own ElectricalSeries object under the name 'trial n' where 'n' is the trial ID. The trials are then linked to the trials dynamic table for easy referencing.

fprintf('Processing Raw Acquisition Data from `%s` (will take a while)\n', rawdata_loc);
@@ -1023,7 +1023,7 @@ 

Export

end %% % To better understand how |spike_times_index| and |spike_times| map to each other, refer to -% from the Extracellular Electrophysiology Tutorial. %% Raw Acquisition Data diff --git a/docs/source/_static/html/tutorials/dynamic_tables.html b/docs/source/_static/html/tutorials/dynamic_tables.html index 160896cf..8abf43c9 100644 --- a/docs/source/_static/html/tutorials/dynamic_tables.html +++ b/docs/source/_static/html/tutorials/dynamic_tables.html @@ -103,7 +103,7 @@ colnames: {'col1' 'col2' 'col3' 'col4'} description: 'A MATLAB table that was converted to a dynamic table' vectordata: [4×1 types.untyped.Set] -

Enumerated (categorical) data

EnumData is a special type of column for storing an enumerated data type. This way each unique value is stored once, and the data references those values by index. Using this method is more efficient than storing a single value many times, and has the advantage of communicating to downstream tools that the data is categorical in nature.

Warning Regarding EnumData

EnumData is currently an experimental feature and as such should not be used in a production environment.
CellTypeElements = types.hdmf_common.VectorData(...
'description', 'fixed set of elements referenced by cell_type' ...
, 'data', {'aa', 'bb', 'cc'} ... % the enumerated elements
);
CellType = types.hdmf_experimental.EnumData( ...
'description', 'this column holds categorical variables' ... % properties derived from VectorData
, 'data', [0, 1, 2, 1, 0] ... % zero-indexed offset to elements.
, 'elements', types.untyped.ObjectView(CellTypeElements) ...
);
 
MyTable = types.hdmf_common.DynamicTable('description', 'an example table');
MyTable.vectordata.set('cell_type_elements', CellTypeElements); % the *_elements format is required for compatibility with pynwb
MyTable.addColumn('cell_type', CellType);

Ragged array columns

A table column with a different number of elements for each row is called a "ragged array column." To define a table with a ragged array column, pass both the VectorData and the corresponding VectorIndex as columns of the DynamicTable object. The VectorData columns will contain the data values. The VectorIndex column serves to indicate how to arrange the data across rows. By convention the VectorIndex object corresponding to a particular column must have have the same name with the addition of the '_index' suffix.
Below, the VectorIndex values indicate to place the 1st to 3rd (inclusive) elements of the VectorData into the first row and 4th element into the second row. The resulting table will have the cell {'1a'; '1b'; '1c'} in the first row and the cell {'2a'} in the second row.
 
col1 = types.hdmf_common.VectorData( ...
'description', 'column #1', ...
'data', {'1a'; '1b'; '1c'; '2a'} ...
);
 
col1_index = types.hdmf_common.VectorIndex( ...
'description', 'column #1 index', ...
'target',types.untyped.ObjectView(col1), ... % object view of target column
'data', [3; 4] ...
);
 
table_ragged_col = types.hdmf_common.DynamicTable( ...
'description', 'an example table', ...
'colnames', {'col1'}, ...
'col1', col1, ...
'col1_index', col1_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1]) ... % 0-indexed, for compatibility with Python
);

Adding ragged array rows

You can add a new row to the ragged array column. Under the hood, the addRow method will add the appropriate value to the VectorIndex column to maintain proper formatting.
table_ragged_col.addRow('col1', {'3a'; '3b'; '3c'}, 'id', 2);

Accessing row elements

You can access data from entire rows of a DynamicTable object by calling the getRow method for the corresponding object. You can supply either an individual row number or a list of row numbers.
my_table.getRow(1)
ans = 1×4 table
 col1col2col3col4
11'a'100'a1'
If you want to access values for just a subset of columns you can pass in the 'columns' argument along with a cell array with the desired column names
my_table.getRow(1:3, 'columns', {'col1'})
ans = 3×1 table
 col1
11
22
33
You can also access specific rows by their corresponding row ID's, if they have been defined, by supplying a 'true' Boolean to the 'useId' parameter
my_table.getRow(1, 'useId', true)
ans = 1×4 table
 col1col2col3col4
12'b'200'b2'
For a ragged array columns, the getRow method will return a cell with different number of elements for each row
table_ragged_col.getRow(1:2)
ans = 2×1 table
 col1
1[{'1a'};{'1b'};{'1c'}]
21×1 cell

Accessing column elements

To access all rows from a particular column use the .get method on the vectordata field of the DynamicTable object
 
my_table.vectordata.get('col2').data
ans = 3×1 cell
'a'
'b'
'c'

Referencing rows of other tables

You can create a column that references rows of other tables by adding a DynamicTableRegion object as a column of a DynamicTable. This is analogous to a foreign key in a relational database. The DynamicTableRegion class takes in an ObjectView object as argument. ObjectView objects create links from one object type referencing another.
dtr_col = types.hdmf_common.DynamicTableRegion( ...
'description', 'references multiple rows of earlier table', ...
'data', [0; 1; 1; 0], ... # 0-indexed
'table',types.untyped.ObjectView(my_table) ... % object view of target table
);
 
data_col = types.hdmf_common.VectorData( ...
'description', 'data column', ...
'data', {'a'; 'b'; 'c'; 'd'} ...
);
 
dtr_table = types.hdmf_common.DynamicTable( ...
'description', 'test table with DynamicTableRegion', ...
'colnames', {'data_col', 'dtr_col'}, ...
'dtr_col', dtr_col, ...
'data_col',data_col, ...
'id',types.hdmf_common.ElementIdentifiers('data', [0; 1; 2; 3]) ...
);

Converting a DynamicTable to a MATLAB table

You can convert a DynamicTable object to a MATLAB table by making use of the object's toTable method. This is a useful way to view the whole table in a human-readable format.
my_table.toTable()
ans = 3×5 table
 idcol1col2col3col4
101'a'100'a1'
212'b'200'b2'
323'c'300'c3'
When the DynamicTable object contains a column that references other tables, you can pass in a Boolean to indicate whether to include just the row indices of the referenced table. Passing in false will result in inclusion of the referenced rows as nested tables.
dtr_table.toTable(false)
ans = 4×3 table
 iddata_coldtr_col
10'a'1×4 table
21'b'1×4 table
32'c'1×4 table
43'd'1×4 table

Creating an expandable table

When using the default HDF5 backend, each column of these tables is an HDF5 Dataset, which by default are set to an unchangeable size. This means that once a file is written, it is not possible to add a new row. If you want to be able to save this file, load it, and add more rows to the table, you will need to set this up when you create the VectorData and ElementIdentifiers columns of a DynamicTable. Specifically, you must wrap the column data with a DataPipe object. The DataPipe class takes in maxSize and axis as arguments to indicate the maximum desired size for each axis and the axis to which to append to, respectively. For example, creating a DataPipe object with a maxSize value equal to [Inf, 1] indicates that the number of rows may increase indifinetely. In contrast, setting maxSize equal to [8, 1] would allow the column to grow to a maximum height of 8.
% create NwbFile object with required fields
file= NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'ExpandableTableTutorial' ...
);
 
% create VectorData objects with DataPipe objects
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', [1, 2], ... # data must be numerical
'maxSize', Inf ...
) ...
);
 
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', [2, 3], ... #data must be numerical
'maxSize', Inf ...
) ...
);
 
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(5, 2), ... #data must be numerical
'maxSize', [5, Inf], ...
'axis', 2 ...
) ...
);
 
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int32([0; 1]), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'id', ids_exp ...
);
% export file
nwbExport(file, 'expandableTableTestFile.nwb');
Now, you can read in the file, add more rows, and save again to file
readFile = nwbRead('expandableTableTestFile.nwb', 'ignorecache');
readFile.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 4, ...
'randomvalues', rand(5,1), ...
'id', 2 ...
)
nwbExport(readFile, 'expandableTableTestFile.nwb');
Note: DataPipe objects change how the dimension of the datasets for each column map onto the shape of HDF5 datasets. See README for more details.

Multidimensional Columns

The order of dimensions of multidimensional columns in MatNWB is reversed relative to the Python HDMF package (see README for detailed explanation). Therefore, the height of a multidimensional column belonging to a DynamicTable object is defined by the shape of its last dimension. A valid DynamicTable must have matched height across columns.

Constructing multidimensional columns

% Define 1D column
simple_col = types.hdmf_common.VectorData( ...
'description', '1D column',...
'data', rand(10,1) ...
);
% Define ND column
multi_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional column',...
'data', rand(3,2,10) ...
);
% construct table
multi_dim_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'simple','multi'}, ...
'simple', simple_col, ...
'multi', multi_col, ...
'id', types.hdmf_common.ElementIdentifiers('data', (0:9)') ... % 0-indexed, for compatibility with Python
);
 

Multidimensional ragged array columns

DynamicTable objects with multidimensional ragged array columns can be constructed by passing in the corresponding VectorIndex column
% Define column with data
multi_ragged_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional ragged array column',...
'data', rand(2,3,5) ...
);
% Define column with VectorIndex
multi_ragged_index = types.hdmf_common.VectorIndex( ...
'description', 'index to multi_ragged_col', ...
'target', types.untyped.ObjectView(multi_ragged_col),'data', [2; 3; 5] ...
);
 
multi_ragged_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'multi_ragged'}, ...
'multi_ragged', multi_ragged_col, ...
'multi_ragged_index', multi_ragged_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1; 2]) ... % 0-indexed, for compatibility with Python
);

Adding rows to multidimensional array columns

DynamicTable objects with multidimensional array columns can also be constructed by adding a single row at a time. This method makes use of DataPipe objects due to the fact that MATLAB doesn't support singleton dimensions for arrays with more than 2 dimensions. The code block below demonstrates how to build a DynamicTable object with a mutidimensional raaged array column in this manner.
% Create file
file = NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test_file' ...
);
 
% Define Vector Data Objects with first row of table
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', 1, ...
'maxSize', Inf ...
) ...
);
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', 10, ...
'maxSize', Inf ...
) ...
);
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(3,2,5), ... #random data
'maxSize', [3, 2, Inf], ...
'axis', 3 ...
) ...
);
random_exp_index = types.hdmf_common.VectorIndex( ...
'description', 'index to random data column', ...
'target',types.untyped.ObjectView(random_exp), ...
'data', types.untyped.DataPipe( ...
'data', uint64(5), ...
'maxSize', Inf ...
) ...
);
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int64(0), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% Create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'randomvalues_index', random_exp_index, ...
'id', ids_exp ...
);
% Export file
nwbExport(file, 'multiRaggedExpandableTableTest.nwb');
% Read in file
read_file = nwbRead('multiRaggedExpandableTableTest.nwb', 'ignorecache');
% add individual rows
read_file.intervals_trials.addRow( ...
'start_time', 2, ...
'stop_time', 20, ...
'randomvalues', rand(3,2,6), ...
'id', 1 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 30, ...
'randomvalues', rand(3,2,3), ...
'id', 2 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 4, ...
'stop_time', 40, ...
'randomvalues', rand(3,2,8), ...
'id', 3 ...
);
 

Learn More!

Python Tutorial

+

Enumerated (categorical) data

EnumData is a special type of column for storing an enumerated data type. This way each unique value is stored once, and the data references those values by index. Using this method is more efficient than storing a single value many times, and has the advantage of communicating to downstream tools that the data is categorical in nature.

Warning Regarding EnumData

EnumData is currently an experimental feature and as such should not be used in a production environment.
CellTypeElements = types.hdmf_common.VectorData(...
'description', 'fixed set of elements referenced by cell_type' ...
, 'data', {'aa', 'bb', 'cc'} ... % the enumerated elements
);
CellType = types.hdmf_experimental.EnumData( ...
'description', 'this column holds categorical variables' ... % properties derived from VectorData
, 'data', [0, 1, 2, 1, 0] ... % zero-indexed offset to elements.
, 'elements', types.untyped.ObjectView(CellTypeElements) ...
);
 
MyTable = types.hdmf_common.DynamicTable('description', 'an example table');
MyTable.vectordata.set('cell_type_elements', CellTypeElements); % the *_elements format is required for compatibility with pynwb
MyTable.addColumn('cell_type', CellType);

Ragged array columns

A table column with a different number of elements for each row is called a "ragged array column." To define a table with a ragged array column, pass both the VectorData and the corresponding VectorIndex as columns of the DynamicTable object. The VectorData columns will contain the data values. The VectorIndex column serves to indicate how to arrange the data across rows. By convention the VectorIndex object corresponding to a particular column must have have the same name with the addition of the '_index' suffix.
Below, the VectorIndex values indicate to place the 1st to 3rd (inclusive) elements of the VectorData into the first row and 4th element into the second row. The resulting table will have the cell {'1a'; '1b'; '1c'} in the first row and the cell {'2a'} in the second row.
 
col1 = types.hdmf_common.VectorData( ...
'description', 'column #1', ...
'data', {'1a'; '1b'; '1c'; '2a'} ...
);
 
col1_index = types.hdmf_common.VectorIndex( ...
'description', 'column #1 index', ...
'target',types.untyped.ObjectView(col1), ... % object view of target column
'data', [3; 4] ...
);
 
table_ragged_col = types.hdmf_common.DynamicTable( ...
'description', 'an example table', ...
'colnames', {'col1'}, ...
'col1', col1, ...
'col1_index', col1_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1]) ... % 0-indexed, for compatibility with Python
);

Adding ragged array rows

You can add a new row to the ragged array column. Under the hood, the addRow method will add the appropriate value to the VectorIndex column to maintain proper formatting.
table_ragged_col.addRow('col1', {'3a'; '3b'; '3c'}, 'id', 2);

Accessing row elements

You can access data from entire rows of a DynamicTable object by calling the getRow method for the corresponding object. You can supply either an individual row number or a list of row numbers.
my_table.getRow(1)
ans = 1×4 table
 col1col2col3col4
11'a'100'a1'
If you want to access values for just a subset of columns you can pass in the 'columns' argument along with a cell array with the desired column names
my_table.getRow(1:3, 'columns', {'col1'})
ans = 3×1 table
 col1
11
22
33
You can also access specific rows by their corresponding row ID's, if they have been defined, by supplying a 'true' Boolean to the 'useId' parameter
my_table.getRow(1, 'useId', true)
ans = 1×4 table
 col1col2col3col4
12'b'200'b2'
For a ragged array columns, the getRow method will return a cell with different number of elements for each row
table_ragged_col.getRow(1:2)
ans = 2×1 table
 col1
1[{'1a'};{'1b'};{'1c'}]
21×1 cell

Accessing column elements

To access all rows from a particular column use the .get method on the vectordata field of the DynamicTable object
 
my_table.vectordata.get('col2').data
ans = 3×1 cell
'a'
'b'
'c'

Referencing rows of other tables

You can create a column that references rows of other tables by adding a DynamicTableRegion object as a column of a DynamicTable. This is analogous to a foreign key in a relational database. The DynamicTableRegion class takes in an ObjectView object as argument. ObjectView objects create links from one object type referencing another.
dtr_col = types.hdmf_common.DynamicTableRegion( ...
'description', 'references multiple rows of earlier table', ...
'data', [0; 1; 1; 0], ... # 0-indexed
'table',types.untyped.ObjectView(my_table) ... % object view of target table
);
 
data_col = types.hdmf_common.VectorData( ...
'description', 'data column', ...
'data', {'a'; 'b'; 'c'; 'd'} ...
);
 
dtr_table = types.hdmf_common.DynamicTable( ...
'description', 'test table with DynamicTableRegion', ...
'colnames', {'data_col', 'dtr_col'}, ...
'dtr_col', dtr_col, ...
'data_col',data_col, ...
'id',types.hdmf_common.ElementIdentifiers('data', [0; 1; 2; 3]) ...
);

Converting a DynamicTable to a MATLAB table

You can convert a DynamicTable object to a MATLAB table by making use of the object's toTable method. This is a useful way to view the whole table in a human-readable format.
my_table.toTable()
ans = 3×5 table
 idcol1col2col3col4
101'a'100'a1'
212'b'200'b2'
323'c'300'c3'
When the DynamicTable object contains a column that references other tables, you can pass in a Boolean to indicate whether to include just the row indices of the referenced table. Passing in false will result in inclusion of the referenced rows as nested tables.
dtr_table.toTable(false)
ans = 4×3 table
 iddata_coldtr_col
10'a'1×4 table
21'b'1×4 table
32'c'1×4 table
43'd'1×4 table

Creating an expandable table

When using the default HDF5 backend, each column of these tables is an HDF5 Dataset, which by default are set to an unchangeable size. This means that once a file is written, it is not possible to add a new row. If you want to be able to save this file, load it, and add more rows to the table, you will need to set this up when you create the VectorData and ElementIdentifiers columns of a DynamicTable. Specifically, you must wrap the column data with a DataPipe object. The DataPipe class takes in maxSize and axis as arguments to indicate the maximum desired size for each axis and the axis to which to append to, respectively. For example, creating a DataPipe object with a maxSize value equal to [Inf, 1] indicates that the number of rows may increase indifinetely. In contrast, setting maxSize equal to [8, 1] would allow the column to grow to a maximum height of 8.
% create NwbFile object with required fields
file= NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'ExpandableTableTutorial' ...
);
 
% create VectorData objects with DataPipe objects
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', [1, 2], ... # data must be numerical
'maxSize', Inf ...
) ...
);
 
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', [2, 3], ... #data must be numerical
'maxSize', Inf ...
) ...
);
 
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(5, 2), ... #data must be numerical
'maxSize', [5, Inf], ...
'axis', 2 ...
) ...
);
 
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int32([0; 1]), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'id', ids_exp ...
);
% export file
nwbExport(file, 'expandableTableTestFile.nwb');
Now, you can read in the file, add more rows, and save again to file
readFile = nwbRead('expandableTableTestFile.nwb', 'ignorecache');
readFile.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 4, ...
'randomvalues', rand(5,1), ...
'id', 2 ...
)
nwbExport(readFile, 'expandableTableTestFile.nwb');
Note: DataPipe objects change how the dimension of the datasets for each column map onto the shape of HDF5 datasets. See README for more details.

Multidimensional Columns

The order of dimensions of multidimensional columns in MatNWB is reversed relative to the Python HDMF package (see README for detailed explanation). Therefore, the height of a multidimensional column belonging to a DynamicTable object is defined by the shape of its last dimension. A valid DynamicTable must have matched height across columns.

Constructing multidimensional columns

% Define 1D column
simple_col = types.hdmf_common.VectorData( ...
'description', '1D column',...
'data', rand(10,1) ...
);
% Define ND column
multi_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional column',...
'data', rand(3,2,10) ...
);
% construct table
multi_dim_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'simple','multi'}, ...
'simple', simple_col, ...
'multi', multi_col, ...
'id', types.hdmf_common.ElementIdentifiers('data', (0:9)') ... % 0-indexed, for compatibility with Python
);
 

Multidimensional ragged array columns

DynamicTable objects with multidimensional ragged array columns can be constructed by passing in the corresponding VectorIndex column
% Define column with data
multi_ragged_col = types.hdmf_common.VectorData( ...
'description', 'multidimensional ragged array column',...
'data', rand(2,3,5) ...
);
% Define column with VectorIndex
multi_ragged_index = types.hdmf_common.VectorIndex( ...
'description', 'index to multi_ragged_col', ...
'target', types.untyped.ObjectView(multi_ragged_col),'data', [2; 3; 5] ...
);
 
multi_ragged_table = types.hdmf_common.DynamicTable( ...
'description','test table', ...
'colnames', {'multi_ragged'}, ...
'multi_ragged', multi_ragged_col, ...
'multi_ragged_index', multi_ragged_index, ...
'id', types.hdmf_common.ElementIdentifiers('data', [0; 1; 2]) ... % 0-indexed, for compatibility with Python
);

Adding rows to multidimensional array columns

DynamicTable objects with multidimensional array columns can also be constructed by adding a single row at a time. This method makes use of DataPipe objects due to the fact that MATLAB doesn't support singleton dimensions for arrays with more than 2 dimensions. The code block below demonstrates how to build a DynamicTable object with a mutidimensional raaged array column in this manner.
% Create file
file = NwbFile( ...
'session_start_time', datetime('2021-01-01 00:00:00', 'TimeZone', 'local'), ...
'identifier', 'ident1', ...
'session_description', 'test_file' ...
);
 
% Define Vector Data Objects with first row of table
start_time_exp = types.hdmf_common.VectorData( ...
'description', 'start times column', ...
'data', types.untyped.DataPipe( ...
'data', 1, ...
'maxSize', Inf ...
) ...
);
stop_time_exp = types.hdmf_common.VectorData( ...
'description', 'stop times column', ...
'data', types.untyped.DataPipe( ...
'data', 10, ...
'maxSize', Inf ...
) ...
);
random_exp = types.hdmf_common.VectorData( ...
'description', 'random data column', ...
'data', types.untyped.DataPipe( ...
'data', rand(3,2,5), ... #random data
'maxSize', [3, 2, Inf], ...
'axis', 3 ...
) ...
);
random_exp_index = types.hdmf_common.VectorIndex( ...
'description', 'index to random data column', ...
'target',types.untyped.ObjectView(random_exp), ...
'data', types.untyped.DataPipe( ...
'data', uint64(5), ...
'maxSize', Inf ...
) ...
);
ids_exp = types.hdmf_common.ElementIdentifiers( ...
'data', types.untyped.DataPipe( ...
'data', int64(0), ... # data must be numerical
'maxSize', Inf ...
) ...
);
% Create expandable table
colnames = {'start_time', 'stop_time', 'randomvalues'};
file.intervals_trials = types.core.TimeIntervals( ...
'description', 'test expdandable dynamic table', ...
'colnames', colnames, ...
'start_time', start_time_exp, ...
'stop_time', stop_time_exp, ...
'randomvalues', random_exp, ...
'randomvalues_index', random_exp_index, ...
'id', ids_exp ...
);
% Export file
nwbExport(file, 'multiRaggedExpandableTableTest.nwb');
% Read in file
read_file = nwbRead('multiRaggedExpandableTableTest.nwb', 'ignorecache');
% add individual rows
read_file.intervals_trials.addRow( ...
'start_time', 2, ...
'stop_time', 20, ...
'randomvalues', rand(3,2,6), ...
'id', 1 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 3, ...
'stop_time', 30, ...
'randomvalues', rand(3,2,3), ...
'id', 2 ...
);
read_file.intervals_trials.addRow( ...
'start_time', 4, ...
'stop_time', 40, ...
'randomvalues', rand(3,2,8), ...
'id', 3 ...
);
 

Learn More!

Python Tutorial


diff --git a/docs/source/_static/html/tutorials/icephys.html b/docs/source/_static/html/tutorials/icephys.html index b6a8677d..ec504c72 100644 --- a/docs/source/_static/html/tutorials/icephys.html +++ b/docs/source/_static/html/tutorials/icephys.html @@ -52,7 +52,7 @@ Add repetitions table Add experimental condition table Write the NWB file -Read the NWB file
The following tutorial describes storage of intracellular electrophysiology data in NWB. NWB supports storage of the time series describing the stimulus and response, information about the electrode and device used, as well as metadata about the organization of the experiment.
Illustration of the hierarchy of metadata tables used to describe the organization of intracellular electrophysiology experiments.

Creating an NWBFile

When creating an NWB file, the first step is to create the NWBFile, which you can create using the NwbFile command.
session_start_time = datetime(2018, 3, 1, 12, 0, 0, 'TimeZone', 'local');
 
 
nwbfile = NwbFile( ...
'session_description', 'my first synthetic recording', ...
'identifier', 'EXAMPLE_ID', ...
'session_start_time', session_start_time, ...
'general_experimenter', 'Dr. Bilbo Baggins', ...
'general_lab', 'Bag End Laboratory', ...
'general_institution', 'University of Middle Earth at the Shire', ...
'general_experiment_description', 'I went on an adventure with thirteen dwarves to reclaim vast treasures.', ...
'general_session_id', 'LONELYMTN' ...
);
 

Device metadata

Device metadata is represented by Device objects.
 
device = types.core.Device();
nwbfile.general_devices.set('Heka ITC-1600', device);

Electrode metadata

Intracellular electrode metadata is represented by IntracellularElectrode objects. Create an electrode object, which requires a link to the device of the previous step. Then add it to the NWB file.
electrode = types.core.IntracellularElectrode( ...
'description', 'a mock intracellular electrode', ...
'device', types.untyped.SoftLink(device), ...
'cell_id', 'a very interesting cell' ...
);
nwbfile.general_intracellular_ephys.set('elec0', electrode);

Stimulus and response data

Intracellular stimulus and response data are represented with subclasses of PatchClampSeries. A stimulus is described by a time series representing voltage or current stimulation with a particular set of parameters. There are two classes for representing stimulus data:
The response is then described by a time series representing voltage or current recorded from a single cell using a single intracellular electrode via one of the following classes:
Below we create a simple example stimulus/response recording data pair for a voltage clamp recording.
ccss = types.core.VoltageClampStimulusSeries( ...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint64(15), ...
'stimulus_description', 'N/A' ...
);
nwbfile.stimulus_presentation.set('ccss', ccss);
 
vcs = types.core.VoltageClampSeries( ...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'capacitance_slow', 100e-12, ...
'resistance_comp_correction', 70.0, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint64(15) ...
);
nwbfile.acquisition.set('vcs', vcs);
You can add stimulus/response recording data pair from a current clamp recording in the same way:
% Create a CurrentClampStimulusSeries object
ccss = types.core.CurrentClampStimulusSeries(...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint16(16), ...
'stimulus_description', 'N/A' ...
);
nwbfile.stimulus_presentation.set('ccss', ccss);
 
% Create a CurrentClampSeries object
ccs = types.core.CurrentClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'bias_current', 1e-12, ...
'bridge_balance', 70e6, ...
'capacitance_compensation', 1e-12, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint16(16) ...
);
nwbfile.acquisition.set('ccs', ccs);
 
IZeroClampSeries is used when the current is clamped to 0.
% Create an IZeroClampSeries object
izcs = types.core.IZeroClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 345.6, ...
'starting_time_rate', 20e3, ...
'sweep_number', uint16(17) ...
);
nwbfile.acquisition.set('izcs', izcs);

Adding an intracellular recording

The IntracellularRecordingsTable relates electrode, stimulus and response pairs and describes metadata specific to individual recordings.
Illustration of the structure of the IntracellularRecordingsTable
ic_rec_table = types.core.IntracellularRecordingsTable( ...
'categories', {'electrodes', 'stimuli', 'responses'}, ...
'colnames', {'recordings_tag'}, ...
'description', [ ...
'A table to group together a stimulus and response from a single ', ...
'electrode and a single simultaneous recording and for storing ', ...
'metadata about the intracellular recording.'], ...
'id', types.hdmf_common.ElementIdentifiers('data', int64([0, 1, 2])), ...
'recordings_tag', types.hdmf_common.VectorData( ...
'data', repmat({'Tag'}, 3, 1), ...
'description', 'Column for storing a custom recordings tag' ...
) ...
);
 
ic_rec_table.electrodes = types.core.IntracellularElectrodesTable( ...
'description', 'Table for storing intracellular electrode related metadata.', ...
'colnames', {'electrode'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'electrode', types.hdmf_common.VectorData( ...
'data', repmat(types.untyped.ObjectView(electrode), 3, 1), ...
'description', 'Column for storing the reference to the intracellular electrode' ...
) ...
);
 
ic_rec_table.stimuli = types.core.IntracellularStimuliTable( ...
'description', 'Table for storing intracellular stimulus related metadata.', ...
'colnames', {'stimulus'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'stimulus', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded stimulus for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 1, -1], ...
'count', [5, 3, -1], ...
'timeseries', [ ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
ic_rec_table.responses = types.core.IntracellularResponsesTable( ...
'description', 'Table for storing intracellular response related metadata.', ...
'colnames', {'response'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'response', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded response for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 2, 0], ...
'count', [5, 3, 5], ...
'timeseries', [ ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
The IntracellularRecordingsTable table is not just a DynamicTable but an AlignedDynamicTable. The AlignedDynamicTable type is itself a DynamicTable that may contain an arbitrary number of additional DynamicTable, each of which defines a "category." This is similar to a table with “sub-headings”. In the case of the IntracellularRecordingsTable, we have three predefined categories, i.e., electrodes, stimuli, and responses. We can also dynamically add new categories to the table. As each category corresponds to a DynamicTable, this means we have to create a new DynamicTable and add it to our table.
% add category
ic_rec_table.categories = [ic_rec_table.categories, {'recording_lab_data'}];
ic_rec_table.dynamictable.set( ...
'recording_lab_data', types.hdmf_common.DynamicTable( ...
'description', 'category table for lab-specific recording metadata', ...
'colnames', {'location'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'location', types.hdmf_common.VectorData( ...
'data', {'Mordor', 'Gondor', 'Rohan'}, ...
'description', 'Recording location in Middle Earth' ...
) ...
) ...
);
In an AlignedDynamicTable all category tables must align with the main table, i.e., all tables must have the same number of rows and rows are expected to correspond to each other by index.
We can also add custom columns to any of the subcategory tables, i.e., the electrodes, stimuli, and responses tables, and any custom subcategory tables. All we need to do is indicate the name of the category we want to add the column to.
% Add voltage threshold as column of electrodes table
ic_rec_table.electrodes.colnames = [ic_rec_table.electrodes.colnames {'voltage_threshold'}];
ic_rec_table.electrodes.vectordata.set('voltage_threshold', types.hdmf_common.VectorData( ...
'data', [0.1, 0.12, 0.13], ...
'description', 'Just an example column on the electrodes category table' ...
) ...
);
 
nwbfile.general_intracellular_ephys_intracellular_recordings = ic_rec_table;

Hierarchical organization of recordings

To describe the organization of intracellular experiments, the metadata is organized hierarchically in a sequence of tables. All of the tables are so-called DynamicTables enabling users to add columns for custom metadata. Storing data in hierarchical tables has the advantage that it allows us to avoid duplication of metadata. E.g., for a single experiment we only need to describe the metadata that is constant across an experimental condition as a single row in the SimultaneousRecordingsTable without having to replicate the same information across all repetitions and sequential-, simultaneous-, and individual intracellular recordings. For analysis, this means that we can easily focus on individual aspects of an experiment while still being able to easily access information about information from related tables. All of these tables are optional, but to use one you must use all of the lower level tables, even if you only need a single row.

Add a simultaneous recording

The SimultaneousRecordingsTable groups intracellular recordings from the IntracellularRecordingsTable together that were recorded simultaneously from different electrodes and/or cells and describes metadata that is constant across the simultaneous recordings. In practice a simultaneous recording is often also referred to as a sweep. This example adds a custom column, "simultaneous_recording_tag."
% create simultaneous recordings table with custom column
% 'simultaneous_recording_tag'
 
[recordings_vector_data, recordings_vector_index] = util.create_indexed_column( ...
{[0, 1, 2],}, ...
'Column with references to one or more rows in the IntracellularRecordingsTable table', ...
ic_rec_table);
 
ic_sim_recs_table = types.core.SimultaneousRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recordings from ', ...
'the IntracellularRecordingsTable table together that were recorded ', ...
'simultaneously from different electrodes.'...
], ...
'colnames', {'recordings', 'simultaneous_recording_tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(12) ...
), ...
'recordings', recordings_vector_data, ...
'recordings_index', recordings_vector_index, ...
'simultaneous_recording_tag', types.hdmf_common.VectorData( ...
'description', 'A custom tag for simultaneous_recordings', ...
'data', {'LabTag1'} ...
) ...
);
 
Depending on the lab workflow, it may be useful to add complete columns to a table after we have already populated the table with rows. That would be done like so:
ic_sim_recs_table.colnames = [ic_sim_recs_table.colnames, {'simultaneous_recording_type'}];
ic_sim_recs_table.vectordata.set( ...
'simultaneous_recording_type', types.hdmf_common.VectorData(...
'description', 'Description of the type of simultaneous_recording', ...
'data', {'SimultaneousRecordingType1'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_simultaneous_recordings = ic_sim_recs_table;

Add a sequential recording

The SequentialRecordingsTable groups simultaneously recorded intracellular recordings from the SimultaneousRecordingsTable together and describes metadata that is constant across the simultaneous recordings. In practice a sequential recording is often also referred to as a sweep sequence. A common use of sequential recordings is to group together simultaneous recordings where a sequence of stimuli of the same type with varying parameters have been presented in a sequence (e.g., a sequence of square waveforms with varying amplitude).
[simultaneous_recordings_vector_data, simultaneous_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SimultaneousRecordingsTable table', ...
ic_sim_recs_table);
 
sequential_recordings = types.core.SequentialRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'simultaneous_recordings from the SimultaneousRecordingsTable ', ...
'table together. This is typically used to group together ', ...
'simultaneous_recordings where the a sequence of stimuli of ', ...
'the same type with varying parameters have been presented in ', ...
'a sequence.' ...
], ...
'colnames', {'simultaneous_recordings', 'stimulus_type'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(15) ...
), ...
'simultaneous_recordings', simultaneous_recordings_vector_data, ...
'simultaneous_recordings_index', simultaneous_recordings_vector_index, ...
'stimulus_type', types.hdmf_common.VectorData( ...
'description', 'Column storing the type of stimulus used for the sequential recording', ...
'data', {'square'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_sequential_recordings = sequential_recordings;

Add repetitions table

The RepetitionsTable groups sequential recordings from the SequentialRecordingsTable. In practice, a repetition is often also referred to a run. A typical use of the RepetitionsTable is to group sets of different stimuli that are applied in sequence that may be repeated.
[sequential_recordings_vector_data, sequential_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SequentialRecordingsTable table', ...
sequential_recordings);
 
 
nwbfile.general_intracellular_ephys_repetitions = types.core.RepetitionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording sequential ', ...
'recordings together. With each SimultaneousRecording typically ', ...
'representing a particular type of stimulus, the RepetitionsTable ', ...
'table is typically used to group sets of stimuli applied in sequence.' ...
], ...
'colnames', {'sequential_recordings'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(17) ...
), ...
'sequential_recordings', sequential_recordings_vector_data, ...
'sequential_recordings_index', sequential_recordings_vector_index ...
);

Add experimental condition table

The ExperimentalConditionsTable groups repetitions of intracellular recording from the RepetitionsTable together that belong to the same experimental conditions.
[repetitions_vector_data, repetitions_vector_index] = util.create_indexed_column( ...
{0, 0}, ...
'Column with references to one or more rows in the RepetitionsTable table', ...
nwbfile.general_intracellular_ephys_repetitions);
 
nwbfile.general_intracellular_ephys_experimental_conditions = types.core.ExperimentalConditionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'repetitions together that belong to the same experimental ', ...
'conditions.' ...
], ...
'colnames', {'repetitions', 'tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([19, 21]) ...
), ...
'repetitions', repetitions_vector_data, ...
'repetitions_index', repetitions_vector_index, ...
'tag', types.hdmf_common.VectorData( ...
'description', 'integer tag for a experimental condition', ...
'data', [1,3] ...
) ...
);

Write the NWB file

nwbExport(nwbfile, 'test_new_icephys.nwb');

Read the NWB file

nwbfile2 = nwbRead('test_new_icephys.nwb', 'ignorecache')
nwbfile2 =
NwbFile with properties: +Read the NWB file
The following tutorial describes storage of intracellular electrophysiology data in NWB. NWB supports storage of the time series describing the stimulus and response, information about the electrode and device used, as well as metadata about the organization of the experiment.
Illustration of the hierarchy of metadata tables used to describe the organization of intracellular electrophysiology experiments.

Creating an NWBFile

When creating an NWB file, the first step is to create the NWBFile, which you can create using the NwbFile command.
session_start_time = datetime(2018, 3, 1, 12, 0, 0, 'TimeZone', 'local');
 
 
nwbfile = NwbFile( ...
'session_description', 'my first synthetic recording', ...
'identifier', 'EXAMPLE_ID', ...
'session_start_time', session_start_time, ...
'general_experimenter', 'Dr. Bilbo Baggins', ...
'general_lab', 'Bag End Laboratory', ...
'general_institution', 'University of Middle Earth at the Shire', ...
'general_experiment_description', 'I went on an adventure with thirteen dwarves to reclaim vast treasures.', ...
'general_session_id', 'LONELYMTN' ...
);
 

Device metadata

Device metadata is represented by Device objects.
 
device = types.core.Device();
nwbfile.general_devices.set('Heka ITC-1600', device);

Electrode metadata

Intracellular electrode metadata is represented by IntracellularElectrode objects. Create an electrode object, which requires a link to the device of the previous step. Then add it to the NWB file.
electrode = types.core.IntracellularElectrode( ...
'description', 'a mock intracellular electrode', ...
'device', types.untyped.SoftLink(device), ...
'cell_id', 'a very interesting cell' ...
);
nwbfile.general_intracellular_ephys.set('elec0', electrode);

Stimulus and response data

Intracellular stimulus and response data are represented with subclasses of PatchClampSeries. A stimulus is described by a time series representing voltage or current stimulation with a particular set of parameters. There are two classes for representing stimulus data:
The response is then described by a time series representing voltage or current recorded from a single cell using a single intracellular electrode via one of the following classes:
Below we create a simple example stimulus/response recording data pair for a voltage clamp recording.
ccss = types.core.VoltageClampStimulusSeries( ...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint64(15), ...
'stimulus_description', 'N/A' ...
);
nwbfile.stimulus_presentation.set('ccss', ccss);
 
vcs = types.core.VoltageClampSeries( ...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'capacitance_slow', 100e-12, ...
'resistance_comp_correction', 70.0, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint64(15) ...
);
nwbfile.acquisition.set('vcs', vcs);
You can add stimulus/response recording data pair from a current clamp recording in the same way:
% Create a CurrentClampStimulusSeries object
ccss = types.core.CurrentClampStimulusSeries(...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint16(16), ...
'stimulus_description', 'N/A' ...
);
nwbfile.stimulus_presentation.set('ccss', ccss);
 
% Create a CurrentClampSeries object
ccs = types.core.CurrentClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'bias_current', 1e-12, ...
'bridge_balance', 70e6, ...
'capacitance_compensation', 1e-12, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint16(16) ...
);
nwbfile.acquisition.set('ccs', ccs);
 
IZeroClampSeries is used when the current is clamped to 0.
% Create an IZeroClampSeries object
izcs = types.core.IZeroClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 345.6, ...
'starting_time_rate', 20e3, ...
'sweep_number', uint16(17) ...
);
nwbfile.acquisition.set('izcs', izcs);

Adding an intracellular recording

The IntracellularRecordingsTable relates electrode, stimulus and response pairs and describes metadata specific to individual recordings.
Illustration of the structure of the IntracellularRecordingsTable
ic_rec_table = types.core.IntracellularRecordingsTable( ...
'categories', {'electrodes', 'stimuli', 'responses'}, ...
'colnames', {'recordings_tag'}, ...
'description', [ ...
'A table to group together a stimulus and response from a single ', ...
'electrode and a single simultaneous recording and for storing ', ...
'metadata about the intracellular recording.'], ...
'id', types.hdmf_common.ElementIdentifiers('data', int64([0, 1, 2])), ...
'recordings_tag', types.hdmf_common.VectorData( ...
'data', repmat({'Tag'}, 3, 1), ...
'description', 'Column for storing a custom recordings tag' ...
) ...
);
 
ic_rec_table.electrodes = types.core.IntracellularElectrodesTable( ...
'description', 'Table for storing intracellular electrode related metadata.', ...
'colnames', {'electrode'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'electrode', types.hdmf_common.VectorData( ...
'data', repmat(types.untyped.ObjectView(electrode), 3, 1), ...
'description', 'Column for storing the reference to the intracellular electrode' ...
) ...
);
 
ic_rec_table.stimuli = types.core.IntracellularStimuliTable( ...
'description', 'Table for storing intracellular stimulus related metadata.', ...
'colnames', {'stimulus'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'stimulus', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded stimulus for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 1, -1], ...
'count', [5, 3, -1], ...
'timeseries', [ ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
ic_rec_table.responses = types.core.IntracellularResponsesTable( ...
'description', 'Table for storing intracellular response related metadata.', ...
'colnames', {'response'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'response', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded response for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 2, 0], ...
'count', [5, 3, 5], ...
'timeseries', [ ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
The IntracellularRecordingsTable table is not just a DynamicTable but an AlignedDynamicTable. The AlignedDynamicTable type is itself a DynamicTable that may contain an arbitrary number of additional DynamicTable, each of which defines a "category." This is similar to a table with “sub-headings”. In the case of the IntracellularRecordingsTable, we have three predefined categories, i.e., electrodes, stimuli, and responses. We can also dynamically add new categories to the table. As each category corresponds to a DynamicTable, this means we have to create a new DynamicTable and add it to our table.
% add category
ic_rec_table.categories = [ic_rec_table.categories, {'recording_lab_data'}];
ic_rec_table.dynamictable.set( ...
'recording_lab_data', types.hdmf_common.DynamicTable( ...
'description', 'category table for lab-specific recording metadata', ...
'colnames', {'location'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'location', types.hdmf_common.VectorData( ...
'data', {'Mordor', 'Gondor', 'Rohan'}, ...
'description', 'Recording location in Middle Earth' ...
) ...
) ...
);
In an AlignedDynamicTable all category tables must align with the main table, i.e., all tables must have the same number of rows and rows are expected to correspond to each other by index.
We can also add custom columns to any of the subcategory tables, i.e., the electrodes, stimuli, and responses tables, and any custom subcategory tables. All we need to do is indicate the name of the category we want to add the column to.
% Add voltage threshold as column of electrodes table
ic_rec_table.electrodes.colnames = [ic_rec_table.electrodes.colnames {'voltage_threshold'}];
ic_rec_table.electrodes.vectordata.set('voltage_threshold', types.hdmf_common.VectorData( ...
'data', [0.1, 0.12, 0.13], ...
'description', 'Just an example column on the electrodes category table' ...
) ...
);
 
nwbfile.general_intracellular_ephys_intracellular_recordings = ic_rec_table;

Hierarchical organization of recordings

To describe the organization of intracellular experiments, the metadata is organized hierarchically in a sequence of tables. All of the tables are so-called DynamicTables enabling users to add columns for custom metadata. Storing data in hierarchical tables has the advantage that it allows us to avoid duplication of metadata. E.g., for a single experiment we only need to describe the metadata that is constant across an experimental condition as a single row in the SimultaneousRecordingsTable without having to replicate the same information across all repetitions and sequential-, simultaneous-, and individual intracellular recordings. For analysis, this means that we can easily focus on individual aspects of an experiment while still being able to easily access information about information from related tables. All of these tables are optional, but to use one you must use all of the lower level tables, even if you only need a single row.

Add a simultaneous recording

The SimultaneousRecordingsTable groups intracellular recordings from the IntracellularRecordingsTable together that were recorded simultaneously from different electrodes and/or cells and describes metadata that is constant across the simultaneous recordings. In practice a simultaneous recording is often also referred to as a sweep. This example adds a custom column, "simultaneous_recording_tag."
% create simultaneous recordings table with custom column
% 'simultaneous_recording_tag'
 
[recordings_vector_data, recordings_vector_index] = util.create_indexed_column( ...
{[0, 1, 2],}, ...
'Column with references to one or more rows in the IntracellularRecordingsTable table', ...
ic_rec_table);
 
ic_sim_recs_table = types.core.SimultaneousRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recordings from ', ...
'the IntracellularRecordingsTable table together that were recorded ', ...
'simultaneously from different electrodes.'...
], ...
'colnames', {'recordings', 'simultaneous_recording_tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(12) ...
), ...
'recordings', recordings_vector_data, ...
'recordings_index', recordings_vector_index, ...
'simultaneous_recording_tag', types.hdmf_common.VectorData( ...
'description', 'A custom tag for simultaneous_recordings', ...
'data', {'LabTag1'} ...
) ...
);
 
Depending on the lab workflow, it may be useful to add complete columns to a table after we have already populated the table with rows. That would be done like so:
ic_sim_recs_table.colnames = [ic_sim_recs_table.colnames, {'simultaneous_recording_type'}];
ic_sim_recs_table.vectordata.set( ...
'simultaneous_recording_type', types.hdmf_common.VectorData(...
'description', 'Description of the type of simultaneous_recording', ...
'data', {'SimultaneousRecordingType1'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_simultaneous_recordings = ic_sim_recs_table;

Add a sequential recording

The SequentialRecordingsTable groups simultaneously recorded intracellular recordings from the SimultaneousRecordingsTable together and describes metadata that is constant across the simultaneous recordings. In practice a sequential recording is often also referred to as a sweep sequence. A common use of sequential recordings is to group together simultaneous recordings where a sequence of stimuli of the same type with varying parameters have been presented in a sequence (e.g., a sequence of square waveforms with varying amplitude).
[simultaneous_recordings_vector_data, simultaneous_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SimultaneousRecordingsTable table', ...
ic_sim_recs_table);
 
sequential_recordings = types.core.SequentialRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'simultaneous_recordings from the SimultaneousRecordingsTable ', ...
'table together. This is typically used to group together ', ...
'simultaneous_recordings where the a sequence of stimuli of ', ...
'the same type with varying parameters have been presented in ', ...
'a sequence.' ...
], ...
'colnames', {'simultaneous_recordings', 'stimulus_type'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(15) ...
), ...
'simultaneous_recordings', simultaneous_recordings_vector_data, ...
'simultaneous_recordings_index', simultaneous_recordings_vector_index, ...
'stimulus_type', types.hdmf_common.VectorData( ...
'description', 'Column storing the type of stimulus used for the sequential recording', ...
'data', {'square'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_sequential_recordings = sequential_recordings;

Add repetitions table

The RepetitionsTable groups sequential recordings from the SequentialRecordingsTable. In practice, a repetition is often also referred to a run. A typical use of the RepetitionsTable is to group sets of different stimuli that are applied in sequence that may be repeated.
[sequential_recordings_vector_data, sequential_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SequentialRecordingsTable table', ...
sequential_recordings);
 
 
nwbfile.general_intracellular_ephys_repetitions = types.core.RepetitionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording sequential ', ...
'recordings together. With each SimultaneousRecording typically ', ...
'representing a particular type of stimulus, the RepetitionsTable ', ...
'table is typically used to group sets of stimuli applied in sequence.' ...
], ...
'colnames', {'sequential_recordings'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(17) ...
), ...
'sequential_recordings', sequential_recordings_vector_data, ...
'sequential_recordings_index', sequential_recordings_vector_index ...
);

Add experimental condition table

The ExperimentalConditionsTable groups repetitions of intracellular recording from the RepetitionsTable together that belong to the same experimental conditions.
[repetitions_vector_data, repetitions_vector_index] = util.create_indexed_column( ...
{0, 0}, ...
'Column with references to one or more rows in the RepetitionsTable table', ...
nwbfile.general_intracellular_ephys_repetitions);
 
nwbfile.general_intracellular_ephys_experimental_conditions = types.core.ExperimentalConditionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'repetitions together that belong to the same experimental ', ...
'conditions.' ...
], ...
'colnames', {'repetitions', 'tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([19, 21]) ...
), ...
'repetitions', repetitions_vector_data, ...
'repetitions_index', repetitions_vector_index, ...
'tag', types.hdmf_common.VectorData( ...
'description', 'integer tag for a experimental condition', ...
'data', [1,3] ...
) ...
);

Write the NWB file

nwbExport(nwbfile, 'test_new_icephys.nwb');

Read the NWB file

nwbfile2 = nwbRead('test_new_icephys.nwb', 'ignorecache')
nwbfile2 =
NwbFile with properties: nwb_version: '2.8.0' file_create_date: [1×1 types.untyped.DataStub] @@ -139,7 +139,7 @@ ); % Device metadata -% Device metadata is represented by objects. diff --git a/docs/source/_static/html/tutorials/intro.html b/docs/source/_static/html/tutorials/intro.html index 5b5fd9c6..f468010b 100644 --- a/docs/source/_static/html/tutorials/intro.html +++ b/docs/source/_static/html/tutorials/intro.html @@ -85,7 +85,7 @@ Write Read Reading Data -Next Steps

Installing MatNWB

Use the code below within the brackets to install MatNWB from source. MatNWB works by automatically creating API classes based on the schema.
%{
!git clone https://github.com/NeurodataWithoutBorders/matnwb.git
addpath(genpath(pwd));
%}

Set up the NWB File

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata using the NwbFile command. For all MatNWB classes and functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value. Ellipses are used for clarity.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'Last, First', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: +Next Steps

Installing MatNWB

Use the code below within the brackets to install MatNWB from source. MatNWB works by automatically creating API classes based on the schema.
%{
!git clone https://github.com/NeurodataWithoutBorders/matnwb.git
addpath(genpath(pwd));
%}

Set up the NWB File

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata using the NwbFile command. For all MatNWB classes and functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value. Ellipses are used for clarity.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'general_experimenter', 'Last, First', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: nwb_version: '2.8.0' file_create_date: [] @@ -139,7 +139,7 @@ units: [] Warning: The following required properties are missing for instance for type "NwbFile": - timestamps_reference_time

Subject Information

You can also provide information about your subject in the NWB file. Create a Subject object to store information such as age, species, genotype, sex, and a freeform description. Then set nwb.general_subject to the Subject object.
Each of these fields is free-form, so any values will be valid, but here are our recommendations:
  • For age, we recommend using the ISO 8601 Duration format
  • For species, we recommend using the formal latin binomal name (e.g. mouse -> Mus musculus, human -> Homo sapiens)
  • For sex, we recommend using F (female), M (male), U (unknown), and O (other)
subject = types.core.Subject( ...
'subject_id', '001', ...
'age', 'P90D', ...
'description', 'mouse 5', ...
'species', 'Mus musculus', ...
'sex', 'M' ...
);
nwb.general_subject = subject;
 
subject
subject =
Subject with properties: + timestamps_reference_time

Subject Information

You can also provide information about your subject in the NWB file. Create a Subject object to store information such as age, species, genotype, sex, and a freeform description. Then set nwb.general_subject to the Subject object.
Each of these fields is free-form, so any values will be valid, but here are our recommendations:
  • For age, we recommend using the ISO 8601 Duration format
  • For species, we recommend using the formal latin binomal name (e.g. mouse -> Mus musculus, human -> Homo sapiens)
  • For sex, we recommend using F (female), M (male), U (unknown), and O (other)
subject = types.core.Subject( ...
'subject_id', '001', ...
'age', 'P90D', ...
'description', 'mouse 5', ...
'species', 'Mus musculus', ...
'sex', 'M' ...
);
nwb.general_subject = subject;
 
subject
subject =
Subject with properties: age: 'P90D' age_reference: 'birth' @@ -151,10 +151,10 @@ strain: '' subject_id: '001' weight: '' -
Note: the DANDI archive requires all NWB files to have a subject object with subject_id specified, and strongly encourages specifying the other fields.

Time Series Data

TimeSeries is a common base class for measurements sampled over time, and provides fields for data and timestamps (regularly or irregularly sampled). You will also need to supply the name and unit of measurement (SI unit).
For instance, we can store a TimeSeries data where recording started 0.0 seconds after start_time and sampled every second (1 Hz):
time_series_with_rate = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0);
For irregularly sampled recordings, we need to provide the timestamps for the data:
time_series_with_timestamps = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'timestamps', linspace(0, 1, 10));
The TimeSeries class serves as the foundation for all other time series types in the NWB format. Several specialized subclasses extend the functionality of TimeSeries, each tailored to handle specific kinds of data. In the next section, we’ll explore one of these specialized types. For a full overview, please check out the type hierarchy in the NWB schema documentation.

Other Types of Time Series

As mentioned previously, there are many subtypes of TimeSeries in MatNWB that are used to store different kinds of data. One example is AnnotationSeries, a subclass of TimeSeries that stores text-based records about the experiment. Similar to our TimeSeries example above, we can create an AnnotationSeries object with text information about a stimulus and add it to the stimulus_presentation group in the NWBFile. Below is an example where we create an AnnotationSeries object with annotations for airpuff stimuli and add it to the NWBFile.
% Create an AnnotationSeries object with annotations for airpuff stimuli
annotations = types.core.AnnotationSeries( ...
'description', 'Airpuff events delivered to the animal', ...
'data', {'Left Airpuff', 'Right Airpuff', 'Right Airpuff'}, ...
'timestamps', [1.0, 3.0, 8.0] ...
);
 
% Add the AnnotationSeries to the NWBFile's stimulus group
nwb.stimulus_presentation.set('Airpuffs', annotations)
ans =
Set with properties: +
Note: the DANDI archive requires all NWB files to have a subject object with subject_id specified, and strongly encourages specifying the other fields.

Time Series Data

TimeSeries is a common base class for measurements sampled over time, and provides fields for data and timestamps (regularly or irregularly sampled). You will also need to supply the name and unit of measurement (SI unit).
For instance, we can store a TimeSeries data where recording started 0.0 seconds after start_time and sampled every second (1 Hz):
time_series_with_rate = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0);
For irregularly sampled recordings, we need to provide the timestamps for the data:
time_series_with_timestamps = types.core.TimeSeries( ...
'description', 'an example time series', ...
'data', linspace(0, 100, 10), ...
'data_unit', 'm', ...
'timestamps', linspace(0, 1, 10));
The TimeSeries class serves as the foundation for all other time series types in the NWB format. Several specialized subclasses extend the functionality of TimeSeries, each tailored to handle specific kinds of data. In the next section, we’ll explore one of these specialized types. For a full overview, please check out the type hierarchy in the NWB schema documentation.

Other Types of Time Series

As mentioned previously, there are many subtypes of TimeSeries in MatNWB that are used to store different kinds of data. One example is AnnotationSeries, a subclass of TimeSeries that stores text-based records about the experiment. Similar to our TimeSeries example above, we can create an AnnotationSeries object with text information about a stimulus and add it to the stimulus_presentation group in the NWBFile. Below is an example where we create an AnnotationSeries object with annotations for airpuff stimuli and add it to the NWBFile.
% Create an AnnotationSeries object with annotations for airpuff stimuli
annotations = types.core.AnnotationSeries( ...
'description', 'Airpuff events delivered to the animal', ...
'data', {'Left Airpuff', 'Right Airpuff', 'Right Airpuff'}, ...
'timestamps', [1.0, 3.0, 8.0] ...
);
 
% Add the AnnotationSeries to the NWBFile's stimulus group
nwb.stimulus_presentation.set('Airpuffs', annotations)
ans =
Set with properties: Airpuffs: [types.core.AnnotationSeries] -

Behavior

SpatialSeries and Position

Many types of data have special data types in NWB. To store the spatial position of a subject, we will use the SpatialSeries and Position classes.
Note: These diagrams follow a standard convention called "UML class diagram" to express the object-oriented relationships between NWB classes. For our purposes, all you need to know is that an open triangle means "extends" (i.e., is a specialized subtype of), and an open diamond means "is contained within." Learn more about class diagrams on the wikipedia page.
SpatialSeries is a subclass of TimeSeries, a common base class for measurements sampled over time, and provides fields for data and time (regularly or irregularly sampled). Here, we put a SpatialSeries object called 'SpatialSeries' in a Position object. If the data is sampled at a regular interval, it is recommended to specify the starting_time and the sampling rate (starting_time_rate), although it is still possible to specify timestamps as in the time_series_with_timestamps example above.
% create SpatialSeries object
spatial_series_ts = types.core.SpatialSeries( ...
'data', [linspace(0,10,100); linspace(0,8,100)], ...
'reference_frame', '(0,0) is bottom left corner', ...
'starting_time', 0, ...
'starting_time_rate', 200 ...
);
 
% create Position object and add SpatialSeries
position = types.core.Position('SpatialSeries', spatial_series_ts);
NWB differentiates between raw, acquired data, which should never change, and processed data, which are the results of preprocessing algorithms and could change. Let's assume that the animal's position was computed from a video tracking algorithm, so it would be classified as processed data. Since processed data can be very diverse, NWB allows us to create processing modules, which are like folders, to store related processed data or data that comes from a single algorithm.
Create a processing module called "behavior" for storing behavioral data in the NWBFile and add the Position object to the module.
% create processing module
behavior_module = types.core.ProcessingModule('description', 'contains behavioral data');
 
% add the Position object (that holds the SpatialSeries object) to the module
% and name the Position object "Position"
behavior_module.nwbdatainterface.set('Position', position);
 
% add the processing module to the NWBFile object, and name the processing module "behavior"
nwb.processing.set('behavior', behavior_module);

Trials

Trials are stored in a TimeIntervals object which is a subclass of DynamicTable. DynamicTable objects are used to store tabular metadata throughout NWB, including for trials, electrodes, and sorted units. They offer flexibility for tabular data by allowing required columns, optional columns, and custom columns.
The trials DynamicTable can be thought of as a table with this structure:
Trials are stored in a TimeIntervals object which subclasses DynamicTable. Here, we are adding 'correct', which will be a logical array.
trials = types.core.TimeIntervals( ...
'colnames', {'start_time', 'stop_time', 'correct'}, ...
'description', 'trial data and properties');
 
trials.addRow('start_time', 0.1, 'stop_time', 1.0, 'correct', false)
trials.addRow('start_time', 1.5, 'stop_time', 2.0, 'correct', true)
trials.addRow('start_time', 2.5, 'stop_time', 3.0, 'correct', false)
 
trials.toTable() % visualize the table
ans = 3×4 table
 idstart_timestop_timecorrect
100.100010
211.500021
322.500030
nwb.intervals_trials = trials;
 
% If you have multiple trials tables, you will need to use custom names for
% each one:
nwb.intervals.set('custom_intervals_table_name', trials);

Write

Now, to write the NWB file that we have built so far:
nwbExport(nwb, 'intro_tutorial.nwb')
We can use the HDFView application to inspect the resulting NWB file.

Read

We can then read the file back in using MatNWB and inspect its contents.
read_nwbfile = nwbRead('intro_tutorial.nwb', 'ignorecache')
read_nwbfile =
NwbFile with properties: +

Behavior

SpatialSeries and Position

Many types of data have special data types in NWB. To store the spatial position of a subject, we will use the SpatialSeries and Position classes.
Note: These diagrams follow a standard convention called "UML class diagram" to express the object-oriented relationships between NWB classes. For our purposes, all you need to know is that an open triangle means "extends" (i.e., is a specialized subtype of), and an open diamond means "is contained within." Learn more about class diagrams on the wikipedia page.
SpatialSeries is a subclass of TimeSeries, a common base class for measurements sampled over time, and provides fields for data and time (regularly or irregularly sampled). Here, we put a SpatialSeries object called 'SpatialSeries' in a Position object. If the data is sampled at a regular interval, it is recommended to specify the starting_time and the sampling rate (starting_time_rate), although it is still possible to specify timestamps as in the time_series_with_timestamps example above.
% create SpatialSeries object
spatial_series_ts = types.core.SpatialSeries( ...
'data', [linspace(0,10,100); linspace(0,8,100)], ...
'reference_frame', '(0,0) is bottom left corner', ...
'starting_time', 0, ...
'starting_time_rate', 200 ...
);
 
% create Position object and add SpatialSeries
position = types.core.Position('SpatialSeries', spatial_series_ts);
NWB differentiates between raw, acquired data, which should never change, and processed data, which are the results of preprocessing algorithms and could change. Let's assume that the animal's position was computed from a video tracking algorithm, so it would be classified as processed data. Since processed data can be very diverse, NWB allows us to create processing modules, which are like folders, to store related processed data or data that comes from a single algorithm.
Create a processing module called "behavior" for storing behavioral data in the NWBFile and add the Position object to the module.
% create processing module
behavior_module = types.core.ProcessingModule('description', 'contains behavioral data');
 
% add the Position object (that holds the SpatialSeries object) to the module
% and name the Position object "Position"
behavior_module.nwbdatainterface.set('Position', position);
 
% add the processing module to the NWBFile object, and name the processing module "behavior"
nwb.processing.set('behavior', behavior_module);

Trials

Trials are stored in a TimeIntervals object which is a subclass of DynamicTable. DynamicTable objects are used to store tabular metadata throughout NWB, including for trials, electrodes, and sorted units. They offer flexibility for tabular data by allowing required columns, optional columns, and custom columns.
The trials DynamicTable can be thought of as a table with this structure:
Trials are stored in a TimeIntervals object which subclasses DynamicTable. Here, we are adding 'correct', which will be a logical array.
trials = types.core.TimeIntervals( ...
'colnames', {'start_time', 'stop_time', 'correct'}, ...
'description', 'trial data and properties');
 
trials.addRow('start_time', 0.1, 'stop_time', 1.0, 'correct', false)
trials.addRow('start_time', 1.5, 'stop_time', 2.0, 'correct', true)
trials.addRow('start_time', 2.5, 'stop_time', 3.0, 'correct', false)
 
trials.toTable() % visualize the table
ans = 3×4 table
 idstart_timestop_timecorrect
100.100010
211.500021
322.500030
nwb.intervals_trials = trials;
 
% If you have multiple trials tables, you will need to use custom names for
% each one:
nwb.intervals.set('custom_intervals_table_name', trials);

Write

Now, to write the NWB file that we have built so far:
nwbExport(nwb, 'intro_tutorial.nwb')
We can use the HDFView application to inspect the resulting NWB file.

Read

We can then read the file back in using MatNWB and inspect its contents.
read_nwbfile = nwbRead('intro_tutorial.nwb', 'ignorecache')
read_nwbfile =
NwbFile with properties: nwb_version: '2.8.0' file_create_date: [1×1 types.untyped.DataStub] @@ -206,7 +206,7 @@ stimulus_presentation: [1×1 types.untyped.Set] stimulus_templates: [0×1 types.untyped.Set] units: [] -
We can print the SpatialSeries data traversing the hierarchy of objects. The processing module called 'behavior' contains our Position object named 'Position'. The Position object contains our SpatialSeries object named 'SpatialSeries'.
read_spatial_series = read_nwbfile.processing.get('behavior'). ...
nwbdatainterface.get('Position').spatialseries.get('SpatialSeries')
read_spatial_series =
SpatialSeries with properties: +
We can print the SpatialSeries data traversing the hierarchy of objects. The processing module called 'behavior' contains our Position object named 'Position'. The Position object contains our SpatialSeries object named 'SpatialSeries'.
read_spatial_series = read_nwbfile.processing.get('behavior'). ...
nwbdatainterface.get('Position').spatialseries.get('SpatialSeries')
read_spatial_series =
SpatialSeries with properties: reference_frame: '(0,0) is bottom left corner' starting_time_unit: 'seconds' @@ -225,16 +225,16 @@ starting_time: 0 starting_time_rate: 200 timestamps: [] -

Reading Data

Counter to normal MATLAB workflow, data arrays are read passively from the file. Calling read_spatial_series.data does not read the data values, but presents a DataStub object that can be indexed to read data.
read_spatial_series.data
ans =
DataStub with properties: +

Reading Data

Counter to normal MATLAB workflow, data arrays are read passively from the file. Calling read_spatial_series.data does not read the data values, but presents a DataStub object that can be indexed to read data.
read_spatial_series.data
ans =
DataStub with properties: filename: 'intro_tutorial.nwb' path: '/processing/behavior/Position/SpatialSeries/data' dims: [2 100] ndims: 2 dataType: 'double' -
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access all the data in the matrix using the load method with no arguments.
read_spatial_series.data.load
ans = 2×100
0 0.1010 0.2020 0.3030 0.4040 0.5051 0.6061 0.7071 0.8081 0.9091 1.0101 1.1111 1.2121 1.3131 1.4141 1.5152 1.6162 1.7172 1.8182 1.9192 2.0202 2.1212 2.2222 2.3232 2.4242 2.5253 2.6263 2.7273 2.8283 2.9293 3.0303 3.1313 3.2323 3.3333 3.4343 3.5354 3.6364 3.7374 3.8384 3.9394 4.0404 4.1414 4.2424 4.3434 4.4444 4.5455 4.6465 4.7475 4.8485 4.9495 +
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access all the data in the matrix using the load method with no arguments.
read_spatial_series.data.load
ans = 2×100
0 0.1010 0.2020 0.3030 0.4040 0.5051 0.6061 0.7071 0.8081 0.9091 1.0101 1.1111 1.2121 1.3131 1.4141 1.5152 1.6162 1.7172 1.8182 1.9192 2.0202 2.1212 2.2222 2.3232 2.4242 2.5253 2.6263 2.7273 2.8283 2.9293 3.0303 3.1313 3.2323 3.3333 3.4343 3.5354 3.6364 3.7374 3.8384 3.9394 4.0404 4.1414 4.2424 4.3434 4.4444 4.5455 4.6465 4.7475 4.8485 4.9495 0 0.0808 0.1616 0.2424 0.3232 0.4040 0.4848 0.5657 0.6465 0.7273 0.8081 0.8889 0.9697 1.0505 1.1313 1.2121 1.2929 1.3737 1.4545 1.5354 1.6162 1.6970 1.7778 1.8586 1.9394 2.0202 2.1010 2.1818 2.2626 2.3434 2.4242 2.5051 2.5859 2.6667 2.7475 2.8283 2.9091 2.9899 3.0707 3.1515 3.2323 3.3131 3.3939 3.4747 3.5556 3.6364 3.7172 3.7980 3.8788 3.9596 -
If you only need a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_spatial_series.data(:, 1:10)
ans = 2×10
0 0.1010 0.2020 0.3030 0.4040 0.5051 0.6061 0.7071 0.8081 0.9091 +
If you only need a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_spatial_series.data(:, 1:10)
ans = 2×10
0 0.1010 0.2020 0.3030 0.4040 0.5051 0.6061 0.7071 0.8081 0.9091 0 0.0808 0.1616 0.2424 0.3232 0.4040 0.4848 0.5657 0.6465 0.7273

Next Steps

This concludes the introductory tutorial. Please proceed to one of the specialized tutorials, which are designed to follow this one.
See the API documentation to learn what data types are available.

@@ -326,10 +326,10 @@ 'data_unit', 'm', ... 'timestamps', linspace(0, 1, 10)); %% -% The class serves as the foundation for all other time series types % in the NWB format. Several specialized subclasses extend the functionality of -% , each tailored to handle specific kinds of data. In the next % section, we’ll explore one of these specialized types. For a full overview, % please check out the Learn more! See the API documentation to learn what data types are available. Other MatNWB tutorials - Python tutorials

Introduction

In this tutorial, we will create fake data for a hypothetical optical physiology experiment with a freely moving animal. The types of data we will convert are:
  • Acquired two-photon images
  • Image segmentation (ROIs)
  • Fluorescence and dF/F response
It is recommended to first work through the Introduction to MatNWB tutorial, which demonstrates installing MatNWB and creating an NWB file with subject information, animal position, and trials, as well as writing and reading NWB files in MATLAB.
Please note: The dimensions of timeseries data in MatNWB should be defined in the opposite order of how it is defined in the nwb-schemas. In NWB, time is always stored in the first dimension of the data, whereas in MatNWB data should be specified with time along the last dimension. This is explained in more detail here: MatNWB <-> HDF5 Dimension Mapping.

Set up the NWB file

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata. For all MatNWB functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'LastName, FirstName', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: + Python tutorials

Introduction

In this tutorial, we will create fake data for a hypothetical optical physiology experiment with a freely moving animal. The types of data we will convert are:
  • Acquired two-photon images
  • Image segmentation (ROIs)
  • Fluorescence and dF/F response
It is recommended to first work through the Introduction to MatNWB tutorial, which demonstrates installing MatNWB and creating an NWB file with subject information, animal position, and trials, as well as writing and reading NWB files in MATLAB.
Please note: The dimensions of timeseries data in MatNWB should be defined in the opposite order of how it is defined in the nwb-schemas. In NWB, time is always stored in the first dimension of the data, whereas in MatNWB data should be specified with time along the last dimension. This is explained in more detail here: MatNWB <-> HDF5 Dimension Mapping.

Set up the NWB file

An NWB file represents a single session of an experiment. Each file must have a session_description, identifier, and session start time. Create a new NWBFile object with those and additional metadata. For all MatNWB functions, we use the Matlab method of entering keyword argument pairs, where arguments are entered as name followed by value.
nwb = NwbFile( ...
'session_description', 'mouse in open exploration',...
'identifier', 'Mouse5_Day3', ...
'session_start_time', datetime(2018, 4, 25, 2, 30, 3, 'TimeZone', 'local'), ...
'timestamps_reference_time', datetime(2018, 4, 25, 3, 0, 45, 'TimeZone', 'local'), ...
'general_experimenter', 'LastName, FirstName', ... % optional
'general_session_id', 'session_1234', ... % optional
'general_institution', 'University of My Institution', ... % optional
'general_related_publications', {'DOI:10.1016/j.neuron.2016.12.011'}); % optional
nwb
nwb =
NwbFile with properties: nwb_version: '2.8.0' file_create_date: [] @@ -139,14 +139,14 @@ stimulus_presentation: [0×1 types.untyped.Set] stimulus_templates: [0×1 types.untyped.Set] units: [] -

Optical Physiology

Optical physiology results are written in four steps:
  1. Create imaging plane
  2. Acquired two-photon images
  3. Image segmentation
  4. Fluorescence and dF/F responses

Imaging Plane

First, you must create an ImagingPlane object, which will hold information about the area and method used to collect the optical imaging data. This requires creation of a Device object for the microscope and an OpticalChannel object. Then you can create an ImagingPlane.
Create a Device representing a two-photon microscope. The fields description, manufacturer, model_number, model_name, and serial_number are optional, but recommended. Then create an OpticalChannel and add both of these to the ImagingPlane.
device = types.core.Device( ...
'description', 'My two-photon microscope', ...
'manufacturer', 'Loki Labs', ...
'model_number', 'ABC-123', ...
'model_name', 'Loki 1.0', ...
'serial_number', '1234567890');
 
% Add device to nwb object
nwb.general_devices.set('Device', device);
 
optical_channel = types.core.OpticalChannel( ...
'description', 'description', ...
'emission_lambda', 500.);
 
imaging_plane_name = 'imaging_plane';
imaging_plane = types.core.ImagingPlane( ...
'optical_channel', optical_channel, ...
'description', 'a very interesting part of the brain', ...
'device', types.untyped.SoftLink(device), ...
'excitation_lambda', 600., ...
'imaging_rate', 5., ...
'indicator', 'GFP', ...
'location', 'my favorite brain location');
 
nwb.general_optophysiology.set(imaging_plane_name, imaging_plane);

Storing Two-Photon Data

You can create a TwoPhotonSeries class representing two photon imaging data. TwoPhotonSeries, like SpatialSeries, inherits from TimeSeries and is similar in behavior to OnePhotonSeries.
InternalTwoPhoton = types.core.TwoPhotonSeries( ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0.0, ...
'starting_time_rate', 3.0, ...
'data', ones(200, 100, 1000), ...
'data_unit', 'lumens');
 
nwb.acquisition.set('2pInternal', InternalTwoPhoton);

Storing One-Photon Data

Now that we have our ImagingPlane, we can create a OnePhotonSeries object to store raw one-photon imaging data.
% using internal data. this data will be stored inside the NWB file
InternalOnePhoton = types.core.OnePhotonSeries( ...
'data', ones(100, 100, 1000), ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0., ...
'starting_time_rate', 1.0, ...
'data_unit', 'normalized amplitude' ...
);
nwb.acquisition.set('1pInternal', InternalOnePhoton);

Motion Correction (optional)

You can also store the result of motion correction using a MotionCorrection object, a container type that can hold one or more CorrectedImageStack objects.
% Create the corrected ImageSeries
corrected = types.core.ImageSeries( ...
'description', 'A motion corrected image stack', ...
'data', ones(100, 100, 1000), ... % 3D data array
'data_unit', 'n/a', ...
'format', 'raw', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);
 
% Create the xy_translation TimeSeries
xy_translation = types.core.TimeSeries( ...
'description', 'x,y translation in pixels', ...
'data', ones(2, 1000), ... % 2D data array
'data_unit', 'pixels', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);
 
% Create the CorrectedImageStack
corrected_image_stack = types.core.CorrectedImageStack( ...
'corrected', corrected, ...
'original', types.untyped.SoftLink(InternalOnePhoton), ... % Ensure `InternalOnePhoton` exists
'xy_translation', xy_translation ...
);
 
% Create the MotionCorrection object
motion_correction = types.core.MotionCorrection();
motion_correction.correctedimagestack.set('CorrectedImageStack', corrected_image_stack);
The motion corrected data is considered processed data and will be added to the processing field of the nwb object using a ProcessingModule called "ophys". First, create the ProcessingModule object and then add the motion_correction object to it, naming it "MotionCorrection".
ophys_module = types.core.ProcessingModule( ...
'description', 'Contains optical physiology data');
ophys_module.nwbdatainterface.set('MotionCorrection', motion_correction);
Finally, add the "ophys" ProcessingModule to the nwb (Note that we can continue adding objects to the "ophys" ProcessingModule without needing to explicitly update the nwb):
nwb.processing.set('ophys', ophys_module);

Plane Segmentation

Image segmentation stores the detected regions of interest in the TwoPhotonSeries data. ImageSegmentation allows you to have more than one segmentation by creating more PlaneSegmentation objects.

Regions of interest (ROIs)

ROIs can be added to a PlaneSegmentation either as an image_mask or as a pixel_mask. An image mask is an array that is the same size as a single frame of the TwoPhotonSeries, and indicates where a single region of interest is. This image mask may be boolean or continuous between 0 and 1. A pixel_mask, on the other hand, is a list of indices (i.e coordinates) and weights for the ROI. The pixel_mask is represented as a compound data type using a ragged array and below is an example demonstrating how to create either an image_mask or a pixel_mask. Changing the dropdown selection will update the PlaneSegmentation object accordingly.
selection = "Create Image Mask"; % "Create Image Mask" or "Create Pixel Mask"
 
% 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);
center = randi(90,2,n_rois);
for i = 1:n_rois
image_mask(center(1,i):center(1,i)+10, center(2,i):center(2,i)+10, i) = 1;
end
 
if selection == "Create Pixel Mask"
ind = find(image_mask);
[y_ind, x_ind, roi_ind] = ind2sub(size(image_mask), ind);
 
pixel_mask_struct = struct();
pixel_mask_struct.x = uint32(x_ind); % Add x coordinates to struct field x
pixel_mask_struct.y = uint32(y_ind); % Add y coordinates to struct field y
pixel_mask_struct.weight = single(ones(size(x_ind)));
% Create pixel mask vector data
pixel_mask = types.hdmf_common.VectorData(...
'data', struct2table(pixel_mask_struct), ...
'description', 'pixel masks');
 
% When creating a pixel mask, it is also necessary to specify a
% pixel_mask_index vector. See the documentation for ragged arrays linked
% above to learn more.
num_pixels_per_roi = zeros(n_rois, 1); % Column vector
for i_roi = 1:n_rois
num_pixels_per_roi(i_roi) = sum(roi_ind == i_roi);
end
 
pixel_mask_index = uint16(cumsum(num_pixels_per_roi)); % Note: Use an integer
% type that can accommodate the maximum value of the cumulative sum
 
% Create pixel_mask_index vector
pixel_mask_index = types.hdmf_common.VectorIndex(...
'description', 'Index into pixel_mask VectorData', ...
'data', pixel_mask_index, ...
'target', types.untyped.ObjectView(pixel_mask) );
 
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'pixel_mask'}, ...
'description', 'roi pixel position (x,y) and pixel weight', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'pixel_mask_index', pixel_mask_index, ...
'pixel_mask', pixel_mask ...
);
 
else % selection == "Create Image Mask"
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'image_mask'}, ...
'description', 'output from segmenting my favorite imaging plane', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'image_mask', types.hdmf_common.VectorData(...
'data', image_mask, ...
'description', 'image masks') ...
);
end

Adding ROIs to NWB file

Now create an ImageSegmentation object and put the plane_segmentation object inside of it, naming it "PlaneSegmentation".
img_seg = types.core.ImageSegmentation();
img_seg.planesegmentation.set('PlaneSegmentation', plane_segmentation);
Add the img_seg object to the "ophys" ProcessingModule we created before, naming it "ImageSegmentation".
ophys_module.nwbdatainterface.set('ImageSegmentation', img_seg);

Storing fluorescence of ROIs over time

Now that ROIs are stored, you can store fluorescence data for these regions of interest. This type of data is stored using the RoiResponseSeries class.
To create a RoiResponseSeries object, we will need to reference a set of rows from the PlaneSegmentation table to indicate which ROIs correspond to which rows of your recorded data matrix. This is done using a DynamicTableRegion, which is a type of link that allows you to reference specific rows of a DynamicTable, such as a PlaneSegmentation table by row indices.
First, we create a DynamicTableRegion that references the ROIs of the PlaneSegmentation table.
roi_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(plane_segmentation), ...
'description', 'all_rois', ...
'data', (0:n_rois-1)');
Then we create a RoiResponseSeries object to store fluorescence data for those ROIs.
roi_response_series = types.core.RoiResponseSeries( ...
'rois', roi_table_region, ...
'data', NaN(n_rois, 100), ... % [nRoi, nT]
'data_unit', 'lumens', ...
'starting_time_rate', 3.0, ...
'starting_time', 0.0);
To help data analysis and visualization tools know that this RoiResponseSeries object represents fluorescence data, we will store the RoiResponseSeries object inside of a Fluorescence object. Then we add the Fluorescence object into the same ProcessingModule named "ophys" that we created earlier.
fluorescence = types.core.Fluorescence();
fluorescence.roiresponseseries.set('RoiResponseSeries', roi_response_series);
 
ophys_module.nwbdatainterface.set('Fluorescence', fluorescence);
Tip: If you want to store dF/F data instead of fluorescence data, then store the RoiResponseSeries object in a DfOverF object, which works the same way as the Fluorescence class.

Writing the NWB file

nwb_file_name = 'ophys_tutorial.nwb';
if isfile(nwb_file_name); delete(nwb_file_name); end
nwbExport(nwb, nwb_file_name);
Warning: The property "grid_spacing_unit" of type "types.core.ImagingPlane" was not exported to file location "/general/optophysiology/imaging_plane" because it depends on the property "grid_spacing" which is unset.
Warning: The property "origin_coords_unit" of type "types.core.ImagingPlane" was not exported to file location "/general/optophysiology/imaging_plane" because it depends on the property "origin_coords" which is unset.

Reading the NWB file

read_nwb = nwbRead(nwb_file_name, 'ignorecache');
Data arrays are read passively from the file. Calling TimeSeries.data does not read the data values, but presents an HDF5 object that can be indexed to read data.
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence')...
.roiresponseseries.get('RoiResponseSeries').data
ans =
DataStub with properties: +

Optical Physiology

Optical physiology results are written in four steps:
  1. Create imaging plane
  2. Acquired two-photon images
  3. Image segmentation
  4. Fluorescence and dF/F responses

Imaging Plane

First, you must create an ImagingPlane object, which will hold information about the area and method used to collect the optical imaging data. This requires creation of a Device object for the microscope and an OpticalChannel object. Then you can create an ImagingPlane.
Create a Device representing a two-photon microscope. The fields description, manufacturer, model_number, model_name, and serial_number are optional, but recommended. Then create an OpticalChannel and add both of these to the ImagingPlane.
device = types.core.Device( ...
'description', 'My two-photon microscope', ...
'manufacturer', 'Loki Labs', ...
'model_number', 'ABC-123', ...
'model_name', 'Loki 1.0', ...
'serial_number', '1234567890');
 
% Add device to nwb object
nwb.general_devices.set('Device', device);
 
optical_channel = types.core.OpticalChannel( ...
'description', 'description', ...
'emission_lambda', 500.);
 
imaging_plane_name = 'imaging_plane';
imaging_plane = types.core.ImagingPlane( ...
'optical_channel', optical_channel, ...
'description', 'a very interesting part of the brain', ...
'device', types.untyped.SoftLink(device), ...
'excitation_lambda', 600., ...
'imaging_rate', 5., ...
'indicator', 'GFP', ...
'location', 'my favorite brain location');
 
nwb.general_optophysiology.set(imaging_plane_name, imaging_plane);

Storing Two-Photon Data

You can create a TwoPhotonSeries class representing two photon imaging data. TwoPhotonSeries, like SpatialSeries, inherits from TimeSeries and is similar in behavior to OnePhotonSeries.
InternalTwoPhoton = types.core.TwoPhotonSeries( ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0.0, ...
'starting_time_rate', 3.0, ...
'data', ones(200, 100, 1000), ...
'data_unit', 'lumens');
 
nwb.acquisition.set('2pInternal', InternalTwoPhoton);

Storing One-Photon Data

Now that we have our ImagingPlane, we can create a OnePhotonSeries object to store raw one-photon imaging data.
% using internal data. this data will be stored inside the NWB file
InternalOnePhoton = types.core.OnePhotonSeries( ...
'data', ones(100, 100, 1000), ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0., ...
'starting_time_rate', 1.0, ...
'data_unit', 'normalized amplitude' ...
);
nwb.acquisition.set('1pInternal', InternalOnePhoton);

Motion Correction (optional)

You can also store the result of motion correction using a MotionCorrection object, a container type that can hold one or more CorrectedImageStack objects.
% Create the corrected ImageSeries
corrected = types.core.ImageSeries( ...
'description', 'A motion corrected image stack', ...
'data', ones(100, 100, 1000), ... % 3D data array
'data_unit', 'n/a', ...
'format', 'raw', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);
 
% Create the xy_translation TimeSeries
xy_translation = types.core.TimeSeries( ...
'description', 'x,y translation in pixels', ...
'data', ones(2, 1000), ... % 2D data array
'data_unit', 'pixels', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);
 
% Create the CorrectedImageStack
corrected_image_stack = types.core.CorrectedImageStack( ...
'corrected', corrected, ...
'original', types.untyped.SoftLink(InternalOnePhoton), ... % Ensure `InternalOnePhoton` exists
'xy_translation', xy_translation ...
);
 
% Create the MotionCorrection object
motion_correction = types.core.MotionCorrection();
motion_correction.correctedimagestack.set('CorrectedImageStack', corrected_image_stack);
The motion corrected data is considered processed data and will be added to the processing field of the nwb object using a ProcessingModule called "ophys". First, create the ProcessingModule object and then add the motion_correction object to it, naming it "MotionCorrection".
ophys_module = types.core.ProcessingModule( ...
'description', 'Contains optical physiology data');
ophys_module.nwbdatainterface.set('MotionCorrection', motion_correction);
Finally, add the "ophys" ProcessingModule to the nwb (Note that we can continue adding objects to the "ophys" ProcessingModule without needing to explicitly update the nwb):
nwb.processing.set('ophys', ophys_module);

Plane Segmentation

Image segmentation stores the detected regions of interest in the TwoPhotonSeries data. ImageSegmentation allows you to have more than one segmentation by creating more PlaneSegmentation objects.

Regions of interest (ROIs)

ROIs can be added to a PlaneSegmentation either as an image_mask or as a pixel_mask. An image mask is an array that is the same size as a single frame of the TwoPhotonSeries, and indicates where a single region of interest is. This image mask may be boolean or continuous between 0 and 1. A pixel_mask, on the other hand, is a list of indices (i.e coordinates) and weights for the ROI. The pixel_mask is represented as a compound data type using a ragged array and below is an example demonstrating how to create either an image_mask or a pixel_mask. Changing the dropdown selection will update the PlaneSegmentation object accordingly.
selection = "Create Image Mask"; % "Create Image Mask" or "Create Pixel Mask"
 
% 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);
center = randi(90,2,n_rois);
for i = 1:n_rois
image_mask(center(1,i):center(1,i)+10, center(2,i):center(2,i)+10, i) = 1;
end
 
if selection == "Create Pixel Mask"
ind = find(image_mask);
[y_ind, x_ind, roi_ind] = ind2sub(size(image_mask), ind);
 
pixel_mask_struct = struct();
pixel_mask_struct.x = uint32(x_ind); % Add x coordinates to struct field x
pixel_mask_struct.y = uint32(y_ind); % Add y coordinates to struct field y
pixel_mask_struct.weight = single(ones(size(x_ind)));
% Create pixel mask vector data
pixel_mask = types.hdmf_common.VectorData(...
'data', struct2table(pixel_mask_struct), ...
'description', 'pixel masks');
 
% When creating a pixel mask, it is also necessary to specify a
% pixel_mask_index vector. See the documentation for ragged arrays linked
% above to learn more.
num_pixels_per_roi = zeros(n_rois, 1); % Column vector
for i_roi = 1:n_rois
num_pixels_per_roi(i_roi) = sum(roi_ind == i_roi);
end
 
pixel_mask_index = uint16(cumsum(num_pixels_per_roi)); % Note: Use an integer
% type that can accommodate the maximum value of the cumulative sum
 
% Create pixel_mask_index vector
pixel_mask_index = types.hdmf_common.VectorIndex(...
'description', 'Index into pixel_mask VectorData', ...
'data', pixel_mask_index, ...
'target', types.untyped.ObjectView(pixel_mask) );
 
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'pixel_mask'}, ...
'description', 'roi pixel position (x,y) and pixel weight', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'pixel_mask_index', pixel_mask_index, ...
'pixel_mask', pixel_mask ...
);
 
else % selection == "Create Image Mask"
plane_segmentation = types.core.PlaneSegmentation( ...
'colnames', {'image_mask'}, ...
'description', 'output from segmenting my favorite imaging plane', ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'image_mask', types.hdmf_common.VectorData(...
'data', image_mask, ...
'description', 'image masks') ...
);
end

Adding ROIs to NWB file

Now create an ImageSegmentation object and put the plane_segmentation object inside of it, naming it "PlaneSegmentation".
img_seg = types.core.ImageSegmentation();
img_seg.planesegmentation.set('PlaneSegmentation', plane_segmentation);
Add the img_seg object to the "ophys" ProcessingModule we created before, naming it "ImageSegmentation".
ophys_module.nwbdatainterface.set('ImageSegmentation', img_seg);

Storing fluorescence of ROIs over time

Now that ROIs are stored, you can store fluorescence data for these regions of interest. This type of data is stored using the RoiResponseSeries class.
To create a RoiResponseSeries object, we will need to reference a set of rows from the PlaneSegmentation table to indicate which ROIs correspond to which rows of your recorded data matrix. This is done using a DynamicTableRegion, which is a type of link that allows you to reference specific rows of a DynamicTable, such as a PlaneSegmentation table by row indices.
First, we create a DynamicTableRegion that references the ROIs of the PlaneSegmentation table.
roi_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(plane_segmentation), ...
'description', 'all_rois', ...
'data', (0:n_rois-1)');
Then we create a RoiResponseSeries object to store fluorescence data for those ROIs.
roi_response_series = types.core.RoiResponseSeries( ...
'rois', roi_table_region, ...
'data', NaN(n_rois, 100), ... % [nRoi, nT]
'data_unit', 'lumens', ...
'starting_time_rate', 3.0, ...
'starting_time', 0.0);
To help data analysis and visualization tools know that this RoiResponseSeries object represents fluorescence data, we will store the RoiResponseSeries object inside of a Fluorescence object. Then we add the Fluorescence object into the same ProcessingModule named "ophys" that we created earlier.
fluorescence = types.core.Fluorescence();
fluorescence.roiresponseseries.set('RoiResponseSeries', roi_response_series);
 
ophys_module.nwbdatainterface.set('Fluorescence', fluorescence);
Tip: If you want to store dF/F data instead of fluorescence data, then store the RoiResponseSeries object in a DfOverF object, which works the same way as the Fluorescence class.

Writing the NWB file

nwb_file_name = 'ophys_tutorial.nwb';
if isfile(nwb_file_name); delete(nwb_file_name); end
nwbExport(nwb, nwb_file_name);
Warning: The property "grid_spacing_unit" of type "types.core.ImagingPlane" was not exported to file location "/general/optophysiology/imaging_plane" because it depends on the property "grid_spacing" which is unset.
Warning: The property "origin_coords_unit" of type "types.core.ImagingPlane" was not exported to file location "/general/optophysiology/imaging_plane" because it depends on the property "origin_coords" which is unset.

Reading the NWB file

read_nwb = nwbRead(nwb_file_name, 'ignorecache');
Data arrays are read passively from the file. Calling TimeSeries.data does not read the data values, but presents an HDF5 object that can be indexed to read data.
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence')...
.roiresponseseries.get('RoiResponseSeries').data
ans =
DataStub with properties: filename: 'ophys_tutorial.nwb' path: '/processing/ophys/Fluorescence/RoiResponseSeries/data' dims: [20 100] ndims: 2 dataType: 'double' -
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access the data in the matrix using the load method.
load with no input arguments reads the entire dataset:
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries').data.load
ans = 20×100
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN +
This allows you to conveniently work with datasets that are too large to fit in RAM all at once. Access the data in the matrix using the load method.
load with no input arguments reads the entire dataset:
read_nwb.processing.get('ophys').nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries').data.load
ans = 20×100
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN @@ -156,12 +156,12 @@ NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN -
If all you need is a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries'). ...
data(1:5, 1:10)
ans = 5×10
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN +
If all you need is a section of the data, you can read only that section by indexing the DataStub object like a normal array in MATLAB. This will just read the selected region from disk into RAM. This technique is particularly useful if you are dealing with a large dataset that is too big to fit entirely into your available RAM.
read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('Fluorescence'). ...
roiresponseseries.get('RoiResponseSeries'). ...
data(1:5, 1:10)
ans = 5×10
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN -
% read back the image/pixel masks and display the first roi
plane_segmentation = read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('ImageSegmentation'). ...
planesegmentation.get('PlaneSegmentation');
 
if ~isempty(plane_segmentation.image_mask)
roi_mask = plane_segmentation.image_mask.data(:,:,1);
elseif ~isempty(plane_segmentation.pixel_mask)
row = plane_segmentation.getRow(1, 'columns', {'pixel_mask'});
pixel_mask = row.pixel_mask{1};
roi_mask = zeros(imaging_shape);
ind = sub2ind(imaging_shape, pixel_mask.y, pixel_mask.x);
roi_mask(ind) = pixel_mask.weight;
end
imshow(roi_mask)

Learn more!

See the API documentation to learn what data types are available.

Other MatNWB tutorials

Python tutorials

See our tutorials for more details about your data type:
Check out other tutorials that teach advanced NWB topics:

+
% read back the image/pixel masks and display the first roi
plane_segmentation = read_nwb.processing.get('ophys'). ...
nwbdatainterface.get('ImageSegmentation'). ...
planesegmentation.get('PlaneSegmentation');
 
if ~isempty(plane_segmentation.image_mask)
roi_mask = plane_segmentation.image_mask.data(:,:,1);
elseif ~isempty(plane_segmentation.pixel_mask)
row = plane_segmentation.getRow(1, 'columns', {'pixel_mask'});
pixel_mask = row.pixel_mask{1};
roi_mask = zeros(imaging_shape);
ind = sub2ind(imaging_shape, pixel_mask.y, pixel_mask.x);
roi_mask(ind) = pixel_mask.weight;
end
imshow(roi_mask)

Learn more!

See the API documentation to learn what data types are available.

Other MatNWB tutorials

Python tutorials

See our tutorials for more details about your data type:
Check out other tutorials that teach advanced NWB topics:


Intracellular electrophysiology

The following tutorial describes storage of intracellular electrophysiology data in NWB. NWB supports storage of the time series describing the stimulus and response, information about the electrode and device used, as well as metadata about the organization of the experiment.
Illustration of the hierarchy of metadata tables used to describe the organization of intracellular electrophysiology experiments.

Creating an NWBFile

When creating an NWB file, the first step is to create the NWBFile, which you can create using the NwbFile command.
session_start_time = datetime(2018, 3, 1, 12, 0, 0, 'TimeZone', 'local');
 
 
nwbfile = NwbFile( ...
'session_description', 'my first synthetic recording', ...
'identifier', 'EXAMPLE_ID', ...
'session_start_time', session_start_time, ...
'general_experimenter', 'Dr. Bilbo Baggins', ...
'general_lab', 'Bag End Laboratory', ...
'general_institution', 'University of Middle Earth at the Shire', ...
'general_experiment_description', 'I went on an adventure with thirteen dwarves to reclaim vast treasures.', ...
'general_session_id', 'LONELYMTN' ...
);
 

Device metadata

Device metadata is represented by Device objects.
 
device = types.core.Device();
nwbfile.general_devices.set('Heka ITC-1600', device);

Electrode metadata

Intracellular electrode metadata is represented by IntracellularElectrode objects. Create an electrode object, which requires a link to the device of the previous step. Then add it to the NWB file.
electrode = types.core.IntracellularElectrode( ...
'description', 'a mock intracellular electrode', ...
'device', types.untyped.SoftLink(device), ...
'cell_id', 'a very interesting cell' ...
);
nwbfile.general_intracellular_ephys.set('elec0', electrode);

Stimulus and response data

Intracellular stimulus and response data are represented with subclasses of PatchClampSeries. A stimulus is described by a time series representing voltage or current stimulation with a particular set of parameters. There are two classes for representing stimulus data:
The response is then described by a time series representing voltage or current recorded from a single cell using a single intracellular electrode via one of the following classes:
Below we create a simple example stimulus/response recording data pair for a voltage clamp recording.
ccss = types.core.VoltageClampStimulusSeries( ...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint64(15), ...
'stimulus_description', 'N/A' ...
);
nwbfile.stimulus_presentation.set('ccss', ccss);
 
vcs = types.core.VoltageClampSeries( ...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'capacitance_slow', 100e-12, ...
'resistance_comp_correction', 70.0, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint64(15) ...
);
nwbfile.acquisition.set('vcs', vcs);
You can add stimulus/response recording data pair from a current clamp recording in the same way:
% Create a CurrentClampStimulusSeries object
ccss = types.core.CurrentClampStimulusSeries(...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint16(16), ...
'stimulus_description', 'N/A' ...
);
nwbfile.stimulus_presentation.set('ccss', ccss);
 
% Create a CurrentClampSeries object
ccs = types.core.CurrentClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'bias_current', 1e-12, ...
'bridge_balance', 70e6, ...
'capacitance_compensation', 1e-12, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint16(16) ...
);
nwbfile.acquisition.set('ccs', ccs);
 
IZeroClampSeries is used when the current is clamped to 0.
% Create an IZeroClampSeries object
izcs = types.core.IZeroClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 345.6, ...
'starting_time_rate', 20e3, ...
'sweep_number', uint16(17) ...
);
nwbfile.acquisition.set('izcs', izcs);

Adding an intracellular recording

The IntracellularRecordingsTable relates electrode, stimulus and response pairs and describes metadata specific to individual recordings.
Illustration of the structure of the IntracellularRecordingsTable
ic_rec_table = types.core.IntracellularRecordingsTable( ...
'categories', {'electrodes', 'stimuli', 'responses'}, ...
'colnames', {'recordings_tag'}, ...
'description', [ ...
'A table to group together a stimulus and response from a single ', ...
'electrode and a single simultaneous recording and for storing ', ...
'metadata about the intracellular recording.'], ...
'id', types.hdmf_common.ElementIdentifiers('data', int64([0, 1, 2])), ...
'recordings_tag', types.hdmf_common.VectorData( ...
'data', repmat({'Tag'}, 3, 1), ...
'description', 'Column for storing a custom recordings tag' ...
) ...
);
 
ic_rec_table.electrodes = types.core.IntracellularElectrodesTable( ...
'description', 'Table for storing intracellular electrode related metadata.', ...
'colnames', {'electrode'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'electrode', types.hdmf_common.VectorData( ...
'data', repmat(types.untyped.ObjectView(electrode), 3, 1), ...
'description', 'Column for storing the reference to the intracellular electrode' ...
) ...
);
 
ic_rec_table.stimuli = types.core.IntracellularStimuliTable( ...
'description', 'Table for storing intracellular stimulus related metadata.', ...
'colnames', {'stimulus'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'stimulus', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded stimulus for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 1, -1], ...
'count', [5, 3, -1], ...
'timeseries', [ ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
ic_rec_table.responses = types.core.IntracellularResponsesTable( ...
'description', 'Table for storing intracellular response related metadata.', ...
'colnames', {'response'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'response', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded response for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 2, 0], ...
'count', [5, 3, 5], ...
'timeseries', [ ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
The IntracellularRecordingsTable table is not just a DynamicTable but an AlignedDynamicTable. The AlignedDynamicTable type is itself a DynamicTable that may contain an arbitrary number of additional DynamicTable, each of which defines a "category." This is similar to a table with “sub-headings”. In the case of the IntracellularRecordingsTable, we have three predefined categories, i.e., electrodes, stimuli, and responses. We can also dynamically add new categories to the table. As each category corresponds to a DynamicTable, this means we have to create a new DynamicTable and add it to our table.
% add category
ic_rec_table.categories = [ic_rec_table.categories, {'recording_lab_data'}];
ic_rec_table.dynamictable.set( ...
'recording_lab_data', types.hdmf_common.DynamicTable( ...
'description', 'category table for lab-specific recording metadata', ...
'colnames', {'location'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'location', types.hdmf_common.VectorData( ...
'data', {'Mordor', 'Gondor', 'Rohan'}, ...
'description', 'Recording location in Middle Earth' ...
) ...
) ...
);
In an AlignedDynamicTable all category tables must align with the main table, i.e., all tables must have the same number of rows and rows are expected to correspond to each other by index.
We can also add custom columns to any of the subcategory tables, i.e., the electrodes, stimuli, and responses tables, and any custom subcategory tables. All we need to do is indicate the name of the category we want to add the column to.
% Add voltage threshold as column of electrodes table
ic_rec_table.electrodes.colnames = [ic_rec_table.electrodes.colnames {'voltage_threshold'}];
ic_rec_table.electrodes.vectordata.set('voltage_threshold', types.hdmf_common.VectorData( ...
'data', [0.1, 0.12, 0.13], ...
'description', 'Just an example column on the electrodes category table' ...
) ...
);
 
nwbfile.general_intracellular_ephys_intracellular_recordings = ic_rec_table;

Hierarchical organization of recordings

To describe the organization of intracellular experiments, the metadata is organized hierarchically in a sequence of tables. All of the tables are so-called DynamicTables enabling users to add columns for custom metadata. Storing data in hierarchical tables has the advantage that it allows us to avoid duplication of metadata. E.g., for a single experiment we only need to describe the metadata that is constant across an experimental condition as a single row in the SimultaneousRecordingsTable without having to replicate the same information across all repetitions and sequential-, simultaneous-, and individual intracellular recordings. For analysis, this means that we can easily focus on individual aspects of an experiment while still being able to easily access information about information from related tables. All of these tables are optional, but to use one you must use all of the lower level tables, even if you only need a single row.

Add a simultaneous recording

The SimultaneousRecordingsTable groups intracellular recordings from the IntracellularRecordingsTable together that were recorded simultaneously from different electrodes and/or cells and describes metadata that is constant across the simultaneous recordings. In practice a simultaneous recording is often also referred to as a sweep. This example adds a custom column, "simultaneous_recording_tag."
% create simultaneous recordings table with custom column
% 'simultaneous_recording_tag'
 
[recordings_vector_data, recordings_vector_index] = util.create_indexed_column( ...
{[0, 1, 2],}, ...
'Column with references to one or more rows in the IntracellularRecordingsTable table', ...
ic_rec_table);
 
ic_sim_recs_table = types.core.SimultaneousRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recordings from ', ...
'the IntracellularRecordingsTable table together that were recorded ', ...
'simultaneously from different electrodes.'...
], ...
'colnames', {'recordings', 'simultaneous_recording_tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(12) ...
), ...
'recordings', recordings_vector_data, ...
'recordings_index', recordings_vector_index, ...
'simultaneous_recording_tag', types.hdmf_common.VectorData( ...
'description', 'A custom tag for simultaneous_recordings', ...
'data', {'LabTag1'} ...
) ...
);
 
Depending on the lab workflow, it may be useful to add complete columns to a table after we have already populated the table with rows. That would be done like so:
ic_sim_recs_table.colnames = [ic_sim_recs_table.colnames, {'simultaneous_recording_type'}];
ic_sim_recs_table.vectordata.set( ...
'simultaneous_recording_type', types.hdmf_common.VectorData(...
'description', 'Description of the type of simultaneous_recording', ...
'data', {'SimultaneousRecordingType1'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_simultaneous_recordings = ic_sim_recs_table;

Add a sequential recording

The SequentialRecordingsTable groups simultaneously recorded intracellular recordings from the SimultaneousRecordingsTable together and describes metadata that is constant across the simultaneous recordings. In practice a sequential recording is often also referred to as a sweep sequence. A common use of sequential recordings is to group together simultaneous recordings where a sequence of stimuli of the same type with varying parameters have been presented in a sequence (e.g., a sequence of square waveforms with varying amplitude).
[simultaneous_recordings_vector_data, simultaneous_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SimultaneousRecordingsTable table', ...
ic_sim_recs_table);
 
sequential_recordings = types.core.SequentialRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'simultaneous_recordings from the SimultaneousRecordingsTable ', ...
'table together. This is typically used to group together ', ...
'simultaneous_recordings where the a sequence of stimuli of ', ...
'the same type with varying parameters have been presented in ', ...
'a sequence.' ...
], ...
'colnames', {'simultaneous_recordings', 'stimulus_type'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(15) ...
), ...
'simultaneous_recordings', simultaneous_recordings_vector_data, ...
'simultaneous_recordings_index', simultaneous_recordings_vector_index, ...
'stimulus_type', types.hdmf_common.VectorData( ...
'description', 'Column storing the type of stimulus used for the sequential recording', ...
'data', {'square'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_sequential_recordings = sequential_recordings;

Add repetitions table

The RepetitionsTable groups sequential recordings from the SequentialRecordingsTable. In practice, a repetition is often also referred to a run. A typical use of the RepetitionsTable is to group sets of different stimuli that are applied in sequence that may be repeated.
[sequential_recordings_vector_data, sequential_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SequentialRecordingsTable table', ...
sequential_recordings);
 
 
nwbfile.general_intracellular_ephys_repetitions = types.core.RepetitionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording sequential ', ...
'recordings together. With each SimultaneousRecording typically ', ...
'representing a particular type of stimulus, the RepetitionsTable ', ...
'table is typically used to group sets of stimuli applied in sequence.' ...
], ...
'colnames', {'sequential_recordings'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(17) ...
), ...
'sequential_recordings', sequential_recordings_vector_data, ...
'sequential_recordings_index', sequential_recordings_vector_index ...
);

Add experimental condition table

The ExperimentalConditionsTable groups repetitions of intracellular recording from the RepetitionsTable together that belong to the same experimental conditions.
[repetitions_vector_data, repetitions_vector_index] = util.create_indexed_column( ...
{0, 0}, ...
'Column with references to one or more rows in the RepetitionsTable table', ...
nwbfile.general_intracellular_ephys_repetitions);
 
nwbfile.general_intracellular_ephys_experimental_conditions = types.core.ExperimentalConditionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'repetitions together that belong to the same experimental ', ...
'conditions.' ...
], ...
'colnames', {'repetitions', 'tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([19, 21]) ...
), ...
'repetitions', repetitions_vector_data, ...
'repetitions_index', repetitions_vector_index, ...
'tag', types.hdmf_common.VectorData( ...
'description', 'integer tag for a experimental condition', ...
'data', [1,3] ...
) ...
);

Write the NWB file

nwbExport(nwbfile, 'test_new_icephys.nwb');

Read the NWB file

nwbfile2 = nwbRead('test_new_icephys.nwb', 'ignorecache')
nwbfile2 =
NwbFile with properties: - - nwb_version: '2.7.0' - file_create_date: [1×1 types.untyped.DataStub] - identifier: 'EXAMPLE_ID' - session_description: 'my first synthetic recording' - session_start_time: [1×1 types.untyped.DataStub] - timestamps_reference_time: [1×1 types.untyped.DataStub] - acquisition: [3×1 types.untyped.Set] - analysis: [0×1 types.untyped.Set] - general: [0×1 types.untyped.Set] - general_data_collection: '' - general_devices: [1×1 types.untyped.Set] - general_experiment_description: 'I went on an adventure with thirteen dwarves to reclaim vast treasures.' - general_experimenter: [1×1 types.untyped.DataStub] - general_extracellular_ephys: [0×1 types.untyped.Set] - general_extracellular_ephys_electrodes: [] - general_institution: 'University of Middle Earth at the Shire' - general_intracellular_ephys: [1×1 types.untyped.Set] - general_intracellular_ephys_experimental_conditions: [1×1 types.core.ExperimentalConditionsTable] - general_intracellular_ephys_filtering: '' - general_intracellular_ephys_intracellular_recordings: [1×1 types.core.IntracellularRecordingsTable] - general_intracellular_ephys_repetitions: [1×1 types.core.RepetitionsTable] - general_intracellular_ephys_sequential_recordings: [1×1 types.core.SequentialRecordingsTable] - general_intracellular_ephys_simultaneous_recordings: [1×1 types.core.SimultaneousRecordingsTable] - general_intracellular_ephys_sweep_table: [] - general_keywords: '' - general_lab: 'Bag End Laboratory' - general_notes: '' - general_optogenetics: [0×1 types.untyped.Set] - general_optophysiology: [0×1 types.untyped.Set] - general_pharmacology: '' - general_protocol: '' - general_related_publications: '' - general_session_id: 'LONELYMTN' - general_slices: '' - general_source_script: '' - general_source_script_file_name: '' - general_stimulus: '' - general_subject: [] - general_surgery: '' - general_virus: '' - intervals: [0×1 types.untyped.Set] - intervals_epochs: [] - intervals_invalid_times: [] - intervals_trials: [] - processing: [0×1 types.untyped.Set] - scratch: [0×1 types.untyped.Set] - stimulus_presentation: [1×1 types.untyped.Set] - stimulus_templates: [0×1 types.untyped.Set] - units: [] -
-
- -

Using NWB Data

last updated: February 9, 2021
In this tutorial, we demonstrate the reading and usage of the NWB file produced in the File Conversion Tutorial. The output is a near-reproduction of Figure 1e from the Li et al publication, showing raster and peristimulus time histogram (PSTH) plots for neural recordings from anterior lateral motor cortex (ALM). This figure illustrates the main finding of the publication, showing the robustness of motor planning behavior and neural dynamics following short unilateral network silencing via optogenetic inhibition.

Reading NWB Files

NWB files can be read in using the nwbRead() function. This function returns a nwbfile object which is the in-memory representation of the NWB file structure.
nwb = nwbRead('out\ANM255201_20141124.nwb');

Constrained Sets

Analyzed data in NWB is placed under the analysis property, which is a Constrained Set. A constrained set consists of an arbitrary amount of key-value pairs similar to Map containers in MATLAB or a dictionary in Python. However, constrained sets also have the ability to validate their own properties closer to how a typed Object would.
You can get/set values in constrained sets using their respective .get()/.set() methods and retrieve all Set properties using the keys() method, like in a containers.Map.
unit_names = keys(nwb.analysis);

Dynamic Tables

nwb.intervals_trials returns a unique type of table called a Dynamic Table. Dynamic tables inherit from the NWB type types.hdmf_common.DynamicTable and allow for a table-like interface in NWB. In the case below, we grab the special column start_time. Dynamic Tables allow adding your own vectors using the vectordata property, which are Constrained Sets. All columns are represented by either a types.hdmf_common.VectorData or a types.hdmf_common.VectorIndex type.

Data Stubs

The data property of the column id in nwb.units is a types.untyped.DataStub. This object is a representation of a dataset that is not loaded in memory, and is what allows MatNWB to lazily load its file data. To load the data into memory, use the .load() method which extracts all data from the NWB file. Alternatively, you can index into the DataStub directly using conventional MATLAB syntax.

Jagged Arrays in Dynamic Tables

With the new addition of addRow and getRow to Dynamic Tables, the concept of jagged arrays can be worked around and no longer require full understanding outside of specific data format concerns or low-level nwb tool development. The below paragraph is retained in its entirety from its original form as purely informational.
All data in a Dynamic Table must be aligned by row and column, but not all data fits into this paradigm neatly. In order to represent variable amounts of data that is localised to each row and column, NWB uses a concept called Jagged Arrays. These arrays consist of two column types: the familiar types.core.VectorData, and the new types.core.VectorIndex. A Vector Index holds no data, instead holding a reference to another Vector Data and a vector of indices that align to the Dynamic Table dimensions. The indices represent the last index boundary in the Vector Data object for the Vector Index row. As an example, an index of three in the first row of the Vector Index column points to the first three values in the referenced Vector Data column. Subsequently, if the next index were a five, it would indicate the fourth and fifth elements in the referenced Vector Data column.
The jagged arrays serve to represent multiple trials and spike times associated to each unit by id. A convenient way to represent these in MATLAB is to use Map containers where each unit's data is indexed directly by its unit id. Below, we utilize getRow in order to build the same Map.
unit_ids = nwb.units.id.data.load(); % array of unit ids represented within this
% Initialize trials & times Map containers indexed by unit_ids
unit_trials = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_times = containers.Map('KeyType',class(unit_ids),'ValueType','any');
last_idx = 0;
for i = 1:length(unit_ids)
unit_id = unit_ids(i);
row = nwb.units.getRow(unit_id, 'useId', true, 'columns', {'spike_times', 'trials'});
unit_trials(unit_id) = row.trials{1};
unit_times(unit_id) = row.spike_times{1};
end

Process Units

We now do the following for each Unit:
  • Filter out invalid trials
  • Separate datasets based on resulting mouse behavior (right/left licks).
  • Derive "sample", "delay", and "response" times for this analyzed neuron.
  • Compose a peristimulus time histogram from the data.
sorted_ids = sort(unit_ids);
Photostim = struct(...
'ind', true,... % mask into xs and ys for this photostim
'period', 'none',...
'duration', 0,... % in seconds
'ramp_offset', 0); % in seconds
% Initialize Map container of plotting data for each unit, stored as structure
Unit = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_struct = struct(...
'id', [],...
'xs', [],...
'ys', [],...
'xlim', [-Inf Inf],...
'sample', 0,...
'delay', 0,...
'response', 0,...
'left_scatter', false,...
'right_scatter', false,...
'photostim', Photostim); % can have multiple photostim
for unit_id = unit_ids'
We first extract trial IDs from the Unit IDs.
unit_trial_id = unit_trials(unit_id);
Then filter out outliers from the Sample, Delay, and Response time points with which we derive a "good enough" estimate.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'PoleInTime', 'PoleOutTime', 'CueTime', 'GoodTrials'});
unit_sample = trial.PoleInTime;
unit_delay = trial.PoleOutTime;
unit_response = trial.CueTime;
unit_good_trials = trial.GoodTrials;
% Subjective parameters
delay_threshold = 0.064;
response_threshold = 0.43;
expected_delay_offset = 1.3; % determined from figure 1a
expected_response_offset = 1.3;
expected_delay = unit_sample + expected_delay_offset;
expected_response = unit_delay + expected_response_offset;
good_delay = (unit_delay > expected_delay - delay_threshold) &...
(unit_delay < expected_delay + delay_threshold);
good_response = (unit_response > expected_response - response_threshold) &...
(unit_response < expected_response + response_threshold);
avg_sample = mean(unit_sample(good_delay & good_response));
avg_delay = mean(unit_delay(good_delay & good_response));
avg_response = mean(unit_response(good_delay & good_response));
Filter the rest of the data by "good" trials.
unit_good_trials = unit_good_trials & good_delay & good_response;
unit_trial_id = unit_trial_id(unit_good_trials);
unit_spike_time = unit_times(unit_id);
unit_spike_time = unit_spike_time(unit_good_trials);
Retrieve good trial data and organize by stimulation type.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'start_time', 'HitR', 'HitL', 'StimTrials', 'PhotostimulationType'});
unit_is_photostim = logical(trial.StimTrials);
unit_stim_type = trial.PhotostimulationType;
unit_no_stim = ~unit_is_photostim & 0 == unit_stim_type;
unit_sample_stim = unit_is_photostim & 1 == unit_stim_type;
unit_early_stim = unit_is_photostim & 2 == unit_stim_type;
unit_middle_stim = unit_is_photostim & 3 == unit_stim_type;
Compose Scatter Plots and the Peristimulus Time Histogram zeroed on the Response time.
xs = unit_spike_time - trial.start_time - avg_response;
ys = unit_trial_id;
curr_unit = unit_struct;
curr_unit.xs = xs;
curr_unit.ys = ys;
curr_unit.left_scatter = logical(trial.HitL);
curr_unit.right_scatter = logical(trial.HitR);
curr_unit.sample = avg_sample - avg_response;
curr_unit.delay = avg_delay - avg_response;
curr_unit.response = 0;
% Photostim periods
curr_unit.photostim.ind = unit_no_stim;
% Sample
if any(unit_sample_stim)
SampleStim = Photostim;
SampleStim.ind = unit_sample_stim;
SampleStim.period = 'Sample';
SampleStim.duration = 0.5;
SampleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = SampleStim;
end
% Early Delay
if any(unit_early_stim)
early_stim_types = unique(unit_stim_type(unit_early_stim));
for i_early_types=1:length(early_stim_types)
early_type = early_stim_types(i_early_types);
EarlyStim = Photostim;
EarlyStim.period = 'Early Delay';
EarlyStim.ind = early_type == unit_stim_type & unit_early_stim;
if early_type == 2
EarlyStim.duration = 0.5;
EarlyStim.ramp_offset = 0.1;
else
EarlyStim.duration = 0.8;
EarlyStim.ramp_offset = 0.2;
end
curr_unit.photostim(end+1) = EarlyStim;
end
end
% Middle Delay
if any(unit_middle_stim)
MiddleStim = Photostim;
MiddleStim.ind = unit_middle_stim;
MiddleStim.period = 'Middle Delay';
MiddleStim.duration = 0.5;
MiddleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = MiddleStim;
end
Unit(unit_id) = curr_unit;
end

Plot Example Neurons

neuron_labels = [2, 3]; % neuron labels from Figure 1e
neuron_ids = [11, 2]; % neuron unit IDs corresponding to the Fig 1e labels
num_conditions = 4; % photostim conditions: nostim, sample, early, middle if applicable
num_neurons = length(neuron_ids);
% Inititalize data structures for each summary plot of categorized neural spike data at specified stimulus condition
RasterPlot = struct(...
'xs', 0,...
'ys', 0);
ConditionPlot = struct(...
'label', '',...
'xlim', 0,...
'sample', 0,...
'delay', 0,...
'response', 0,...
'right_scatter', RasterPlot,...
'left_scatter', RasterPlot,...
'psth_bin_window', 0,...
'stim_type', '');
fig = figure;
% Plot neural spike data for each neuron and stimulus condition in a subplot array: num_neurons (rows) x num_conditions (columns)
for nn=1:num_neurons
Neuron = Unit(neuron_ids(nn));
% Initialize structure with neural + stimulus condition data
CurrPlot = ConditionPlot;
CurrPlot.xlim = [min(Neuron.xs) max(Neuron.xs)];
CurrPlot.sample = Neuron.sample;
CurrPlot.delay = Neuron.delay;
CurrPlot.response = Neuron.response;
% Plot each neuron/condition
plot_row = (nn - 1) * num_conditions;
for cc=1:num_conditions
ax = subplot(num_neurons, num_conditions, plot_row + cc, 'Parent', fig);
Stim = Neuron.photostim(cc);
CurrPlot.stim_type = Stim.period;
if strcmp(Stim.period, 'none')
CurrPlot.label = sprintf('Neuron %d', neuron_labels(nn));
CurrPlot.psth_bin_window = 9;
else
CurrPlot.label = Stim.period;
CurrPlot.psth_bin_window = 2;
end
stim_left_scatter_ind = Stim.ind & Neuron.left_scatter;
stim_left_scatter_trials = Neuron.ys(stim_left_scatter_ind);
CurrPlot.left_scatter.xs = Neuron.xs(stim_left_scatter_ind);
[~,CurrPlot.left_scatter.ys] = ismember(stim_left_scatter_trials,unique(stim_left_scatter_trials));
stim_right_scatter_ind = Stim.ind & Neuron.right_scatter;
stim_right_scatter_trials = Neuron.ys(stim_right_scatter_ind);
CurrPlot.right_scatter.xs = Neuron.xs(stim_right_scatter_ind);
[~,CurrPlot.right_scatter.ys] = ismember(stim_right_scatter_trials,unique(stim_right_scatter_trials));
plot_condition(ax, CurrPlot);
end
end
- - -

Helper Functions

PSTH helper function
function [psth_xs, psth_ys] = calculate_psth(xs, bin_window, bin_width)
[bin_counts, edges] = histcounts(xs, 'BinWidth', bin_width);
psth_xs = edges(1:end-1) + (bin_width / 2);
moving_avg_b = (1/bin_window) * ones(1,bin_window);
psth_ys = filter(moving_avg_b, 1, bin_counts);
end
Plotter function for each stimulus condition
function plot_condition(ax, ConditionPlot)
left_cdata = [1 0 0]; % red
right_cdata = [0 0 1]; % blue
hist_margin = 50;
scatter_margin = 10;
% Calculate PSTH values
% moving average over 200 ms as per figure 1e
hist_bin_width = 0.2 / ConditionPlot.psth_bin_window;
[left_psth_xs, left_psth_ys] =...
calculate_psth(ConditionPlot.left_scatter.xs, ConditionPlot.psth_bin_window, hist_bin_width);
[right_psth_xs, right_psth_ys] =...
calculate_psth(ConditionPlot.right_scatter.xs, ConditionPlot.psth_bin_window, hist_bin_width);
right_scatter_offset = min(ConditionPlot.right_scatter.ys);
right_scatter_height = max(ConditionPlot.right_scatter.ys) - right_scatter_offset;
left_scatter_offset = min(ConditionPlot.left_scatter.ys);
left_scatter_height = max(ConditionPlot.left_scatter.ys) - left_scatter_offset;
psth_height = max([left_psth_ys right_psth_ys]);
left_y_offset = hist_margin...
+ psth_height...
- left_scatter_offset;
right_y_offset = scatter_margin...
+ left_y_offset...
+ left_scatter_offset...
+ left_scatter_height...
- right_scatter_offset;
subplot_height = right_y_offset...
+ right_scatter_offset...
+ right_scatter_height;
hold(ax, 'on');
% PSTH
plot(ax, left_psth_xs, left_psth_ys, 'Color', left_cdata);
plot(ax, right_psth_xs, right_psth_ys, 'Color', right_cdata);
% Scatter Plot
scatter(ax,...
ConditionPlot.left_scatter.xs,...
left_y_offset + ConditionPlot.left_scatter.ys,...
'Marker', '.',...
'CData', left_cdata,...
'SizeData', 1);
scatter(ax,...
ConditionPlot.right_scatter.xs,...
right_y_offset + ConditionPlot.right_scatter.ys,...
'Marker', '.',...
'CData', right_cdata,...
'SizeData', 1);
% sample, delay, response lines
line(ax, repmat(ConditionPlot.sample, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
line(ax, repmat(ConditionPlot.delay, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
line(ax, repmat(ConditionPlot.response, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
% blue bar for photoinhibition period
if ~strcmp(ConditionPlot.stim_type, 'none')
stim_height = subplot_height;
stim_width = 0.5; % seconds
% end time relative to 'go' cue as described in the paper.
switch ConditionPlot.stim_type
case 'Sample'
end_offset = 1.6;
case 'Early Delay'
end_offset = 0.8;
case 'Middle Delay'
end_offset = 0.3;
otherwise
error('Invalid photostim period `%s`', ConditionPlot.stim_type);
end
stim_offset = ConditionPlot.response - stim_width - end_offset;
patch_vertices = [...
stim_offset, 0;...
stim_offset, stim_height;...
stim_offset+stim_width, stim_height;...
stim_offset+stim_width, 0];
patch(ax,...
'Faces', 1:4,...
'Vertices', patch_vertices,...
'FaceColor', '#B3D3EC',... % light blue shading
'EdgeColor', 'none',...
'FaceAlpha', 0.8);
end
title(ax, ConditionPlot.label);
xlabel(ax, 'Time (Seconds)');
ylabel(ax, 'Spikes s^{-1}')
xticks(ax, [-2 0 2]);
yticks(ax, [0 max(10, round(psth_height, -1))]);
% legend(ax, [scatter_left_plot, scatter_right_plot], {'Left Lick', 'Right Lick'},...
% 'location', 'northwestoutside');
ax.TickDir = 'out';
ax.XLim = ConditionPlot.xlim;
ax.YLim = [0 subplot_height];
hold(ax, 'off');
end
-
- -

Intracellular electrophysiology

The following tutorial describes storage of intracellular electrophysiology data in NWB. NWB supports storage of the time series describing the stimulus and response, information about the electrode and device used, as well as metadata about the organization of the experiment.
Illustration of the hierarchy of metadata tables used to describe the organization of intracellular electrophysiology experiments.

Creating an NWBFile

When creating an NWB file, the first step is to create the NWBFile, which you can create using the NwbFile command.
session_start_time = datetime(2018, 3, 1, 12, 0, 0, 'TimeZone', 'local');
 
 
nwbfile = NwbFile( ...
'session_description', 'my first synthetic recording', ...
'identifier', 'EXAMPLE_ID', ...
'session_start_time', session_start_time, ...
'general_experimenter', 'Dr. Bilbo Baggins', ...
'general_lab', 'Bag End Laboratory', ...
'general_institution', 'University of Middle Earth at the Shire', ...
'general_experiment_description', 'I went on an adventure with thirteen dwarves to reclaim vast treasures.', ...
'general_session_id', 'LONELYMTN' ...
);
 

Device metadata

Device metadata is represented by Device objects.
 
device = types.core.Device();
nwbfile.general_devices.set('Heka ITC-1600', device);

Electrode metadata

Intracellular electrode metadata is represented by IntracellularElectrode objects. Create an electrode object, which requires a link to the device of the previous step. Then add it to the NWB file.
electrode = types.core.IntracellularElectrode( ...
'description', 'a mock intracellular electrode', ...
'device', types.untyped.SoftLink(device), ...
'cell_id', 'a very interesting cell' ...
);
nwbfile.general_intracellular_ephys.set('elec0', electrode);

Stimulus and response data

Intracellular stimulus and response data are represented with subclasses of PatchClampSeries. A stimulus is described by a time series representing voltage or current stimulation with a particular set of parameters. There are two classes for representing stimulus data:
The response is then described by a time series representing voltage or current recorded from a single cell using a single intracellular electrode via one of the following classes:
Below we create a simple example stimulus/response recording data pair.
ccss = types.core.VoltageClampStimulusSeries( ...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint64(15), ...
'stimulus_description', 'N/A' ...
);
 
nwbfile.stimulus_presentation.set('ccss', ccss);
 
vcs = types.core.VoltageClampSeries( ...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'capacitance_slow', 100e-12, ...
'resistance_comp_correction', 70.0, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint64(15) ...
);
nwbfile.acquisition.set('vcs', vcs);

Adding an intracellular recording

The IntracellularRecordingsTable relates electrode, stimulus and response pairs and describes metadata specific to individual recordings.
Illustration of the structure of the IntracellularRecordingsTable
ic_rec_table = types.core.IntracellularRecordingsTable( ...
'categories', {'electrodes', 'stimuli', 'responses'}, ...
'colnames', {'recordings_tag'}, ...
'description', [ ...
'A table to group together a stimulus and response from a single ', ...
'electrode and a single simultaneous recording and for storing ', ...
'metadata about the intracellular recording.'], ...
'id', types.hdmf_common.ElementIdentifiers('data', int64([0, 1, 2])), ...
'recordings_tag', types.hdmf_common.VectorData( ...
'data', repmat({'Tag'}, 3, 1), ...
'description', 'Column for storing a custom recordings tag' ...
) ...
);
 
ic_rec_table.electrodes = types.core.IntracellularElectrodesTable( ...
'description', 'Table for storing intracellular electrode related metadata.', ...
'colnames', {'electrode'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'electrode', types.hdmf_common.VectorData( ...
'data', repmat(types.untyped.ObjectView(electrode), 3, 1), ...
'description', 'Column for storing the reference to the intracellular electrode' ...
) ...
);
 
ic_rec_table.stimuli = types.core.IntracellularStimuliTable( ...
'description', 'Table for storing intracellular stimulus related metadata.', ...
'colnames', {'stimulus'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'stimulus', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded stimulus for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 1, -1], ...
'count', [5, 3, -1], ...
'timeseries', [ ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(ccss), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
ic_rec_table.responses = types.core.IntracellularResponsesTable( ...
'description', 'Table for storing intracellular response related metadata.', ...
'colnames', {'response'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'response', types.core.TimeSeriesReferenceVectorData( ...
'description', 'Column storing the reference to the recorded response for the recording (rows)', ...
'data', struct( ...
'idx_start', [0, 2, 0], ...
'count', [5, 3, 5], ...
'timeseries', [ ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs), ...
types.untyped.ObjectView(vcs) ...
] ...
)...
)...
);
 
The IntracellularRecordingsTable table is not just a DynamicTable but an AlignedDynamicTable. The AlignedDynamicTable type is itself a DynamicTable that may contain an arbitrary number of additional DynamicTable, each of which defines a "category." This is similar to a table with “sub-headings”. In the case of the IntracellularRecordingsTable, we have three predefined categories, i.e., electrodes, stimuli, and responses. We can also dynamically add new categories to the table. As each category corresponds to a DynamicTable, this means we have to create a new DynamicTable and add it to our table.
% add category
ic_rec_table.categories = [ic_rec_table.categories, {'recording_lab_data'}];
ic_rec_table.dynamictable.set( ...
'recording_lab_data', types.hdmf_common.DynamicTable( ...
'description', 'category table for lab-specific recording metadata', ...
'colnames', {'location'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([0, 1, 2]) ...
), ...
'location', types.hdmf_common.VectorData( ...
'data', {'Mordor', 'Gondor', 'Rohan'}, ...
'description', 'Recording location in Middle Earth' ...
) ...
) ...
);
In an AlignedDynamicTable all category tables must align with the main table, i.e., all tables must have the same number of rows and rows are expected to correspond to each other by index.
We can also add custom columns to any of the subcategory tables, i.e., the electrodes, stimuli, and responses tables, and any custom subcategory tables. All we need to do is indicate the name of the category we want to add the column to.
% Add voltage threshold as column of electrodes table
ic_rec_table.electrodes.colnames = [ic_rec_table.electrodes.colnames {'voltage_threshold'}];
ic_rec_table.electrodes.vectordata.set('voltage_threshold', types.hdmf_common.VectorData( ...
'data', [0.1, 0.12, 0.13], ...
'description', 'Just an example column on the electrodes category table' ...
) ...
);
 
nwbfile.general_intracellular_ephys_intracellular_recordings = ic_rec_table;

Hierarchical organization of recordings

To describe the organization of intracellular experiments, the metadata is organized hierarchically in a sequence of tables. All of the tables are so-called DynamicTables enabling users to add columns for custom metadata. Storing data in hierarchical tables has the advantage that it allows us to avoid duplication of metadata. E.g., for a single experiment we only need to describe the metadata that is constant across an experimental condition as a single row in the SimultaneousRecordingsTable without having to replicate the same information across all repetitions and sequential-, simultaneous-, and individual intracellular recordings. For analysis, this means that we can easily focus on individual aspects of an experiment while still being able to easily access information about information from related tables. All of these tables are optional, but to use one you must use all of the lower level tables, even if you only need a single row.

Add a simultaneous recording

The SimultaneousRecordingsTable groups intracellular recordings from the IntracellularRecordingsTable together that were recorded simultaneously from different electrodes and/or cells and describes metadata that is constant across the simultaneous recordings. In practice a simultaneous recording is often also referred to as a sweep. This example adds a custom column, "simultaneous_recording_tag."
% create simultaneous recordings table with custom column
% 'simultaneous_recording_tag'
 
[recordings_vector_data, recordings_vector_index] = util.create_indexed_column( ...
{[0, 1, 2],}, ...
'Column with references to one or more rows in the IntracellularRecordingsTable table', ...
ic_rec_table);
 
ic_sim_recs_table = types.core.SimultaneousRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recordings from ', ...
'the IntracellularRecordingsTable table together that were recorded ', ...
'simultaneously from different electrodes.'...
], ...
'colnames', {'recordings', 'simultaneous_recording_tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(12) ...
), ...
'recordings', recordings_vector_data, ...
'recordings_index', recordings_vector_index, ...
'simultaneous_recording_tag', types.hdmf_common.VectorData( ...
'description', 'A custom tag for simultaneous_recordings', ...
'data', {'LabTag1'} ...
) ...
);
 
Depending on the lab workflow, it may be useful to add complete columns to a table after we have already populated the table with rows. That would be done like so:
ic_sim_recs_table.colnames = [ic_sim_recs_table.colnames, {'simultaneous_recording_type'}];
ic_sim_recs_table.vectordata.set( ...
'simultaneous_recording_type', types.hdmf_common.VectorData(...
'description', 'Description of the type of simultaneous_recording', ...
'data', {'SimultaneousRecordingType1'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_simultaneous_recordings = ic_sim_recs_table;

Add a sequential recording

The SequentialRecordingsTable groups simultaneously recorded intracellular recordings from the SimultaneousRecordingsTable together and describes metadata that is constant across the simultaneous recordings. In practice a sequential recording is often also referred to as a sweep sequence. A common use of sequential recordings is to group together simultaneous recordings where a sequence of stimuli of the same type with varying parameters have been presented in a sequence (e.g., a sequence of square waveforms with varying amplitude).
[simultaneous_recordings_vector_data, simultaneous_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SimultaneousRecordingsTable table', ...
ic_sim_recs_table);
 
sequential_recordings = types.core.SequentialRecordingsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'simultaneous_recordings from the SimultaneousRecordingsTable ', ...
'table together. This is typically used to group together ', ...
'simultaneous_recordings where the a sequence of stimuli of ', ...
'the same type with varying parameters have been presented in ', ...
'a sequence.' ...
], ...
'colnames', {'simultaneous_recordings', 'stimulus_type'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(15) ...
), ...
'simultaneous_recordings', simultaneous_recordings_vector_data, ...
'simultaneous_recordings_index', simultaneous_recordings_vector_index, ...
'stimulus_type', types.hdmf_common.VectorData( ...
'description', 'Column storing the type of stimulus used for the sequential recording', ...
'data', {'square'} ...
) ...
);
 
nwbfile.general_intracellular_ephys_sequential_recordings = sequential_recordings;

Add repetitions table

The RepetitionsTable groups sequential recordings from the SequentialRecordingsTable. In practice, a repetition is often also referred to a run. A typical use of the RepetitionsTable is to group sets of different stimuli that are applied in sequence that may be repeated.
[sequential_recordings_vector_data, sequential_recordings_vector_index] = util.create_indexed_column( ...
{0,}, ...
'Column with references to one or more rows in the SequentialRecordingsTable table', ...
sequential_recordings);
 
 
nwbfile.general_intracellular_ephys_repetitions = types.core.RepetitionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording sequential ', ...
'recordings together. With each SimultaneousRecording typically ', ...
'representing a particular type of stimulus, the RepetitionsTable ', ...
'table is typically used to group sets of stimuli applied in sequence.' ...
], ...
'colnames', {'sequential_recordings'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64(17) ...
), ...
'sequential_recordings', sequential_recordings_vector_data, ...
'sequential_recordings_index', sequential_recordings_vector_index ...
);

Add experimental condition table

The ExperimentalConditionsTable groups repetitions of intracellular recording from the RepetitionsTable together that belong to the same experimental conditions.
[repetitions_vector_data, repetitions_vector_index] = util.create_indexed_column( ...
{0, 0}, ...
'Column with references to one or more rows in the RepetitionsTable table', ...
nwbfile.general_intracellular_ephys_repetitions);
 
nwbfile.general_intracellular_ephys_experimental_conditions = types.core.ExperimentalConditionsTable( ...
'description', [ ...
'A table for grouping different intracellular recording ', ...
'repetitions together that belong to the same experimental ', ...
'conditions.' ...
], ...
'colnames', {'repetitions', 'tag'}, ...
'id', types.hdmf_common.ElementIdentifiers( ...
'data', int64([19, 21]) ...
), ...
'repetitions', repetitions_vector_data, ...
'repetitions_index', repetitions_vector_index, ...
'tag', types.hdmf_common.VectorData( ...
'description', 'integer tag for a experimental condition', ...
'data', [1,3] ...
) ...
);

Write the NWB file

nwbExport(nwbfile, 'test_new_icephys.nwb');

Read the NWB file

nwbfile2 = nwbRead('test_new_icephys.nwb', 'ignorecache')
nwbfile2 =
NwbFile with properties: + + nwb_version: '2.7.0' + file_create_date: [1×1 types.untyped.DataStub] + identifier: 'EXAMPLE_ID' + session_description: 'my first synthetic recording' + session_start_time: [1×1 types.untyped.DataStub] + timestamps_reference_time: [1×1 types.untyped.DataStub] + acquisition: [1×1 types.untyped.Set] + analysis: [0×1 types.untyped.Set] + general: [0×1 types.untyped.Set] + general_data_collection: '' + general_devices: [1×1 types.untyped.Set] + general_experiment_description: 'I went on an adventure with thirteen dwarves to reclaim vast treasures.' + general_experimenter: [1×1 types.untyped.DataStub] + general_extracellular_ephys: [0×1 types.untyped.Set] + general_extracellular_ephys_electrodes: [] + general_institution: 'University of Middle Earth at the Shire' + general_intracellular_ephys: [1×1 types.untyped.Set] + general_intracellular_ephys_experimental_conditions: [1×1 types.core.ExperimentalConditionsTable] + general_intracellular_ephys_filtering: '' + general_intracellular_ephys_intracellular_recordings: [1×1 types.core.IntracellularRecordingsTable] + general_intracellular_ephys_repetitions: [1×1 types.core.RepetitionsTable] + general_intracellular_ephys_sequential_recordings: [1×1 types.core.SequentialRecordingsTable] + general_intracellular_ephys_simultaneous_recordings: [1×1 types.core.SimultaneousRecordingsTable] + general_intracellular_ephys_sweep_table: [] + general_keywords: '' + general_lab: 'Bag End Laboratory' + general_notes: '' + general_optogenetics: [0×1 types.untyped.Set] + general_optophysiology: [0×1 types.untyped.Set] + general_pharmacology: '' + general_protocol: '' + general_related_publications: '' + general_session_id: 'LONELYMTN' + general_slices: '' + general_source_script: '' + general_source_script_file_name: '' + general_stimulus: '' + general_subject: [] + general_surgery: '' + general_virus: '' + intervals: [0×1 types.untyped.Set] + intervals_epochs: [] + intervals_invalid_times: [] + intervals_trials: [] + processing: [0×1 types.untyped.Set] + scratch: [0×1 types.untyped.Set] + stimulus_presentation: [1×1 types.untyped.Set] + stimulus_templates: [0×1 types.untyped.Set] + units: [] +
+
+ +

Using NWB Data

last updated: February 9, 2021
In this tutorial, we demonstrate the reading and usage of the NWB file produced in the File Conversion Tutorial. The output is a near-reproduction of Figure 1e from the Li et al publication, showing raster and peristimulus time histogram (PSTH) plots for neural recordings from anterior lateral motor cortex (ALM). This figure illustrates the main finding of the publication, showing the robustness of motor planning behavior and neural dynamics following short unilateral network silencing via optogenetic inhibition.

Reading NWB Files

NWB files can be read in using the nwbRead() function. This function returns a nwbfile object which is the in-memory representation of the NWB file structure.
nwb = nwbRead('out\ANM255201_20141124.nwb');

Constrained Sets

Analyzed data in NWB is placed under the analysis property, which is a Constrained Set. A constrained set consists of an arbitrary amount of key-value pairs similar to Map containers in MATLAB or a dictionary in Python. However, constrained sets also have the ability to validate their own properties closer to how a typed Object would.
You can get/set values in constrained sets using their respective .get()/.set() methods and retrieve all Set properties using the keys() method, like in a containers.Map.
unit_names = keys(nwb.analysis);

Dynamic Tables

nwb.intervals_trials returns a unique type of table called a Dynamic Table. Dynamic tables inherit from the NWB type types.hdmf_common.DynamicTable and allow for a table-like interface in NWB. In the case below, we grab the special column start_time. Dynamic Tables allow adding your own vectors using the vectordata property, which are Constrained Sets. All columns are represented by either a types.hdmf_common.VectorData or a types.hdmf_common.VectorIndex type.

Data Stubs

The data property of the column id in nwb.units is a types.untyped.DataStub. This object is a representation of a dataset that is not loaded in memory, and is what allows MatNWB to lazily load its file data. To load the data into memory, use the .load() method which extracts all data from the NWB file. Alternatively, you can index into the DataStub directly using conventional MATLAB syntax.

Jagged Arrays in Dynamic Tables

With the new addition of addRow and getRow to Dynamic Tables, the concept of jagged arrays can be worked around and no longer require full understanding outside of specific data format concerns or low-level nwb tool development. The below paragraph is retained in its entirety from its original form as purely informational.
All data in a Dynamic Table must be aligned by row and column, but not all data fits into this paradigm neatly. In order to represent variable amounts of data that is localised to each row and column, NWB uses a concept called Jagged Arrays. These arrays consist of two column types: the familiar types.core.VectorData, and the new types.core.VectorIndex. A Vector Index holds no data, instead holding a reference to another Vector Data and a vector of indices that align to the Dynamic Table dimensions. The indices represent the last index boundary in the Vector Data object for the Vector Index row. As an example, an index of three in the first row of the Vector Index column points to the first three values in the referenced Vector Data column. Subsequently, if the next index were a five, it would indicate the fourth and fifth elements in the referenced Vector Data column.
The jagged arrays serve to represent multiple trials and spike times associated to each unit by id. A convenient way to represent these in MATLAB is to use Map containers where each unit's data is indexed directly by its unit id. Below, we utilize getRow in order to build the same Map.
unit_ids = nwb.units.id.data.load(); % array of unit ids represented within this
% Initialize trials & times Map containers indexed by unit_ids
unit_trials = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_times = containers.Map('KeyType',class(unit_ids),'ValueType','any');
last_idx = 0;
for i = 1:length(unit_ids)
unit_id = unit_ids(i);
row = nwb.units.getRow(unit_id, 'useId', true, 'columns', {'spike_times', 'trials'});
unit_trials(unit_id) = row.trials{1};
unit_times(unit_id) = row.spike_times{1};
end

Process Units

We now do the following for each Unit:
  • Filter out invalid trials
  • Separate datasets based on resulting mouse behavior (right/left licks).
  • Derive "sample", "delay", and "response" times for this analyzed neuron.
  • Compose a peristimulus time histogram from the data.
sorted_ids = sort(unit_ids);
Photostim = struct(...
'ind', true,... % mask into xs and ys for this photostim
'period', 'none',...
'duration', 0,... % in seconds
'ramp_offset', 0); % in seconds
% Initialize Map container of plotting data for each unit, stored as structure
Unit = containers.Map('KeyType',class(unit_ids),'ValueType','any');
unit_struct = struct(...
'id', [],...
'xs', [],...
'ys', [],...
'xlim', [-Inf Inf],...
'sample', 0,...
'delay', 0,...
'response', 0,...
'left_scatter', false,...
'right_scatter', false,...
'photostim', Photostim); % can have multiple photostim
for unit_id = unit_ids'
We first extract trial IDs from the Unit IDs.
unit_trial_id = unit_trials(unit_id);
Then filter out outliers from the Sample, Delay, and Response time points with which we derive a "good enough" estimate.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'PoleInTime', 'PoleOutTime', 'CueTime', 'GoodTrials'});
unit_sample = trial.PoleInTime;
unit_delay = trial.PoleOutTime;
unit_response = trial.CueTime;
unit_good_trials = trial.GoodTrials;
% Subjective parameters
delay_threshold = 0.064;
response_threshold = 0.43;
expected_delay_offset = 1.3; % determined from figure 1a
expected_response_offset = 1.3;
expected_delay = unit_sample + expected_delay_offset;
expected_response = unit_delay + expected_response_offset;
good_delay = (unit_delay > expected_delay - delay_threshold) &...
(unit_delay < expected_delay + delay_threshold);
good_response = (unit_response > expected_response - response_threshold) &...
(unit_response < expected_response + response_threshold);
avg_sample = mean(unit_sample(good_delay & good_response));
avg_delay = mean(unit_delay(good_delay & good_response));
avg_response = mean(unit_response(good_delay & good_response));
Filter the rest of the data by "good" trials.
unit_good_trials = unit_good_trials & good_delay & good_response;
unit_trial_id = unit_trial_id(unit_good_trials);
unit_spike_time = unit_times(unit_id);
unit_spike_time = unit_spike_time(unit_good_trials);
Retrieve good trial data and organize by stimulation type.
trial = nwb.intervals_trials.getRow(unit_trial_id, 'useId', true,...
'columns', {'start_time', 'HitR', 'HitL', 'StimTrials', 'PhotostimulationType'});
unit_is_photostim = logical(trial.StimTrials);
unit_stim_type = trial.PhotostimulationType;
unit_no_stim = ~unit_is_photostim & 0 == unit_stim_type;
unit_sample_stim = unit_is_photostim & 1 == unit_stim_type;
unit_early_stim = unit_is_photostim & 2 == unit_stim_type;
unit_middle_stim = unit_is_photostim & 3 == unit_stim_type;
Compose Scatter Plots and the Peristimulus Time Histogram zeroed on the Response time.
xs = unit_spike_time - trial.start_time - avg_response;
ys = unit_trial_id;
curr_unit = unit_struct;
curr_unit.xs = xs;
curr_unit.ys = ys;
curr_unit.left_scatter = logical(trial.HitL);
curr_unit.right_scatter = logical(trial.HitR);
curr_unit.sample = avg_sample - avg_response;
curr_unit.delay = avg_delay - avg_response;
curr_unit.response = 0;
% Photostim periods
curr_unit.photostim.ind = unit_no_stim;
% Sample
if any(unit_sample_stim)
SampleStim = Photostim;
SampleStim.ind = unit_sample_stim;
SampleStim.period = 'Sample';
SampleStim.duration = 0.5;
SampleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = SampleStim;
end
% Early Delay
if any(unit_early_stim)
early_stim_types = unique(unit_stim_type(unit_early_stim));
for i_early_types=1:length(early_stim_types)
early_type = early_stim_types(i_early_types);
EarlyStim = Photostim;
EarlyStim.period = 'Early Delay';
EarlyStim.ind = early_type == unit_stim_type & unit_early_stim;
if early_type == 2
EarlyStim.duration = 0.5;
EarlyStim.ramp_offset = 0.1;
else
EarlyStim.duration = 0.8;
EarlyStim.ramp_offset = 0.2;
end
curr_unit.photostim(end+1) = EarlyStim;
end
end
% Middle Delay
if any(unit_middle_stim)
MiddleStim = Photostim;
MiddleStim.ind = unit_middle_stim;
MiddleStim.period = 'Middle Delay';
MiddleStim.duration = 0.5;
MiddleStim.ramp_offset = 0.1;
curr_unit.photostim(end+1) = MiddleStim;
end
Unit(unit_id) = curr_unit;
end

Plot Example Neurons

neuron_labels = [2, 3]; % neuron labels from Figure 1e
neuron_ids = [11, 2]; % neuron unit IDs corresponding to the Fig 1e labels
num_conditions = 4; % photostim conditions: nostim, sample, early, middle if applicable
num_neurons = length(neuron_ids);
% Inititalize data structures for each summary plot of categorized neural spike data at specified stimulus condition
RasterPlot = struct(...
'xs', 0,...
'ys', 0);
ConditionPlot = struct(...
'label', '',...
'xlim', 0,...
'sample', 0,...
'delay', 0,...
'response', 0,...
'right_scatter', RasterPlot,...
'left_scatter', RasterPlot,...
'psth_bin_window', 0,...
'stim_type', '');
fig = figure;
% Plot neural spike data for each neuron and stimulus condition in a subplot array: num_neurons (rows) x num_conditions (columns)
for nn=1:num_neurons
Neuron = Unit(neuron_ids(nn));
% Initialize structure with neural + stimulus condition data
CurrPlot = ConditionPlot;
CurrPlot.xlim = [min(Neuron.xs) max(Neuron.xs)];
CurrPlot.sample = Neuron.sample;
CurrPlot.delay = Neuron.delay;
CurrPlot.response = Neuron.response;
% Plot each neuron/condition
plot_row = (nn - 1) * num_conditions;
for cc=1:num_conditions
ax = subplot(num_neurons, num_conditions, plot_row + cc, 'Parent', fig);
Stim = Neuron.photostim(cc);
CurrPlot.stim_type = Stim.period;
if strcmp(Stim.period, 'none')
CurrPlot.label = sprintf('Neuron %d', neuron_labels(nn));
CurrPlot.psth_bin_window = 9;
else
CurrPlot.label = Stim.period;
CurrPlot.psth_bin_window = 2;
end
stim_left_scatter_ind = Stim.ind & Neuron.left_scatter;
stim_left_scatter_trials = Neuron.ys(stim_left_scatter_ind);
CurrPlot.left_scatter.xs = Neuron.xs(stim_left_scatter_ind);
[~,CurrPlot.left_scatter.ys] = ismember(stim_left_scatter_trials,unique(stim_left_scatter_trials));
stim_right_scatter_ind = Stim.ind & Neuron.right_scatter;
stim_right_scatter_trials = Neuron.ys(stim_right_scatter_ind);
CurrPlot.right_scatter.xs = Neuron.xs(stim_right_scatter_ind);
[~,CurrPlot.right_scatter.ys] = ismember(stim_right_scatter_trials,unique(stim_right_scatter_trials));
plot_condition(ax, CurrPlot);
end
end
+ + +

Helper Functions

PSTH helper function
function [psth_xs, psth_ys] = calculate_psth(xs, bin_window, bin_width)
[bin_counts, edges] = histcounts(xs, 'BinWidth', bin_width);
psth_xs = edges(1:end-1) + (bin_width / 2);
moving_avg_b = (1/bin_window) * ones(1,bin_window);
psth_ys = filter(moving_avg_b, 1, bin_counts);
end
Plotter function for each stimulus condition
function plot_condition(ax, ConditionPlot)
left_cdata = [1 0 0]; % red
right_cdata = [0 0 1]; % blue
hist_margin = 50;
scatter_margin = 10;
% Calculate PSTH values
% moving average over 200 ms as per figure 1e
hist_bin_width = 0.2 / ConditionPlot.psth_bin_window;
[left_psth_xs, left_psth_ys] =...
calculate_psth(ConditionPlot.left_scatter.xs, ConditionPlot.psth_bin_window, hist_bin_width);
[right_psth_xs, right_psth_ys] =...
calculate_psth(ConditionPlot.right_scatter.xs, ConditionPlot.psth_bin_window, hist_bin_width);
right_scatter_offset = min(ConditionPlot.right_scatter.ys);
right_scatter_height = max(ConditionPlot.right_scatter.ys) - right_scatter_offset;
left_scatter_offset = min(ConditionPlot.left_scatter.ys);
left_scatter_height = max(ConditionPlot.left_scatter.ys) - left_scatter_offset;
psth_height = max([left_psth_ys right_psth_ys]);
left_y_offset = hist_margin...
+ psth_height...
- left_scatter_offset;
right_y_offset = scatter_margin...
+ left_y_offset...
+ left_scatter_offset...
+ left_scatter_height...
- right_scatter_offset;
subplot_height = right_y_offset...
+ right_scatter_offset...
+ right_scatter_height;
hold(ax, 'on');
% PSTH
plot(ax, left_psth_xs, left_psth_ys, 'Color', left_cdata);
plot(ax, right_psth_xs, right_psth_ys, 'Color', right_cdata);
% Scatter Plot
scatter(ax,...
ConditionPlot.left_scatter.xs,...
left_y_offset + ConditionPlot.left_scatter.ys,...
'Marker', '.',...
'CData', left_cdata,...
'SizeData', 1);
scatter(ax,...
ConditionPlot.right_scatter.xs,...
right_y_offset + ConditionPlot.right_scatter.ys,...
'Marker', '.',...
'CData', right_cdata,...
'SizeData', 1);
% sample, delay, response lines
line(ax, repmat(ConditionPlot.sample, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
line(ax, repmat(ConditionPlot.delay, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
line(ax, repmat(ConditionPlot.response, 1, 2), [0 subplot_height],...
'Color', 'k', 'LineStyle', '--');
% blue bar for photoinhibition period
if ~strcmp(ConditionPlot.stim_type, 'none')
stim_height = subplot_height;
stim_width = 0.5; % seconds
% end time relative to 'go' cue as described in the paper.
switch ConditionPlot.stim_type
case 'Sample'
end_offset = 1.6;
case 'Early Delay'
end_offset = 0.8;
case 'Middle Delay'
end_offset = 0.3;
otherwise
error('Invalid photostim period `%s`', ConditionPlot.stim_type);
end
stim_offset = ConditionPlot.response - stim_width - end_offset;
patch_vertices = [...
stim_offset, 0;...
stim_offset, stim_height;...
stim_offset+stim_width, stim_height;...
stim_offset+stim_width, 0];
patch(ax,...
'Faces', 1:4,...
'Vertices', patch_vertices,...
'FaceColor', '#B3D3EC',... % light blue shading
'EdgeColor', 'none',...
'FaceAlpha', 0.8);
end
title(ax, ConditionPlot.label);
xlabel(ax, 'Time (Seconds)');
ylabel(ax, 'Spikes s^{-1}')
xticks(ax, [-2 0 2]);
yticks(ax, [0 max(10, round(psth_height, -1))]);
% legend(ax, [scatter_left_plot, scatter_right_plot], {'Left Lick', 'Right Lick'},...
% 'location', 'northwestoutside');
ax.TickDir = 'out';
ax.XLim = ConditionPlot.xlim;
ax.YLim = [0 subplot_height];
hold(ax, 'off');
end
+
+ +