MATLAB: Pass part of structure field name to function - matlab

I need to pass a part of a structure's name into a function.
Examples of a available structs:
systems.system1.stats.equityCurve.relative.exFee
systems.system1.stats.equityCurve.relative.inFee
systems.system2.stats.equityCurve.relative.exFee
systems.system2.stats.equityCurve.relative.inFee
systems.system1.returns.aggregated.exFee
systems.system1.returns.aggregated.inFee
systems.system2.returns.aggregated.exFee
systems.system2.returns.aggregated.inFee
... This goes on...
Within a function, I loop through the structure as follows:
function mat = test(fNames)
feeString = {'exFee', 'inFee'};
sysNames = {'system1', 'system2'};
for n = 1 : 2
mat{n} = systems.(sysNames{n}).stats.equityCurve.relative.(feeString{n});
end
end
What I like to handle in a flexible way within the loop is the middle part, i.e. the part after systems.(sysNames{n}) and before .(feeString{n}) (compare examples).
I am now looking for a way to pass the middle part as an input argument fNames into the function. The loop should than contain something like
mat{n} = systems.(sysNames{n}).(fName).(feeString{n});

How about using a helper function such as
function rec_stru = recSA(stru, field_names)
if numel(field_names) == 1
rec_stru = stru.(field_names{1});
else
rec_stru = recSA(stru.(field_names{1}), field_names(2:end));
end
This function takes the intermediate field names as a cell array.
This would turn this statement:
mat{n} = systems.(sysNames{n}).stats.equityCurve.relative.(feeString{n});
into
mat{n} = recSA(systems.(sysNames{n}), {'stats', 'equityCurve', 'relative', feeString{n}});
The first part of the cell array could then be passed as an argument to the function.

This is one of those cases where matlab is a bit unhelpful in the documentation. There is a way to use the fieldnames function in matlab to get the list of all the fields and iterate over that using dynamic fields.
systems.system1.stats.equityCurve.relative.exFee='T'
systems.system1.stats.equityCurve.relative.inFee='E'
systems.system2.stats.equityCurve.relative.exFee='S'
systems.system2.stats.equityCurve.relative.inFee='T'
systems.system1.returns.aggregated.exFee='D'
systems.system1.returns.aggregated.inFee='A'
systems.system2.returns.aggregated.exFee='T'
systems.system2.returns.aggregated.inFee='A'
dynamicvariable=fieldnames(systems.system1)
This will return a cell matrix of the field names which you can use to iterate over.
systems.system1.(dynamicvariable{1})
ans =
equityCurve: [1x1 struct]
Ideally you would have your data structure fixed in such a way that you know how many levels of depth are in your data structure.

Related

MATLAB: Using get in cellfun for a cell array of objects

