Plotting segments of a stacked bar chart to avoid duplicate legend entries

7 Ansichten (letzte 30 Tage)
Tim
Tim am 23 Apr. 2025
Kommentiert: Tim am 1 Sep. 2025 um 1:34
I am plotting a series of drill holes as a stacked bar chart. Segments with the same stratigraphic name need to be the same colour. It visaully works hole by hole but the legend duplicates according to the number of "objects" on the graph,according to this old question https://au.mathworks.com/matlabcentral/answers/816090-eliminate-duplicates-in-a-legend
So I am tryng to plot by straigraphy instead, but I dont think I can plot a stacked bar chart becasue
  • by drillhole, I could build a vector for hole and add another stacked bar. The vector only had to be the difference between positions downhole and the function know what to do with it (ie a hole with 2 stratigraphies from 0m-10m-55m stacks when y = [0 10 45])
  • but by stratigraphy, the coloured segments (y axis) are over various drill holes and therefore cannot be plot as an individual stacked bar each time.
Problems are
  • how to pass starting and ending y values for each segment
  • but x values must be unique so cannot have 2 y values for each x
Drillholes share stratigraphy but the current code builds drillhole by drillhole. When it comes to the legend, each statigraphy/segment is an independent object instead of all same coloured segments being the same object. Hence the repeating legend. Names and colours are assigned in a loop.
Since drillholes share stratigraphy following the advice in the above link would be to change the loop to by stratigraphy instead of by drillhole. This would mean graphing only the yellow or red segments at a time but...
  • it appears stacked bar charts cannot create the top segment of a stacked bar before they create the segments below it.
  • the y numbers fed to the bar function must be lengths of segments, not the positions of segments on the y axis
figure;
hold on;
dhs = unique(StratbyDH.DRILLHOLE_NO);
for i = 1:size(dhs, 1)
idx = find(StratbyDH.DRILLHOLE_NO == dhs(i));
depths = [StratbyDH.STRAT_DEPTH_FROM(idx) StratbyDH.STRAT_DEPTH_TO(idx)];
strats = string(StratbyDH.STRAT_NAME(idx));
depths = sort(depths, 1, "ascend")
depths = [depths(1,1) depths(:,2)'] * -1
y = diff(depths);
bh = bar(i, y, 'stacked', 'LineWidth', 0.25);
for l = 1:max(size(bh))
bh(l).FaceColor = 'flat';
% currCols stores the colours for stratigraphic names
idx = find( currCols.Strats == strats(l) );
if isempty(idx)
rgbc = [1 1 1];
namec = "";
else
rgbc = table2array(currCols(idx,3:5));
namec = strats(l);
end
bh(l).CData = rgbc;
bh(l).DisplayName = namec;
end
end
  2 Kommentare
Mathieu NOE
Mathieu NOE am 23 Apr. 2025
it's not obvious to see what the end result shoud look like , I just wondered if a patch based code would help you - see example below
% Sample data
y = 1;
yw = 0.25;
data = [2, 4, 6, 7];
% main code
[M,N] = size(data);
data_min = min(data,[],'all');
data_max = max(data,[],'all');
map = colormap('jet');
[mmap,nmap] = size(map);
% Create the stacked plot using patch
datacum = [0 cumsum(data)];
figure;
for k = 2:numel(datacum)
% Plot the patches
xx = [datacum(k-1) datacum(k) ];
ind = fix(1+(mmap-1)*(data(k-1)-data_min)/(data_max-data_min));
patch([xx, fliplr(xx)],[y+yw y+yw, y-yw y-yw], map(ind,:), 'FaceAlpha', 0.5);
% Display the values as labels at the center of the bars.
text(mean(xx),y,num2str(data(k-1)),'VerticalAlignment','middle','HorizontalAlignment','center')
end
ylim([floor(y-yw) ceil(y+yw)]);
Tim
Tim am 28 Apr. 2025
Matplotlib's Patches works so maybe it will work here. I will try later. Thanks Matheiu.

Melden Sie sich an, um zu kommentieren.

Antworten (1)

Benjamin Kraus
Benjamin Kraus am 1 Sep. 2025 um 0:32
@Tim: You started by saying "It visaully works hole by hole but the legend duplicates according to the number of "objects" on the graph ... so I am tryng to plot by straigraphy instead."
I suspect rather than trying to convolute your data to fit into fewer objects, your better bet is to explicitly exclude certain entries from the legend.
The other answer you referenced (https://www.mathworks.com/matlabcentral/answers/816090) was the correct solution for that particular issue, but not the correct solution for your particular situation.
There are two ways to eliminate "redundant" entries in the legend:
  • The first is to combine multiple objects into a single object. In that other MATLAB Answers question they were plotting multiple markers using multiple line objects (one marker per line object). This is very inefficient and in addition to creating multiple legend entries, it also significantly slows down MATLAB to create a ton of Line objects with one dot per object. In this case it was trivial to merge the markers into a single Line object, which not only fixes the legend issue, but also makes things faster.
  • The second method is to explicitly exclude entries from the legend. This is the solution you want to use, and is the solution you can use when combining multiple objects into a single object either doesn't make sense or doesn't work.
There are three different ways to exclude entries from the legend.
Option 1: Tell the legend specifically which objects to include.
When you call the legend command, an optional input allows you to specify specifically which objects to include in the legend.
For example:
b = bar(magic(5)); % This creates five bar objects.
% When you call legend, specify only four bar objects, and the fifth will
% be excluded.
legend(b([1 2 4 5]), ["Bar 1", "Bar 2", "Bar 4", "Bar 5"]);
I like this approach because it allows you to specify names for your graphics objects in a way that is less error prone (you don't accidentally assign the wrong name to the wrong object). Because the first input argument specifies the objects, and the second input argument specifies the names, it is easier to make sure that those two arguments match one-to-one. However, an even better way to make sure you get the correct names is to use the DisplayName property, which I see you using above.
Option 2: Tell each object that you don't want in the legend to hide themselves from the legend.
Each graphics object that could show up in the legend has a property called Annotation that can be used to control whether the object show-up in the legend. For example:
b = bar(magic(5)); % This creates five bar objects.
for i = 1:5
b(i).DisplayName = "Bar " + i;
if i == 3
% Tell the third bar object that it should not be included in the legend.
b(i).Annotation.LegendInformation.IconDisplayStyle = "off";
end
end
% Turn on the legend and see only four bar objects.
legend
This option works nicely if you are already looping through your objects one-by-one, so adding an additional line of code to set the Annotation.LegendInformation.IconDisplayStyle isn't a big change in your code. It also works nicely if you are setting the DisplayName for each object one-by-one in a loop, as I did above.
Option 3: Disable AutoUpdate on the legend.
I'm including this third option for completeness, but I don't think it will work well in your particular case. In particular, it won't work with bar because the only way to create grouped or stacked bars is by using a single call to the bar command.
You can:
  • Plot the objects you want in the legend first.
  • Turn on the legend without AutoUpdate.
  • Plot the remaining objects after.
For example:
plot(magic(4)); % Create four lines you want in the legend.
legend(AutoUpdate=false); % Create a legend without AutoUpdate.
hold on
plot(1:4); % Plot the last line that you don't want in the legend.

Kategorien

Mehr zu Graphics Object Properties finden Sie in Help Center und File Exchange

Produkte


Version

R2022a

Community Treasure Hunt

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

Start Hunting!

Translated by