Matlab: Cell contents assignment to a non-cell array object - arrays

I am getting the above mentioned error while going through multiple loops. I really don't know how to explain the problem, But i will try my best
code:
function this = tempaddfilt(this,varargin)
fc = linspace(1,200,(200/0.5));
main = struct('seg_err',{},'sig_err',{},'filt_err',{},'fc',{});
for a = 1:length(fc) % fc
q = 0;
w = 0
for i = 1:length(this.segments) % total number signal
for k = 1:length(this.segments{i}) % total number of segments
filt_sig = eval(this.segments{i}(k).signal,this.segments{i}(k).signal(1)); % apply filter to the ith singal and kth segemnt
filt_sig = filt_sig';
main{i}(k).seg_err(a) = std(filt_sig-this.segments{i}(k).ref); % calculate the standard divitation of the filtered signal and previously calculated signal.
q = q+main{i}(k).seg_err(a); add all the error of the segments for the same FC
end
main{i}(1).sig_err(a) = q; % assign the sum of all error of the all segemnts of the same signal
w = w+main{i}(1).sig_err(a); % add all the error of the signals
end
main.filt_err = w; % assign the sum of all error of the all signals
end
this.error_norm = [this.error_norm ;main];
end
end
Basically I have 3 loops, 1st loop is for fc, 2nd loop is for signal and 3rd loop is for segemnts of the singal. Program works fine when fc = 1.
But when fc is 2, i get the following error:
Cell contents assignment to a non-cell array object.
in the line:
main{i}(k).seg_err(a) = std(filt_sig-this.segments{i}(k).ref);
that is when i =1 , k=1 ,a = 2

The problem seems to be with the way how you want to access the members of the main struct dynamically. You declared main as a struct, with
main = struct('seg_err',{},'sig_err',{},'filt_err',{},'fc',{});
BUT struct members can not be accessed by using braces {}. Here is a reference to a previous discussion related to dynamic indexing of struct arrays. So, basically, the problem is with "main{i}", which is not a valid way of dynamically indexing members of a struct.
Try following.
Change the struct declaration to explanation
main = struct('seg_err',[],'sig_err',[],'filt_err',[],'fc',[]);
Then, extract the field names by
FieldNames = fieldnames(main);
Then, you can reference the struct members like in
for
loopIndex = 1:numel(FieldNames)
main.(FieldNames{loopIndex})(1).seg_err(1) = 1;
end

Related

Issues with arrays and matlab coder

I've been working on creating c code with Matlab-coder. I have a function called melfunction that gives an error for line 20 of the code below.
function [ c ] = melfunction( x )
bank=melbankm(24,256,8000,0,0.4,'t');
bank=full(bank); %full() convert sparse matrix to full matrix
bank=bank/max(bank(:));
w=1+6*sin(pi*[1:12]./12);
w=w/max(w);
xx=double(x);
xx=filter([1-0.9375],1,xx);
xx=enframe(xx,256,80)
p = zeros(1,256); < --------------------- SOLUTION CHANGE TO p = zeros(256)
m = zeros(1,12); < --------------------- SOLUTION CHANGE TO p = zeros(12)
for i=1:size(xx,1)
y=xx(i,:);
s=y'.*hamming(256);
t=abs(fft(s));
t=t.^2;
p(i,:) = t; < --------------------------- ERROR HERE
c1=dctcoef*log(bank*t(1:129));
c2=c1.*w';
m(i,:)=c2;
end
error message looks like this
Error using melfunction (line 20)
Index exceeds array dimensions. Index value 2 exceeds valid range [1-1] of array p.
Error in TESTINPUTS (line 2)
d0=melfunction(x)
If I come up with an answer I will post it when I find one.
Ok so I messed with it a bit and found that I believe I did not know how to correctly initialize p and m . In an attempt to add these lines of code for another part of Matlab-Coder I initialized the p and m variables incorrectly. This is the original initialization.
p = zeros(1,256);
m = zeros(1,12);
this is what I changed it too.
p = zeros(256);
m = zeros(12);

Is it possible to average across array of structure sub-fields in Matlab without looping?

