Matplotlib: выравнивание меток оси Y в многоуровневых диаграммах
На графике ниже у меня есть две диаграммы рассеяния, которые имеют разную шкалу чисел, поэтому их метки оси 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]()
Ответы
Ответ 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)
![enter image description here]()
Ответ 2
Как указано в комментарии, то, что вы ищете, решается с помощью 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
Вот функция, которую я написал для автоматической выравнивания меток, но она не работает в script, только в интерактивном режиме.
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')
![примерная цифра]()
Ответ 4
Я предоставляю решение в конце, но сначала я расскажу, какой путь не приводит к успеху.
Я недавно просмотрел этот вопрос и потратил довольно много времени на попытки различных решений, т.е. попытался почти все возможные комбинации преобразований между различными системами координат и их связь с 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 описывает только архитектуру, представляет простые случаи, но не объясняет ситуации как это. Также я удивляюсь, что методы, принимающие или возвращающие координаты, не указывают в своей докштрине, какие типы координат являются таковыми. Наконец, я нашел очень полезным этот учебник.
Решение
В конце, вместо того, чтобы возиться с преобразованиями, я создал в 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')