How to set the margins for a matplotlib figure? - python

I am generating an on-screen figure that has two subplots: one is an image and the other is a graph. The margins are extremely large around the figures.
How do I adjust the margins around the figures?
Most questions that I searched for involved saving images (bbox seemed perfect), and using axes instead of subplots for absolute positioning.
Here is the code I used to generate the figure:
def __init__(self, parent):
wx.Panel.__init__(self, parent)
...
self.figure, (self.picture, self.intensity) = \
plt.subplots(nrows=2, figsize=(12, 5))
self.figure.set_dpi(80)
#self.figure.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1)
#self.picture.imshow(np.random.uniform()) #size=(5, 50)))
self.intensity.plot(np.random.random()) #size=641))
self.intensity.autoscale(axis='x', tight=True)

Have a look at plt.tight_layout() or plt.subplots_adjust() or fig.savefig(bbox_inches='tight').
With subplots_adjust you can adjust most parameters, while tight_layout() and bbox_inches='tight' are more or less semi automatic.

You can also use plt.set(), with the attributes given in the adjust plot menu, e.g. set(top=0.82) etc.

Related

Adjusting subplots to make space for colorbar

I was trying to plot some data and found constrained layout very helpful in maintaining margins and spaces between subplots. However, when I add a colorbar it reduces the width of all subplots and creates extra white space in the subplots above. This bcomes a problem when such plots are put up in reports where a lot of space goes waste because of extra space taken by colorbar.
I was wondering how I can avoid this and make only the image plots to resize when the colorbar is added without afecting the subplots above and no extra white space is created. An example code of the problem I am facing is:
fig, ax = plt.subplots(4,2, constrained_layout=True)
ax[0,0].plot(range(10))
ax[0,1].plot(range(10))
ax[1,0].plot(range(10))
ax[1,1].plot(range(10))
ax[2,0].pcolor(np.random.rand(2,2))
ax[2,1].pcolor(np.random.rand(2,2))
ax[3,0].pcolor(np.random.rand(2,2))
im = ax[3,1].pcolor(np.random.rand(2,2))
bar = fig.colorbar(im,ax=[[ax[2,0],ax[2,1]],[ax[3,0],ax[3,1]]])
It'll be better if I can get this done with contrantrained_layout=True.
I don't have much experience with adjusting the color bar, but what about the idea of adding a new axis and placing the color bar in the center? I set the placement values manually. I wanted to put the color bar in the bottom two graphs to make the widths the same, but I couldn't do that.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(4,2, constrained_layout=True)
ax[0,0].plot(range(10))
ax[0,1].plot(range(10))
ax[1,0].plot(range(10))
ax[1,1].plot(range(10))
ax[2,0].pcolor(np.random.rand(2,2))
ax[2,1].pcolor(np.random.rand(2,2))
ax[3,0].pcolor(np.random.rand(2,2))
im = ax[3,1].pcolor(np.random.rand(2,2))
cax = fig.add_axes([0.48, 0.11, 0.05, 0.36])
bar = fig.colorbar(im,cax=cax,ax=[[ax[2,0],ax[2,1]],[ax[3,0],ax[3,1]]])
fig.subplots_adjust(wspace=0.7, hspace=0.5)
plt.show()

How do I set the size of the axes patch so that the plot labels aren't clipped (matplotlib)

I have a graph in which I've set the axis labels to scientific notation using
formatter = mpl.ticker.FormatStrFormatter('%4.2e')
axis2.yaxis.set_major_formatter(formatter)
However, the axes.patch (or whatever is the right way to express the 'canvas' extent of the plot) doesn't adjust so the tick labels and axis label are clipped:
How do I adjust the extent of the axes portion of the plot. Changing the page size (figsize = ...) doesn't do it, since that just scales the overall plot area, resulting in the same clipping problem.
You can use the method tight_layout, which will accommodate the plot in the figure available space.
Example
from pylab import *
f = figure()
f.add_subplot(111)
f.tight_layout()
show()
Hope it helps.
Cheers
Just call fig.tight_layout() (assuming you have a Figure object defined).

Matplotlib adjust figure margin

