Any simple way to change a struct with fields of length "n" to a struct of length"n"?

8 次查看(过去 30 天)
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 个评论
Guillaume
Guillaume 2016-3-11
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?
Art
Art 2016-3-11
After much searching and some therapy, this code will do what I want, however it takes WAY too long for what I need. It appears to create an array of the two starting structs (that contain different fields), although I may not be clear on that part:
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];
allStructs = {'Mystruct' 'Yourstruct'};
totind = 0;
for ind = 1:2
thisStruct = eval(char(allStructs(ind)));
fn = [rot90(fieldnames(thisStruct))]';
nItems = numel(thisStruct.(fn{1})); %%all fields are same length
sf=fn';
sf(2,1:numel(fn))={{}};
sf = sf(:)';
S=struct(sf{:});
for f=1:numel(fn)
for n = 1:nItems %%nItems: -1 : 1;
S(n).(fn{f}) = thisStruct.(fn{f})(:,n);
end
end
%%include struct "S" into "all_messages" and "all_times".
%%"all_times" is used to sort the cell array "all_messages".
for ind = 1:numel(S)
totind = totind + 1;
all_messages(totind) = {S(ind)};
all_times(totind) = S(ind).time;
end
end
[~,msg_order] = sort(all_times);
ordered_msgs = all_messages(msg_order);
Typing ordered_msgs{1} gives a struct with fields time, field2 and field3, typing ordered_msgs{2} gives a struct with time and fieldX.
Any way to make this faster?

请先登录,再进行评论。

回答(3 个)

Jos (10584)
Jos (10584) 2016-3-11
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 个评论
Art
Art 2016-3-11
编辑:Art 2016-3-11
Well, thanks for the feedback but let me clarify. I inherited two very usable structs which are, in fact, separate messages that are normally handled as separate entities. Because there are some fields common to both (not indicated in this example), it would be very handy for me to incorporate them into one time-ordered variable for plotting and analysis.
Just looking for an easy way of doing this.
Art
Art 2016-3-11
BTW Jos, the messages are based on time. If there is no time at a particular index, there won't be that index point in any field.

请先登录,再进行评论。


Guillaume
Guillaume 2016-3-11
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)

Kelly Kearney
Kelly Kearney 2016-3-11
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 个评论
Art
Art 2016-3-11
Thanks to both you, Kelly, and Guillaume for great help. Let me add one more wrinkle: if there were one field with char instead of num, could your examples work with this?
e.g, in your example Kelly, if
A.field2 = [{'blah'} {'blah'} {'xxxx'} {'yyyy'} {'zzzzz'}];
Kelly Kearney
Kelly Kearney 2016-3-12
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.

请先登录,再进行评论。

类别

Help CenterFile Exchange 中查找有关 Structures 的更多信息

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by