web-dev-qa-db-ja.com

matplotlib:積み上げ散布図でのy軸ラベルの整列

下のプロットには、数値スケールが異なる2つの散布図があるため、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()

stacked scatter plots

24
dimka

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)

enter image description here

26
Greg Whittier

この質問を書いてから、matplotlibはラベルを揃える使いやすい関数を追加しました。ラベルの配置を強制する正しい方法は、図を表示する前に関数fig.align_labels()を使用することです。

さらに細かい制御が必要な場合は、関数Figure.align_xlabels()またはFigure.align_ylabels()を使用することもできます。

質問に投稿されたコードの実際のバージョンは次のとおりです。ソリューションを実行するために1行だけ追加されました(2番目から最後の行)。

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()

詳細は ラベルの整列に関するMatplotlibのドキュメント を参照してください。

9
Steven Noyce

コメントに投稿されているように、あなたが探しているものはset_label_coords()ここで説明 として使用して解決されます。あなたの場合、それは次のようなものになります:

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)
3

これは、ラベルを自動的に配置するために作成した関数ですが、スクリプトでは機能せず、対話的にのみ機能します。

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')

example figure

1
Samuel Powell

最後に解決策を提供しますが、最初にどちらの方法が成功につながらないかを伝えます。

私は最近この問題を再検討し、さまざまな解決策を試すことにかなりの時間を費やしました。つまり、さまざまな座標系間の変換のほぼすべての可能な組み合わせ、およびtight_layout()との関係を試しました。 backend_pdf、インタラクティブメディアについては説明できません。しかし簡単に言うと、私の結論は、どのように位置を見つけて変換しようとしても、このレベルでは軸ラベルを揃えることはできないということです。私はどういうわけかそれが可能であるべきだと思います、例えば、どういうわけか内部的にmatplotlibはサブプロット自体の軸を整列させることができます。 PDFファイルに2回描画し、その間に以下のようなことを行った場合にのみ、より良い位置を達成できましたが、まだ整列されていません。

# 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 はアーキテクチャの概要を説明するだけで、簡単なケースを提示しますが、このような状況については説明しません。また、座標を受け入れるまたは返すメソッドは、それらのdocstring内でどのタイプの座標がそれらであるかを通知しないことにも驚いています。最後に、私は非常に便利な このチュートリアル を見つけました。

ソリューション

最後に、変換をいじるのではなく、GridSpecに高さ0と非表示の軸の追加行を作成しました(y軸ラベルの幅0の列でも同じことができます)。次に、これらのサブプロットのラベルを追加し、verticalalignmenttopに設定しました。

# 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')
0
deeenes