I have a function that processes an individual data set, and stores the outcome metrics in a data set like so:
trial_data.output_metric_a.value_1 = 2;
...
trial_1 = trial_data;
This function is applied to a number of different trials and are stored as an array struct:
% trial_1.output_metric_a.value_1 = 4
% trial_2.output_metric_a.value_1 = 2
trials = [trial_1 ; trial_2];
Is it possible to get averages and standard deviations of the sub-field values
without looping through the data structure?
Ideally:
mean_trials = mean(trials)
mean_trials.output_metric_a.value_1 == 3 % true
A possible loop implementation to solve this could be (obviously this leaves much to be desired):
output_metric_a_value_1 = [];
...
for i:length(trials)
output_metric_a_value_1(end+1) = trials(i).output_metric_a.value_1;
... % For each output metric and value
end
mean_trials.output_metric_a.value_1 = mean(output_metric_a_value_1);
You can convert the main struct to cell, then operate on the contents:
% your data base:
trial_1.output_metric_a.value_1 = 4
trial_2.output_metric_a.value_1 = 2
trials = [trial_1 ; trial_2];
% convert to cell:
Ctrials=struct2cell(trials);
Atrials=[Ctrials{:}];
meanTrials=mean([Atrials.value_1])
meanTrials=
3
Special Solution (not-nested struct > array)
As already mentioned, aiming for just one level of struct fields (not nested) one can basically go for a one-liner:
sarr_mean = cellfun(#(fn) mean([sarr.(fn)]), fieldnames(sarr))
Remark: In the not-nested case, there is not really a need to assign the resulting array back to a struct. If required, you can do it analogously to the full solution below.
Full Solution (nested struct > nested struct)
However, with arbitrarily nested arrays, I suggest to use a function such as the following:
% f... function handle
% s... nested struct array
function sarr_res = nestedSarrFun(f, s)
if isstruct(s)
% get fieldnames:
fns = fieldnames(s);
% get content:
con = cellfun(#(fn) nestedSarrFun(f, [s.(fn)]), ...
fns, 'UniformOutput', false);
% create return struct
fnsCon = reshape([fns(:), con(:)]', [1,2*numel(fns)]);
sarr_res = struct(fnsCon{:});
else
sarr_res = f(s);
end
end
Usage Example
Define example struct array and apply mean via nestedSarrFun:
% define example struct array "sarr" with fields
% .foo.bar1
% .bar2
% .dings
sarr = struct();
sarr(1).foo.bar1 = 2;
sarr(1).foo.bar2 = 7;
sarr(1).dings = 1;
sarr(2).foo.bar1 = 5;
sarr(2).foo.bar2 = 5;
sarr(2).dings = 2;
% apply mean to all nested fields:
sarr_mean = nestedSarrFun(#mean, sarr);
Example Result:
sarr_mean.foo.bar1 = 3.5
sarr_mean.foo.bar2 = 6
sarr_mean.dings = 1.5
In Matlab2017 (I am not sure about older versions), an array of structs can return an array of the field of its structs like so:
struct1.x = 1;
struct2.x = 2;
% array of 2 structs:
struct_array = [struct1, struct2];
% array of field x of each struct:
[struct_array.x]
which returns
ans =
1 2
In your case, the data is not in a field of your struct, but in a subfield output_metric_a. Therefore, you first need to this twice:
trial1.output_metric_a.value_1 = 1;
trial2.output_metric_a.value_1 = 2;
trials = [trial1, trial2];
output_metric_a_array = [trials.output_metric_a];
value_1_array = [output_metric_a_array.value_1];
mean_of_value_1 = mean(value_1_array)
which returns
mean_of_value_1 =
1.5000

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)

Vectorizing and assigning structs and fields

I would like to vectorize operations with structs and have spent some hours searching but I couldn’t find the solution.
I have a struct as follows:
clear all
n= 10;
for i=1:n
mystruct(i).x = i;
mystruct(i).y = i;
mystruct(i).z = 0;
end
I want to add all x to all y values and assign them to z.
This works:
test1 = [mystruct.x] + [mystruct.y];
test2 = arrayfun(#(a)mystruct(a).x+mystruct(a).y,1:n);
However, assigning the array doesn’t:
mystruct.z = [mystruct.x] + [mystruct.y];
mystruct.z = mystruct.x + mystruct.y;
I can generate a new struct:
mystruct2 = struct('z',num2cell ([mystruct.x]+[mystruct.y]));
But the next operation again doesn't work:
mystruct.z = mystruct2.z;
I could of course use a for loop:
for i=1:n
mystruct(i).z = mystruct(i).x + mystruct(i).y;
end
But that’s not what I want as this would make my code quite nasty and will not work for parallel operations using "parfor".
How can I perform this operation?
I believe you want to use the deal function, which is used to distribute inputs to outputs and works with structures starting with MATLAB 7.
With appropriate use of [square brackets], you can use this:
[mystruct2.z] = deal([mystruct(1:n).x] + [mystruct(1:n).y]);
which outputs:
ans =
2 4 6
Yay!

Variable dimensions in file don't match with dimension of indexing subscript if one dimension is singleton

I want to test a function func(par1,par2,par3) with all combinations of the parameters par1, par2 and par3 and store the output in a .mat file. My code looks like this at the moment:
n1 = 3;
n2 = 1;
n3 = 2;
parList1 = rand(1,n1); % n1,n2,n3 is just some integer
parList2 = rand(1,n2); % the lists are edited by hand in the actual script
parList3 = rand(1,n3);
saveFile = matfile('file.mat','Writable',true);
% allocate memory
saveFile.output = NaN(numel(parList1),numel(parList2),numel(parList3));
counter1 = 0;
for par1 = parList1
counter1 = counter1 + 1;
counter2 = 0; % reset inner counter
for par2 = parList2
counter2 = counter2 + 1;
counter3 = 0; % reset inner counter
for par3 = parList3
counter3 = counter3 + 1;
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
end
end
This works except if parList3 has only one item, i.e. if n3 = 1. Then the saveFile.output has singleton dimensions and I get the error
Variable 'output' has 2 dimensions in the file, this does not match the 3 dimensions in the indexing subscripts.
Is there a elegant way to fix this?
The expression in the for statement needs to be a row array, not a column array as in your example. The loops will exit after the first value with your code. Set a breakpoint on the saveFile.output command to see what I mean. With a column array, par1 will not be a scalar as desired, but the whole parList1 column. With a row array, par1 will iterate through each value of parList1 as intended
Another thing is that you need to reset your inner counters (counter2 and counter2) or your second and third dimensions will blow up larger than you expected.
The n3=1 problem is expected behavior because matfile defines the variables with fixed number of dimensions and it will treat saveFile.output as 2D. Once you have fixed those issues, you can solve the n3=1 problem by changing the line,
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
to
if n3==1, saveFile.output(counter1,counter2) = sum([par1,par2,par3]);
else saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
By now I realized that actually in matfiles all the singleton dimensions, except for the first two are removed.
In my actual programm I decided to save the data in the file linearly and circumvent matfile's lack of linear indexing capability by using the functions sub2ind and ind2sub.

Resources