The following code gives me a plot with significant margins above and below the figure. I don't know how to eliminate the noticeable margins. subplots_adjust does not work as expected.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(10),range(10))
ax.set_aspect('equal')
plt.tight_layout()
tight_layout eliminates some of the margin, but not all of the margins.
What I wanted is actually setting the aspect ratio to any customized value and eliminating the white space at the same time.
Update: as Pierre H. puts it, the key is to change the size of the figure container. So my question is: Could you suggest a way to accommodate the size of the figure to the size of the axes with arbitrary aspect ratio?
In other words, first I create a figure and an axes on it, and then I change the size of the axes (by changing aspect ratio for example), which in general will leave a portion of the figure container empty. At this stage, we need to change the size of the figure accordingly to eliminate the blank space on the figure container.
I just discovered how to eliminate all margins from my figures. I didn't use tight_layout(), instead I used:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(20,20))
ax = plt.subplot(111,aspect = 'equal')
plt.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
Hope this helps.
After plotting your chart you can easily manipulate margins this way:
plot_margin = 0.25
x0, x1, y0, y1 = plt.axis()
plt.axis((x0 - plot_margin,
x1 + plot_margin,
y0 - plot_margin,
y1 + plot_margin))
This example could be changed to the aspect ratio you want or change the margins as you really want.
In other stacktoverflow posts many questions related to margins could make use of this simpler approach.
Best regards.
tight_layout(pad=0) will meet your need.
http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.tight_layout
I think what you need is, and it works well for me.
plt.axis('tight')
This command will automatically scale the axis to fit tightly to the data. Also check the answer of Nuno Aniceto for a customized axis. The documents are in https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.axis.
Be aware that
plt.savefig(filename, bbox_inches='tight')
will help remove white space of all the figure including labels, etc, but not the white space inside the axes.
You should use add_axes if you want exact control of the figure layout. eg.
left = 0.05
bottom = 0.05
width = 0.9
height = 0.9
ax = fig.add_axes([left, bottom, width, height])
I think the subplot_adjust call is irrelevant here since the adjustment is overridden by tight_layout. Anyway, this only change the size of the axes inside the figure.
As tcaswell pointed it, you need to change the size of the figure. Either at creation (my proposition below) or after, using fig.set_size_inches. I'm here creating a figure with a 1:1 aspect ratio using the figsize=(6,6) argument (of course 6 inches is an arbitrary choice):
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(111)
ax.plot(range(10),range(10))
ax.set_aspect('equal')
plt.tight_layout()
You can use like:
plt.subplots_adjust(wspace=1,hspace=0.5,left=0.1,top=0.9,right=0.9,bottom=0.1)
And delete the item bbox_inches='tight' in plt.savefig().

matplotlib - subplots with fixed aspect ratio

I have a problem with plotting multiple subplots. I would like to set the PHYSICAL aspect ratio of the subplots to a fixed value.
In my example I have 12 subplots (4 rows and 3 columns) on a landscape A4 figure. There all subplots are nicely placed on the whole figure, and for all subplots the height is nearly equal to the width.
But if I change the layout of my figure to portrait, the subplots are stretched vertically.
And this is exactly what should not happen. I would like to have the same height and width of the subplots as on the landscape figure. Is there a possibility that the aspect ratio of the subplots stay the same?
Thanks in advance,
Peter
EDIT:
I have found a workaround. But this just works for non-logarithmic axes...
aspectratio=1.0
ratio_default=(ax.get_xlim()[1]-ax.get_xlim()[0])/(ax.get_ylim()[1]-ax.get_ylim()[0])
ax.set_aspect(ratio_default*aspectratio)
Actually, what you're wanting is quite simple... You just need to make sure that adjustable is set to 'box' on your axes, and you have a set aspect ratio for the axes (anything other than 'auto').
You can either do this with the adjustable kwarg when you create the subplots. Alternatively, you can do this after their creation by calling ax.set_adjustable('box'), or by calling ax.set_aspect(aspect, adjustable='box') (where aspect is either 'equal' or a number).
Now, regardless of how the figure is resized, the subplots will maintain the same aspect ratio.
For example:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1, adjustable='box', aspect=0.3)
ax2 = fig.add_subplot(2,1,2)
ax1.plot(range(10))
ax2.plot(range(10))
plt.show()
Now, compare how the top subplot responds to resizing, vs. how the bottom subplot responds:
The initial plot
Resized to a vertical layout:
Resized to a horizontal layout:
Your workaround works for me. After plotting the data, I call the following function:
def fixed_aspect_ratio(ratio):
'''
Set a fixed aspect ratio on matplotlib plots
regardless of axis units
'''
xvals,yvals = gca().axes.get_xlim(),gca().axes.get_ylim()
xrange = xvals[1]-xvals[0]
yrange = yvals[1]-yvals[0]
gca().set_aspect(ratio*(xrange/yrange), adjustable='box')
In reply to the remark about the solution not working for logarithmic plots in the edit to the original question - you need to adapt as follows:
def fixed_aspect_ratio_loglog(ratio):
'''
Set a fixed aspect ratio on matplotlib loglog plots
regardless of axis units
'''
xvals,yvals = gca().axes.get_xlim(),gca().axes.get_ylim()
xrange = log(xvals[1])-log(xvals[0])
yrange = log(yvals[1])-log(yvals[0])
gca().set_aspect(ratio*(xrange/yrange), adjustable='box')
(Adaptation for semilog plots should now be obvious)

