How to duplicate structure field (of non-scalar structure) in Matlab? - arrays

Suppose I have non-scalar structure
res = struct(); res(1).name = 'hello'; res(2).name = 'world';
Now I want to copy entire content of name field to another field, say tag.
Neither of the following worked:
>> res.tag = res.name;
Scalar structure required for this assignment.
>> [res.tag] = [res.name];
Insufficient number of outputs from right hand side of equal sign to satisfy assignment.
>> {res.tag} = {res.name};
{res.tag} = {res.name};
↑
Error: The expression to the left of the equals sign is not a valid target for an assignment.
Any other ideas?

Use
[res(:).tag] = res(:).name;
or more simply, as you have discovered yourself:
[res.tag] = res.name;
The syntax with square brackets on the left-hand side is similar to that used for capturing several outputs returned by a function: [out1, out2] = fun(...); see MATLAB special characters.
Actually, the syntax res.tag produces a comma-separated list; and [...] is standard for assigning values to each element in one such list; see Assigning output from a comma-separated list.
The right-hand side of the assignment should be another comma-separated list. If it is a single element, or you want to specify a list manually, you need deal:
values = {10, 20};
[res.test] = values{:}; % works. {:} produces a comma-separated list
[res.test] = 10,20; % doesn't work. Use `deal`
[res.test] = deal(10,20); % works
[res.test] = 10; % doesn't work, unless `res` is scalar. Use `deal`
[res.test] = deal(10); % also works. 10 is repeated as needed
The reason why your attempt [res.tag] = [res.name]; doesn't work is that [res.name] on the right-hand side concatenates the results of the comma-separated list res.name into one array, and so it's the same case as [res.test] = 10; above.

Related

Shifting array to left or right OCaml

