How to gather results with parfor in MATLAB - matlab

I have a function called SpreadFinder which returns an array of structures:
[ spreads ] = SpreadFinder( pp, corr_thres, i_range )
The return object looks like this:
spreads =
1x4026 struct array with fields:
px
tuple
beta
The code is:
m = containers.Map(0, 0, 'uniformValues',false); m.remove(0);
parfor col = 1:8
spreads = SpreadFinder(pp, 0.8, i_ranges(:,col)');
m(col) = spreads;
end
When I run it, I have this error:
Subscripted assignment dimension mismatch.
I know the Map can be used to store an array of structures. (https://stackoverflow.com/a/2365526)
This code below works. So I don't think it's a problem of dimension...
m(1) = spreads(1:2);
m(2) = spreads(1:4);
The objective is to merge all the results into one map and possibly iterate over this map after the parallel task.
Can someone help me out?
Best,

IIRC, you cannot assign to a containers.Map inside a parfor because a containers.Map is not sliceable. If you want to assign elements to an array inside a parfor, it must be sliceable. What I mean by sliceable is that you are able to extract a subset of the values via the colon operator (i.e. 1:3, 2:4), etc. You cannot do that with a containers.Map unless you specifically use the values function, and from there you can specify a cell array of keys and get the values from there. Using () syntax only permits you to access one key at a time.
Therefore, what I would suggest you do is assign the outputs to a cell array, and use this cell array as values into your containers.Map. Something like this:
%// Create temporary cell array of values
c = cell(1,8);
%// Run parfor for each col
parfor col = 1:8
c{col} = SpreadFinder(pp, 0.8, i_ranges(:,col)');
end
%// Make a new containers.Map for these values
m = containers.Map(1:8, c, 'uniformvalues', false);
%clear c; %// Optional if you don't want the temporary cell array to stick around

Related

Add a number to a struct field [duplicate]

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

MATLAB QUESTIONS on logical indexing and cell2struct function

First Question:
Hello there!
I am trying to assign this vector
v(mod(v,2)~=0)=0
The operations is supposed to replace odd numbers in a vector with 0. I am trying to assign this vector to result variable in a function.
something goes wrong when I try this
function
function [result1,result2] = myfunction(v)
v(mod(v,2)==0)= 0;
result1 = v;
v(mod(v,2)~=0) = 0;
result2 = v;
return
QUESTION 2:
I am trying to figure out an alternative way to express the the function cell2struct in for-loop format
for example,
if we have a cell array with 2 dimensions containing food labels. Their names, caloric count and price, each in one column. Can we write a function that's can transfer the information in cells to a struct that contain each of the fields above?
Thanks
Question #1
You are setting all even numbers to 0, but then you are using this mutated result to search for odd numbers and setting them to 0. This will probably not give you what you intended as you are using a modified copy of the original vector, so it would be prudent that you keep a copy of the vector before doing each operation.
function [result1,result2] = myfunction(v)
vcopy = v; %// Make a copy
vcopy(mod(vcopy,2)==0)= 0; %// Find even numbers and set to 0
result1 = vcopy;
vcopy = v; %// Make another copy
vcopy(mod(vcopy,2)~=0) = 0; %// Find odd numbers and set them to 0.
result2 = vcopy;
return
Question #2
Yup. If you have a list of field names stored in f and their corresponding entities for each field stored in c, simply use a loop like so:
function [s] = my_cell2struct(c, f)
for idx = 1 : numel(f)
s.(f{idx}) = c{idx};
end
The above code does no error checking, so you need to make sure that the total number of elements in c matches those of f. Also, c and f must be cell arrays. Notice that s wasn't declared at all in the function. Also, using the dot operator combined with enclosing brackets and a string that goes inside the enclosing brackets allows you to dynamically create field names on the fly. As such, for each string in f, we access the corresponding value stored in c, and we create a field name that contains this value.
Here's a reproducible example from the MathWorks documentation:
c = {'tree',37.4,'birch'};
f = {'category','height','name'};
s = cell2struct(c, f, 2)
s =
category: 'tree'
height: 37.4000
name: 'birch'
Notice that I use cell2struct here from native MATLAB to produce the above structure. Doing the above for loop which is wrapped in a function called my_cell2struct, we get:
c = {'tree',37.4,'birch'};
f = {'category','height','name'};
s = my_cell2struct(c, f)
s =
category: 'tree'
height: 37.4000
name: 'birch'

Create a matrix combining many variables by using their names and a for loop

Suppose I have n .mat files and each are named as follows: a1, a2, ..., an
And within each of these mat files there is a variable called: var (nxn matrix)
I would like to create a matrix: A = [a1.var a2.var, ..., an.var] without writing it all out because there are many .mat files
A for-loop comes to mind, something like this:
A = []
for i = 1:n
[B] = ['a',num2str(i),'.mat',var];
A = [A B]
end
but this doesn't seem to work or even for the most simple case where I have variables that aren't stored as a(i) but rather 'a1', 'a2' etc.
Thank you very much!
load and concatenate 'var' from each of 'a(#).mat':
n = 10;
for i = n:-1:1 % 1
file_i = sprintf('a%d.mat', i); % 2
t = load(file_i, 'var');
varsCell{i} = t.var; % 3
end
A = [varsCell{:}]; % concatenate each 'var' in one step.
Here are some comment on the above code. All the memory-related stuff isn't very important here, but it's good to keep in mind during larger projects.
1)
In MATLAB, it is rarely a good idea or necessary to grow variables during a for loop. Each time an element is added, MATLAB must find and allocate a new block of RAM. This can really slow things down, especially for long loops or large variables. When possible, pre-allocate your variables (A = zeros(n,n*n)). Alternatively, it sometimes works to count backwards in the loop. MATLAB pre-allocates the whole array, since you're effectively telling it the final size.
2)
Equivalent to file_i = ['a',num2str(i),'.mat'] in this case, sprintf can be clearer and more powerful.
3)
Store each 'var' in a cell array. This is a balance between allocating all the needed memory and the complication of indexing into the correct places of a preallocated array. Internally, the cell array is a list of pointers to the location of each loaded 'var' matrix.
to create a test set...
generate 'n' matrices of n*n random doubles
save each as 'a(#).mat' in current directory
for i = 1:n
var = rand(n);
save(sprintf('a%d.mat',i), 'var');
end
Code
%%// The final result, A would have size nX(nXn)
A = zeros(n,n*n); %%// Pre-allocation for better performance
for k =1:n
load(strcat('a',num2str(k),'.mat'))
A(1:n,(k-1)*n+1:(k-1)*n+n) = var;
end

