matplotlib:在堆叠散点图中对齐 y 轴标签

Posted

技术标签:

【中文标题】matplotlib:在堆叠散点图中对齐 y 轴标签【英文标题】:matplotlib: Aligning y-axis labels in stacked scatter plots 【发布时间】:2013-10-17 02:59:20 【问题描述】:

在下面的图中,我有两个具有不同数字比例的散点图,因此它们的 Y 轴标签未对齐。有什么方法可以强制 y 轴标签中的水平对齐?

import matplotlib.pylab as plt
import random
import matplotlib.gridspec as gridspec

random.seed(20)
data1 = [random.random() for i in range(10)]
data2 = [random.random()*1000 for i in range(10)]

gs = gridspec.GridSpec(2,1)
fig = plt.figure()

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size =16)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size =16)

plt.show()

【问题讨论】:

你想要什么在这里描述:matplotlib.org/faq/… 【参考方案1】:

您可以使用 set_label_coords 方法。

import matplotlib.pylab as plt
import random
import matplotlib.gridspec as gridspec

random.seed(20)
data1 = [random.random() for i in range(10)]
data2 = [random.random()*1000 for i in range(10)]

gs = gridspec.GridSpec(2,1)
fig = plt.figure()

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size =16)
ax.get_yaxis().set_label_coords(-0.1,0.5)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size =16)
ax.get_yaxis().set_label_coords(-0.1,0.5)

【讨论】:

谢谢!我试图按照“ax.get_yaxis().get_label().set_ha('left')”的方式做一些事情,但它没有用。我想我没有完全理解标签对齐的工作原理。 有没有办法自动确定两个轴上刻度标签的最小可接受距离是多少? IE。找出独立的自动位置是什么,然后明确地将它们都设置为最大? @andybuckley,我只是在玩弄自动对齐标签。您可以使用ax.yaxis.label.get_position() 获取位置的最小/最大值,但坐标空间与ax.yaxis.set_label_coords() 中使用的不同——您必须使用ax.yaxis.label.set_position() 和一点技巧:@987654326 @ 否则它会在您调用draw()时重置位置@ @SamPowell 很好,谢谢。不幸的是,这是一个很好的例子,说明从 mpl 中获得真正的“出版质量”图是多么痛苦,而无需在理想的几行绘图代码周围添加很多 非常繁琐 hack:- /【参考方案2】:

自从编写这个问题以来,matplotlib 添加了一个易于使用的对齐标签的功能。强制对齐标签的正确方法是在显示图形之前使用函数fig.align_labels()

如果您需要更细粒度的控制,也可以使用Figure.align_xlabels()Figure.align_ylabels()的函数。

这是问题中发布的代码的工作版本。只添加了一行(倒数第二行)来制定解决方案。

import matplotlib.pylab as plt
import random
import matplotlib.gridspec as gridspec

random.seed(20)
data1 = [random.random() for i in range(10)]
data2 = [random.random()*1000 for i in range(10)]

gs = gridspec.GridSpec(2,1)
fig = plt.figure()

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size =16)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size =16)

fig.align_labels()
plt.show()

更多信息请参考the Matplotlib Documentation on Aligning Labels。

【讨论】:

【参考方案3】:

正如评论中所发布的,您正在寻找的解决方法是使用 set_label_coords() 作为 described here。对于您的情况,它将类似于:

labelx = -0.5

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size=16)
ax.yaxis.set_label_coords(labelx, 0.5)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size=16)
ax.yaxis.set_label_coords(labelx, 0.5)

【讨论】:

【参考方案4】:

这是我编写的用于自动对齐标签的函数,但它似乎不适用于脚本,只能交互。

