I have a number of plots where the x- and y-axes are in centimeter units, and I am already using axis('equal') to ensure proper aspect ratios. I would like to print out those plots so that when I measure distances within my axes, the distances correspond to real-world centimeters.
That is, a line that is 3 units (cm) long in the plot should print out to be 3 cm long. (A more complex example would be drawing a ruler in Matplotlib, and then printing it out for use /as/ a ruler.) I found solutions in matlab and mathematica, but not for Matplotlib. Is there a magic formula to achieve this? I believe it will require a special combination/ordering of: figure(figsize=??), axis('equal')
, fig.canvas.draw()
, fig.savefig('filename',format="??")
, possibly some math with fig.bbox
parameters, and perhaps one or more dpi-settings. I have tried many combinations but haven't hit on the right one. And maybe there is a way-easier approach ...
First, we write a general function to get the size of a figure. We then calculate x to set = x set previously x target x actual for x being the width and height. The intuition behind this equation is that we figure out how off the actual image’s size is from our target, and use this to update what we tell matplotlib to do.
The intuition behind this equation is that we figure out how off the actual image’s size is from our target, and use this to update what we tell matplotlib to do. However, this is an approximation, and we repeat it to get a better fit.
Inside the figure method, you have to pass the fig size as width and height as a tuple. For example, If I want to change the size of the plot to the width of 8 inches and a height of 3 inches, then I will execute the following lines of code. The other method to change the size of the plot is using the plt.rcParams [“figure.figsize”].
The intuition behind this equation is that we figure out how off the actual image’s size is from our target, and use this to update what we tell matplotlib to do. However, this is an approximation, and we repeat it to get a better fit. The full code is below:
Consider this example. Where I specify exactly the dimension of my axes in cm. matplotlib works in inches, so I convert to inches. And then I also save it with a particular dpi (128) so that it matches the designed dimensions in my display. This of course varies for every display. I found that by trial and error, even though there might be other methods. Well here the code:
left_margin = 1. # cm
right_margin = 1. # cm
figure_width = 10. # cm
figure_height = 7. # cm
top_margin = 1. # cm
bottom_margin = 1. # cm
box_width = left_margin + figure_width + right_margin # cm
box_height = top_margin + figure_height + bottom_margin # cm
cm2inch = 1/2.54 # inch per cm
# specifying the width and the height of the box in inches
fig = figure(figsize=(box_width*cm2inch,box_height*cm2inch))
ax = fig.add_subplot(111)
ax.plot([1,2,3])
fig.subplots_adjust(left = left_margin / box_width,
bottom = bottom_margin / box_height,
right = 1. - right_margin / box_width,
top = 1. - top_margin / box_height,
)
fig.savefig('ten_x_seven_cm.png', dpi=128)
# dpi = 128 is what works in my display for matching the designed dimensions.
Add this to @pablo reyes' answer, check that the printer is at 100%, and it's pretty close;
ax.set_ylim(0,7)
ax.set_xlim(0,10)
ax.plot([0.5, 1.5],[0.25, 0.25],label='One cm?')
ax.plot([6,6],[1,2], label='One cm?')
ax.legend()
we force the axis to be a size we know, we make its data-transform match the real world, and we can "print a ruler".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With