Manipulating superclass datasets within subclass methods

1 view (last 30 days)
I am having some difficulty designing a subclass of dataset in Matlab (R2010b). I am experienced programming Matlab, but new to using its OOP features. My constructor seems to be fine, but when I try to build some methods that access data in the dataset, I can't seem to get it to work. Here is an example:
classdef mydataset < dataset
properties
end
methods
function [obj] = mydataset(obs,array)
% obs is N x 1 cell array of strings
% array is N x 2 double array
obj = obj@dataset({array,'Field1','Field2'},'ObsNames',obs)
end
function [val] = computeValue(obj)
col = obj.Field1;
% I get an error above regardless of how I try to access the dataset.
% e.g. col = double(obj(obs,'Field1')) also does not work.
% Some more code using col to determine val
end
end
In my method computeValue, I am trying to access the data in the dataset using dataset syntax, i.e. on the command line I could access Field1 using ".". It complains there is no method, property, or field Field1 for class mydataset. If I try the alternate syntax
col = double(obj(:,'Field1')); it complains about the size of obj, e.g. "Index exceeds matrix dimensions".
I found a workaround using subsref:
function [val] = computeValue(obj)
s.type = '.';
s.subs = 'Field1';
col = subsref(obj,s);
% Some more code using col to determine val
end
Although the workaround works, it is not very convenient and largely defeats the purpose of wanting a subclass of dataset. Is there some attribute or something simple I am missing?
I like the functionality of dataset and hope to get this, or something like it, to work so I can define custom methods extending dataset. I also tried simply making dataset a property, but the syntax got cluttered.
Thank you very much.
PS: I am not active here, so I hope cross posting from stack overflow is ok. I did not get the answer I need there so I'm trying here. I imagine some of you are active on both. Cheers.
  2 Comments
Geoff Hayes
Geoff Hayes on 7 Jun 2014
That is strange behaviour! I don't have the dataset class definition so I can't reproduce exactly what you have above, but I did get a similar error and behaviour if I created a simple class with private properties
properties(Access=private)
Field1;
Field2;
ObsNames;
end
Due to lack of originality, I called this class dataset. I then added a simple constructor to just initialize the properties, and the public subsref function
function v = subsref(M, S)
switch S.type
case '()'
v = 'this is ok';
case '{}'
error('{} indexing not supported');
case '.'
v = builtin('subsref', M, S); % as per documentation
end
end
Then just created the mydataset as you did, with a simpler constructor and the same computeValue method. Sure enough, this method failed with the same error when it tried to access Field1 as obj.Field1. BUT, like you (?), I could access this field from the command line (in the Command Window) and get the value returned!!
So I defined a property that is private in the base class, and I can't access that property in a method of a derived class. This makes sense. BUT I can access this same field outside of the class at the command line?? If I remove the subsref method in the base class then I can't access this property at the command line either. So the behaviour of subsref is questionable - it allows access to private members even when it shouldn't.
I'm just guessing that it is the private properties that are causing this problem/behaviour, and I don't have a solution. I think that the behaviour that you are experiencing within the class when trying to access a private data member/property makes sense. But the fact that you can access that same member by sbusref is a little surprising. (I'm using MATLAB R2014a.)
Eric
Eric on 7 Jun 2014
Thanks Geoff! It may help to see my motivation by putting some context to my question. A colleague was under pressure to get a code together quickly. He is new to Matlab, but not new to OOP, so he built a pretty large code in a short time. We got through the crunch period and now I'm coming in to help with "phase II" with a longer-term focus. When I look at his classes, they are very "dataset-like", but not quite. Now I'm hoping to modify his classes using datasets without breaking his code while allowing me to continue development using the cleaner dataset features.
For example, his claases look like this:
classdef mydataset % no inheritance
properties
Obs % N x 1 cell array of strings
Field1 % N x 1 double array
Field2 % N x 1 double array
end
methods
function [obj] = mydataset(obs,field1,field2)
obj.Obs = obs;
obj.Field1 = field1;
obj.Field2 = field2;
end
function [val] = getField1(obj,Ind)
ix = arrayfun(@(x) find(ismember(obj.Obs,x)), Ind);
val = obj.Field1(ix);
end
end
end
If I could get "obj" to be a datasset, then his getField1 could be rewritten as
function [val] = getField1(obs,Ind)
val = double(obs(Ind,'Field1'));
end
I would also hope to be able to keep the syntax he used:
md = mydataset(obs,field1,field2);
val = md.getField1(Ind);
while allowing me to use the dataset syntax
dval = md(Ind,'Field1');
going forward.
Any ideas how to achieve this would be sincerely appreciated.

Sign in to comment.

Accepted Answer

Matt J
Matt J on 7 Jun 2014
Edited: Matt J on 8 Jun 2014
It appears that the dataset superclass has a subsref method defined, and hence is inherited by your subclass mydataset. When the class possesses a subsref method, indexing behavior outside class methods (e.g., at the command line) is different from inside them.
Inside the classdef, dot indexing never calls the class' subsref method. Instead, it is always interpreted as an attempt to access properties and methods of the class. In the first version of the mydataset classdef that you posted, Field1 and Field2 are not properties of the class (they are just data stored in some protected superclass property), so your attempt to access them by dot-indexing inside a class method fails. To get at the data, you must do so indirectly through explicit calls to subsref (as in your workaround), or whatever other indirect access methods the dataset class might have defined.
Outside the class definition, indexing expressions always call the class' subsref method, in this case subsref@dataset or subsref@mydataset, if you chose to overload it, and do whatever subsref dictates.
It's not entirely clear to me what indexing behavior you are trying to achieve, and where you want to achieve it. "Where" is important because indexing behavior cannot be the same both inside and outside the classdef. However, be aware that you are always free to invoke class methods in functional form, i.e.,
classmethod(obj,arg1,arg2,...)
will always work whether inside the class definition or elsewhere, whereas outside the class definition
obj.classmethod(arg1,arg2)
will invoke subsref@mydataset.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!