Modifying elements of fields in structures

This is similar to Select fields that start with specific letters in for loops - (mathworks.com), but I thought of making a separate post as I would like ask two questions.
I have a humongus structure known as
big_struct
with several fields. Each of these fields are doubles (30 x 1) dimension. So, they contain 30 numerical values.
Examples of these doubles are price_SH_RES_output_SH, price_AM_RES_output_SH etc.
I have two questions, but the second question is more important.
Q1: The following code becomes cumbersome when we add multiple fields, and I was wondering how to make it efficient and succint. If you could show the modifed code (perhaps by using a for loop?), then that would be very helpful.
interested_variable_vec = ["price", "output", "bond"];
interested_town_vec = ["South Hadley", "Amherst", "State College"];
town_code = ["SH", "AM", "SC"];
choose_variable1 = "price_SH";
choose_variable2 = "output_SH";
choose_variable3 = "bond_SH";
[~,town_idx] = ismember(town_of_interest, town_vec);
if town_idx == 0
error('unknown town of interest: "%s"', town_of_interest);
end
if ~strcmp(interested_variable, 'bond')
fieldname1 = interested_variable + "_" + town_code(town_idx) +"_RES_" + choose_variable1;
fieldname2 = interested_variable + "_" + town_code(town_idx) +"_RES_" + choose_variable2;
fieldname3 = interested_variable + "_" + town_code(town_idx) +"_RES_" + choose_variable3;
end
if strcmp(interested_variable, 'bond')
if ~strcmp(town_code, 'SH')
fieldname1 = interested_variable + "_" + town_code(town_idx) + "_RES_" + choose_variable1;
fieldname2 = interested_variable + "_" + town_code(town_idx) + "_RES_" + choose_variable2;
fieldname3 = interested_variable + "_" + town_code(town_idx) + "_RES_" + choose_variable2;
end
end
if ~isfield(big_struct, fieldname1)
error('town and variable combination does not exist: "%s"', fieldname1);
end
if ~isfield(big_struct, fieldname2)
error('town and variable combination does not exist: "%s"', fieldname1);
end
impulse1 = big_struct.(fieldname1);
impulse2 = big_struct.(fieldname2);
impulse3 = big_struct.(fieldname3);
Q2. How do we modify the elements of a field? For instance, impulse1 is a (30 x 1). I would like the first 3 elements of impulse1, impulse2, impulse3 to take the first value in these respective fields
Example: if the elements of impulse1 are:
1.21
1.01
0.95
0.85
0.78
0.66
...
0.01
Then, I would like to the elements to be modified to:
1.21
1.21
1.21
1.01
0.95
0.85
...
...
0.01
I would like to do the same for the other fields and use them them below
% Add the projections of the variables with the forecast intervals
% create a (23 x 1) dimension of empty array with 0s
% length(impulse) = 20 as the horizon is 20 quarters into the future
difference = zeros(4 + length(impulse1) - 1, 1);
for ii = 1:4
% impulse prints the values from IRFs = (20 x 1) array
difference(ii:horizon+ii-1) = difference(ii:horizon+ii-1) + 3*(impulse1 .* impulse2 .* impulse3);
end
Thank you so much for your help in advance.

댓글 수: 10

