How to create subplots with little vertical spacing?

Hello
I want to do a figure in Matlab consisting of a grid of images (subplots). I have used the subaxis toolbox :
subaxis(4,6,1, 'Spacing', 0.03, 'Padding', 0, 'Margin', 0);
The problem is that the vertical space between the subplots (images) is too big.
How can this be solved? The horizontal spacing is ok but I want the vertical spacing the same as the horizontal spacing. With the above toolbox this seems not working.
I'm also interested in other solutions than the mentioned toolbox.

 Accepted Answer

The subaxis command allows you to specify different values for vertical and horizontal spacing. Try playing around with different values to get what you want. Remember that the spacing/padding/margins are defined in terms of normalized coordinates, so if your figure isn't square, the vertical and horizontal distances won't be quite the same.
For example, the following will give you very little vertical spacing:
iax = 1; % Or whichever
subaxis(4, 6, iax, 'sh', 0.03, 'sv', 0.01, 'padding', 0, 'margin', 0);

7 Comments

Sepp
Sepp on 14 Aug 2015
Edited: Sepp on 14 Aug 2015
It did not decrease the vertical spacing. I think the problem is that my images are not square. The dimensions are 85 (width) x 35 (height).
Are you by any chance plotting to these axes using imshow, or calling axis equal or some other equivalent? In that case, you need to understand the difference between the axis position and the plot box position. The plot box is modified to fit your image, but the axis position stays the same:
figure('color', 'w');
props = {'spacing', 0.03, 'margin', 0};
ax(1) = subaxis(4,6,1, props{:});
ax(2) = subaxis(4,6,2, props{:});
ax(3) = subaxis(4,6,7, props{:});
for ii = 1:3
imagesc(rand(35,85), 'parent', ax(ii));
end
arrayfun(@(ax) axis(ax, 'equal'), ax)
arrayfun(@(ax) axis(ax, 'tight'), ax)
So while the spacing between the axes is exactly as you've specified, there's a lot of extra space introduced by the axis rescaling.
The best way to make sure your axes take up exactly as much space as you need would be to position them manually, as Cedric suggested. Or you could calculate the exact margins and spacing you would need in order to get all the subplots where you want them (mostly by adding extra margin space above and below).
I have found the problem. It is the axis('image'); command. Here is what I'm doing:
figure('Name', 'Brain Slices', 'NumberTitle', 'off', 'units', 'normalized', 'outerposition', [0 0 1 1]);
for i = 1:size(rawVolume,3)
slice = rawVolume(:,:,i,1);
slice= squeeze(slice);
slice = rot90(slice);
subaxis(3,4,i, 'Spacing', 0.03, 'Padding', 0, 'Margin', 0);
imagesc(slice);
colormap gray;
caxis([ 0 2300 ]);
axis('image');
axis('tight');
axis('off');
end
With axis('image'); the results looks as follows:
Without axis('image'); it looks as follows:
The problem is now that the images are stretched in y-direction. How can I get this spacing without any stretching?
As I said, you have to do the calculations... Assuming you want 3% spacing horizontally, and the same spacing vertically (in pixels, not percentage):
rawVolume = rand(85,35,24);
nr = 6; % # rows
nc = 4; % # columns
sh = 0.03; % horizontal spacing (normalized units)
f = figure;
fpos = get(f, 'position');
% Convert 0.03 normalized to pixels by multiplying
% by figure width
shpx = fpos(3)*sh;
% Calculate subplot width, in pixels
% (Figure width-spacing between subplots)/# plots
wpx = (fpos(3) - shpx*(nc-1))/nc;
% Calculate subplot height: proportional to width
% based on image size
hpx = (size(rawVolume,2)./size(rawVolume,1)) * wpx;
% You want the same spacing vertically as horizontally,
% translate from pixels to normalized units by diving
% by figure height
sv = shpx./fpos(4);
% The leftover vertical space (figure height - vertical
% spacing between subplots - height of 4 subplots) will
% be the bottom margin. Divide by figure height to get
% normalized units.
mar = (fpos(4) - hpx*nr - shpx*(nr-1))./fpos(4);
% Now plot
props = {'sh', sh, 'sv', sv, 'mar', 0, 'mb', mar};
for ii = 1:size(rawVolume,3)
ax(ii) = subaxis(nr,nc,ii, props{:});
imagesc(rot90(rawVolume(:,:,ii)));
axis('image', 'tight', 'off');
end
Thank you for your detailed answer. I will try it out. Would it be possible that you can comment your script? I just don't understand the calculations.
I have tried it out. It works perfectly. Thank you so much for it.
I've added a bunch of comments to the example above to walk you through exactly what each line does.

