I need to iterate through a large number of LinearModel fit Objects and have them stored in a logical way, preferably through an indexing method, e.g. model{x,y}. Less preferable is eval(), which I have tried and in any case is not working. I get the error Index exceeds matrix dimensions. - although the string expression works just fine outside of eval.
counter = 48;
str=strcat('model',+num2str(counter)); % Dynamic variable name
str1 = strcat(str,'=fitlm(tbl,modelspec)'); % Full string to be evaluated
eval(str1)
Ideally I wanted to do, while iterating through x
model{x,y} = fitlm(tbl,modelspec) % This is the equivalent expression
But the error I get is
"Assignment using {} is not allowed for a FitObject."
I think this is similar to this question - with no answer:
Dynamic Objects in Matlab
Managed to solve this by assigning to a cell structure.
e.g.
model = cell(3,3) % Pre-assign a cell structure
for x=1:3
for y=1:3
model{x,y} = fitlm(tbl,modelspec);
end
end
Related
In matlab, is it possible to make the iterative variable a cell array? is there a workaround? This is the code I ideally want, but throws errors:
dim={};
a=magic(5);
for dim{1}=1:5
for dim{2}=1:5
a(dim{:})=1; %aimed to be equivalent to a(dim{1},dim{2})=1;
end
end
for dim{1}=1:5
↑
Error: Invalid expression. When calling a function or indexing a variable, use
parentheses. Otherwise, check for mismatched delimiters.
I tested that you cannot have A(1), or A{1} or A.x as index variable. https://www.mathworks.com/help/matlab/ref/for.html doesn't explicitly prohibit that, but it doesn't allow it either.
After very slight changes on your code, this should achieve what you seem to want:
dim={};
a = magic(5);
for dim1=1:5
dim{1} = dim1;
for dim2=1:5
dim{2} = dim2;
a(dim{:})=1; %aimed to be equivalent to a(dim{1},dim{2})=1;
end
end
However, I believe the following is a slightly better solution keeping the spirit of "use a cell array to index in your array":
CV = combvec(1:5,1:5); % get all combinations from (1,1) to (5,5). 2x25 double array. This function is a part of deep learning toolbox. Alternatives are available.
CM = num2cell(CV); % 2x25 cell array. Each element is a single number.
for dim = CM
% dim is a 2x1 cell array, eg {2,3}.
a(dim{:}) = 1; % as above.
end
However, none of these are likely a good solution to the underlying problem.
I have this (quite long) Matlab code with nested loops where I want to parallelize the main time-consuming iteration. The only variable that (apparently) gives me problems is DMax, where I get the error:
Error: The variable DMax in a `parfor` cannot be classified.
See Parallel for Loops in MATLAB, "Overview".
This is a draft of my code:
t0=matrix (Maxiter,1); % This is a big matrix whose dimensions are reported in brachets
Maxiter = 1E6;
DMax = zeros(Maxiter,40);
% Other Stuff
for j=1:269
% Do more stuff
for soil=1:4
parfor i =1:Maxiter
k(i,soil) = a %k is a real number
a(i,soil) = b %similar to k
% Do a lot of stuff
for t= (floor(t0(i,soil))+1):40
DMax(i,t) = k(i,soil)*((t-t0(i,soil))^a(i,soil));
% Do some more stuff
end
end
end
end
for time=1:40
% Do the final stuff
end
I guess the problem is in the way I defined DMax, but I do not know what it could be more precisely. I already looked on the web but with not very satisfying results.
It is very clearly described in the documentation that each variable inside parfor must be classified into one of several types. Your DMax variable should be a sliced variable (arrays whose segments are operated on by different iterations of the loop), but in order to be classified as such, all the following conditions must hold:
Type of First-Level Indexing — The first level of indexing is either parentheses, (), or braces, {}.
Fixed Index Listing — Within the first-level parenthesis or braces, the list of indices is the same for all occurrences of a
given variable.
Form of Indexing — Within the list of indices for the variable, exactly one index involves the loop variable.
Shape of Array — The array maintains a constant shape. In assigning to a sliced variable, the right-hand side of the assignment cannot be [] or '', because these operators attempt to
delete elements.
Clearly, Fixed Index Listing property does not hold since you reference it as DMax(i,t) where t changes its values. There's an identical example described in the documentation, please pay attention. So one workaround would be to use a temporary variable inside the inner loop, and then assign the whole row back to DMax.
Also note that variable a cannot be classified into any category either. That's not to mention that it's not defined in your example at all. Please read the guide carefully and make sure it can be classified into one of the categories. Rewrite the code if needed, e.g. introducing new temporary variables.
Here's the code where DMax usage is corrected:
Maxiter = 1E6;
t0 = randn(Maxiter,1); % This is a big matrix whose dimensions are reported in brachets
DMax = zeros(Maxiter,40);
% Other Stuff
for j = 1:269
% Do more stuff
for soil = 1:4
parfor i = 1:Maxiter
k(i,soil) = a %k is a real number
a(i,soil) = b %similar to k
% Do a lot of stuff
tmp = zeros(1,40);
for t = (floor(t0(i,soil))+1):40
tmp(t) = k(i,soil)*((t-t0(i,soil))^a(i,soil));
% Do some more stuff
end
DMax(i,:) = tmp;
end
end
end
for time = 1:40
% Do the final stuff
end
I have 40 structures in my Workspace. I Need to write a script to calculate the directional derivatives of all the elements. Here is the code :
[dx,dy] = gradient(Structure_element_1.value);
dxlb = min(min(dx));
dxub = max(max(dx));
dylb = min(min(dy));
dyub = max(max(dy));
[ddx,ddy] = gradient(gradient(Structure_element_1.value));
ddxlb = min(min(ddx));
ddxub = max(max(ddx));
ddylb = min(min(ddy));
ddyub = max(max(ddy));
This is the code for one element. I Need to find out the same for all the 40 elements and then use it later. Can anyone help with this.
To answer your literal question, you should store the variables in a structure array or at least a cell array. If all of your structures have the same fields, you can access all of them by indexing a single array variable, say Structure_element:
for i = 1:numel(Structure_element)
field = Structure_element(i).value
% compute gradients of field
end
Now to address the issue of the actual gradient computation. The gradient function computes an approximation for , where is your matrix of data. Normally, a MATLAB function is aware of how many output arguments are requested. When you call gradient(gradient(F)), the outer gradient is called on the first output of the inner gradient call. This means that you are currently getting an approximation for .
I suspect that you are really trying to get . To do this, you have to get both outputs from the inner call to gradient, pass them separately to the
outer call, and choose the correct output:
[dx,dy] = gradient(F);
[ddx, ~] = gradient(dx);
[~, ddy] = gradient(dy);
Note the separated calls. The tilde was introduced as a way to ignore function arguments in MATLAB Release 2009b. If you have an older version, just use an actual variable named junk or something like that.
Suppose I have a struct array arr, where each element has a bunch of fields, including one called val. I'd like to increment each element's val field by some constant amount, like so:
for i = 1:length(arr)
arr(i).val = arr(i).val + 3;
end
This obviously works, but I feel there should be a way to do this in just one line of code (and no for loop). The best I've come up with is two lines and requires a temp variable:
newVals = num2cell([arr.val] + 3);
[arr.val] = deal(newVals{:});
Any ideas? Thanks.
Just a note, the deal isn't necessary there:
[arr.val] = newVals{:}; % achieves the same as deal(newVals{:})
The only other way I know how to do this (without the foor loop) is using arrayfun to iterate over each struct in the array:
% make a struct array
arr = [ struct('val',0,'id',1), struct('val',0,'id',2), struct('val',0,'id',3) ]
% some attempts
[arr.val]=arr.val; % fine
[arr.val]=arr.val+3; % NOT fine :(
% works !
arr2 = arrayfun(#(s) setfield(s,'val',s.val+3),arr)
That last command loops over each struct in arr and returns a new one where s.val has been set to s.val=3.
I think this is actually less efficient than your previous two-liner and the for loop though, because it returns a copy of arr as opposed to operating in-place.
(It's a shame Matlab doesn't support layered indexing like [arr.val]=num2cell([arr.val]+3){:}).
I like Carl's and mathematical.coffee's original ideas.
I have multiple similar lines to express, so for concision of my mainline code,
I went ahead and made the generic subfunction
function varargout = clist(in)
varargout = {in{:}};
end
then I could express each such line in a fairly readable way
[arr.var] = clist(num2cell([arr.var]+3));
[arr.var2] = clist(num2cell([arr2.var]/5+33));
Are all the fields in that struct scalar, or the same size? If so, the idiomatic Matlab way to do this is to rearrange your struct to be a scalar struct with arrays in each of its fields, instead of an array of structs with scalar values in the fields. Then you can do vectorized operations on the fields, like arr.val = arr.val + 3;. See if you can rearrange your data. Doing it this way is much more efficient in both time and memory; that's probably why Matlab doesn't provide convenient syntax for operating over fields of arrays of structs.
if the struct array you are trying to set is a set of graphics objects (line handles, figure handles, axes handles, etc), then you need to use the function set:
x = (1:10)';
Y = rand(10,5);
l = plot(x,Y,'-k'); % returns an array of line handles in l
set(l,'Color','r'); % sets the property 'Color' for all the five lines in l
I've often found myself doing something like this:
unprocessedData = fetchData(); % returns a vector of structs or objects
processedData = []; % will be full of structs or objects
for dataIdx = 1 : length(unprocessedData)
processedDatum = process(unprocessedData(dataIdx));
processedData = [processedData; processedDatum];
end
Which, whilst functional, isn't optimal - the processedData vector is growing inside the loop. Even mlint warns me that I should consider preallocating for speed.
Were data a vector of int8, I could do this:
% preallocate processed data array to prevent growth in loop
processedData = zeros(length(unprocessedData), 1, 'int8');
and modify the loop to fill vector slots rather than concatenate.
is there a way to preallocate a vector so that it can subsequently hold structs or objects?
Update: inspired by Azim's answer, I've simply reversed the loop order. Processing the last element first forces preallocation of the entire vector in the first hit, as the debugger confirms:
unprocessedData = fetchData();
% note that processedData isn't declared outside the loop - this breaks
% it if it'll later hold non-numeric data. Instead we exploit matlab's
% odd scope rules which mean that processedData will outlive the loop
% inside which it is first referenced:
for dataIdx = length(unprocessedData) : -1 : 1
processedData(dataIdx) = process(unprocessedData(dataIdx));
end
This requires that any objects returned by process() have a valid zero-args constructor since MATLAB initialises processedData on the first write to it with real objects.
mlint still complains about possible array growth, but I think that's because it can't recognise the reversed loop iteration...
In addition to Azim's answer, another way to do this is using repmat:
% Make a single structure element:
processedData = struct('field1',[],'field2',[]);
% Make an object:
processedData = object_constructor(...);
% Replicate data:
processedData = repmat(processedData,1,nElements);
where nElements is the number of elements you will have in the structure or object array.
BEWARE: If the object you are making is derived from the handle class, you won't be replicating the object itself, just handle references to it. Depending on your implementation, you might have to call the object constructor method nElements times.
Since you know the fields of the structure processedData and you know its length, one way would be the following:
unprocessedData = fetchData();
processedData = struct('field1', [], ...
'field2', []) % create the processed data struct
processedData(length(unprocessedData)) = processedData(1); % create an array with the required length
for dataIdx = 1:length(unprocessedData)
processedData(dataIdx) = process(unprocessedData(dataIdx));
end
This assumes that the process function returns a struct with the same fields as processedData.
You can pass in a cell array to struct of the appropriate size:
processedData = struct('field1', cell(nElements, 1), 'field2', []);
This will make a structure array that is the same size as the cell array.