I'm having some trouble around arrays in OCaml.
Namely, I want to just shift the elements to the right or left based on a value I pass.
Example: # let a = [|1;2;3;4;5|], # shift_array a 7;;- : int array array = [|4;5;1;2;3|]
I wrote the following code but I keep getting a syntax error where the second array is formed.
let shift_array arr x =
let size = Array.length arr in
let sec_arr = Array.make size 0 in
for i = 0 to size - 1 do
if i < x
then (sec_arr.(size - x + 1) <- arr.(i))
else (sec_arr.(i-x) <- arr.(i))
done;;
I'm just not 100% sure how to print out the new array.
EDIT: fixed it by adding in to the second and third line.
The problem now is that the function has type int array -> int -> unit and the one I'm trying to get is 'a array -> int -> 'a array. Is there some way to work around that ?
It should be let size = Array.length arr in, notice the in, which you're missing.
The let expression in OCaml has the form let <var> = <expr> in <body> and should not be (but commonly is) confused with the let definition that can occur only on the top-level (as an element of a module), which has form let <name> = <body>.
In your example, you have both, the top-level definition, let shift_array = <body> and two let expressions (though, you have used the wrong syntax for them.
EDIT:
Since OP edited the post, here is the corresponding edit.
You function doesn't return anything, it creates a new array, does the cycle, but doesn't return anything but the unit value (which is the value to which the for cycle evaluates). So you have to add one more line, that will contain the expression, to which the whole function will evaluate. Hint the sequencing operator ; is what you need, when you have expression x;y;z the computer evaluates x, then y, and finally z and the value of the whole expression of x;y;z is the value of z.

Cannot fill cell array with mixed string/numeric data

I am trying to fill an empty cell array with mixed numeric and string data to subsequently write to Excel and have come up with a problem. The following MCVE should reproduce the issue:
First, I create the cell array and set the top left element to an empty string. So far so good:
xls_array = cell(7,6);
xls_array{1,1} = '';
Then I need to populate the first line and the first column of the array with their respective headers. These are variables (cell arrays) created elsewhere in the code, but for the purpose of the MCVE, I have created some artificial ones:
col_headers = {'Channel 1';'Channel 2';'Channel 3';'Channel 4';'Channel 5'};
xls_array(1,2:end) = col_headers';
row_headers = {'Var A';'Var B';'Var C';'Var D';'Var E';'Var F'};
xls_array(2:end,1) = row_headers;
So far, so good. Now I need to add the actual data to the cell array and that's where the problem arises. Again, for the purpose of the MCVE, I have generated some random data:
data = rand(6,5);
xls_array{2:end,2:end} = data;
This gives me the following error message:
>> xls_array{2:end,2:end} = data;
error: invalid dot name structure assignment because the structure array is empty. Specify a subscript on the structure array to resolve.
If I try normal brackets instead of curly brackets, it doesn't error out but doesn't give me the expected result either:
xls_array(2:end,2:end) = data;
>> xls_array{2,2}
ans =
0.326180 0.169640 0.381373 0.416490 0.283456
0.350000 0.366084 0.409047 0.619715 0.962095
0.526219 0.466591 0.553932 0.930187 0.460585
0.983679 0.324129 0.964619 0.080852 0.786360
0.069995 0.835966 0.266789 0.673177 0.796602
0.741368 0.233794 0.022568 0.012975 0.248514
i.e. it has copied the entire numeric array data in each element of the cell array xls_array(2:end,2:end).
What I want instead is to copy data into the remaining blank cells of the cell array, i.e. data(1,1) should be in xls_array{2,2}, data(1,2) in xls_array{2,3}, etc...
I have also tried the following, to no avail:
>> xls_array{2:end,2:end}(:) = data;
error: a cs-list cannot be further indexed
Can anybody help?
You need to convert the numeric array to a cell array and use parentheses for indexing:
xls_array(2:end,2:end) = num2cell(data);
Curly brackets can be used this way:
[xls_array{2:end,2:end}] = num2cell(data){:};
Alternatively you can use the concatenation operator to create the result:
xls_array = [{''}, col_headers.'; row_headers, num2cell(data)];
Why other methods don't work?
Curly brackets are used for a special type of indexing which returns the contents of a cell array as a comma separated list . If the LHS is a comma separated list the RHS should be also be a comma separated list that the number of its elements isn't less than the that of LHS and square brackets should be used around the LHS. More about comma separated lists can be found in the documentation. So xls_array{2:end,2:end} = data; is wrong because data can be interpreted as a comma separates list containing only one element but the LHS requires more elements and also the square brackets are missing.
The example xls_array(2:end,2:end) = data; I think is a bug in Octave because a cell array is an array that the type of its elements is cell. When parentheses are used for normal cell indexed assignment, the right hand side of the assignment should be of type cell and there is no implicit conversion form other types to cell type. So xls_array(2:end,2:end) = data; should be wrong because data is of double type. However we can see that the behavior of Octave is that data is implicitly converted to a cell {data} and then assigned to elements of LHS.

How to return the leaves of a struct as vector in Matlab?

Often I need to access the leaves of data in a structured array for calculations.
How is this best done in Matlab 2017b?
% Minimal working example:
egg(1).weight = 30;
egg(2).weight = 33;
egg(3).weight = 34;
someeggs = mean([egg.weight]) % works fine
apple(1).properties.weight = 300;
apple(2).properties.weight = 330;
apple(3).properties.weight = 340;
someapples = mean([apple.properties.weight]) %fails
weights = [apple.properties.weight] %fails too
% Expected one output from a curly brace or dot indexing expression,
% but there were 3 results.
If only the top level is a non-scalar structure array, and every entry below is a scalar structure, you can collect the leaves with a call to arrayfun, then do your calculation on the returned vector:
>> weights = arrayfun(#(s) s.properties.weight, apple) % To get the vector
weights =
300 330 340
>> someapples = mean(arrayfun(#(s) s.properties.weight, apple))
someapples =
323.3333
The reason [apple.properties.weight] fails is because dot indexing returns a comma-separated list of structures for apple.properties. You would need to collect this list into a new structure array, then apply dot indexing to that for the next field weight.
You can collect properties into a temporary structure array, and then use it as normal:
apple_properties = [apple.properties];
someapples = mean([apple_properties.weight]) %works
This wouldn't work if you had even more nested levels. Perhaps something like this:
apple(1).properties.imperial.weight = 10;
apple(2).properties.imperial.weight = 15;
apple(3).properties.imperial.weight = 18;
apple(1).properties.metric.weight = 4;
apple(2).properties.metric.weight = 7;
apple(3).properties.metric.weight = 8;
Not that I would advise such a structure, but it works as a toy example. In that case you could, do the same as the previous in two steps... or you could use arrayfun.
weights = arrayfun(#(x) x.properties.metric.weight, apple);
mean(weights)

Pythonic way in matlab to decompose array to variables

So I want to decompose array to multiple variables.
For example,
I have 'data' array of (136,9) size which is of double type.
I want to decompose the values of data(1,:) to multiple variables something like below:
[frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = data(1,:);
In python it was straightforward, but above code gives following error in matlab:
Insufficient number of outputs from right hand side of equal sign to satisfy
assignment.
I can go with something like
frm_id = data(1,1);
seq_id = data(1,2);
%ect
But I do believe there must be matlab (more neat) way to do this operation.
Thanks!
You can use num2cell to convert the matrix to a cell array then copy contents of the cell to each variable:
C = num2cell(data,1);
[frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = C{:};
I can only suggest you to create a function like this:
function [frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = myfunction (data)
frm_id = data(:,1);
seq_id = data(:,2);
xmin = data(:,3);
ymin = data(:,4);
w = data(:,5);
h = data(:,6);
temp1 = data(:,7);
temp2 = data(:,8);
temp3 = data(:,9);
so in your main code
[frm_id,seq_id,xmin,ymin,w,h,temp1,temp2,temp3] = myfunction(data);

generalizing scalar inputs to array inputs for user-defined fucntions

The round function can take a scalar and operate on it. However it can also take an array and operate on it in expected manner.
>> round(2.3)
ans =
2
>> round([2.3,3.4])
ans =
2 3
I similarly have a function and want it to work in the ""expected"" manner for array inputs. It works well for scalar inputs. I could run a for loop and evaluate my function on each element of the array but what other smarter ways do i have available?
For further concreteness, i have:
function [a,b]=func(c,d,e,f)
And i have d,e,f but i want to evaluate the function to several values of c:-
g=[];
for i=1:10
[a,b]=func(c(i),d,e,f);
g=[g;[a,b]];
end
I am not fully sure how to apply arrayfun though i believe it is what i should use.
What you are looking for is the arrayfun function.
Here is the documentation: http://www.mathworks.com/help/matlab/ref/arrayfun.html
Say, I have this function:
function res = myTest(a,b)
size(a) % for checking that we work on each element
res = a+1;
res = res +b;
I have a matrix A
A = magic(3);
I want to apply myTest with a equal to each element in A
A1 = arrayfun(#(x) myTest(x,2),A)
(x is replaced by the elements of the array declared after the comma)

Resources