-
Notifications
You must be signed in to change notification settings - Fork 2
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
Block
andAnimal
objects directly from theTank
level - access the needed data directly from the block
- filter
Blocks
andAnimals
byMetadata
orKeys
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)];
end
Bs1
and Bs2
are equivalent. They contain the 2nd and 5th Blocks of the first three Animals of tankObj
Note: the keyword
end
is 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.Animal
is scalar, then using a single index returns the corresponding element of the.Children
(array) property of "Child"nigelObj
objects.
Return first
Block
directly fromAnimal
using numeric indexing.
bb = 1;
B1 = animalObj{bb};
B2 = animalObj.Children(bb);
-
B1
andB2
are 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 vectornigelObj
objects requires two indexing subscripts.
- If
nigeLab.Animal
is a vector, then two indexing subscript arguments are required.- The first subscript argument addresses the
Animal
object(s) to be accessed in the array. - The second subscript argument references the
.Block
object(s) from the.Children
(array) property of theAnimal
objects 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
, andblockArray3
are equivalent.
** NOTE: **
B = animalObj{bb};
is unsupported for nigeLab.Animal arrays.
- To access
nigeLab.Animal
indexed bybb
from 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
Block
in the.Children
property oftankobj.Children(1)
- Simultaneously, return the first two
Block
objects in the.Children
property oftankObj.Children(2)
- Note: Returned array
B
could be:empty
,scalar
, or[1 x 2] nigeLab.Block
array.
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 (
aa
andbb
in examples) do not need to be numeric. -
'Public'
field of'Key'
propertystruct
can 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, theBlock
corresponding to it is returned as anempty
Block
object.
- If a
Return (scalar)
Block
that matcheskey1
, searching all elements of.Children
oftankObj.Children([1,2])
.
aa=[1,2];
bb=key1; % e.g. key1 = getKey(blockObj1,'Public');
B = tankObj{aa,bb};
Return the
Block
object or array for elements matchingkey1
orkey2
in the.Children
property 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
key1
among theBlock
objects in the.Children
property oftankObj.Children(1)
- Simultaneously, this looks for
Block
objects with matches forkey2
andkey3
in theBlock
objects in the array.Children
oftankObj.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
.Meta
property to select the correctAnimal
orBlock
object(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 name
error 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
nigelObj
as 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
Animal
objects should have the same fields in .Meta and allBlock
objects 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 referenceBlock
elements two levels below theBlock
hierarchical 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.Shortcuts
now has a new format.
-
default.Shortcuts
now returns astruct
. Each field of the returnedstruct
is a shortcut keyword.- Each keyword
struct
field contains a secondarystruct
with 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 correspondingsubfields
entry. Each logical element indicates that its correspondingsubfields
entry 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
+defaults
file, 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,
snippet1
andsnippet2
are equivalent.- Notice that the Shortcut keyword is the same as the corresponding
field
in the Shortcutsstruct
, but does not need to correspond to any existing field in theBlock
object 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
snippet1
andsnippet2
are equivalent.
Use even more compact indexing syntax
- Fields in
Channels
FieldType
defined inFields
of theBlock.m
default 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
snippet1
andsnippet2
are 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
Raw
data, 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
snippet1
andsnippet2
are 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
Channels
sub-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 array
inC
, which the first sample from channel 1 in its firstcell
and 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 array
inC
, which contains the first spike of channel 1 in its firstcell
and the first two spikes of channel 2 its secondcell
.
Note: The operator end is not available in shortcuts!