dpb
dpb 2022년 8월 12일
I think I commented on this before that burying the data into the struct makes for difficult addressing -- I'd suggest explore converting to a table with which you can then use grouping variables and rowfun and other neat builtin features for processing such data by values of given variables.
I'd also suggest that you'd find the lookup much simpler if you converted the various variables such as town, etc., etc., to categorical variables -- then the "==" operator works directly rather than the two-step process of ismember plus performance will be enhanced as it's faster to locate a categorical variable than do string matching.
Storing important information in the names of fields is a bad design. In your case the list of field names works as a data base. Prefer to store the data as data, not encoded in the names of variables or fields.
This looks like a typical copy&paste error:
if ~isfield(big_struct, fieldname2)
error('town and variable combination does not exist: "%s"', fieldname1);
% should be: fieldname2
end
" I would like the first 3 elements of impulse1, impulse2, impulse3 to take the first value in these respective fields"
big_struct.(fieldname1)(1:3) = big_struct.(fieldname1)(1);
big_struct.(fieldname2)(1:3) = big_struct.(fieldname2)(1);
big_struct.(fieldname3)(1:3) = big_struct.(fieldname3)(1);
dpb
dpb 2022년 8월 13일
편집: dpb 2022년 8월 14일
The 2nd-3rd elements are not replaced in the example but two copies of the first element prepended to existing array content...
big_struct.(fieldname1)=[repmat(big_struct.(fieldname1)(1),2,1);big_struct.(fieldname1)];
...
alphabetagamma
alphabetagamma 2022년 8월 15일
편집: alphabetagamma 2022년 8월 15일
Thank you @dpb and @Jan for your suggestions and the helpful code. Yes, I agree that it is better to work with data as data and not through fields. The reason my data is stored in fields is that I was working in another language - Dynare, and using the output from Dynare in MATLAB. All output from Dynare is stored in fields. Some of those are (30 x 1) sized fields, others are (1x1), so the sizes are varying. So, I wasn't sure, and I don't know how to convert numerous fields (more than 50) stored in big structure to a big database.
@dpb, I wonder if you can explain what you meant by "two copies of the first element prepended to existing array content..."
Just following the pattern you gave above in the "What I want..." description --
ORIG EXAMPLE
1.21
1.21
1.21 1.21
1.01 1.01
0.95 0.95
0.85 0.85
... ...
0.01 0.01
The second column is what you posted as desired output -- maybe it isn't what you meant, but it is what is there. There are three copies of the ORIGINAL first element, true, but the ORIGINAL content is there in its entirety as well so the series length has been increased by 2 (the prepended elements).
The other solution from @Jan replaced the 2:3 elements instead leaving the length the same but replacing the ORIGINAL 2nd and 3rd values entirely.
ORIG JAN'S
1.21 1.21
1.01 1.21
0.95 1.21
0.85 0.85
... ...
0.01 0.01
I dunno which is the result actually wanted, but the first is what you described/showed, the latter is Jan's solution; only you know for sure which (if either) is what was/is intended.
Thank for clarifying in details; that's helpful. Jan's solution is very close enough, which is nice Perhaps I didn't clearly explain, so I'll give an example of the desired outcome.
INDEX ORIG Desired
1 1.21 1.21
2 1.01 1.21
3 0.95 1.21
4 0.85 1.01
5 0.80 0.95
6 0.77 0.85
7 0.70 0.80
... ... ...
28 0.56
29 0.52
30 0.01 O.56
Do you think its possible to get this? I hope its not too complicated.
OK, "exercise for student!" :)
It's my solution with a minor modification to the second part of the RHS vector below.
big_struct.(fieldname1)=[repmat(big_struct.(fieldname1)(1),2,1);big_struct.(fieldname1)];
HINT: How would you write the addressing expression get all except the last two elements of the original vector?
Thanks a lot, that worked. :) Here's how I tried to answer your question in the hint:
field = big_struct.(fieldname1);
field1 = field(1:end-2,:)
Is this correct?
dpb
dpb 2022년 8월 16일
Yes, although you can do it without needing the temporary variable by adding the subscripting expression into the first. Since the array is a 1D vector, you can also do away with the second subscript.
"Student" receives "A-" :)
Haha thanks for the grade, and for helping me understand solve this problem.

댓글을 달려면 로그인하십시오.

답변 (0개)

카테고리

도움말 센터File Exchange에서 Loops and Conditional Statements에 대해 자세히 알아보기

질문:

2022년 8월 12일

댓글:

2022년 8월 16일

Community Treasure Hunt

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

Start Hunting!

Translated by