imagine you have a cell Array "test" with two dimensions "e" and "f" of different size. Is it possible to convert this with one dimension to a struct while the other one to the substruct? It works with a Loop, but i don't like Loops. Cause the struct is empty before i can't arrange in an Array with [...].
StrA.SubA.SubSubA=struct('SubSubSubA',[],'SubSubSubB',[]);
count_e=4;
count_f=2;
for e=1:count_e
for f=1:count_f
StrA.SubA(e,1).SubSubA(f,1).SubSubSubA=test{e,f};
end
end
Thanks in advance
You can do this without loops using struct, num2cell, and a couple of transposes:
test = num2cell(rand(4, 2)); % Sample 4-by-2 cell array of random data
StrA = struct('SubA', struct('SubSubA', num2cell(struct('SubSubSubA', test, ...
'SubSubSubB', []).', 1)).');
Although I don't know if it will be any faster or more readable than the loop-based solution.
I should also note that in your loop-based solution, only substructures StrA.SubA(1).SubSubA(1) and StrA.SubA(1).SubSubA(2) will have a SubSubSubB field. All other substructures in StrA.SubA(2:4) will only contain the field SubSubSubA.
Related
I have 5 Matlab structs. I would like to iterate over them. My current solution is the following:
all_structs = [struct1,struct2,struct3,struct4,struct5];
for single_struct = all_structs
% do stuff to each struct here
end
However, each of the structs above has a matrix with a lot of data (including some other properties). Also, whatever I change in the single_struct is not passed back to the original struct.
Question: How do I fix that? Does Matlab copy all that data again when I create the vector all_structs? Or is the data from each of the structs (struct1,...,struct5) passed by reference? Is there a better way to iterate over multiple structs?
Thanks for helping!
struct will not be passed by reference. You will need to loop over the elements in all_structs using an index and then access and modify using that index. If you need something to be treated as reference you will need to define a class for it and make the class inherit from handle. Suggested reading
for i = 1:numel(all_structs)
% do stuff to each struct here
all_structs(i).data = ones(10,5); % your code here
end
I would suggest also reading on arrayfun, though it is useful if you want to do an operation and get results. From your description it sounds like you want to modify the structs.
In case you want to modify content of original structs, without making a copy, you can use a cell array of structs names.
Then iterate the names, and use eval to modify the content.
Using eval is inefficient, so don't make it a habit...
See the following code sample:
%Create sample structs (each struct has a data element).
struct1.data = 1;
struct2.data = 2;
struct3.data = 3;
%Create a cell array containing structs names as strings.
struct_names = {'struct1', 'struct2', 'struct3'};
%Iterate all structs names
%Modify data elements of each struct using eval.
for i = 1:length(struct_names)
sname = struct_names{i}; %Get struct name from cell array.
%Evaluate a string like: 'struct1.data = struct1.data + 1;'
eval([sname, '.data = ', sname, '.data + 1;']);
end
I have a structure in Matlab, each field contains elements with varying numbers of variables. I would like to remove duplicates of numbers that appear within the same field: I know the unique() function and know how to use it to scan through fields one at a time but not an entire field.
I think I want something like:
structure(1:length(structure)).field=unique(structure(1:length(structure)).field
and get
original
field=[1,2,3] [1,4,5] [2,5,8]
to turn into
field=[1,2,3] [4,5] [8]
Maybe a complicated for loop similar to below (isn't working) which would grab the values from the first element in the field, search through each additional element, and if that value is present set it equal to =[];, and iterate through that way?
for n=1:length(RESULTS)
for m=1:length(RESULTS(n).Volumes)
for l=1:length(RESULTS)
for o=1:length(RESULTS(l).Volumes)
if RESULTS(n).Volumes(m)==RESULTS(l).Volumes(o)
RESULTS(l).Volumes(o)=[];
end
end
end
end
end
Thanks!
This is a quick-and-dirty attempt, but you might be able to improve on it. Assume your struct array and field are sa(:).v. I'm also assuming that the field contains a 1xn array of numbers, as in your example. First, make a "joint" array with the concatenation of all field values and filter the non-unique values:
joint = cell2mat({sa.v});
[uniqJoint,~,backIdx] = unique(joint);
The "uniqJoint" array has also been sorted, but the "backIdx" array contains the indices that, if applied to uniqJoint, will rebuild the original "joint" array. We need to somehow connect those to the original indices (i,j) into the struct array and within the field value sa(i).v(j). To do that, I tried creating an array of the same size as "joint" that contains the indices of the struct that originally had the corresponding element in the "joint" array:
saIdx = cell2mat(arrayfun(#(i) i * ones(1,length(sa(i).v)), ...
1:length(sa), 'UniformOutput', false));
Then you can edit (or, in my case, copy and modify the copy of) the struct array to set the field to the values that have not appeared before. To do that, I keep a logical array to mark the indices of "backIdx" as "already used", in which case I skip those values when rebuilding the fields of each struct:
sb = sa;
used = false(length(backIdx));
for i = 1:length(sa)
origInd = find(saIdx == i); % Which indices into backIdx correspond to this struct?
newInd = []; % Which indices will be used?
for curI = backIdx(origInd)
if ~used(curI)
% Mark as used and add to the "to copy" list
used(curI) = true;
newInd(end+1) = curI;
end
end
% Rewrite the field with only the indices that were not used before
sb(i).v = uniqJoint(newInd);
end
In the end, the data in sb(i).v contains the same numbers as sa(i).v without repeats, and removing those that appeared in any previous elements of the struct.
% Data
Fields = {'History','Maths','English','French','Geography','Philosophy'}';
Students= horzcat({'5','7','3','6','10','9'}',{'6','7','7','6','10','9'}',{'5','2','3','9','1','9'}',{'1','4','9','6','6','9'}',{'8','8','3','8','10','9'}',{'5','5','5','9','8','7'}' );
Table_Score = horzcat(Fields,Students);
PS: I know there are certain ways to organize better the raw data to facilitate thing but imagine that it is an pratical case where I want to allocate cells and set values without mentionning directly the name of these variables, but calling them from the cell arrays where they are listed.
% Imagine there are much more fields of study, I will just mention a few
List_of_study_fields = {'History','Maths','English','French','Geography','Philosophy'};
nb_fields = length(List_of_study_fields);
% Imagine there are much more students, I will just mention a few
List_of_students = {'Damien','Michael','Louis', 'Daniel','Isabelle','Jessica'};
%% 1) Pre-allocate "student names" variables from the "student names" (cell array):
% Each variable is the student name in the list.
% Scores will be affected to each variable over the loop. 6 students, 6
% variables:
for ii = 1 : length(List_of_students)
Init_var_cell_array = cell(nb_fields,1);
assignin('caller', List_of_students{ii}, Init_var_cell_array);
end
Ok, If I stop the script at this point and check for the cell variable "Damien", it is a cell 6x1, like I want.
Question 1) I think it is enough to affirm the cell is pre-allocated! Am I wrong to think so?
%% 2) Get scores data for every student. For the example, I write the code for the first student only (Damien)
for bb = 1: length(List_of_study_fields)
Damien(bb) = Table_Score(bb,2);
end
If I keep running until the end, at this point I get the message:
"The variable Damien appears to change size on every loop iteration (within a script). Consider pre-allocating for speed."
Question 2) So, isn't cell array Damien" correctly preallocated ? or is the array preallocated, but in a way that Code Analyzer does not recognize?
Question 3) Finally, imagine the list of students is very long and the scores table is huge, how could I collect all data in one cell array per student without bothering writing their name systematically?
Could I have something as follows:
for aa = 1:length(List_of_student)
for bb = 1: length(List_of_study_fields)
assign(List_of_student{aa}, Table_Score(bb,aa+1))
end
end
So that in the end all my variables Damien, Michael, etc.. are 6x1 cell array ?
Thank you in advance!
Q1: see comments above - I very rarely need to use assignin - I suspect this could be solved in a better way.
Q2: Preallocating cells is much less important than arrays/matrix because each cell can contain different things so its difficult to preallocate efficiently.
Q3: As mentioned in the comments above, store your data in a dynamic struct:
for aa = 1:length(List_of_student)
for bb = 1: length(List_of_study_fields)
yourData.(List_of_student{aa}).(List_of_study_fields{bb}) = Table_Score(bb,aa+1);
end
end
I have a cell array that is a list of file names. I transposed them because I find that easier to work with. Now I am attempting to go through each line in each cell and remove the lines based on their file extension. Eventually, I want to use this list as file names to import data from. This is how I transpose the list
for i = 1:numel(F);
a = F(1,i);
b{i} = [a{:}'];
end;
The code I am using to try and read the data in each cell keeps giving me the error input must be of type double or string. Any ideas?
for i = 1:numel(b);
for k = 1:numel(b{1,i});
b(cellfun(textscan(b{1,i}(k,1),'%s.lbl',numel(b)),b))=[];
end;
end;
Thanks in advance.
EDIT: This is for MATLAB. Should have been clear on that. Thanks Brian.
EDIT2: whos for F is
Name Size Bytes Class Attributes
b 1x11 13986188 cell
while for a is
Name Size Bytes Class Attributes
a 1x1 118408 cell
From your description I am not certain how your F array looks, but assuming
F = {'file1.ext1', 'file2.ext2', 'file3.ext2', 'file2.ext1'};
you could remove all files ending with .ext2 like this:
F = F(cellfun('isempty', regexpi(F, '\.ext2$')));
regexpi, which operates on each element in the cell array, returns [] for all files not matching the expression. The cellfun call converts the cell array to a logical array with false at positions corresponding to files ending with .ext2and true for all others. The resulting array may be used as a logical index to F that returns the files that should be kept.
You're using cellfun wrong. It's signature is [A1,...,Am] = cellfun(func,C1,...,Cn). It takes a function as first argument, but you're passing it the result of textscan, which is a cell array of the matching strings. The second argument is a cell array as it should be, but it doesn't make sense to call it over and over in a loop. `cellfunĀ“'s job is to write the loop for you when you want to do the same thing to every cell in a cell array.
Instead of parsing the filename yourself with textscan, I suggest you use fileparts
Since you're already looping over the cell array in transpose-step, it might make sense to do the filtering there. It might look something like this:
for i = 1:numel(F);
a = F(1,i);
[~,~,ext] = fileparts(a{:});
if strcmpi(ext, '.lbl')
b{i} = [a{:}'];
end
end;
I have a cell array of length 3 and I want to make a for loop with another cell array with length of 6 , so how can I add extra 3 cells for the first array in order to make the 2 cell arrays equal and to use my for loop in MATLAB?
For example, with 2 inputs:
type = { '12' '62' '5' };
colour = {'re' 'green' 'yellow' 'brown' 'blue' 'black'};
for i = 1:length(colour)
if isequal(colour(i), type(:))
result(i) = type(i);
else
end
end
I need to make the type cell array with the same size with colour cell array (I think I have to add extra 3 empty cells in side the type cell array).
I have to address several issues in your code first:
If you use a cell array, you must use curly braces ({}) to extract elements from it. Instead of writing colour(i) you should be writing colour{i}.
This is not a problem, but it's a matter of good practice. If you don't need to handle the else part of the if statement, don't write it at all.
Preallocate memory so that arrays don't grow inside the loop (it slows down the program). Specifically, add the line result = cell(size(colour)); before the for loop.
Your isequal logic is flawed. Practically, it would always return false because colour{1} is one element and type{:} is many.
According to your example, types contain numbers and colours letters, although they are both strings. Does it make sense to compare the two?
Now, regarding your question, it's up to you to decide how the for loop runs. Since you don't mention what you want to achieve (you rather ask how you want to achieve something without saying what exactly), I cannot say what your for loop should look like, if necessary at all. Maybe you meant to use ismember instead of isequal? If so, the fixed code can look like this:
result = cell(size(colour));
for i = 1:length(colour)
if ismember(colour{i}, type)
result{i} = type{i};
end
end
or shorter, like this:
result = cell(size(colour));
[found, idx] = ismember(colour, type);
result(found) = type{idx(found)}
If you provide more details, maybe I can refine my answer so that it helps you more.