After dabbling in C# I'm now keen to use some OOP in Matlab - to date I've done none!
Let's say that I define a class for a data object with a handful of properties...
classdef TestRigData
properties
testTemperature
sampleNumber
testStrainAmplitude
sampleMaterial
rawData
end
methods
% Some constructors and data manipulation methods in here
end
end
...where 'rawData' would be a m-by-n array of actual experimental data, and the other values being doubles or strings to help identify each specific experiment.
If I have an array of TestRigData objects, what would be the best way of finding the indices of objects which meet specific criteria (e.g. testTemperature == 200)? Or getting all the unique values of a property (e.g. all the unique sample numbers in this collection).
If they were arrays of their own, (myNewArray = [3 5 7 22 60 60 5]) it would be easy enough using the find() and unique() functions. Is there a class I can inherit from here which will allow something like that to work on my object array? Or will I have to add my own find() and unique() methods to my class?
You can assign an ID value (a hash value in the general case) to TestRigData objects and store it as a new property. You can then extract all ID values at once to a cell array, e.g {yourarray.id} (or [yourarray.id] if the ID values are scalars), allowing you to apply find and unique with ease.
Adding your own find and unique is definitely possible, of course, but why make life harder? ;)
The suggestion of creating this as a handle class (rather than value class) is something I need to think about more in the future... after having put together some initial code, going back and trying to change classdef TestRigData to classdef TestRigData < handle seems to be causing issues with the constructor.
Bit unclear of how I would go about using a hash value unique to each object... but the syntax of extracting all values to an array is ultimately what got me in the right direction.
Getting a new object array which is the subset of the original big data array conforming to a certain property value is as easy as:
newObjectArray = oldObjectArray([oldObjectArray.testTemperature]==200);
Or for just the indices...
indicesOfInterest = find([oldObjectArray.testTemperature]==200);
Or in the case of non-scalar values, e.g. string property for sample material...
indicesOfInterest = find(strcmpi({oldObjectArray.sampleMaterial},'steel'));
Related
Say I have a class subclassing double, and I want to add a string (Similar to the 'extendDouble' in the documentation). Is there an easy way to access the actual numeric value without the extra properties, particular for reassigning? Or if I want to change the value, will I have to recreate the value as a new member of the class with the new value and the same string?
e.g.
classdef myDouble < double
properties
string
end
methods
function obj = myDouble(s)
% Construct object (simplified)
obj.string = s;
end
end
end
----------
x = myDouble(2,'string')
x =
2 string
x = 3
x =
3 string
Short answer: NO. There is no easy way to access a single member of a class when the class contains more than one member. You'll always have to let MATLAB know which part of the class you want to manipulate.
You have multiple questions in your post but let's tackle the most interesting one first:
% you'd like to instanciate a new class this way (fine)
x = myDouble(2,'string')
x =
2 string
% then you'd like to easily refer to the only numeric part of your class
% for assignment => This can NEVER work in MATLAB.
x = 3
x =
3 string
This can never work in MATLAB because of how the interpreter works. Consider the following statements:
% direct assignment
(1) dummy = 3
% indexed assignments
(2) dummy(1) = 3
(3) dummy{1} = 3
(4) dummy.somefieldname = 3
You would like the simplicity of the first statement for assignment, but this is the one we cannot achieve. The statement 2, 3 and 4 are all possible with some fiddling with subasgn and subsref.
The main difference between (1) and [2,3,4] is this:
Direct assignment:
In MATLAB, when you execute a direct assignment to a simple variable name (without indexing with () or {} or a field name) like dummy=3, MATLAB does not check the type of dummy beforehand, in fact it does not even check whether the variable dummy exists at all. No, with this kind of assignment, MATLAB goes the quickest way, it immediately create a new variable dummy and assign it the type and value accordingly. If a variable dummy existed before, too bad for it, that one is lost forever (and a lot of MATLAB users have had their fingers bitten once or twice by this behavior actually as it is an easy mistake to overwrite a variable and MATLAB will not raise any warning or complaint)
Indexed assignments:
In all the other cases, something different happens. When you execute dummy(1)=3, you are not telling MATLAB "create a new dummy variable with that value", you are telling MATLAB, "find the existing dummy variable, find the existing subindex I am telling you, then assign the value to that specific subindex". MATLAB will happlily go on, if it finds everything it does the sub-assignment, or it might complains/error about any kind of misassignment (wrong index, type mismatch, indices length mismatch...).
To find the subindex, MATLAB will call the subassgn method of dummy. If dummy is a built-in class, the subassgn method is also built in and usually under the hood, if dummy is a custom class, then you can write your own subassgn and have full control on how MATLAB will treat the assignment. You can check for the type of the input and decide to apply to this field or another if it's more suitable. You can even do some range check and reject the assignment altogether if it is not suitable, or just assign a default value. You have full control, MATLAB will not force you to anything in your own subassgn.
The problem is, to trigger MATLAB to relinquish control and give the hand to your own subassgn, you have to use an indexed assignment (like [2,3 or 4] above). You cannot do that with type (1) assignment.
Other considerations: You also ask if you can change the numeric part of the class without creating a new object. The answer to that is no as well. This is because of the way value classes work in matlab. There could be a long explanation of what happens under the hood, but the best example is from the MATLAB example you referenced yourself. If we look at the class definition of ExtendDouble, then observe the custom subassgn method which will perform the change of numeric value, what happens there is:
obj = ExtendDouble(b,obj.DataString);
So even Mathworks, to change the numeric value of their extended double class, have to recreate a brand new one (with a new numeric value b, and transfering the old string value obj.DataString).
I have an array as shown here. Here Bandit is a class that I created.
bandits = [Bandit(m1),Bandit(m2),Bandit(m3)];
Now, I want to do the following. Following is Python code which immediately gives me the maxarg of the value of the mean of each of these objects.
j = np.argmax([b.mean for b in bandits])
How can I do the same in MATLAB? To give more clarity, every bandit object has an attribute mean_value. I.e. if b1 is a bandit object, then I can get that value using dot operator (b1.mean_value). I want to find which among b1, b2, b3 has maximum mean_val and need to get the index for it. (See the python code above. If b2 has the highest mean_val, then finally, j will contain index 2.)
arrayfun applies a function to each element of an array. It results in a new array with the results of the operation. To this result you can then apply max as usual:
[~,arg] = max(arrayfun(#mean,bandits));
Note that this might not work if you have overloaded the subsref or size methods for the Bandit class.
Edit:
So now I understand that mean was not a function but an attribute. The operation x.mean can be expressed as the function call subsref(x,substruct('.','mean')). Thus, it is possible to change the solution above to call this function on each array element:
op = #(x)subsref(x,substruct('.','mean'))
[~,arg] = max(arrayfun(op,bandits));
That is, instead of calling the function mean, we call the function subsref to index the attribute mean.
If bandits is a simple struct array, then the following will work also:
[~,arg] = max([bandits.mean]);
Here, bandits.mean will extract the mean value for each element of the struct array, yielding a comma-separated list. This list is captured with the square brackets to form a vector. This vector is again input into the max function as usual.
I'm not sure if this latter solution works also for custom classes. I don't have your Bandit class to test. Please let me know if this latter solution works, so I can update the post with correct information.
I have an enum like this:
classdef(Enumeration) bla_type < int32
enumeration
bla_one(1)
bla_2(2)
end
end
I can get the 'string representation of an element' like this:
char(bla_type.bla_one)
=>
bla_one
Unfortunately, matlab coder does not like this. Are there any alternatives?
There's no elegant built-in way in Coder to do this; the enumerated type becomes a standard enum in C, and the enumeration function in MATLAB is unavailable in Coder. The simplest, but unpleasant, way to do it is to create a function with a switch statement with the manually populated string names. It's not nice, since now you have to maintain the names in two places.
However, one way that does work nicely is to use one of the more powerful features of Coder: coder.const.
The solution is to have a function that creates a table of the enumeration members and their values. This function itself cannot be compiled, but rather is called during compilation to build a lookup table in the resulting C code. We can use this lookup table in a Coder compatible function to get the data.
Imagine we have an enumerated type like this (in someenum.m):
classdef someenum < int32 %#codegen
enumeration
First_thing (0)
Second_thing (2)
Another_thing (3)
No_thing (4000)
end
end
We also then have the build-time function called 'buildsomeenum2name.m':
function [namearray, memberidx] = buildsomeenum2name
%BUILDSOMEENUM2NAME Compile-time creation of lookup table for someenum
% THIS FUNCTION IS NOT CODER COMPATIBLE, BUT IS CALLED DURING COMPILE
% TO CREATE A LOOKUP TABLE.
[members, names]=enumeration('someenum');
maxlen = 0;
for i=1:numel(names)
maxlen = max(maxlen, numel(names{i}));
end
namearray = char(zeros(numel(names), maxlen));
for i=1:numel(names)
namearray(i, 1:numel(names{i})) = names{i};
end
memberidx = int32(members); %#ok<NASGU>
end
When buildsomeenum2name is called in MATLAB, it creates an array of string names of all the members of the enumerated type and another vector list of their numeric values in the same order.
Here's the cool part. MATLAB Coder can evaluate functions at build time and turn them into constants. These constants become literals in the resulting C code, rather than actual code. Since the functions are evaluated at build time, the enumeration information is put into a nice table, therefore if we make a Coder-compatible lookup function, we can use it to convert the member types into a string. We'll call this function 'someenum2name.m':
function name = someenum2name(enum) %#codegen
%SOMEENUM2NAME Get the string name of an enumerated type
% The following line loads namearray and memberidx with constant arrays
coder.extrinsic('buildsomeenum2name');
[namearray, memberidx] = coder.const(#buildsomeenum2name);
% First find the index of the enumerated type in the memberidx vector
index = find(memberidx==int32(enum));
if isempty(index)
name = 'UNKNOWN';
return;
end
name = deblank(namearray(index,:));
end
This function uses the coder.const command to evaluate buildsomeenum2name at compile time and create the lookup tables. We have to instruct Coder not to try to compile buildsomeenum2name, so use the coder.extrinsic command to tell it to ignore the function. Then someenum2name can look up the index for the string and pull it out (deblank is used because the strings in the array have trailing 0's that need to be pulled out.) The function someenum2name can be called both within MATLAB and in Coder compiled code.
This method keeps everything in-sync, so if you ever add a new member to the enum or rearrange them, the coder.const function will make sure that the values are rebuilt in the output code so that someenum2name works.
At the command line, this looks like:
>> someenum2name(someenum.No_thing)
ans =
No_thing
Try [~,s]=enumeration('bla_type'). You get a cell array of strings containing the name of elements in s. So bla_one will be in s{1}. Don't know whether that is supported by MATLAB coder though.
I came across some matlab code that did the following:
thing.x=linspace(...
I know that usually the . operator makes the next operation elementwise, but what does it do by itself? Is this just a sub-object operator, like in C++?
Yes its subobject.
You can have things like
Roger.lastname = "Poodle";
Roger.SSID = 111234997;
Roger.children.boys = {"Jim", "John"};
Roger.children.girls = {"Lucy"};
And the things to the right of the dots are called fields.
You can also define classes in Matlab, instatiate objects of those classes, and then if thing was one of those objects, thing.x would be an instance variable in that object.
The matlab documentation is excellent, look up "fields" and "classes" in it.
There are other uses for ., M*N means multiploy two things, if M, N are both matrices, this implements the rules for matrix multiplication to get a new matrix as its result. But M.*N means, if M, N are same shape, multiply each element. And so no like that with more subtleties, but out of scope of what you asked here.
As #marc points out, dot is also used to reference fields and subfields of something matlab calls a struct or structure. These are a lot like classes, subclasses and enums, seems to me. The idea is you can have a struct data say, and store all the info that goes with data like this:
olddata = data; % we assume we have an old struct like the one we are creating, we keep a reference to it
data.date_created=date();
data.x_axis = [1 5 2 9];
data.notes = "This is just a trivial example for stackoverflow. I didn't check to see if it runs in matlab or not, my bad."
data.versions.current = "this one";
data.versions.previous = olddata;
The point is ANY matlab object/datatype/whatever you want to call it, can be referenced by a field in the struct. The last entry shows that we can even reference another struct in the field of a struct. The implication of this last bit is we could look at the date of creation of the previous verions:
data.versions.previous.date_created
To me this looks just like objects in java EXCEPT I haven't put any methods in there. Matlab does support java objects which to me look a lot like these structs, except some of the fields can reference functions.
Technically, it's a form of indexing, as per mwengler's answer. However, it can also be used for method invocation on objects in recent versions of MATLAB, i.e.
obj.methodCall;
However note that there is some inefficiency in that style - basically, the system has to first work out if you meant indexing into a field, and if not, then call the method. It's more efficient to do
methodCall(obj);
I have a struct array called AnalysisResults, that may contain any MATLAB datatypes, including other struct arrays and cell arrays.
Then I have a string called IndexString, which is the index to a specific subfield of StructArray, and it may contain several indices to different struct arrays and cell arrays, for example:
'SubjectData(5).fmriSessions{2}.Stats' or 'SubjectData(14).TestResults.Test1.Factor{4}.Subfactor{3}'.
And then I have a variable called DataToBeEntered, which can be of any MATLAB datatype, usually some kind of struct array, cell array or matrix.
Using eval, it is easy to enter the data to the field or cell indexed by IndexString:
eval([ 'AnalysisResults.', IndexString, ' = DataToBeEntered;' ])
But is it possible to avoid using eval in this? setfield doesn't work for this.
Thank you :)
Well, eval surely is the easiest way, but also the dirtiest.
The "right" way to do so, I guess, would be to use subsasgn. You will have to parse the partial MATLAB command (e.g. SubjectData(5).fmriSessions{2}.Stats) into the proper representation for those functions. Part of the work can be done by substruct, but that is the lightest part.
So for example, SubjectData(5).fmriSessions{2}.Stats would need to be translated into
indexes = {'.' , 'SubjectData',
'()', {5},
'.' , 'fmriSessions',
'{}', {2},
'.' , 'Stats'};
indexStruct = substruct(indexes{:});
AnalysisResult = subsasgn(AnalysisResult, indexStruct, DataToBeEntered);
Where you have to develop the code such that the cell array indexes is made as above. It shouldn't be that hard, but it isn't trivial either. Last year I ported some eval-heavy code with similar purpose and it seemed easy, but it is quite hard to get everything exactly right.
You can use dynamic field names:
someStruct.(someField) = DataToBeEntered;
where someField is a variable holding the field name, but you will have to parse your IndexString to single field name and indices.