What are brackets [] doing in Matlab, if not filled with numbers?
Let's assume we have some objects obj1, obj2 and obj3 of a ClassA. Apparently it's possible to combine them with brackets in a .. don't know, what it actually is
objects = [obj1 obj2 obj3];
>> class(objects)
ans =
ClassA
>> objects
objects =
1x3 ClassA handle
Properties:
name
...
Whats this called?
How to iteratively build such thing?
objects = []; objects(end+1) = current_obj; does not work
objects{end+1} = current_obj; creates a cell
How can one convert to this e. g. from a cell with objects?
When using the [] notation again on a field it gives
K>> [objects.name]
ans =
Object1Object2Object3
K>> class([objects.name])
ans =
char
[obj1 obj2 obj3] is an array of objects of class ClassA, just like [1 2 3] is an array of numbers.
If you type a = []; a(2) = 1, MATLAB will return a as [0 1], in other words it will fill any unspecified elements of a with a default element which, in the case of numbers, is zero.
When you type objects = []; objects(2) = current_obj, MATLAB similarly attempts to put current_obj in the requested position 2 of objects, and then to fill the unspecified elements with default objects of class ClassA. To do this, it calls the constructor of ClassA, but you need to know that it calls the constructor with no input arguments.
Therefore, if you want to be able to support this sort of array filling with objects of your class, you need to implement the class constructor so that it will not error when called with zero input arguments. For example, you could simply check nargin, and if it's zero, supply some default inputs, otherwise accept whatever inputs were provided.
By the way, by default [] is of class double. If you want to create an empty array of class ClassA, you can use objects = ClassA.empty. empty is a built-in method of all MATLAB classes. You may find that you avoid some errors by making sure that you don't accidentally attempt to concatenate doubles with objects of class ClassA.
If you really need an empty array of objects, use some object you have and index "nothing" (from 2 to 1):
x=obj1(2:1)
Result is an empty array with a matching type. Here you can append using x(end+1). Alternatively you can use similar code to append. If x does not exist it will be created with a matching type.
if exist('x','var')
x(end+1)=obj
else
x(1)=obj
end
What you are doing is creating structures. What I would do is create the same structure for objects and then overwrite the 1st index with your first object, and then iterate through the rest:
% Assume we have classA.m file available
obj1 = classA();
obj2 = classA();
obj3 = classA();
objects = obj1;
objects(end+1) = obj2;
objects(end+1) = obj3;
Square brackets are for concatenation
If you have square brackets with elements between them, you are concatenating the elements.
The elements can abe scalars, strings, vectors, matrices and so on.
Example
Assuming that the name fields of the objects struct contains a string, you can concatenate all of them like so:
[objects.name]
The result will be:
[objects(1).name objects(2).name ... objects(end).name]
Related
I have an object called TestData() that handles data processing and places it in a file in a specified format. One of the properties is data, which is stored as an array of type double. Here is the object and its constructor:
classdef TestData
properties
metaData = []; % stores meta data in Nx2 array
data = []; % stores data in PxQ array
colLabels = []; % labels columns
colUnits = []; % provides units
metaPrint % used to print metaData
temp % debugging purposes only
end
methods
%****************************************************************************%
%Function: TestData
%Purpose: Constructor used to allocate data arrays
%****************************************************************************%
function this = TestData() %constructor
this.metaPrint = [];
this.temp = [];
end %TestData()
The data that is placed into the object comes from a .m file external to the object, like so:
myTestData=TestData; % Generate an instance of the object
% Data
ErrorLine1 = zeros([length(ErrorLine')+2 1]); % Empty Vector to store 11X1
ErrorLine data
ErrorLine1(2:end-1) = ErrorLine(1:end);
mat = [Avec' Bvec' Invec' Ovec' ErrorLine1 PercentErrorOD'];
myTestData.data = mat;
So, when I set myTestData.data = mat this places the data in the object, but then calls the getter and setter functions for that object. One of the principle reasons for using these functions is to filter the data and determine if it is the correct type of data (in this case, the format would need to be double). When I try this in the code, however, it doesn't seem to work. Here is what I have written for the getter:
function data = get.data(this)
data = this.data;
end %getData
And the setter:
function this = set.data(this, data)
i = arrayfun(#(n)strcmp(class(this.data(n)), 'doube'), 1:numel(this.data)); %#ok<STISA>
disp(i)
if any(i == 0)
disp("WE HAVE A ZERO")
msg = "Data in object's 'data' property is not of type double";
error(msg);
else
this.data = data;
disp('Hi from setter')
end
end % set.data
If I run the strcmp and any functionality through the command window it works! When I run it from the editor, though, it always displays the "Hi from setter" string, even if I change "double" in the compare to "string." So, I am just not sure why it is not entering the if statement.
If there is anything that you can see in my code that could be modified to make it more efficient please let me know. Also, if there is anything else I can do my best. Thanks in advance!
In your setter, you have this line:
i = arrayfun(#(n)strcmp(class(this.data(n)), 'doube'), 1:numel(this.data));
But note that here you have not yet done this.data = data. When you test this.data, you test the old value of the property, not the value you want to assign. You need to test data.
Instead of using strcmp on the result of class, you can better test with isa. Furthermore, unless data is a cell array or a struct array, each of the elements will always be of the same class. You don't need to iterate over the whole array to test its type, just look at the type of the array itself. In the case of the cell array and the struct array, the indexing has to be different to extract elements. So, data(n) will always have the same type as data. Thus your setter can be written as:
function this = set.data(this, data)
if ~isa(data,'double')
error('WRONG!')
end
this.data = data;
end
Another issue with the code: if any(i == 0). any collapses one dimension of the input array. So if i is a 2D matrix, then the output is a row vector. Each element will be true if any of the elements in the given column is true. The if statement is only true if all elements of the given expression are nonzero. That means that all columns must have at least one 0 value for this expression to trigger.
Instead, do if any(i(:)==0). Here we make i into a column vector (this doesn't copy data, it's efficient), and thus any will return a single (scalar) value. If you have MATLAB R2018b, then you also do if any(i,'all'), which is equivalent.
You'll often see code doing if any(any(i==0)), but this fails if i happens to have three or more dimensions. It is also inefficient, the forms above are better.
I started a code where I have to generate a lot of instances of a given class. I want to save them into an array like I would do in python.
This is what I want to do:
object_list = [];
for i=1:100
my_object = MyClass(i)
object_list(i) = my_object
end
In Python this works because I can just append whatever I want to my list.
How should I do this in Matlab?
/The error is get is this:
"Conversion to double from MyClass is not possible."
I guess the Matlab array behaves differently than a generic python list, and actually needs to contain doubles. /
You can initialize your array as an array of objects:
object_list(100) = MyClass(100);
for i=1:100
object_list(i) = MyClass(i);
end
More information on Initialize Object Arrays.
I have a cell that has different data types (cell, logical, double, char) except structure. Now I have to write a function that will sort out different data types and output a structure with the field of those data types. The fields have to appear according to their appearance in the cell. So, if the first 'n' element(s) of the cell is double and the (n+1)th element is a char then the first field of the output structure will be double and second field will be char.
Below is an example where buildStructure is the function header. sa is the output structure.
ca = {'Moriarty', [true, false], false, {'Pink Suitcase'}}
sa = buildStructure(ca)
sa=>
char: {'Moriarty'}
logical: {[true, false] [false]}
cell: {{'Pink Suitcase'}}
I tried it writing a for loop to store different data types in different cells. However, then I am feeling so lost. How can I figure out which data type appeared when? To do that I stored all the classes in a huge string then used 'strfind' to find the place (thus time) of particular data type. But it is making things only complex. Any help will be appreciated! Thanks.
There are tests for all the data types. see: iscell, ischar, islogial and so on. Their results can be used to index the input.
you can complete this example code:
function out = magicfun(varargin)
il = cellfun(#islogical,varargin);
out = struct('logical',{varargin(il)});
You can use class(), isa(), and unique() to do it generically. It's like bdecaf's approach, but that'll require you to write a test for every type and use a variety of functions. Using class and isa will generalize to data of any type using a single test, and will be shorter to write.
Exact Types Only
By comparing class names from class(), you can partition the input in to types based on the exact (most specific) type of each input. The 'stable' option for unique() keeps the output fields in the order of the first occurrences of the types in the input. (In production code I would probably omit the 'stable' so the output ordering is canonicalized based on the type name, but it depends on your requirements.)
function out = break_types(in)
%BREAK_TYPES Partition a cell array based on the types of its contents
inTypes = cellfun(#class, in, 'UniformOutput',false);
[types,ax,bx] = unique(inTypes, 'stable');
out = struct;
for i = 1:numel(types)
ix = (bx == i);
out.(types{i}) = in(ix);
end
This is pretty complete and should work with anything that didn't do something silly like override class() or isa().
>> ca = {'Moriarty', [true, false], false, {'Pink Suitcase'}};
>> break_types(ca)
ans =
char: {'Moriarty'}
logical: {[1 0] [0]}
cell: {{1x1 cell}}
>>
Considering Inheritance
If you use isa(), you'll also pick up inheritance relationships for classes. For basic Matlab types, this will give you the same answer as the other implementation. But for classes that inherit from other types, it will categorize them in to all the types they match in the input and required lists.
function out = break_types(in)
%BREAK_TYPES Partition a cell array based on the types of its contents
inTypes = cellfun(#class, in, 'UniformOutput',false);
types = unique(inTypes, 'stable');
out = struct;
for i = 1:numel(types)
ix = cellfun(#(x) isa(x, types{i}), in);
out.(types{i}) = in(ix);
end
If you want to ensure that the output struct has an entry for some types even if there are no inputs of that type (so its field would contain an empty array), just append those type names to types before passing them to unique:
requiredTypes = { 'cell' 'int8', 'double', 'float' };
types = unique([inTypes requiredTypes], 'stable');
I have an array of structs and would like to set all empty attributes to NaN:
structArray =
29x1 struct array with fields:
value
id
How do I set all struct.value attributes to NaN, if they are empty?
If they are empty the conversion [structArray.value] omits the empty elements...
Given this:
x(29).id = [];
x(29).value = [];
You can set the value of all .id fields like this
[x.value] = deal(nan);
To set only a particular subset of values define a mask of values to set and then use it in your assignment statement:
maskEmptyId = arrayfun( #(a)isempty(a.id), x );
[x(maskEmptyId).id] = deal(nan);
As #Pursuit explained there is an excellent way to replace the empty fields with NaNs
However, you may also be interested in a different approach.
Instead of replacement in hindsight you may be able to prevent the empty spots to occur in the first place. Assuming they are empty because nothing has been assigned to them, you can simply initialize your struct with NaNs.
For example:
structArray = struct ('id',[],'value',NaN)
Calling this before you assign anything to structArray would initialize the value field with NaN, but will still initialize the id to be empty.
I have no idea what's going on here. I'm using R2006b. Any chance someone out there with a newer version could test to see if they get the same behavior, before I file a bug report?
code: (bug1.m)
function bug1
S = struct('nothing',{},'something',{});
add_something(S, 'boing'); % does what I expect
add_something(S.something,'test'); % weird behavior
end
function add_something(X,str)
disp('X=');
disp(X);
disp('str=');
disp(str);
end
output:
>> bug1
X=
str=
boing
X=
test
str=
??? Input argument "str" is undefined.
Error in ==> bug1>add_something at 11
disp(str);
Error in ==> bug1 at 4
add_something(S.something,'test');
It looks like the emptiness/nothingness of S.something allows it to shift the arguments for a function call. This seems like Very Bad Behavior. In the short term I want to find away around it (I'm trying to make a function that adds items to an initially empty cell array that's a member of a structure).
Edit:
Corollary question: so there's no way to construct a struct literal containing any empty cell arrays?
As you already discovered yourself, this isn't a bug but a "feature". In other words, it is the normal behavior of the STRUCT function. If you pass empty cell arrays as field values to STRUCT, it assumes you want an empty structure array with the given field names.
>> s=struct('a',{},'b',{})
s =
0x0 struct array with fields:
a
b
To pass an empty cell array as an actual field value, you would do the following:
>> s = struct('a',{{}},'b',{{}})
s =
a: {}
b: {}
Incidentally, any time you want to set a field value to a cell array using STRUCT requires that you encompass it in another cell array. For example, this creates a single structure element with fields that contain a cell array and a vector:
>> s = struct('strings',{{'hello','yes'}},'lengths',[5 3])
s =
strings: {'hello' 'yes'}
lengths: [5 3]
But this creates an array of two structure elements, distributing the cell array but replicating the vector:
>> s = struct('strings',{'hello','yes'},'lengths',[5 3])
s =
1x2 struct array with fields:
strings
lengths
>> s(1)
ans =
strings: 'hello'
lengths: [5 3]
>> s(2)
ans =
strings: 'yes'
lengths: [5 3]
ARGH... I think I found the answer. struct() has multiple behaviors, including:
Note If any of the values fields is
an empty cell array {}, the MATLAB
software creates an empty structure
array in which all fields are also
empty.
and apparently if you pass a member of a 0x0 structure as an argument, it's like some kind of empty phantom that doesn't really show up in the argument list. (that's still probably a bug)
bug2.m:
function bug2(arg1, arg2)
disp(sprintf('number of arguments = %d\narg1 = ', nargin));
disp(arg1);
test case:
>> nothing = struct('something',{})
nothing =
0x0 struct array with fields:
something
>> bug2(nothing,'there')
number of arguments = 2
arg1 =
>> bug2(nothing.something,'there')
number of arguments = 1
arg1 =
there
This behaviour persists in 2008b, and is in fact not really a bug (although i wouldn't say the designers intended for it):
When you step into add_something(S,'boing') and watch the first argument (say by selecting it and pressing F9), you'd get some output relating to the empty structure S.
Step into add_something(S.something,'test') and watch the first argument, and you'd see it's in fact interpreted as 'test' !
The syntax struct.fieldname is designed to return an object of type 'comma separated list'. Functions in matlab are designed to receive an object of this exact type: the argument names are given to the values in the list, in the order they are passed. In your case, since the first argument is an empty list, the comma-separated-list the function receives starts really at the second value you pass - namely, 'test'.
Output is identical in R2008b:
>> bug1
X=
str=
boing
X=
test
str=
??? Input argument "str" is undefined.
Error in ==> bug1>add_something at 11
disp(str);
Error in ==> bug1 at 4
add_something(S.something,'test'); % weird behavior