Boxplot and histogram in one plot

84 ビュー (過去 30 日間)
Jakob Seifert
Jakob Seifert 2021 年 5 月 6 日
コメント済み: RJ 2021 年 7 月 31 日
Hey,
I think this question has been asked before:
But I could not find any proper solution;
so is there a way to achieve something like this:
I'm looking forward to hear your suggestions :)
  1 件のコメント
Sambit Supriya Dash
Sambit Supriya Dash 2021 年 5 月 6 日
Yes it is possible to get boxplot and histogram in one plot,
Example,
x = [1 2 3;4 5 6;7 8 9];
y = [5 4 3; 5 7 9; 1 9 3];
figure(1)
hist(x)
hold on
boxplot(y)
hold off
Hope, it helps.

サインインしてコメントする。

採用された回答

Adam Danz
Adam Danz 2021 年 5 月 7 日
編集済み: Adam Danz 2021 年 5 月 7 日
This method uses patch objects to display histograms next to each boxplot. It's based on another answer that displays probability density distributions next to vertical scatter plots.
You only need to replace the x and y data and can copy-paste the rest of the code.
Inputs.
x : 1xn vector defining the x coordinate of n boxplots.
y : mxn matrix of raw data for n boxplots
rng('default') % for reproducibility
x = 0:5:30;
y = (randn(300, numel(x)) + linspace(.5,5,numel(x))) .* linspace(.5,2,numel(x));
Set spacing
binWidth = 0.4; % histogram bin widths
hgapGrp = .05; % horizontal gap between pairs of boxplot/histograms (normalized)
hgap = 0.2; % horizontal gap between boxplot and hist (normalized)
Compute histogram counts & edges
hcounts is an nx2 cell array containing the {counts, edges} for each distribution.
maxCount is the maximum bin count across all distributionts, used to normalize patch heights
hcounts = cell(size(y,2),2);
for i = 1:size(y,2)
[hcounts{i,1}, hcounts{i,2}] = histcounts(y(:,i),'BinWidth',binWidth);
end
maxCount = max([hcounts{:,1}]);
Plot boxplots
fig = figure();
ax = axes(fig);
hold(ax,'on')
xInterval = mean(diff(sort(x))); % x-interval (best if x is at a fixed interval)
normwidth = (1-hgapGrp-hgap)/2;
boxplotWidth = xInterval*normwidth;
boxplot(ax,y,'Positions',x,'Widths',boxplotWidth,'OutlierSize',3,'Labels',compose('%d',x))
Add vertical histograms (patches)
histX0 = x + boxplotWidth/2 + hgap; % histogram base
maxHeight = xInterval*normwidth; % max histogram height
patchHandles = gobjects(1,size(y,2));
for i = 1:size(y,2)
% Normalize heights
height = hcounts{i,1}/maxCount*maxHeight;
% Compute x and y coordinates
xm = [zeros(1,numel(height)); repelem(height,2,1); zeros(2,numel(height))] + histX0(i);
yidx = [0 0 1 1 0]' + (1:numel(height));
ym = hcounts{i,2}(yidx);
% Plot patches
patchHandles(i) = patch(xm(:),ym(:),[0 .75 1],'FaceAlpha',.4);
end
xlim([-2.5, 32.5])
Method 2 with color control
With just a few changes to the code above, you can use boxplotGroup() from the file exchange to set up colors of boxplots and histogram similar to the example in your question.
%% Inputs
rng('default') % for reproducibility
x = 0:5:30;
y = (randn(300, numel(x)) + linspace(.5,5,numel(x))) .* linspace(.5,2,numel(x));
%% Set Spacing
binWidth = 0.4; % histogram bin widths
hgapGrp = .15; % horizontal gap between pairs of boxplot/histograms (normalized)
hgap = 0.06; % horizontal gap between boxplot and hist (normalized)
%% Compute histogram counts & edges
hcounts = cell(size(y,2),2);
for i = 1:size(y,2)
[hcounts{i,1}, hcounts{i,2}] = histcounts(y(:,i),'BinWidth',binWidth);
end
maxCount = max([hcounts{:,1}]);
%% Plot boxplotsGroup()
fig = figure();
ax = axes(fig);
hold(ax,'on')
% Convert y (mxn matrix) to 1xn cell array of mx1 vectors, required by boxplotWidths
yc = mat2cell(y,size(y,1),ones(1,size(y,2)));
xInterval = 1; %x-interval is always 1 with boxplot groups
normwidth = (1-hgapGrp-hgap)/2;
boxplotWidth = xInterval*normwidth;
% Define colors for each boxplot
colors = lines(size(y,2));
% Plot colored boxplots
bph = boxplotGroup(ax,yc,'Widths',boxplotWidth,'OutlierSize',3,'PrimaryLabels',compose('%d',x),'Colors',colors);
set(findobj(bph.boxplotGroup,'-property','LineWidth'), 'LineWidth', 1) % increase line widths
%% Add vertical histograms (patches) with matching colors
xCoordinate = 1:size(y,2); %x-positions is always 1:n with boxplot groups
histX0 = xCoordinate + boxplotWidth/2 + hgap; % histogram base
maxHeight = xInterval*normwidth; % max histogram height
patchHandles = gobjects(1,size(y,2));
for i = 1:size(y,2)
% Normalize heights
height = hcounts{i,1}/maxCount*maxHeight;
% Compute x and y coordinates
xm = [zeros(1,numel(height)); repelem(height,2,1); zeros(2,numel(height))] + histX0(i);
yidx = [0 0 1 1 0]' + (1:numel(height));
ym = hcounts{i,2}(yidx);
% Plot patches
patchHandles(i) = patch(xm(:),ym(:),colors(i,:),'EdgeColor',colors(i,:),'LineWidth',1,'FaceAlpha',.45);
end
  5 件のコメント