def align_labels(axes_list,axis='y',align=None):
    if align is None:
        align = 'l' if axis == 'y' else 'b'
    yx,xy = [],[]
    for ax in axes_list:
        yx.append(ax.yaxis.label.get_position()[0])
        xy.append(ax.xaxis.label.get_position()[1])

    if axis == 'x':
        if align in ('t','top'):
            lim = max(xy)
        elif align in ('b','bottom'):
            lim = min(xy)
    else:
        if align in ('l','left'):
            lim = min(yx)
        elif align in ('r','right'):
            lim = max(yx)

    if align in ('t','b','top','bottom'):
        for ax in axes_list:
            t = ax.xaxis.label.get_transform()
            x,y = ax.xaxis.label.get_position()
            ax.xaxis.set_label_coords(x,lim,t)
    else:
        for ax in axes_list:
            t = ax.yaxis.label.get_transform()
            x,y = ax.yaxis.label.get_position()
            ax.yaxis.set_label_coords(lim,y,t)

还有一个例子:

fig,ax = subplots(2,2)
ax00,ax01 = ax[0]
ax10,ax11 = ax[1]
ax00.set_ylim(1000,5000)
ax00.set_ylabel('top')
ax10.set_ylabel('bottom')
ax10.set_xlabel('left')
ax11.set_xlabel('right')
ax11.xaxis.axis_date()
fig.autofmt_xdate()
#we have to call draw() so that matplotlib will figure out the automatic positions
fig.canvas.draw()
align_labels(ax[:,0],'y')
align_labels(ax[1],'x')

【讨论】:

【参考方案5】:

我在最后提供了一个解决方案,但首先我会告诉你哪种方式不会成功。

我最近重新审视了这个问题,并花了相当长的时间尝试各种解决方案,即尝试不同坐标系之间几乎所有可能的转换组合,以及它们与tight_layout() 的关系。我只用backend_pdf 进行了实验,所以我不知道交互式媒体。但简而言之,我的结论是,无论您如何尝试找出位置并尝试转换它们,在此级别都不可能对齐轴标签。我想不知何故它应该是可能的,例如在内部 matplotlib 能够以某种方式对齐子图本身的轴。 只有两次绘制到 pdf 文件中并在中间执行以下操作,我才能获得更好的位置,但仍然没有对齐:

# sorry for the `self`, this is from a class
def align_x_labels(self):
    self.lowest_ax = min(self.axes.values(),
                         key = lambda ax: ax.xaxis.label.get_position()[1])
    self.lowest_xlab_dcoo = self.lowest_ax.transData.transform(
        self.lowest_ax.xaxis.label.get_position())
    list(
        map(
                lambda ax: \
                    ax.xaxis.set_label_coords(
                        self.fig.transFigure.inverted().transform(
                            ax.transAxes.transform((0.5, 0.5)))[0],
                        self.fig.transFigure.inverted().transform(
                            self.lowest_xlab_dcoo)[1],
                        transform = self.fig.transFigure
                    ),
                self.axes.values()
            )
    )

遗憾的是无法实现这样的基本功能,并且在不同的绘图步骤中如何转换和重新调整不同的坐标空间是模糊的。非常感谢看到对此的明确解释,因为matplotlib webpage 仅概述了架构,提供了简单的案例,但未能解释此类情况。我也很惊讶接受或返回坐标的方法没有在他们的文档字符串中说明这些坐标的类型。终于发现很有用的this tutorial。

解决方案

最后,我没有弄乱转换,而是在GridSpec 中创建了另一行零高度和不可见轴(对于 y 轴标签也可以使用零宽度列)。然后我为这些子图添加了标签,将verticalalignment 设置为top

# get one of the zero height phantom subplots to `self.ax`:
self.get_subplot(i, 1)
# set empty ticklabels:
self.ax.xaxis.set_ticklabels([])
self.ax.yaxis.set_ticklabels([])
# set the axis label:
self.ax.set_xlabel(labtext, fontproperties = self.fp_axis_lab)
# and this is matter of aesthetics
# sometimes bottom or center might look better:
self.ax.xaxis.label.set_verticalalignment('top')

【讨论】:

以上是关于matplotlib:在堆叠散点图中对齐 y 轴标签的主要内容,如果未能解决你的问题,请参考以下文章

使用 matplotlib 的 x-y 散点图中误差线的颜色图

matplotlib 在散点图中不显示图例

如何在matplotlib散点图中规范化色彩映射?

突出显示 matplotlib 散点图中的特定点

Matplotlib:避免“散点/点/蜂群”图中的重叠数据点

带有颜色渐变的 Matplotlib 3D 散点图