3

In the past, I've been making extensive use of Matlab's table class. This very simple code, inside a script or at the prompt, works as expected:

varNames = {'Date_time', 'Concentration_1', 'Concentration_2'};
testTable = array2table(zeros(5,3), 'VariableNames', varNames)

Now, I have the same table as the property of a handle class.

classdef TestClass < handle
    properties
        testTable (:,3) table
    end
    methods
        function testMethod(obj)
            varNames = {'Date_time', 'Concentration_1', 'Concentration_2'};
            obj.testTable = array2table(zeros(5,3), 'VariableNames', varNames);
            obj.testTable.Properties.VariableNames
        end
    end
end

If I execute the following at the command prompt, the zeros are assigned to the table, but the VariableNames keep their default value, i.e., {'Var1', 'Var2'} etc.

tc = TestClass; tc.testMethod

Even tc.testTable.Properties.VariableNames = varNames does not change them.

Is this a bug, or am I missing something? (I am using Matlab R2017b)

winkmal
  • 622
  • 1
  • 6
  • 16
  • 3
    It might be a bug with the type/size enforcement, the issue goes away when you remove it. I'd suggest filing a [bug report](https://www.mathworks.com/support/bugreports/). – sco1 Jan 24 '18 at 14:42
  • 1
    It's definitely a bug with the size enforcement, even specifying a default table value with explicit variable names gets overwritten by `'Var1', 'Var2', ...` but performs as expected as soon as the size enforcement is removed. – sco1 Jan 24 '18 at 15:08
  • `testTable (:,3) table` means that it must be a something-by-3 array of tables, not that it must be a something-by-3 table. You can't have arrays of tables, so this constraint doesn't make sense. Use an explicit validator function as suggested in @excaza s answer. – Sam Roberts Jan 24 '18 at 15:48
  • Mathworks has confirmed this is a bug – sco1 Jan 26 '18 at 15:24

1 Answers1

4

This appears to be a bug with MATLAB's property size validation, as the behavior disappears when it is removed:

classdef SOcode < handle
    properties
        testTable(:,3) = table(1, 2, 3, 'VariableNames', {'a', 'b', 'c'});
    end
end

>> asdf.testTable

ans =

  1×3 table

    Var1    Var2    Var3
    ____    ____    ____

    1       2       3

vs.

classdef SOcode < handle
    properties
        testTable = table(1, 2, 3, 'VariableNames', {'a', 'b', 'c'});
    end
end

>> asdf.testTable

ans =

  1×3 table

    a    b    c
    _    _    _

    1    2    3

Until TMW resolves the bug, this can be worked around with a custom validation function in order to preserve the desired behavior:

classdef SOcode < handle
    properties
        testTable table {TheEnforcer(testTable)}
    end
    methods
        function testMethod(obj)
            varNames = {'Date_time', 'Concentration_1', 'Concentration_2', 'hi'};
            obj.testTable = array2table(zeros(5,4), 'VariableNames', varNames);
            obj.testTable.Properties.VariableNames
        end
    end
end

function TheEnforcer(inT)
ncolumns = 3;
if ~isempty(inT)
    if size(inT, 2) ~= ncolumns
        error('An Error')
    end
end
end
sco1
  • 12,154
  • 5
  • 26
  • 48
  • 1
    I'm not sure I'd describe this as a bug exactly, but it's certainly unclear behaviour. The property size `(:,3)` says that `testTable` must be a something-by-3 array of tables (*not* a something-by-3 table); but since you can't have arrays of tables, this doesn't make sense as a constraint. I'd suggest that this is a failure of documentation, not a "bug". In any case, your suggestion to use a validation function is the right way to achieve what OP is trying for. Maybe call it `mustHaveThreeVars` rather than `TheEnforcer`? – Sam Roberts Jan 24 '18 at 15:46
  • I disagree with your assertion about what the property size means for tables. Other than the variable name behavior the size validation works as expected. This is absolutely a bug. – sco1 Jan 24 '18 at 16:01
  • I'm suggesting that if the size validation partially works with `table`, then it's fortunate rather than by explicit design - so if it also partially _doesn't_ work, then that's not exactly a bug (although I understand why you might think it was). And that if you file a bug report, I bet MathWorks will resolve it by modifying the documentation to say something like "don't do property size validation with classes that have overloaded `size` in an unusual way, like `table`, because the property size is intended to mean that it is an actual array of the class". – Sam Roberts Jan 24 '18 at 16:39
  • But in any case - a bug report would be a good thing, even it results in clearer documentation rather than a change in functionality. – Sam Roberts Jan 24 '18 at 16:41
  • That's a nonsensical caveat. A proper size validation method should be able to return the appropriate result without having to call `subasgn`. At the very least, when `subasgn` clones the table it should preserve the variable names, which it does not. – sco1 Jan 24 '18 at 17:03
  • Just imagine if you made a class that overloaded `size` to call `rand`, and used that where `table` is used here. There *have* to be some restrictions on the classes that you can use for properties with size-constraints, and if you break those restrictions, something weird is going to happen. I'm suggesting that something similar is going on here, and that the right solution is to document those restrictions more clearly (which could be as simple as "don't use classes with an overloaded `size`"). Anyway - no need to argue, you've given the right workaround and have my upvote. – Sam Roberts Jan 24 '18 at 17:34