Adam Danz
Adam Danz 2021 年 7 月 31 日
An easy workaround when using vectors of unequal length is to pad the shorter vectors with NaN values so all vecs are the same length and then you can combine them into a matrix. padarray may be helpful.
For labels, use text() function or labelpoints().
RJ
RJ 2021 年 7 月 31 日
Awesome thanks, I got it working with padcat.

サインインしてコメントする。

その他の回答 (1 件)

Scott MacKenzie
Scott MacKenzie 2021 年 5 月 6 日
編集済み: Scott MacKenzie 2021 年 5 月 7 日
This solution uses tiledlayout and boxchart, but you can adjust to use subplot and boxplot if you are running an older version of MATLAB:
n = 7;
y = randn(100,n);
tiledlayout(1, 2*n);
for i=1:n
nexttile;
boxchart(y(:,i));
set(gca,'visible', 'off', 'ylim', [-4 4]);
nexttile;
histogram(y(:,i), 20, 'orientation', 'horizontal');
set(gca,'visible', 'off', 'ylim', [-4 4]);
end
f = gcf;
f.Color = 'w';
f.Units = 'normalized';
f.Position = [.1 .2 .8 .4];
  6 件のコメント
Adam Danz
Adam Danz 2021 年 5 月 7 日
編集済み: Adam Danz 2021 年 5 月 7 日
One more step: it's important to set ylim so that the extent of the histograms align with the extent of the boxplots/outliers. For example, in the 5th pair of axes the boxplot does not show any outliers at the top but the histogram extends beyond the upper cap. This is because of a difference in y-limits between the neighboring axes.
Also, the ylim needs to match for all axes so that vertical differenced between columns of data are preserved.
For example, compare these two figures using the exact same data.
n = 7;
y = randn(100,n) .* [1:3:19];
figure()
boxchart(y)
figure()
tiledlayout(1, 2*n);
for i=1:n
nexttile;
boxchart(y(:,i));
set(gca,'visible', 'off');
nexttile;
histogram(y(:,i), 20, 'orientation', 'horizontal');
set(gca,'visible', 'off');
end
f = gcf;
f.Color = 'w';
f.Units = 'normalized';
f.Position = [.1 .2 .8 .4];
Scott MacKenzie
Scott MacKenzie 2021 年 5 月 7 日
Hey, good point. Thanks. I just adjusted my solution to prevent this -- by setting the y-axis limits.

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeFormatting and Annotation についてさらに検索

製品


リリース

R2021a

Community Treasure Hunt

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

Start Hunting!

Translated by