Sign in to comment.

More Answers (2)

Cedric
Cedric on 13 Aug 2015
Edited: Cedric on 13 Aug 2015
You can store/use the axis handles of both subplots and access/modify their properties. Here is an example:
t = 0:0.1:10 ;
hAxis(1) = subplot( 2, 1, 1 ) ;
plot( t, sin(t) ) ;
hAxis(2) = subplot( 2, 1, 2 ) ;
plot( t, cos(t) ) ;
This produces:
Then you can access axes properties through their handles. E.g. for querying all available properties:
>> set( hAxis(1) )
ALim: {}
ALimMode: {'auto' 'manual'}
ActivePositionProperty: {'position' 'outerposition'}
...
Browsing all properties, you realize that the Position property is probably what you need to update. For getting a specific property, e.g. Position:
>> pos = get( hAxis(1), 'Position' )
pos =
0.1300 0.5838 0.7750 0.3412
where the first two coordinates are the horizontal and vertical relative (in [0,1]) coordinates of the origin of the axis with respect to the lower left corner of the figure, and the second two are the relative width and height. Say you want to make the first axis taller and closer to the second:
>> pos(2) = 0.5 ; % Shift down.
>> pos(4) = 0.45 ; % Increase height.
>> set( hAxis(1), 'Position', pos ) ;
and when you execute it, you get:
PS: I would favor this type of approache over 3rd party functions, because it is easy to learn using handles, and then you are really free to design almost whatever you want (e.g. small inserts in larger plots). For this purpose, the last thing that you need to know is the AXES function, which creates a new axes graphic object in a figure. To illustrate:
hAxis(3) = axes( 'Position', [0.66, 0.55, 0.2, 0.2] ) ;
[X,Y,Z] = peaks( 15 ) ;
surf( X, Y, Z);
adds the following insert:

2 Comments

Last example, for the fun of it:
load clown ;
nRows = 3 ;
nCols = 5 ;
% - Create figure, set position/size to almost full screen.
figure() ;
set( gcf, 'Units', 'normalized', 'Position', [0.1,0.1,0.8,0.8] ) ;
% - Create grid of axes.
[blx, bly] = meshgrid( 0.05:0.9/nCols:0.9, 0.05:0.9/nRows:0.9 ) ;
hAxes = arrayfun( @(x,y) axes( 'Position', [x, y, 0.9*0.9/nCols, 0.9*0.9/nRows] ), blx, bly, 'UniformOutput', false ) ;
% - "Plot data", update axes parameters.
for k = 1 : numel( hAxes )
axes( hAxes{k} ) ;
image( X ) ;
set( gca, 'Visible', 'off' ) ;
end
colormap( map ) ;
Here you see that it takes 2 lines of code for defining your own grid of axes. It is easy to wrap this into a function of your own, which fills the axes with whatever you need to plot/display.
jump to use subaxis if you just need to get it done right now (which is generally when I'm Googling for answers), but definitely come back and learn this ASAP, because this is a better solution than subaxis : you can easily set things up literally any way you want, and I'm kicking myself for not learning this a year ago.

Sign in to comment.

subaxis(4,6,1, 'Spacing', 0.03, 'Padding', 0, 'Margin', 0, 'SpacingVert', 0.03);

Categories

Asked:

on 13 Aug 2015

Commented:

KAE
on 20 Aug 2021

Community Treasure Hunt

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

Start Hunting!