Pyplot: Drawing figure in a custom scale (both x and y)

Viewed 177

I've been plotting a dataframe using the following code within a Jupyter Notebook: For comparision with older data only available on paper in the scale 0.005mm=1cm, I need to export and print the graph in the same scale: 0.005mm in the figure (both x and y-axis) have to be 1cm in the figure.

Is there any way how I can define a custom scale? For information, the x-range and y-range are not fixed, they will vary depending on the data I am loading into the dataframe.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns            
import matplotlib.ticker as ticker


df = pd.DataFrame(np.array([[1.7, 0], [1.75, -0.012], [1.8, 0]]),
                   columns=['pos', 'val'])      
            
# Plot results
sns.set()
plt.figure(figsize=(20,30))

plt.plot(df['pos'], df['val'])

ax = plt.axes()
ax.set_aspect('equal')

plt.xlabel('Position [mm]')
plt.ylabel('Höhe [mm]')

ax.xaxis.set_major_locator(ticker.MultipleLocator(0.005))
ax.yaxis.set_major_locator(ticker.MultipleLocator(0.005))

plt.show()

Result

2 Answers

In a comment I suggested to use matplotlib.transforms — well I was wrong, the way to go is to shamelessly steal from Matplotlib's Demo Fixed Size Axes…

enter image description here

(the figure was resized by StackOverflow to fit in the post, but you can check that the proportions are correct)

import matplotlib.pyplot as plt

from mpl_toolkits.axes_grid1 import Divider, Size
from mpl_toolkits.axes_grid1.mpl_axes import Axes

cm = lambda d: d/2.54

x, y = [1700.0, 1725.0, 1750.0], [0.0, -12.0, 0.0] # μm
dx, dy = 50.0, 12.0

# take margins into account
xmin, xmax = min(x)-dx*0.05, max(x)+dx*0.05
ymin, ymax = min(y)-dy*0.05, max(y)+dy*0.05
dx, dy = xmax-xmin, ymax-ymin

# 5 μm data == 1 cm plot
scale = 5/1
xlen, ylen = dx/scale, dy/scale

# Now we know the extents of our data and the axes dimension,
# so we can set the Figure dimensions, taking borders into account
left, right = 2, 1
bot, top = 1.5, 1.5
fig = plt.figure(
    figsize=(cm(left+xlen+right), cm(bot+ylen+top)),
    dpi=118)

# change bg color to show so that one can measure the figure
# and the axes when pasted into SO and do their math…
fig.set_facecolor('xkcd:grey teal')

##########  Below is stolen from Matplotlib Fixed Size Axes
########## (please don't ask me…)
# Origin and size of the x axis and y axis
h = [Size.Fixed(cm(left)), Size.Fixed(cm(xlen))]
v = [Size.Fixed(cm(bot)), Size.Fixed(cm(ylen))]
divider = Divider(fig, (0.0, 0.0, 1., 1.), h, v, aspect=False)
# NB: Axes is from mpl_toolkits.axes_grid1.mpl_axes
ax = Axes(fig, divider.get_position())
ax.set_axes_locator(divider.new_locator(nx=1, ny=1))
fig.add_axes(ax)
#########  Above is stolen from Matplotlib Fixed Size Axes Demo 

plt.plot(x,y)
plt.grid()
ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax), yticks=range(-12,1,3),
       xlabel='X/μm', ylabel='Y/μm',
       title='X vs Y, 1 cm on plot equals 5 μm')
fig.suptitle('Figure dimensions: w = %.2f cm, h = %.2f cm.'%(
    left+xlen+right, bot+ylen+top))

fig.savefig('Figure_1.png',
            # https://stackoverflow.com/a/4805178/2749397, Joe Kington's
            facecolor=fig.get_facecolor(), edgecolor='none')

1 inch = 2.54 cm, so 254/0.005 = 50800 dpi

plt.figure(figsize=(20,30), dpi=50800)
Related