Foreach loop problems in MATLAB

I have the following piece of code:
for query = queryFiles
queryImage = imread(strcat('Queries/', query));
queryImage = im2single(rgb2gray(queryImage));
[qf,qd] = vl_covdet(queryImage, opts{:}) ;
for databaseEntry = databaseFiles
entryImage = imread(databaseEntry.name);
entryImage = im2single(rgb2gray(entryImage));
[df,dd] = vl_covdet(entryImage, opts{:}) ;
[matches, H] = matchFeatures(qf,qf,df,dd) ;
result = [result; query, databaseEntry, length(matches)];
end
end
It is my understanding that it should work as a Java/C++ for(query:queryFiles), however the query appears to be a copy of the queryFiles. How do I iterate through this vector normally?
I managed to sort the problem out. It was mainly to my MATLAB ignorance. I wasn't aware of cell arrays and that's the reason I had this problem. That and the required transposition.
From your code it appears that queryFiles is a numeric vector. Maybe it's a column vector? In that case you should convert it into a row:
for query = queryFiles.'
This is because the for loop in Matlab picks a column at each iteration. If your vector is a single column, it picks the whole vector in just one iteration.
In MATLAB, the for construct expects a row vector as input:
for ii = 1:5
will work (loops 5 times with ii = 1, 2, ...)
x = 1:5;
for ii = x
works the same way
However, when you have something other than a row vector, you would simply get a copy (or a column of data at a time).
To help you better, you need to tell us what the data type of queryFiles is. I am guessing it might be a cell array of strings since you are concatenating with a file path (look at fullfile function for the "right" way to do this). If so, then a "safe" approach is:
for ii = 1:numel(queryFiles)
query = queryFiles{ii}; % or queryFiles(ii)
It is often helpful to know what loop number you are in, and in this case ii provides that count for you. This approach is robust even when you don't know ahead of time what the shape of queryFiles is.
Here is how you can loop over all elements in queryFiles, this works for scalars, row vectors, column vectors and even high dimensional matrices:
for query = queryFiles(:)'
% Do stuff
end
Is queryFiles a cell array? The safest way to do this is to use an index:
for i = 1:numel(queryFiles)
query = queryFiles{i};
...
end

Create an array of 'Geostruct' Data in Matlab

Right now I am creating mapstructs in Matlab and then exporting them individually as shape files using the shapewrite() function.
However, instead of exporting them individually I want to store all of them into an array and then save it at the end as one single shapefile which holds all of the points from the mapstructs stored in the array.
My problem is I don't know how to initialize an array to hold these mapstructs. I've tried
`a = struct(sizeofarray)`
but it isn't compatible with mapstructs. I would appreciate any help!
You can store any kind of data in a cell array:
a = cell(sizeofarray,1);
You can then assign them like this:
a{1} = firstmapstruct;
a{2} = secondmapstruct;
However, if I understand you correctly you have mapstructs from the MATLAB Mapping Toolbox and want to concat structs of this form:
firstmapstruct =
609x1 struct array with fields:
Geometry
BoundingBox
X
Y
STREETNAME
RT_NUMBER
CLASS
ADMIN_TYPE
LENGTH
So you should probably do
a = firstmapstruct;
a(end+1:end+numel(secondmapstruct))= secondmapstruct;
and so on...
If all of your individual mapstructs have the same fields, you should be able to initialize a structure array by replicating one of your mapstructs using the function REPMAT:
a = repmat(mapstruct1,1,N); %# A 1-by-N structure array
Then just fill in each element as needed:
a(2) = mapstruct2; %# Assign another mapstruct to the second array element
a(3).X = ...; %# Assign a value to the X field of the third element
a(3).Y = ...; %# Assign a value to the Y field of the third element
You can find out more information about Geographic Data Structures in this documentation.