Closed yongrenjie closed 5 months ago
FWIW here's the existing code using gridspec
import penguins as pg
clipc = pg.read("./nmr/200804-6a-sc", 10, 1)
# make 2D spectrum 3x3 and projections 1x3, 3x1
gridspec_kw = {"width_ratios": [1, 3], "height_ratios": [1, 3]}
fig, axs = pg.subplots(2, 2, gridspec_kw=gridspec_kw)
# assign them to easy-to-remember variables
axblank, axf2, axf1, axmain = list(axs.flat)
# plot the 2D spectrum
clipc.stage(levels=2e5)
pg.mkplot(axmain, autolabel="nucl")
# get rid of the top-left empty space
axblank.remove()
# 1D projections
f2projp = clipc.f2projp()
f2projp.stage(color="blue")
pg.mkplot(axf2, xlabel="")
# the xlim should be set to the maximum and minimum chemical shift, i.e.
# we don't want overhang at the ends
p = f2projp.ppm_scale()
axf2.set_xlim((max(p), min(p)))
# get rid of x-axis
axf2.xaxis.set_visible(False)
axf2.spines["bottom"].set_visible(False)
# magic
from matplotlib import transforms
base_trfm = axf1.transData
rot_trfm = transforms.Affine2D().rotate_deg(90)
scale_trfm = transforms.Affine2D().scale(sx=1, sy=-1)
f1projp = clipc.f1projp()
f1projp.stage(color="blue", plot_options={"transform": scale_trfm + rot_trfm + base_trfm})
pg.mkplot(ax=axf1)
p = f1projp.ppm_scale()
axf1.set_ylim(max(p), min(p)) # this automatically inverts the y-axis
# Don't need to invert xaxis because mkplot() does it for us.
# axf1.invert_xaxis()
# However, we do need to disable the xaxis.
axf1.xaxis.set_visible(False)
axf1.spines["bottom"].set_visible(False)
# Let's put the y-axis ticks on the right.
axmain.yaxis.tick_right()
# And change the y-label position.
axmain.yaxis.label.set_rotation(0)
axmain.yaxis.label.set_horizontalalignment("left")
axmain.yaxis.label.set_verticalalignment("top")
axmain.yaxis.set_label_coords(1.02, 1)
# show the plot
pg.tight_layout()
pg.show()
Here's the code using axes_divider. This allows us to use subplots() to generate series of axes and then manipulate the individual axes after that.
fig, ax = pg.subplots(1, 1, figsize=(7, 7))
clipc.stage(levels=2e5)
pg.mkplot(ax=ax, autolabel="nucl")
### The only difference from before is that we use ax_divider instead of gridspec.
from mpl_toolkits.axes_grid1 import make_axes_locatable
ax_divider = make_axes_locatable(ax)
axf2 = ax_divider.append_axes("top", size="20%", pad="2%")
axf1 = ax_divider.append_axes("left", size="20%", pad="2%")
### Same code as before.
# 1D projections
f2projp = clipc.f2projp()
f2projp.stage(color="blue")
pg.mkplot(axf2, xlabel="")
# the xlim should be set to the maximum and minimum chemical shift, i.e.
# we don't want overhang at the ends
p = f2projp.ppm_scale()
axf2.set_xlim((max(p), min(p)))
# get rid of x-axis
axf2.xaxis.set_visible(False)
axf2.spines["bottom"].set_visible(False)
# magic
from matplotlib import transforms
base_trfm = axf1.transData
rot_trfm = transforms.Affine2D().rotate_deg(90)
scale_trfm = transforms.Affine2D().scale(sx=1, sy=-1)
f1projp = clipc.f1projp()
f1projp.stage(color="blue", plot_options={"transform": scale_trfm + rot_trfm + base_trfm})
pg.mkplot(ax=axf1)
p = f1projp.ppm_scale()
axf1.set_ylim(max(p), min(p)) # this automatically inverts the y-axis
# Don't need to invert xaxis because mkplot() does it for us.
# axf1.invert_xaxis()
# However, we do need to disable the xaxis.
axf1.xaxis.set_visible(False)
axf1.spines["bottom"].set_visible(False)
ax.yaxis.tick_right()
ax.yaxis.label.set_rotation(0)
ax.yaxis.label.set_horizontalalignment("left")
ax.yaxis.label.set_verticalalignment("top")
ax.yaxis.set_label_coords(1.02, 1)
pg.show()
There's also https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.autoscale.html
set tight=True
to remove the padding added on either edge of the spectrum.
So far I've figured out how to do this when there's only one spectrum on an entire Figure, by passing gridspec_kw to subplots(). However, I think we can do this more generally (and with fewer inconsistencies with the existing API) using the axes_divider module, something like this: https://matplotlib.org/3.1.1/gallery/axes_grid1/demo_colorbar_with_axes_divider.html
The API should be kwarg(s) for _stage2d(), perhaps a dictionary
projection_options
, with keysf1
(bool),f2
(bool),f1_dataset
(optional Dataset1DProj, defaults tods.f1projp()
),f2_dataset
(optional Dataset1DProj, defaults tods.f2projp()
),ratio
(float, ratio of main spectrum width vs projection), and maybe more keys to control geometry (whether f1proj appears on the left or the right...?).How do we pass plot options, e.g. the color of projections? Can't have a nested dictionary can we...