I've been stuck with this for a while and I couldn't find something similar asked previously (or I have failed in doing so)
My situation is fairly simple: I have a cell array of objects. They are all the same object and I have a get function for this kind of object which is: get (obj, attr), where obj is the object in question and attr is a integer from 1-6. Depending on the number the get function returns the corresponding attribute.
I would like to obtain all of my "position" attributes from all my objects which are in the corresponding cell array (this would be attr = 2). I know that cellfun performs a function on all cells, but the question is, how do I use my get function here for all my objects, taking into account that the function is get (obj, attr) ?
Thanks in advance
Firstly, by using get as a custom function you are shadowing the built-in get function - this is bad practise!
With this in mind, and to avoid confusion with the built-in get function which has similar syntax, I'm going to use getattr as a stand-in for your custom function which accpets an object and an integer 1 to 6.
pos = cellfun( #(obj) getattr( obj, 2 ), myCellOfObjects, 'uni', 0 );
By specifying 'uni', 0, the output doesn't have to be scalar and will be put into a cell array. This is useful when, for example, you have a multi-element array for your position.
This is equivalent to the following loop:
pos = cell( numel(myCellOfObjects), 1 );
for ii = 1:numel(pos)
pos{ii} = getattr( myCellOfObjects{ii}, 2 );
end
If ever in doubt about cellfun or arrayfun, just write a loop first - they are essentially the same but more concise.
There is a trick to this some are unaware of: you can pass multiple arguments to cellfun like this:
cellfun(#(obj,attr) get(obj,attr), {obj1,obj2},{attr1,attr2},'uni',0)
if you want to get one attribute of the cellarray (instead of providing an attribute for every object in the cellarray), then you can simply use this
cellfun(#(x) getattr(x,attr),obj,'uni',0)
put into anonymous function for convenience:
get_attr = #(obj,attr) cellfun(#(x) getattr(x,attr),obj,'uni',0)
%use:
get_attr(obj_in_cellarray,'myattribute')
%returns cell array of object attributes
I haven't run any of these functions since you didn't provide any example data / code. Please test and feedback.

Dealing with numbered variable names MATLAB

This is a bit of a long problem:
I am building an extension to some already existing software that outputs data as a structure array each time it is run. They always have the same name (structureArray)
I want to take all of these structure arrays and use them for analysis in a single code with for loops and cell arrays.
So I now have 3 structure arrays from this existing software, which I have named structureArray1, structureArray2 and structureArray3. I have used the following method for putting each of these into a cell array called "storage".
[filename, pathname] = uigetfile('*.mat','Please select your structure arrays',...
'Multiselect','on');
storage = cell(1,numel(filename));
for x = 1 : numel(filename)
storage{x} = load([pathname filename{x}]);
end
Now here's the problem:
in each structureArray(1,2,3) (now within "storage") there is a matrix called "magV". I would like to have a 1x3 cell array, with the first cell containing magV from structureArray1, the second cell containing magV from structureArray2 and so on...
My attempt so far:
magnitude_V = cell(1,numel(storage));
for y = 1 : numel(storage)
magnitude_V{y} = storage{1,y}.structureArray1.velocityMap.magV;
end
But because all of the structure arrays have a different number at the end, I can't use this method...
Thank you so much for any help because this is driving me mad -.-
You can refer to a structure's fied by a string in parenthesis, e.g. sometruct.('somefield'):
magnitude_V = cell(1,size(storage,2));
for y = 1 : size(storage,2)
magnitude_V{y} = storage{y}.(['structureArray' num2str(y)]).velocityMap.magV;
end

Call a function with several properties of an object

For (MEX) function calls it would be really nice to pass several properties of one object at once. Instead of foo(myObj.propA, myObj.propB) I want something like foo(myObj.[propA,propB].
Is this even possible?
With structs it is possible to use the getfield() function to get the data from more than one field, e.g.:
getfield(myStruct, {index}, {'fieldA', 'fieldB'})
But unfortunately, the following attempt to get more than one property from an object results in an error (Index exceeds matrix dimensions):
getfield(myObj, {index}, {'propA', 'propB'})
Maybe the only possibility is to write a function which returns several output arguments:
[varargout] = getProps(object,propnames)
for p=1:numel(propnames)
varargout{p} = object.(propnames{p});
end
But if I call another function with that function as input, e.g. sum(getProps(myObj,propnames)) only the first output argument of getProps is passed and I fall into despair. Is there any other way?
For an object, you'd use get, not getfield (or dynamic access in a loop like you showed).
>> h = figure;
>> get(h,{'Position','Renderer'})
ans =
[1x4 double] 'opengl'
This doesn't work for all objects, but for MATLAB graphics objects it does work. To deal with any class, you can use your function, but with a custom cell output instead of varargout:
function C = getProps(object,propnames)
for p = 1:numel(propnames),
C{p} = object.(propnames{p});
end
Then inside whatever function you write, you can get a comma-separated list of all properties with C{:}, which will be suitable for a function that expects each property name input as a separate argument (e.g. C = getProps(myObj,propnames); x = myFun(h,C{:}).

Matlab dynamic fieldnames structure with cell arrays

How can i access the following structure path with dynamic fieldnames:
var = 'refxtree.CaseDefinition.FlowSheetObjects.MaterialStreamObjects{8}.MaterialStreamObjectParams.Pressure.Value.Text';
fields = textscan(var,'%s','Delimiter','.');
refxtree.(fields{:}) does not work because MaterialStreamObjects contains a cell array of which I want to access the 8th cell and then continue down the structure path.
In the end I want to get and set the fieldvalues.
You need to build the appropriate input to subsref, possibly using substruct. Look at the MATLAB help.
You can define an anonymous function to navigate this particular kind of structure of the form top.field1.field2.field3{item}.field4.field5.field6.field7 (as an aside: is it really necessary to have such a complicated structure?).
getField = #(top,fields,item)top.(fields{1}).(fields{2}).(fields{3}){item}.(fields{4}).(fields{5}).(fields{6}).(fields{7})
setField = #(top,fields,item,val)subsasgn(top.(fields{1}).(fields{2}).(fields{3}){item}.(fields{4}).(fields{5}).(fields{6}),struct('type','.','subs',fields{7}),val);
You use the functions by calling
fieldValue = getField(refxtree,fields,8);
setField(refxtree,fields,8,newFieldValue);
Note that fields is required to have seven elements. If you want to generalize the above, you will have to dynamically create the above functions
In this case, it is easier to just use EVAL:
str = 'refxtree.CaseDefinition.FlowSheetObjects.MaterialStreamObjects{8}.MaterialStreamObjectParams.Pressure.Value.Text';
%# get
x = eval(str)
%# set
evalc([str ' = 99']);

Provide variable argument list

I have a function which accepts a variable number of input variables. The problem is, the number of input arguments I'm going to provide varies. As such, I store all the arguments in a structure:
function grandAvg(datafiles)
% Load up all averaged datafiles
avgs = struct();
for n=1:length(datafiles)
avgs(n).avg = load(datafiles{n});
end
My question is, is there a way to expand this argument for the function? I need a way to convert this:
% DOES NOT WORK
avg = ft_timelockgrandaverage(cfg, avgs);
to this:
% WOULD WORK, BUT DO NOT WANT TO TYPE IT OUT
avg = ft_timelockgrandaverage(cfg, avgs(1).avg, ..., avgs(n).avg);
EDIT TO ADD: So apparently my question wasn't clear. I know how to construct the function using varargin. My question was, if I am trying to use a build-in function which I don't want to or can't modify, how can I provide arguments in a variable manner? I.e., I don't know ahead of time how many argument's I'll be providing, so when I call the function, I'll have to call it with X number of arguments. In effect, I'm looking for a way to turn this:
someVar <1xN struct>
into this:
someVar1 <1x1 struct>
someVar2 <1x1 struct>
...
someVarN <1x1 struct>
in a programmatic manner. Thanks!
An alternative to using a structure array to store your data would be to use a cell array. For example:
nFiles = numel(datafiles); %# Number of files
avgs = cell(1,nFiles); %# Initialize an empty cell array
for iFile = 1:nFiles %# Loop over the files
avgs{iFile} = load(datafiles{iFile}); %# Load the data into each cell
end
avg = ft_timelockgrandaverage(cfg, avgs{:}); %# Pass the contents to a function
The syntax avgs{:} dumps the contents of the cell array into what's called a comma-separated list. It is equivalent to typing avgs{1}, avgs{2}, ... , avgs{end}. The syntax foo(:).bar from the answer you found also creates a comma-separated list, but I find that using cell arrays for such a purpose is generally cleaner than using a structure array.
yes you can use variable length input argument list
varargin
http://www.mathworks.com/help/techdoc/ref/varargin.html
So, after playing around, I've got it. Using the example from above:
Given an 1xN struct named foo, where each foo(n) contains the field bar, I would call the function as:
function(foo(:).bar);
This is the same as typing
function(foo(1).bar, foo(2).bar, ..., foo(N).bar);
In this way, I can dynamically expand or shrink foo and still have no problem calling the function.
You can surely do such a thing, by means of the varargin construct in MATLAB. This will be something like:
avg = ft_timelockgrandaverage(cfg, avgs.avg);
And for the function ft_timelockgrandaverage
function output = ft_timelockgrandaverage(config, varargin)
% your code here
varargin will be a cell array: {avgs(1).avg, avgs(2).avg, ..., avgs(3).avg} which you can process.