How to name different groupes of variables in one vector with indices of another?

1 view (last 30 days)
Hello!
I have the following code:
Generator = [3111 3112 3111].' % Gen1 = 3111, Gen2 = 3112, Gen3 = 3111 Input which could be in a different order or more inputs
mod1 ={'\Delta \omega' '\Delta \delta'} % statevariables of one modell
mod2 = {'\Delta \psi' '\Delta v' '\Delta Tw'} % statevariables of second modell
master_mod = {mod1, mod2}
v = [master_mod{mod(Generator,10)}]
The ouput is a 1×7 cell array, which I'm using later to name bars in a bar plot. I'd like to add the index of the Generator to the coresponding variable, so I would get an array like this:. I tried to use num2string but I could only figure out super slow code with a for loop which didn't really work (got 21 instead of 7 cells...).
How can I add the little indices to my variables?
  2 Comments
dpb
dpb on 12 Aug 2019
That's kinda' cute for that far...I had to scratch my head some over how the mod() thingie was working! :)
Interesting problem...I don't have time at this instant but will try to look in again later this evening if somebody else hasn't already solved it...

Sign in to comment.

Accepted Answer

Adam Danz
Adam Danz on 12 Aug 2019
Edited: Adam Danz on 13 Aug 2019
Updated interpretation
Generator = [3111 3112 3111].';
mod1 ={'\Delta \omega' '\Delta \delta'};
mod2 = {'\Delta \psi' '\Delta v' '\Delta Tw'};
master_mod = {mod1, mod2};
v = master_mod(mod(Generator,10));
vSubscript = cellfun(@(x,d)cellfun(@(x0,d)sprintf('%s_{%d}',x0,d), x,repmat({d},size(x)),'UniformOutput',false),v,num2cell(1:numel(v)),'UniformOutput',false);
newLabels = [vSubscript{:}];
Result
newLabels =
1×7 cell array
Columns 1 through 2
{'\Delta \omega_{1}'} {'\Delta \delta_{1}'}
Columns 3 through 5
{'\Delta \psi_{2}'} {'\Delta v_{2}'} {'\Delta Tw_{2}'}
Columns 6 through 7
{'\Delta \omega_{3}'} {'\Delta \delta_{3}'}
Old interpretation
If you add 1 space to the end of each string, you can replace the empty spaces with subscripts like this.
Generator = [3111 3112 3111].'
mod1 ={'\Delta \omega ' '\Delta \delta '} % 1 space after each sub-string
mod2 = {'\Delta \psi ' '\Delta v ' '\Delta Tw '} % 1 space after each sub-string
% ^ ^ ^ ^ ^ ^ % spaces shown
master_mod = {mod1, mod2}
v = [master_mod{mod(Generator,10)}];
subscripts = 1:numel(v);
indices = strsplit(sprintf('_{%d} ',subscripts)); % underscore will become subscript
newLabels = strrep(v,' ',indices(1:end-1)); % add indices
set(gca, 'XTickLabel', newLabels)
  11 Comments
Adam Danz
Adam Danz on 13 Aug 2019
Edited: Adam Danz on 13 Aug 2019
Yes, it's possible. But first, two warnings.
  1. It's possible that there are some inefficiencies in what you're doing. Creating complex labels, then removing some, etc. Just a general reminder to stay organized and efficient even if it requires putting more time into reorganizing code.
  2. The solution below requires an exact match (except for upper/lower case).
idx = strcmpi(string_xlabel, '\Delta \delta_{3}');
string_xlabel(idx) = [];
Unrelated to that, I agree with Guillaume that a nested cellfun(cellfun()) is ugly and compose() might be cleaner.
Guillaume
Guillaume on 13 Aug 2019
Edited: Guillaume on 13 Aug 2019
for i=1:length(string_xlabel)
if string_xlabel(i) == {'\Delta\delta_{3}'}
string_xlabel = string_xlabel - string_xlabel(i)
end
end
is certainly never going to work. You don't use == to compare char vectors (or cell arrays) and deleting elements has never been done with the subtraction operator. Plus deleting elements while you're iterating over the element indices is a sure way to get your index out of sync with the elements.
The easiest:
string_xlabel(strcmp(string_xlabel, '\Delta\delta_{3}')) = [];
Note that if it's several elements that need removing:
string_xlabel(ismember(string_xlabel, {'\Delta\delta_{3}', '\Delta\omega_{5}'})) = []; %remove all occurences of dd3 or do5

Sign in to comment.

More Answers (1)

Guillaume
Guillaume on 13 Aug 2019
Edited: Guillaume on 13 Aug 2019
It's annoying that compose doesn't accept positions identifiers in the format string, otherwise this could have been done in just one line.
I'd just use a loop, arrayfun or cellfun, wihichever you prefer. First, the format string, I see no point in having a cell array of cell arrays of char vectors. Just have one format for each possibility (which it looks like is just 2 at the moment):
formats = {'\\Delta\\omega_{%1$d}\\Delta\\delta_{%1$d}', ... need the \\ to avoid it being interpreted by sprintf.
'\\Delta\\psi_{%1$d}\\Deltav_{%1$d}\\DeltaTw_{%1$d}'};
With a loop:
labels = cell(size(Generator));
for labelidx = 1:numel(Generator)
labels{labelidx} = sprintf(formats{mod(Generator(labelidx), 10)}, labelidx);
end
With arrayfun:
labels = arrayfun(@(f, n) sprintf(formats{f}, n), mod(Generator, 10), (1:numel(Generator))', 'UniformOutput', false)
With cellfun:
labels = cellfun(@sprintf, formats(mod(Generator, 10)), num2cell(1:numel(Generator)), 'UniformOutput', false)
edited format to have the digits subscripted
  2 Comments
Salome Hohl
Salome Hohl on 13 Aug 2019
I agree, I can put all variables belonging to a certain number of a Generator together in formats. But actually afterwards I'm using the variables to do stability analysis with eigenvalues.
Each diagramm presents the participation of the (state) variables of one instable eigenvalue. So it could be, i.e. that I'm using as names of the first, second and third bar in one diagramm. So I still need my vector v with all the added variables after each other to then 'take' them to reference the x-axis.
As an example with
Generator = [3111 3111 3112]
I would get v = [] but need
v = [ ] so that I could index the bars in the plots (jpg): the one with two bars would get and the second one . The reference between variable names and bars is working perfectly. Just the generator index is missing.
Guillaume
Guillaume on 13 Aug 2019
Edited: Guillaume on 13 Aug 2019
In that case:
formats = {{'\\Delta\\omega_{%d}', '\\Delta\\delta_{%d}'}, ... need the \\ to avoid it being interpreted by sprintf.
{'\\Delta\\psi_{%d}', '\\Deltav_{%d}', '\\DeltaTw_{%d}'}};
We can now use compose since there's no need for positional identifiers
With a loop:
labels = cell(size(Generator));
for labelidx = 1:numel(Generator)
labels{labelidx} = compose(formats{mod(Generator(labelidx), 10)}, labelidx);
end
With arrayfun:
labels = arrayfun(@(f, n) compose(formats{f}, n), mod(Generator, 10), (1:numel(Generator))', 'UniformOutput', false)
With cellfun:
labels = cellfun(@compose, formats(mod(Generator, 10)), num2cell(1:numel(Generator)), 'UniformOutput', false)
Basically, the same as before but with compose instead of sprintf since compose is happy to work with multiple format strings at once.

Sign in to comment.

Categories

Find more on Loops and Conditional Statements 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!