MATLAB Answers

Identifying and keeping duplicates but with a suffix

1 view (last 30 days)
Jason
Jason on 27 Aug 2020
Edited: dpb on 28 Aug 2020
Hello, I have a column vector that I use for x values for a Bar Chart from data extracted from a UITable.
Y=cell2mat(data(:,4))
X=data(:,2);
Xdata = categorical(X);
X = reordercats(Xdata,X);
b1=bar(X,Y,'Parent',app.UIAxes2);
My x data (Column vector) is one of 3 numbers 405, 532 or 660.
The data can be any of these and with any number of repeats e.g
405
405
405
405
405
or
405
532
532
660
405
The problem is the bar chart cannot handle repeat values. I'd still like to plot and thought one way would be to identify repeats and place an _ with a number, but am not sure how to
e.g
405
405
405
405
405
becomes
405_1
405_2
405_3
405_4
405_5
and
405
532
532
660
405
becomes
405_1
532_1
532_2
660
405_2
Thanks for any help

  2 Comments

dpb
dpb on 27 Aug 2020
That sounds like a really bad idea...but I don't understand what you would want your plot to look like???
What would your ideal chart be if didn't have the limitation? Are multiple X of the same value really different Y or should you just not use sum(Y) conditional on X or similar?
With the data similar to what you've shown, it would seem there would be a very high number of X values that would make bar plot very crowded?
Knowing more of what data represent and what is purpose of plot could lead to better answers...
ADDENDUM:
I think what you want is to create a grouped plot on the unique(X) with the Y associated with each.
To do this, pass unique(X) as the x vector and create a yBar data array where each each unique x value is matched by a row that contains the y values associated with it. This would then be a 3xM array; M would be the maximum number of observations of a given x; the elements in the shorter rows of the array would be NaN which will be silently ignored by bar()
It would be more convenient if one could pass a cell array instead of only an ordinary array, but that would appear to be the way to solve your wants.
Jason
Jason on 27 Aug 2020
The data represents Integrated fluorescence spectra of samples with some repeats. The number of repeats could be different for each value (e.g. below its 3 for the 405 and 2 for the 532 x-values)
The ideal chart would just be with the wavelength numbers being as they are without any suffixing going on.
The bar chart isn't crowded at all (note to make it work I had to suffix the category values with a,b, etc.)

Sign in to comment.

Accepted Answer

dpb
dpb on 27 Aug 2020
OK, that helps...actually, that's not too hard and even easier than the grouped solution -- I don't quite follow what your data storage must be with the mixing of cell2mat for Y and not for X so I'll write as if both are simple double arrays -- you can adapt to whatever data actually is that we can't see--I would tend to avoid creating copies of the same data, but since this is apparently small samples, it won't be that expensive to do so here.
[X,ix]=sort(X); % order by wave number
Y=Y(ix); % arrange counts to correspond
hBar=bar(Y,'Parent',app.UIAxes2); % plot against the ordinal position to avoid the duplicates
xticklabels(categorical(X)) % use the categorical names as tick labels
Can fixup the appearance as wanted from here...
The alternative with the grouping solution would be
>> [x y] % some made up sample data
ans =
405 529
532 166
532 602
660 263
405 655
>>
% the engine
N=groupcounts(x); % count elements in each wave number
Y=nan(numel(N),max(N)); % build the grouping array for bar()
[X,~,ib]=unique(x); % get the unique wave numbers/groups and group identity
yy=accumarray(ib,y,[],@(v) {v}); % separate by group to cell array
for i=1:3, Y(i,1:N(i))=yy{i}; end % and put by row into the 2D double array
figure
hBar=bar(X,Y);
xticklabels(categorical(X))
set(hBar,'FaceColor','y')
hAx=gca;
hAx.Color='k';
hAx.GridColor='w';
hAx.GridAlpha=0.5;
title('Gropued by Wave Number')
grid on
The above produced

  3 Comments

Jason
Jason on 27 Aug 2020
This is great thankyou. Dont suppose the bars can be coloured by wave number?
dpb
dpb on 27 Aug 2020
Of course they could... :) I thought the yellow on black the effect you were going for! :
Oh. Well, when you group bars, the bars for each member of the groups are one bar object handle so here you could color the left and right bars separately, but don't think you can make the two for each group the same...
I've railed at TMW about how difficult it is to work with bar() for years -- they've made some things a little easier, but others are still difficult or take some kludgy workarounds.
The klunky way here would be the expedient of the NaN elements for one of the bars or each group one-at-a-time and as many bar plots as the maximum number of repeated elements. That's not too bad with only two or three but would get pretty messy with more like your first example.
I'll have to mull that one over some and see if anything simpler comes to mind.
dpb
dpb on 27 Aug 2020
ADDENDUM:
NB: In the above code for the grouped barplot, the for...end loop indices are hardcoded as 1:3. That works for the sample dataset; in general that should be
for i=1:numel(N), Y(i,1:N(i))=yy{i}; end % and put by row into the 2D double array
to iterate over however many wave numbers are actually in the dataset.

Sign in to comment.

More Answers (1)

dpb
dpb on 28 Aug 2020
Edited: dpb on 28 Aug 2020
OK, this isn't complete, but is probably the simplest way to build something to do what you'd like -- it would need to be refined and made into a function, but the general idea is pretty simple...using the same [x y] data had before in sorted order...
colors=reshape([hBar.FaceColor],3,[]).'; % save the default color map from previous for convenience
figure
hBar(1)=bar(1,Y(1)); % the first one at ordinal position 1
hold on % prepare to add on top existing bar
c=1; % initial color index value
j=1; % x position location for each bar
for i=2:size(X,1) % add rest to same plot
if X(i)~=X(i-1), j=j+0.5; end % if new wave number, put separation to simulate groups
c=c+1; % increment color for next group
j=j+1; % increment the ordinal number
hBar(i)=bar(j,Y(i),'FaceColor',colors(c,:)); % plot at adjusted ordinal position in group color
end
xticks([hBar.XEndPoints]) % put tick marks at middle each bar
xticklabels(categorical(X)) % label with the wave numbers
title('Simulated Grouped by Wave Number')
grid on
Now, one has a separate bar() object handle for each bar so can set color as desired...this just cycles through the (somewhat ugly) default sequence; you can define whatever color array you wish.
The biggest refinement I see missing here is not computing the midpoint of the groups for locating the tick marks so only have one label for the wave number group--I ran out of time altho don't think it's hard, one just needs a running sum of the XendPoints values for each group and compute the mean for that set when the group/wave number changes and save that value in array to use for xticks argument. Then you will use the unique categorical variable as labels instead of the full X array.
The above initial step gets to this point --
Well, OK, I lied... :) Couldn't stand it!!!
The modifications are --
N=groupcounts(x); % bring this back...
tk=zeros(size(N)); % accumlator for the tick position calculation
hBar(1)=bar(1,Y(1));
tk(1)=hBar(1).XEndPoints; % initial position (if choose to use something other than 1)
c=1;j=1;
for i=2:size(X,1)
if X(i)~=X(i-1)
j=j+0.5;
tk(c)=tk(c)/N(c); % calculate the group tick average for midpoint for last group
c=c+1;
end
j=j+1;h
Bar(i)=bar(j,Y(i),'FaceColor',colors(c,:));
tk(c)=tk(c)+hBar(i).XEndPoints; % accumulate running sum for working group
end
xticks(tk)
xticklabels(unique(X))

  0 Comments

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!

Translated by