# Create a boxchart with half-boxes

12 views (last 30 days)
Joey on 25 Feb 2024
Commented: Voss on 26 Feb 2024
Hi,
i am trying to generate a boxchart with halfboxes to compare 2 datasets. Unfortunately there is no attribute to create a half-box in the boxchart function. so my idea was to plot 2 times the same dataset and shift them that they overlap and then make one boxplot completly white. You can see the results in the pictures. Now i want to fuse these two plots but i am facing the problem that when i generate a plot that consists both figures the white boxes are in foreground. does anyone have an idea how to do this? i am also open to other solutions.
Joel

Voss on 25 Feb 2024
Edited: Voss on 25 Feb 2024
As you may have realized already, there's no ordering of overlapping red, blue, and two white boxcharts that will give you the look you want. You'd need white #1 on top of red, blue on top of white #1, white #2 on top of blue, but red on top of white #2, which is a contradiction.
One thing you can do is to reproduce the built-in boxchart appearance using primitive lines and patches. Of course this requires calculating the statistics like boxchart does, but that's not too hard because boxchart is well-documented.
Something like this:
data1 = randn(100,5);
data2 = randn(100,5);
figure
grid on
box_width = 0.5;
% calculate the median and the box and whisker upper and lower limits for data1
box_med1 = median(data1,1);
q = quantile(data1,[0.25, 0.5, 0.75],1);
box_min1 = q(1,:);
box_max1 = q(3,:);
IQR = box_max1-box_min1;
is_outlier1 = data1 > q(3,:)+1.5*IQR | data1 < q(1,:)-1.5*IQR;
data1_temp = data1;
data1_temp(is_outlier1) = NaN;
whisker_min1 = min(data1_temp,[],1);
whisker_max1 = max(data1_temp,[],1);
% calculate the median and the box and whisker upper and lower limits for data2
box_med2 = median(data2,1);
q = quantile(data2,[0.25, 0.5, 0.75],1);
box_min2 = q(1,:);
box_max2 = q(3,:);
IQR = box_max2-box_min2;
is_outlier2 = data2 > q(3,:)+1.5*IQR | data2 < q(1,:)-1.5*IQR;
data1_temp = data2;
data1_temp(is_outlier2) = NaN;
whisker_min2 = min(data1_temp,[],1);
whisker_max2 = max(data1_temp,[],1);
% create the whiskers and box outlines for data1
N1 = size(data1,2);
xd = (1:N1)-box_width*[0.5;0;0;1;1;0;0;0.5;NaN]/2;
yd = [whisker_min1;whisker_min1;box_min1;box_min1;box_max1;box_max1;whisker_max1;whisker_max1;NaN(1,N1)];
line(xd(:),yd(:),'Color','r')
% create the whiskers and box outlines for data2
N2 = size(data2,2);
xd = (1:N2)+box_width*[0.5;0;0;1;1;0;0;0.5;NaN]/2;
yd = [whisker_min2;whisker_min2;box_min2;box_min2;box_max2;box_max2;whisker_max2;whisker_max2;NaN(1,N2)];
line(xd(:),yd(:),'Color','b')
% create the box interior for data1
xd = (1:N1)-box_width*[0;0;1;1;0]/2;
yd = [box_min1;box_max1;box_max1;box_min1;box_min1];
patch('XData',xd,'YData',yd,'FaceColor','r','FaceAlpha',0.2,'EdgeColor','none')
% create the box interior for data2
xd = (1:N2)+box_width*[0;0;1;1;0]/2;
yd = [box_min2;box_max2;box_max2;box_min2;box_min2];
patch('XData',xd,'YData',yd,'FaceColor','b','FaceAlpha',0.2,'EdgeColor','none')
% create the median line for data1
xd = (1:N1)-box_width*[0;1;NaN]/2;
yd = [box_med1;box_med1;NaN(1,N1)];
line(xd(:),yd(:),'Color','r')
% create the median line for data2
xd = (1:N2)+box_width*[0;1;NaN]/2;
yd = [box_med2;box_med2;NaN(1,N2)];
line(xd(:),yd(:),'Color','b')
% create the outliers line for data1
xd = 1:N1;
[~,idx] = find(is_outlier1);
xd = xd(idx)-box_width*0.25;
yd = data1(is_outlier1);
line(xd(:),yd(:),'Color','none','Marker','o','MarkerEdgeColor','r')
% create the outliers line for data2
xd = 1:N2;
[~,idx] = find(is_outlier2);
xd = xd(idx)+box_width*0.25;
yd = data2(is_outlier2);
line(xd(:),yd(:),'Color','none','Marker','o','MarkerEdgeColor','b')
For comparison, built-in overlapping boxcharts:
figure
hold on
grid on
b1 = boxchart(data1,'BoxFaceColor','r','BoxEdgeColor','r','WhiskerLineColor','r','MarkerColor','r');
b2 = boxchart(data2,'BoxFaceColor','b','BoxEdgeColor','b','WhiskerLineColor','b','MarkerColor','b');
Joey on 26 Feb 2024
Voss on 26 Feb 2024
You're welcome!

Austin M. Weber on 25 Feb 2024
Edited: Austin M. Weber on 25 Feb 2024
Since there is not already a built-in function for making "half" box plots, it might be beneficial to to write your own function (in case you want to make more of these plots in the future).
I have made a relatively simple function for you to use/modify to your needs (see attached). Just note that the approach I took was not to make regular boxchart objects, but rather I have written the function to plot all of the features of the boxplots using the rectangle function. Below is an example of how it works:
% Create two data arrays with the same number of columns
rng(1) % <-- for reproducibility
data1 = normalize(randn(30,10),'Range',[0.05 0.95]);
rng(2) % <-- change RNG seed so that the second data array is different from the first
data2 = normalize(randn(30,10),'Range',[0.05 0.95]);
% Plot the data
h = halfboxplot(data1,data2);
I also added documentation to the function, which you can view by typing the following into your command window:
doc halfboxplot
Feel free to modify the function to fit your needs. I might try to improve this function later on and add it to the File Exchange. If I do I will let you know.
Joey on 26 Feb 2024
Thank you, this is also a very nice approach!