Hiding axis text in matplotlib plots

I'm trying to plot a figure without tickmarks or numbers on either of the axes (I use axes in the traditional sense, not the matplotlib nomenclature!). An issue I have come across is where matplotlib adjusts the x(y)ticklabels by subtracting a value N, then adds N at the end of the axis.
This may be vague, but the following simplified example highlights the issue, with '6.18' being the offending value of N:
import matplotlib.pyplot as plt
import random
prefix = 6.18
rx = [prefix+(0.001*random.random()) for i in arange(100)]
ry = [prefix+(0.001*random.random()) for i in arange(100)]
plt.plot(rx,ry,'ko')
frame1 = plt.gca()
for xlabel_i in frame1.axes.get_xticklabels():
xlabel_i.set_visible(False)
xlabel_i.set_fontsize(0.0)
for xlabel_i in frame1.axes.get_yticklabels():
xlabel_i.set_fontsize(0.0)
xlabel_i.set_visible(False)
for tick in frame1.axes.get_xticklines():
tick.set_visible(False)
for tick in frame1.axes.get_yticklines():
tick.set_visible(False)
plt.show()
The three things I would like to know are:
How to turn off this behaviour in the first place (although in most cases it is useful, it is not always!) I have looked through matplotlib.axis.XAxis and cannot find anything appropriate
How can I make N disappear (i.e. X.set_visible(False))
Is there a better way to do the above anyway? My final plot would be 4x4 subplots in a figure, if that is relevant.
Instead of hiding each element, you can hide the whole axis:
frame1.axes.get_xaxis().set_visible(False)
frame1.axes.get_yaxis().set_visible(False)
Or, you can set the ticks to an empty list:
frame1.axes.get_xaxis().set_ticks([])
frame1.axes.get_yaxis().set_ticks([])
In this second option, you can still use plt.xlabel() and plt.ylabel() to add labels to the axes.
If you want to hide just the axis text keeping the grid lines:
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
Doing set_visible(False) or set_ticks([]) will also hide the grid lines.
If you are like me and don't always retrieve the axes, ax, when plotting the figure, then a simple solution would be to do
plt.xticks([])
plt.yticks([])
I've colour coded this figure to ease the process.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
You can have full control over the figure using these commands, to complete the answer I've add also the control over the spines:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# X AXIS -BORDER
ax.spines['bottom'].set_visible(False)
# BLUE
ax.set_xticklabels([])
# RED
ax.set_xticks([])
# RED AND BLUE TOGETHER
ax.axes.get_xaxis().set_visible(False)
# Y AXIS -BORDER
ax.spines['left'].set_visible(False)
# YELLOW
ax.set_yticklabels([])
# GREEN
ax.set_yticks([])
# YELLOW AND GREEN TOGHETHER
ax.axes.get_yaxis().set_visible(False)
I was not actually able to render an image without borders or axis data based on any of the code snippets here (even the one accepted at the answer). After digging through some API documentation, I landed on this code to render my image
plt.axis('off')
plt.tick_params(axis='both', left=False, top=False, right=False, bottom=False, labelleft=False, labeltop=False, labelright=False, labelbottom=False)
plt.savefig('foo.png', dpi=100, bbox_inches='tight', pad_inches=0.0)
I used the tick_params call to basically shut down any extra information that might be rendered and I have a perfect graph in my output file.
Somewhat of an old thread but, this seems to be a faster method using the latest version of matplotlib:
set the major formatter for the x-axis
ax.xaxis.set_major_formatter(plt.NullFormatter())
One trick could be setting the color of tick labels as white to hide it!
plt.xticks(color='w')
plt.yticks(color='w')
or to be more generalized (#Armin Okić), you can set it as "None".
When using the object oriented API, the Axes object has two useful methods for removing the axis text, set_xticklabels() and set_xticks().
Say you create a plot using
fig, ax = plt.subplots(1)
ax.plot(x, y)
If you simply want to remove the tick labels, you could use
ax.set_xticklabels([])
or to remove the ticks completely, you could use
ax.set_xticks([])
These methods are useful for specifying exactly where you want the ticks and how you want them labeled. Passing an empty list results in no ticks, or no labels, respectively.
You could simply set xlabel to None, straight in your axis. Below an working example using seaborn
from matplotlib import pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax.set(xlabel=None)
plt.show()
Just do this in case you have subplots
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
ax[0].set_yticklabels([]) # x-axis
ax[0].set_xticklabels([]) # y-axis

Categories

Resources