boxchart - different box width according to number of data points

Hi,
I am looking for a way to set the width of a boxplot according to the number of datapoints within a boxchart.
See attachet an example how it looks like in R. When I try this in matlab, I get an error, because matlab accepts only scalars and no vectors. I would prefer to do all my statistics with matlab, so this function would be very helpful.
Thank you
Markus

1 Comment

Submit it as an enhancement request.
You could try to make it work with boxchart by use of hold on and plotting each bar invidvidually. This would take some doing to manage to get the positions correct and whether could be managed or not I don't know for sure, but other than drawing one from basic primitives, it's all that comes to mind at present.

Sign in to comment.

 Accepted Answer

I'll add a shorter, cleaner version as well...go ahead and accept the answer since it is the basis for the result desired if you would...
D=readtable("Data.csv");
D.Lagen=categorical(D.Lagen);
D.D_Lage=categorical(D.D_Lage);
[G,ixG]=findgroups(D.D_Lage); % find the grouping in the D_Lage variable
rows=histc(G,unique(G)); % count how many of each
bw=rows/sum(rows); % compute the relative size fractions
figure
hold on
for i=1:length(rows)
ix=(D.D_Lage==ixG(i)); % logical addressing each by the grouping id vector
bc(i)=boxchart(D.D_Lage(ix),D.ft0(ix),'BoxWidth',bw(i));
if i>1, bc(i).BoxFaceColor=bc(1).BoxFaceColor; end
end
is much shorter code-wise...while if you look it up you'll find that histc has been deprecated, for such counting as this it works flawlessly and I've had issues with how histcounts wants to define bin boundaries that it doesn't always put things into the desired bin. accumarray is the old-timers solution, but its syntax is somewhat more arcane for newcomers.

More Answers (1)

With sufficient perserverence it looks as though should be able to make boxchart work...the doc had an easy example to illustrate the idea...
tbl = readtable('TemperatureData.csv');
Error using readtable (line 517)
Unable to find or open 'TemperatureData.csv'. Check the path and filename or file permissions.
tbl.Month = categorical(tbl.Month,monthOrder);
hBxCh=boxchart(tbl.Month(iJan),tbl.TemperatureF(iJan),'GroupByColor',tbl.Year(iJan)) % base boxplot; note are two objects, one for each month
iJan=tbl.Month=="January"; % so, pick only one month
figure
hBxCh=boxchart(tbl.Month(iJan),tbl.TemperatureF(iJan),'GroupByColor',tbl.Year(iJan)) % chart it
hBxCh(1).BoxWidth=0.25; % now can set its width
Although it's bound to be tedious, it looks as though one could manage to arrange the complete data such that one could plot it a variable at a time and thereby to get additional objects that could then have their individual bar widths set.
With the by grouping option, it certainly is a reasonable enhancement request to be able to control the barwidth by group as well.
ERRATUM:
Well, pooh! The supplied data file isn't available on this platform...it's
>> which -all temperaturedata.csv
C:\MLR2021b\examples\graphics\data\TemperatureData.csv
>>
on desktop install...first time I've tried to use a demo example and the data haven't been here...I attached the image of what it looks like here.

4 Comments

Hi dpb,
Thanks for your answer.
I tried it in this way:
The Data-file is attached.
clc
clearvars
Data=readtable("Data.csv");
D=Data;
D.Lagen=categorical(D.Lagen);
D.D_Lage=categorical(D.D_Lage);
D05=D(D.D_Lage=="5",:);
[r05 c05]=size(D05);
D10=D(D.D_Lage=="10",:);
[r10 c10]=size(D10);
D15=D(D.D_Lage=="15",:);
[r15 c15]=size(D15);
D20=D(D.D_Lage=="20",:);
[r20 c20]=size(D20);
rows=[r05 r10 r15 r20];
[rows_all c_all]=size(D);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%+
figure
bc=boxchart(D.D_Lage,D.ft0);
bw=zeros(1,length(rows));
hold on
for i=1:length(rows)
bw(i)=1/rows_all*rows(i);
bc(i).BoxWidth=bw(i);
end
hold off
But it changes the width of all boxes, when it comes to the 2nd loop, and after that I get the error:
"Unrecognized property 'BoxWidth' for class 'matlab.graphics.GraphicsPlaceholder'."
Regards Markus
That would be the expected result with calling boxchart that way; as documented, if you pass an array, it draws one object of bars by grouping. The example and my perturbation of the example used the grouping by color variable option so each color group was a boxchart object and the granularity was created by only passing the values associated with each group, one at a time in the loop. That was the key step your code above is missing.
D=readtable("Data.csv");
D.Lagen=categorical(D.Lagen);
D.D_Lage=categorical(D.D_Lage);
%head(D);
D05=D(D.D_Lage=="5",:);
[r05 c05]=size(D05);
D10=D(D.D_Lage=="10",:);
[r10 c10]=size(D10);
D15=D(D.D_Lage=="15",:);
[r15 c15]=size(D15);
D20=D(D.D_Lage=="20",:);
[r20 c20]=size(D20);
rows=[r05 r10 r15 r20];
[rows_all c_all]=size(D);
bw=rows/rows_all;
figure
hold on
for i=1:length(rows)
ix=(D.D_Lage==categorical(5*i)); % logical addressing each
bc(i)=boxchart(D.D_Lage(ix),D.ft0(ix),'BoxWidth',bw(i));
end
bc
bc =
1x4 BoxChart array: BoxChart BoxChart BoxChart BoxChart
figure
hold on
for i=1:length(rows)
ix=D.D_Lage==categorical(5*i);
bc(i)=boxchart(D.D_Lage(ix),D.ft0(ix),'BoxWidth',bw(i));
if i>1, bc(i).BoxFaceColor=bc(1).BoxFaceColor; end
end
Note the above made a logical indexing expression for each pass through the loop that is the value of each group in turn; then passed only those values for each call inside the loop. This then, created a new barchart object for each(*) and by using the grouping variable, put them on the x-axis in the right location. The case then also set a color for each bar based on the internal color order; the second sets all to the default color of the first; niceties such as that are obvious extensions.
You note the difference is there are now four objects to mung on, not just the one...but we had to manually create each to do so.
Note also that the generic coding way to deal with the indices would be to make your D05, D10, ... variables an array so could index inside the loop rather than where I calculated them. In this case that was easy, in general, that may be "not so much", so being able to index would be key.
(*) barchart sill created one chart object for each element of the array of handles; it just doeesn't know you're fooling it into only plotting one bar at a time of the whole thing that will eventually mimic what the single call would produce as one object. Fortunately, unlike boxplot, TMW did create it to be able to use hold on and get access to al least most of the properties of interest for such customization; one just has to be able to recognize you can go a little further in how it is called to do something not envisioned by the authors. Again, this would be a most excellent enhancement suggestion/request.
Thank you very much for the fast help.
This was exactely, what I needed!
Kind regards
Markus
Glad to help; kinda' an interesting challenge of what would appear to be a logical ability already...anyways, the only real wart I see with this is the whisker line is also proportional to the bar width so for the narrower ones it is almost invisible. Unfortunately, the WhiskerLines hidden property is currently still just a placeholder; there is no way to get access to the line handles in order to change their length; one would have to add code to add another line where should be of a given length to create the effect.

Sign in to comment.

Categories

Products

Release

R2024a

Asked:

on 23 Aug 2024

Edited:

dpb
on 29 Aug 2024

Community Treasure Hunt

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

Start Hunting!