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

сложенные диаграммы рассеяния

    Вы можете использовать метод 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) 

    введите описание изображения здесь

    Как указано в комментарии, то, что вы ищете, решается с помощью 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) 

    Вот функция, которую я написал для автоматического выравнивания меток, но она не работает в скрипте, только в интерактивном режиме.

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

    примерный рисунок

    Я предоставляю решение в конце, но сначала я расскажу, какой путь не приводит к успеху.

    Я недавно просмотрел этот вопрос и потратил довольно много времени на то, чтобы попробовать различные решения, т. tight_layout() Попробовать практически все возможные комбинации преобразований между различными системами координат и их связь с 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')