Discover MakerZone

MATLAB and Simulink resources for Arduino, LEGO, and Raspberry Pi

Learn more

Discover what MATLAB® can do for your career.

Opportunities for recent engineering grads.

Apply Today

Manipulating superclass datasets within subclass methods

Asked by Eric on 7 Jun 2014
Latest activity Commented on by Eric on 8 Jun 2014

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 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.

This seems to be an issue that was discussed here http://www.mathworks.ca/matlabcentral/answers/16652-overriding-subsref-and-subsasgn-effect-on-private-properties

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 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.

Eric

Products

No products are associated with this question.

1 Answer

Answer by Matt J on 7 Jun 2014
Edited by Matt J on 8 Jun 2014
Accepted answer

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.

1 Comment

Eric on 8 Jun 2014

Thank you Matt.

Matt J

Contact us