-
Notifications
You must be signed in to change notification settings - Fork 3
Startup_Shortcuts
Easier indexing to prevent cumbersome array references and iterations.
By exploiting the subsref methods of Tank, Animal and Block it is possibile to use the {} operator to quickly access the needed nigelObjs without having to navigate the full data structure.
That means that you can:
- access
BlockandAnimalobjects directly from theTanklevel - access the needed data directly from the block
- filter
BlocksandAnimalsbyMetadataorKeys
Use
{}brackets to access hierarchical elements more easily.
-
A = tankObj{aa};is equivalent toA = tankObj.Children(aa); -
B = tankObj{aa,bb};is equivalent toB = tankObj.Children(aa).Children(bb);
Note: aa and bb can be arrays. In that case bb is used to index the animals selected with aa.
aa = 1:3;
bb = [2 5];
Bs1 = tankObj{aa,bb};
Bs2 = [];
As= tankObj{aa};
for a = 1:numel(As)
Bs2 = [Bs2 As(a).Children(bb)];
endBs1 and Bs2 are equivalent. They contain the 2nd and 5th Blocks of the first three Animals of tankObj
Note: the keyword
endis not available with{}indexing, while:can be used.
nigeLab.Animal changes its behavior depending on if it is a scalar or vector object
Only the use of single-index with
{}is allowed for scalar objects.
- If
nigeLab.Animalis scalar, then using a single index returns the corresponding element of the.Children(array) property of "Child"nigelObjobjects.
Return first
Blockdirectly fromAnimalusing numeric indexing.
bb = 1;
B1 = animalObj{bb};
B2 = animalObj.Children(bb);-
B1andB2are equivalent.
Don't do this, please.
bb = 1;
cc = 1:64; % Channels? Who knows
B = animalObj{bb,cc}; % This will throw a warning- Issues a warning and ignores all index arguments except
bb(the first)- This effectively makes the previous statement equivalent to:
bb = 1;
B = animalObj.Children(bb);Use of
{}with vectornigelObjobjects requires two indexing subscripts.
- If
nigeLab.Animalis a vector, then two indexing subscript arguments are required.- The first subscript argument addresses the
Animalobject(s) to be accessed in the array. - The second subscript argument references the
.Blockobject(s) from the.Children(array) property of theAnimalobjects returned by the first argument.
- The first subscript argument addresses the
- This makes it functionally equivalent to the tankObj syntax, see the examples below.
Return the first three Block objects from the second and fourth Animal in the array.
aa = [2,4]; % Animal indices from array
bb = 1:3; % Block indices (must have at least 3 in .Children)
% Standard "old-fashioned" way:
blockArray1 = animalObj(aa).Children(bb);
% which will throw an error due to the fact that the . operator doesn't really get along with arrays
% Nigel syntax:
blockArray2 = animalObj{aa,bb};
% Alternatively, if animalObj is the full set of tankObj.Children:
blockArray3 = tankObj{aa,bb};- The objects returned by
blockArray1,blockArray2, andblockArray3are equivalent.
** NOTE: **
B = animalObj{bb};is unsupported for nigeLab.Animal arrays.
- To access
nigeLab.Animalindexed bybbfrom an array:A = animalObj(2); % Second animal in animalObj array
{}allows asymmetric indexing, which applies different index subscripts to each element in an array.
- Each returned element is contained in a cell array.
- The second argument must be a cell array, with the same number of cells as indices in the first subscript argument.
Return the first
Blockin the.Childrenproperty oftankobj.Children(1)
- Simultaneously, return the first two
Blockobjects in the.Childrenproperty oftankObj.Children(2)
- Note: Returned array
Bcould be:empty,scalar, or[1 x 2] nigeLab.Blockarray.
B = tankObj{[1,2],{1,[1,2]}}; is equivalent to
b1 = tankObj{1,1};
b2 = tankObj{2,1:2};
B = [b1 b2];Use
'Key'in place of numeric indexing subscript arguments.
- Indices (
aaandbbin examples) do not need to be numeric. -
'Public'field of'Key'propertystructcan be used equivalently.-
'Key': Random "unique" alphanumeric char array (starts with alphabetical element always) - For example, you could use a numeric vector for the first index and a
'Key'as the second index. - To address multiple objects at once using
'Key'-indexing, assign each'Key'as a cell array element.
-
-
Note: Unlike numeric-indexing,
'Key'-indexing will not throw an error if an "out-of-range" key is requested.- If a
Block'Key'has no valid matches, theBlockcorresponding to it is returned as anemptyBlockobject.
- If a
Return (scalar)
Blockthat matcheskey1, searching all elements of.ChildrenoftankObj.Children([1,2]).
aa=[1,2];
bb=key1; % e.g. key1 = getKey(blockObj1,'Public');
B = tankObj{aa,bb};Return the
Blockobject or array for elements matchingkey1orkey2in the.Childrenproperty oftankObj.Children([1,2])
...
% Processing returns key1, key2
% Keys to two blocks of interest
...
aa = [1,2];
bb={key1,key2};
B = tankObj{aa,bb};Use
'Key'-indexing instead of numeric indexing for asymmetric access.
B = tankObj{[1,2], {key1, {key2 key3} } };- This looks for a match to
key1among theBlockobjects in the.Childrenproperty oftankObj.Children(1) - Simultaneously, this looks for
Blockobjects with matches forkey2andkey3in theBlockobjects in the array.ChildrenoftankObj.Children(2)
You can go crazy with this apparently. (FB had too much fun it looks like)
B = tankObj{ {keya1, keya2} , {keyb1, {keyb2 keyb3} } };- Technically, this is supported, but it probably needs more testing and may lead to unexpected results.
Don't do this, please. Please.
B = tankObj{ [1,2], {1, {key1 key2} } };Mixing numeric and 'Key'-based indexing for a single index argument is not supported.
Use the Metadata stored in the
.Metaproperty to select the correctAnimalorBlockobject(s)
- Metadata filtering follows matlab's
Name-Value comma-separated syntax. - If no field with the given name is present in the .Meta struct, an
Unrecognized field nameerror is raised. - When more then one filter is included (i.e. more then one Name-Value pair), they are concatenated with a logical &.
- When more then one filter is included (i.e. more then one Name-Value pair), they need to be wrapped in cell array.
- This indexing can be applied to arrays of
nigelObjas well. In this case the array is treated equally to a single object on the above level.
A = tankObj{'Species','Rat'};
allAnimals = tankObj.Children;
A = allAnimals {'Species','Rat'};
A2 = tankObj{{'Species','Rat','ElectrodeLayout','X'}};
A2 = allAnimals{{'Species','Rat','ElectrodeLayout','X'}};In A we have all animals with a field .Meta.Species == 'Rat', while in A2 we have all the rats that have the ElectrodeLayout 'X'.
Note! Nigel doesn't like when the .Meta strutures are not uniform. All
Animalobjects should have the same fields in .Meta and allBlockobjects should have the same fields in .Meta.
You can go on and index the blocks as well, directly from the tank level.
B = tankObj{{'Species','Rat'},{'RecDate','19700101'}};This will return in B all the Block objects with 'RecDate' equals to '19700101' collected from all the Animal objects wirh 'Species' equal to 'Rat'.
Asymmetric indexing is not supperted for Meta-based indexing (yet).
We gazed far too long into the abyss
- By using
{}in conjunction withnigeLab.defaults.Shortcuts, it is possible to directly referenceBlockelements two levels below theBlockhierarchical level.
- This makes it convenient to work with various fields such as
nigeLab.Block.Channels.Spikes, which have extra '.' elements and long, cumbersome names for weary fingers to type.
default.Shortcutsnow has a new format.
-
default.Shortcutsnow returns astruct. Each field of the returnedstructis a shortcut keyword.- Each keyword
structfield contains a secondarystructwith two fields:-
subfields: A cell array of char arrays, each starting with aFieldType(e.g.'Channels') and listing in order the'.'-indexed fields to get to the Shortcut. -
indexable: A logical array that must have the same number of elements as its correspondingsubfieldsentry. Each logical element indicates that its correspondingsubfieldsentry requires()-based numeric indexing (iftrue) or not.
-
- Each keyword
Access the first 100 samples in the
'Raw'data of the channeliCh
% Typical, boring "hooman" way:
snippet = blockObj.Channels(iCh).Raw(1:100);By configuring ~/+nigeLab/+defaults/Shortcuts.m, we can make the same reference shorter.
- In the
+defaultsfile, add:
pars.x.subfields = {'Channels', 'Raw'};
pars.x.indexable = [true , true];- Next, run:
% Load new Shortcuts parameters directly from +defaults file
updateParams(blockObj,'Shortcuts','Direct');- Now, the Shortcut should work:
% Typical, boring "hooman" way:
snippet1 = blockObj.Channels(iCh).Raw(1:100);
% Exciting, superior "Nigel" way:
snippet2 = blockObj{'x',iCh,1:100};- In this example,
snippet1andsnippet2are equivalent.- Notice that the Shortcut keyword is the same as the corresponding
fieldin the Shortcutsstruct, but does not need to correspond to any existing field in theBlockobject or relate in any way to itsFields.
- Notice that the Shortcut keyword is the same as the corresponding
Use Shortcuts to access samples of a digital stream.
% Inferior "hooman" method:
snippet = blockObj.Streams.DigIO(iCh).data(1:100);- First, add the following to the config file:
pars.io.subsfield = {'Streams', 'DigIO', 'data'};
pars.io.indexable = [false , true , true];- Notice that
'Streams'is not()-indexed. In any reference to'Streams', it is strictly.-indexed.- e.g.
value = blockObj.Streams.(streamsField).(substreamsField);
- e.g.
After updating Shortcuts parameters (see above) 'Streams.DigIO' can be referenced more easily:
k = getStreamIndex(blockObj,'trial-running');
% Carpal-tunnel-inducing "hooman" method:
snippet1 = blockObj.Streams.DigIO(k).data(1:100);
% Undeniably optimal Nigel way:
snippet2 = blockObj{'io',k,1:100};- The returned values of
snippet1andsnippet2are equivalent.
Use even more compact indexing syntax
- Fields in
ChannelsFieldTypedefined inFieldsof theBlock.mdefault file may be indexed directly fromBlock.
Use two
()-based indices instead of three:
% For Nigel, when he is feeling sensible:
snippet1 = blockObj.Channels(iCh).Raw(1:100);
% For Nigel, when he is feeling fancy:
snippet2 = blockObj.raw(iCh,1:100);- The returned values of
snippet1andsnippet2are equivalent.- (See example re:
'x'above for explanation of setting up this style of shortcut for'raw')
- (See example re:
Warning: Advanced indexing is only available in {}, not using direct indexing!
Return identically indexed elements using a single array.
- This may be convenient when you are accessing something like
Rawdata, which should have an identical number of samples for each amplifier channel of a given recording (Block).
Return the first ten samples from the first two channels.
% Infuriatingly verbose "hooman" method:
snippet1 = nan(2,10);
for iCh = 1:2
snippet1(iCh,:) = blockObj.Channels(iCh).Raw.data(1:10);
end
% Refined, elegant Nigel way:
snippet2 = blockObj{'raw',[1,2], 1:10};- The values returned in
snippet1andsnippet2are identical.
Access different indices in different channels using curly brackets.
- This makes it possible to return different numbers of elements from an array.
- This is particularly useful, for example, if you think different
Channelssub-fields should return different numbers of elements based on indexing derived elsewhere.
- This is particularly useful, for example, if you think different
Return the first sample from channel 1, and the first two samples from channel 2.
% Nigel would be unlikely to use it this way:
C = blockObj{'raw',[1,2],{1,[1,2]}};- This returns a
cell arrayinC, which the first sample from channel 1 in its firstcelland the first two samples from channel 2 in its secondcell.
% Nigel would do this though:
C = blockObj{'spikes',[1,2],{1,[1,2]}}; % Spikes likely to have different # per channel- This returns a
cell arrayinC, which contains the first spike of channel 1 in its firstcelland the first two spikes of channel 2 its secondcell.
Note: The operator end is not available in shortcuts!
