Any simple way to change a struct with fields of length "n" to a struct of length"n"?
7 views (last 30 days)
Show older comments
I have 2 structs each containing a field called 'time', along with other differently named fields of length "n"
i.e.
Mystruct.time = [1 4 6 8 9]
Mystruct.field2 = [5 6 7 8 9]
Mystruct.field3 = [23 423 2 4 7]
Yourstruct.time = [3 7]
Yourstruct.fieldX = [5 6]
What I need to do is create a time ordered "orderedStruct" that contains each index of these two structs, ordered by their time fields.
e.g. in this example I could type in "orderedStruct(num)" and could get the result:
orderedStruct(1) = struct with fields 'time' = 1, 'field2' = 5; 'field3' = 23;
orderedStruct(2) = struct with fields 'time' = 3, 'fieldX' = 5
orderedStruct(7) = struct with fields 'time' = 9, 'field2' = 9; 'field3' = 7;
Is there an efficient way to do this? I can imagine putting everything in a nested loop, from 1:number of structs and within that loop another going from 1:number of fields, but my structs could have 300 fields, with length of 50,000 or more. This would be a huge time hog.
Any ideas?
Thanks in advance
5 Comments
Guillaume
on 11 Mar 2016
Note that in an array of structure, all structures must have the same fields, so in your example orderedStruct must also have field2 and field3. What value should these be?
Answers (3)
Jos (10584)
on 11 Mar 2016
This question clearly shows how not thinking about your data management in the beginning will get you into trouble later on.
What do you want to have as the value of a field for which there is no time point ...?
3 Comments
Guillaume
on 11 Mar 2016
Regarding your latest comment, there is of course a simpler way to achieve the same thing, without eval and only looping once over the structures:
MyStruct = struct('Time', [1 4 6 8 9], 'field2', [5 6 7 8 9], 'field3', [23 423 2 4 7]);
YourStruct = struct('Time', [3 7], 'fieldX', [5 6]);
allstructs = {MyStruct, YourStruct}; %store structures in a cell array so we can iterate over them.
reshapedstructs = cell(size(allstructs));
timings = cell(size(allstructs));
%transform each struct from a struct of arrays into an array of structs of scalars
for sidx = 1:numel(allstructs)
fn = fieldnames(allstructs{sidx});
svalues = cell2mat(struct2cell(allstructs{sidx}));
reshapedstruct = cell2struct(num2cell(svalues), fn, 1); %the vertcat will fail if fields of the struct have different size
reshapedstructs{sidx} = num2cell(reshapedstruct'); %split the array of structs into a row cell array of scalar struct
timings{sidx} = allstructs{sidx}.Time;
end
%flatten the cell arrays and reorder by timing
reshapedstructs = [reshapedstructs{:}];
timings = [timings{:}];
[~, order] = sort(timings);
reshapedstructs = reshapedstructs(order);
celldisp(reshapedstructs)
A similar way to achieve the same in even less lines of code, but probably slower:
allstructs = {MyStruct, YourStruct};
reshapedstructs = cellfun(@(s) num2cell(cell2struct(num2cell(cell2mat(struct2cell(s))), ...
fieldnames(s), 1)'), allstructs, ...
'UniformOutput', false);
timings = cellfun(@(s) s.Time, allstructs, 'UniformOutput', false);
reshapedstructs = [reshapedstructs{:}];
timings = [timings{:}];
[~, order] = sort(timings);
reshapedstructs = reshapedstructs(order);
celldisp(reshapedstructs)
0 Comments
Kelly Kearney
on 11 Mar 2016
How about this?
A.time = [1 4 6 8 9];
A.field2 = [5 6 7 8 9];
A.field3 = [23 423 2 4 7];
B.time = [3 7];
B.fieldX = [5 6];
t = unique([A.time, B.time]);
[tf1,loc1] = ismember(A.time, t);
[tf2,loc2] = ismember(B.time, t);
flds = unique([fieldnames(A); fieldnames(B)]);
data = nan(length(flds), length(t));
for ii = 1:length(flds)
if isfield(A, flds{ii})
data(ii,loc1) = A.(flds{ii});
end
if isfield(B, flds{ii})
data(ii,loc2) = B.(flds{ii});
end
end
data = num2cell(data);
C = cell2struct(data, flds, 1);
You didn't specify how you wanted missing values or duplicated times to be treated. So in this example, times without a field value are indicated by NaNs, and any time that has data in both structures simply uses the data from the second one.
2 Comments
Kelly Kearney
on 12 Mar 2016
No, it would need to be modified a bit. Instead of preallocating data with NaNs, you'd need to set it up as a cell array, and add in a few num2cell's when assigning the numeric arrays to it. I'm away from my computer right now but I'll try to post a modified version later.
See Also
Categories
Find more on Structures in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!