我正在尝试使用以下“属性”创建Matplotlib图:
下面的Python代码创建的图形符合我的所有要求,除了次要(右)轴标签被裁剪,我找不到解决方法。有人知道如何修改我的代码或建议一种方法让辅助标签完全出现在图中,同时仍然保持“紧”的边界框?
import numpy as np
import matplotlib.pyplot as plt
def main():
# Data definition (toy example)
xticks = [0, 0.5, 1, 1.5, 2, 2.0, 2.5, 3]
xlim = [0, 3]
pyticks = [2.8, 3.5, 4.2, 5.0, 5.7, 6.4, 7.2, 7.9, 8.6, 9.4, 10.1, 10.8]
syticks = [5.6, 6.0, 6.4, 6.9, 7.3, 7.8, 8.2, 8.7, 9.1, 9.6, 10, 10.4]
prim_indep = np.array([0, 1, 2, 3])
prim_dep = np.array([3.5, 5.7, 10.1, 8.7])
sec_indep = np.array([0.5, 1, 1.5])
sec_dep = np.array([10, 9.0, 6.0])
# Figure creation
fig = plt.figure(figsize=(6.4, 4.8))
axis_prim = plt.subplot(111)
setup_axis(axis_prim, xticks, pyticks, xlim)
plt.tight_layout(pad=0, w_pad=0, h_pad=2)
axis_sec = fig.add_axes(axis_prim.get_position(), frameon=False)
setup_axis(axis_sec, xticks, syticks, xlim)
axis_sec.yaxis.set_label_position('right')
axis_sec.yaxis.set_ticks_position('right')
axis_sec.tick_params(axis='x', which='both', length=0, labelbottom='off')
axis_sec.xaxis.grid(True, which='both', zorder=2)
axis_sec.yaxis.grid(True, which='both', zorder=2)
axis_prim.set_zorder(axis_sec.get_zorder()+1)
axis_prim.patch.set_visible(False)
# Plot data
axis_prim.plot(prim_indep, prim_dep, color='k', linewidth=3)
plot_marker(axis_prim, prim_indep, prim_dep, 'k')
axis_sec.plot(sec_indep, sec_dep, color='b', linestyle='--', linewidth=3)
plot_marker(axis_sec, sec_indep, sec_dep, 'b')
#
fig.savefig('test.png', pad_inches=0)
plt.close('all')
def setup_axis(axis, xticks, yticks, xlim):
"""Specify axis features"""
axis.tick_params(axis='x', which='major', labelsize=14, zorder=4)
axis.tick_params(axis='y', which='major', labelsize=14, zorder=4)
axis.xaxis.set_ticks(xticks)
axis.yaxis.set_ticks(yticks)
axis.set_xlim(xlim)
axis.set_axisbelow(True)
def plot_marker(axis, indep, dep, color):
"""Plot marker with "standard" sizing"""
axis.plot(
indep, dep,
color=color, linestyle='', linewidth=0, marker='o',
markeredgecolor=color, markersize=9,
markeredgewidth=3, markerfacecolor='w',
)
if __name__ == '__main__':
main()
上面的代码生成了下图:
Image file produces by code immediately above
我找到的解决方案是使用 tight_layout 函数的 rect 参数。图中绘制了两次:
可能有办法避免两张抽签。产生具有我想要的要求的图形的代码如下所示,“正确”的图像低于该图像。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
def get_tightbbox_adjustment(fig, axis_prim, axis_sec):
"""
Calculates distance between rightmsot edge of secondary dependent axis
tick labels and rightmost edge of rightmost tick label of independent axis
"""
right_edge = get_edge(fig, axis_sec.yaxis)
xaxis_edge = get_edge(fig, axis_prim.xaxis)
return right_edge-xaxis_edge
def bbox(fig, obj):
"""Returns bounding box of an object"""
renderer = fig.canvas.get_renderer()
return obj.get_window_extent(renderer=renderer).transformed(
fig.dpi_scale_trans.inverted()
)
def get_edge(fig, axis):
"""Find rightmost edge of an axis tick labels"""
tlabels = [label for label in axis.get_ticklabels()]
return max([bbox(fig, label).xmax for label in tlabels])
def draw(fsize, save=False, fbbox=None):
# Data definition (toy example)
xticks = [0, 0.5, 1, 1.5, 2, 2.0, 2.5, 3]
xlim = [0, 3]
pyticks = [2.8, 3.5, 4.2, 5.0, 5.7, 6.4, 7.2, 7.9, 8.6, 9.4, 10.1, 10.8]
syticks = [5.6, 6.0, 6.4, 6.9, 7.3, 7.8, 8.2, 8.7, 9.1, 9.6, 10, 10.4]
prim_indep = np.array([0, 1, 2, 3])
prim_dep = np.array([3.5, 5.7, 10.1, 8.7])
sec_indep = np.array([0.5, 1, 1.5])
sec_dep = np.array([10, 9.0, 6.0])
# Figure creation
fig = plt.figure(figsize=fsize)
#
axis_prim = plt.subplot(1, 1, 1)
setup_axis(axis_prim, xticks, pyticks, xlim)
plt.tight_layout(rect=fbbox, pad=0, h_pad=2)
#
axis_sec = plt.axes(axis_prim.get_position(), frameon=False)
setup_axis(axis_sec, xticks, syticks, xlim)
axis_sec.yaxis.set_label_position('right')
axis_sec.yaxis.set_ticks_position('right')
axis_sec.tick_params(axis='x', which='both', length=0, labelbottom='off')
axis_sec.xaxis.grid(True, which='both', zorder=2)
axis_sec.yaxis.grid(True, which='both', zorder=2)
#
axis_prim.set_zorder(axis_sec.get_zorder()+1)
axis_prim.patch.set_visible(False)
# Plot data
axis_prim.plot(prim_indep, prim_dep, color='k', linewidth=3)
plot_marker(axis_prim, prim_indep, prim_dep, 'k')
axis_sec.plot(sec_indep, sec_dep, color='b', linestyle='--', linewidth=3)
plot_marker(axis_sec, sec_indep, sec_dep, 'b')
#
if save:
fig.savefig('test.png', box='tight', pad_inches=0)
else:
FigureCanvasAgg(fig).draw()
plt.close('all')
return fig, axis_prim, axis_sec
def main():
fsize = (6.4, 4.8)
fig, axis_prim, axis_sec = draw(fsize)
fbbox = axis_prim.get_position()
delta = get_tightbbox_adjustment(fig, axis_prim, axis_sec)/fsize[0]
fbbox = [0, 0, 1-delta, 1]
draw(fsize, save=True, fbbox=fbbox)
def plot_marker(axis, indep, dep, color):
"""Plot marker with "standard" sizing"""
axis.plot(
indep, dep,
color=color, linestyle='', linewidth=0, marker='o',
markeredgecolor=color, markersize=9,
markeredgewidth=3, markerfacecolor='w',
)
def setup_axis(axis, xticks, yticks, xlim):
"""Specify axis features"""
axis.tick_params(axis='x', which='major', labelsize=14, zorder=4)
axis.tick_params(axis='y', which='major', labelsize=14, zorder=4)
axis.xaxis.set_ticks(xticks)
axis.yaxis.set_ticks(yticks)
axis.set_xlim(xlim)
axis.set_axisbelow(True)
if __name__ == '__main__':
main()