Adjust figure size with respect to the axes size? - python

Let's say I create a simple plot with matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
plot stuff etc .. .
After I finished the plotting routines I want to adjust the size of my figure (but keeping all aspect ratios the same), such that the total height of ax is set to a constant value i.e. 3 inches.
Is there a nice and short way to archieve this ?
EDIT: I know how to change the figure size, but here i want to adjust it according to the size of the axes.

Here is the function to set figure height in inch unit:
def set_axes_height(ax, h):
fig = ax.figure
aw, ah = np.diff(ax.transAxes.transform([(0, 0), (1, 1)]), axis=0)[0]
fw, fh = fig.get_size_inches()
dpi = fig.get_dpi()
scale = h / (ah / dpi)
fig.set_size_inches(fw*scale, fh*scale, forward=True)

Related

matplotlib how to set plot size (with dpi), not figure size

I could find a way to set a figure size with dpi
px = 1/plt.rcParams['figure.dpi']
fig = plt.figure(figsize=(1580*px, 25*px))
(reference: https://matplotlib.org/stable/gallery/subplots_axes_and_figures/figure_size_units.html)
fig = plt.figure(figsize=(1580*px, 25*px))
plt.plot(xx, y[0], label='min')
plt.plot(xx, y[1], label='max')
plt.yticks(y_ticks, y_tick_labels)
plt.ylim(top=y_max)
plt.legend()
However, how do you set the plot size?
I want my plot or graph to be full of (1580px, 25px)
but if I set the figure size and plot graphs using the above code, then the graph does not fit the figure (1580px, 25px). Even worse, labels or ticks are not shown well in the figure like below.
I want my graph size to be the above white space size( for example, 1580px, 25px) and then draw ticks and labels outside the white space (then figure size should be bigger than the given plot size). But I couldn't find a way to set the plot size. I could only find a way to set the figure size.
import matplotlib.pyplot as plt
import numpy as np
def axes_with_pixels(width, height, margin=0.2):
px = 1/plt.rcParams['figure.dpi']
fig_width, fig_height = np.array([width, height]) / (1 - 2 * margin)
fig, ax = plt.subplots(figsize=(fig_width*px, fig_height*px))
fig.subplots_adjust(left=margin, right=1-margin,
bottom=margin, top=1-margin)
return fig, ax
fig, ax = axes_with_pixels(580, 80) # Specify the Axes size in pixels
X = np.linspace(0, 10, 10)
Y0 = np.sin(X)
Y1 = np.cos(X)
plt.plot(X, Y0, label='min')
plt.plot(X, Y1, label='max')
plt.legend()
As you can see, the Axes (plot area) is exactly 580 * 80 pixels. (Note, the shown width of 581 pixels is due to the offset of the right edge.)
However, axes_with_pixels can be only used to set a single Axes with a specified pixels. If you want a figure to have multiple Axes with some specified pixels, then you have to consider wspace and hspace in subplots_adjust to get the figure size.

Read height of legend in Python

I have some plots with a lot of information and lines, so sometimes I tend to put the legend outside the plot itself using bbox_to_anchor. I also prefer to have a title of the plot, but this will positionally coincide with the legend in that case. The following example below is just an illustration of the problem.
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)
r = 1 + np.sin(4 * np.pi * t)
q = 1 + np.sin(6 * np.pi * t)
fig, ax = plt.subplots()
ax.plot(t, s, label='S')
ax.plot(t, r, label='R')
ax.plot(t, q, label='Q')
leg = ax.legend(loc=3, ncol=3, bbox_to_anchor=(.0, 1.02, 1., .102), borderaxespad=0., mode='expand')
ax.set_title('SIMPLE PLOT', y=1.1)
plt.show()
To avoid this, I set some kind of y-value (e.g. y=1.1). I would like to automate this process because I keep updating the same plot with new data, so the legend grows in size, and I need to adjust the position of the title accordingly.
Is there a way to automate this process?
Is there a function in Python that is able to read the height of the legend so that this can be used to adjust the title position?
The height of the legend is determined at draw time. You can get it after having drawn the figure via legend.get_window_extent(). The resulting bounding box is in units of pixels. In order to find the offset of the title, you will need to subtract the upper limit of the legend from the upper limit of the axes. So you need to get the axes position in pixels as well.
The title can be offset either in figure coordinates (y=1.1) or points (pad=20). I would suggest to use points here, to make it independent of the size of the axes. So you can calculate the difference in upper positions, convert from pixels to points (i.e. distance [pixels] * ppi / dpi) and add some constant offset in points (because usually you would not want the title to sit exactly on the border of the legend). Then use that number as pad.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(constrained_layout=True)
ax.plot([1,2,3], np.random.rand(3,5), label='Label')
leg = ax.legend(loc="lower center", ncol=3, bbox_to_anchor=(.0, 1.02, 1., 1.02),
borderaxespad=0, mode='expand')
fig.canvas.draw()
leg_box = leg.get_window_extent()
ax_box = ax.get_position().transformed(fig.transFigure)
pad = (leg_box.y1 - ax_box.y1)*72./fig.dpi + 6
ax.set_title('SIMPLE PLOT', pad=pad)
plt.show()
Note that here I also used constrained_layout to have the title not cropped by the figure boundaries.

How do you limit the y-axis height in matplotlib?

How do you limit the height of the y-axis in matplotlib figure? I am trying to both display the x axis, and reduce the height of the figure for this 1D plot.
I have tried setting ticks, figure sizes, tight_layout, margin, etc. with no luck.
Also, changing the ylimits just spans the full figure height no matter what limits I choose.
import numpy as np
import matplotlib.pyplot as plot
from matplotlib import rcParams
x = np.array([3500])
y = np.array([0])
rcParams['toolbar'] = 'None'
plot.figure(num=None, figsize=(4, 1), dpi=80, facecolor='w')
plot.axes(frameon=False)
plot.yticks([0])
plot.xlim(0, 6000)
plot.ylim(-0.1, 0.1)
plot.plot(x, y, 'x', markersize=10)
plot.show()
Current figure:
Desired figure:
Try this:
plot.ylim(lower_limit, upper_limit)
Where lower_limit is the value you want to set for the bottom of the y-axis and upper_limit is the value for the top.
np.random.seed(0)
x = np.random.randn(100)
y = np.random.randn(100)
plot.figure(num=None, figsize=(4, 1), dpi=80, facecolor='w')
plot.axes(frameon=False)
plot.ylim(-10, 10)
plot.plot(x, y, '.')
The code in the question already produces the desired result, when adding
plot.tight_layout() at the end.
Of course decreasing figure size even further, shrinks the plot even more. E.g.
figsize=(4, 0.7)
Simply changing figsize to (4, 0.3), I get something that more or less looks like your desired output (except with ticks at the top as well, but those can also be removed):

Fix the plot size after displaying a matrix with spy

I have a matplotlib figure that am using embedded into a pyQT GUI, therefore I need to recycle the figure to display several resuls.
When I display a matrix using the spy function, I get what I spect:
However when I clear the figure and plot a series I get this:
Instead of this:
Which I get if I plot the series without displaying a matrix before.
So a script to reproduce the issue is:
from matplotlib.pyplot import figure, show
import numpy
fig = figure()
ax = fig.add_subplot(111)
mat = numpy.random.randn(20, 20)
# display the matrix
ax.spy(mat, markersize=5)
x = numpy.linspace(0, 1, 100)
y = x**2 + x - 5
ax.clear()
ax.plot(x, y)
I have also tried
ax.relim() # make sure all the data fits
ax.autoscale() # auto-scale
But it doesn't do anything noticeable.
plt.spy will automatically set the aspect ratio of the axes to 'equal' in order to ensure that the sparsity plot for a square matrix looks square. If the x-axis scale of your series is much larger than that of the y-axis, an equal aspect ratio will result in a very long and thin line plot.
To switch back to the 'default' mode where the aspect ratio is determined automatically you can call ax.set_aspect('auto'):
from matplotlib.pyplot import figure, show
import numpy
fig = figure()
ax = fig.add_subplot(111)
mat = numpy.random.randn(20, 20)
# display the matrix
ax.spy(mat, markersize=5)
x = numpy.linspace(0, 1, 100)
y = x**2 + x - 5
ax.clear()
ax.set_aspect('auto')
ax.plot(x, y)

Do all matplotlib figures have to be square?

It seems the only way I can make the figure a rectangle (to save vertical space), is by skewing the image which makes all text on the screen skew also.
How can I make the height of the figure smaller, without touching the width, or the text presentation?
The figsize keyword argument can be passed when creating a matplotlib figure using plt.figure() as shown below.
The figsize keyword argument states the figure size in inches. You pass a 2-item tuple of the form (width, height). This allows you to choose the size (and hence aspect ratio) of your figure before you plot any data. Below I have created a figure with width=16in and height=4in.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 1000)
y = np.sin(x)
fig = plt.figure(figsize=(16,4))
ax = fig.add_subplot(1,1,1)
ax.plot(x,y)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()

Categories

Resources