matplotlib: alineación de las tags del eje y en diagtwigs de dispersión astackdos

En la siguiente gráfica tengo dos gráficos de dispersión que tienen una escala numérica diferente, por lo que sus tags del eje Y no están alineadas. ¿Hay alguna manera de forzar la alineación horizontal en las tags del eje 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() 

parcelas de dispersión apiladas

Puedes usar el método 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) 

introduzca la descripción de la imagen aquí

Desde la escritura de esta pregunta, matplotlib ha agregado una función fácil de usar que alinea las tags. La forma correcta de forzar la alineación de las tags es usar la función fig.align_labels() antes de mostrar la figura.

Si necesita un control más preciso, también puede usar las funciones Figure.align_xlabels() o Figure.align_ylabels() .

Aquí hay una versión de trabajo del código publicado en la pregunta. Solo se ha agregado una línea (la segunda a la última línea) para promulgar la solución.

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

Consulte la documentación de Matplotlib sobre tags de alineación para obtener más información.

Como se publicó en el comentario, lo que busca se resuelve utilizando set_label_coords() como se describe aquí . Para tu caso será algo como:

 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) 

Aquí hay una función que escribí para alinear automáticamente las tags, pero no parece funcionar en un script, solo de forma interactiva.

 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) 

Y un ejemplo:

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

figura de ejemplo

Ofrezco una solución al final, pero primero digo qué camino no conduce al éxito.

Revisé este problema recientemente, y pasé bastante tiempo probando varias soluciones, es decir, probando casi todas las combinaciones posibles de transformaciones entre los diferentes sistemas de coordenadas y su relación con el tight_layout() . Experimenté solo con backend_pdf , por lo que no puedo hablar sobre medios interactivos. Pero brevemente, mi conclusión es que no importa cómo intente averiguar las posiciones e intentar transformarlas, en este nivel no es posible alinear las tags de los ejes. Supongo que de alguna manera debería ser posible, por ejemplo, de alguna manera internamente matplotlib puede alinear los ejes de las subplots en sí. Solo con dibujar en el archivo pdf dos veces y hacer algo como abajo entre medias, podría lograr mejores posiciones, pero aún no alineado:

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

Es una pena que no se pueda lograr una funcionalidad tan básica, y no está claro cómo los diferentes espacios de coordenadas se transforman y se vuelven a escalar en los diferentes pasos del trazado. Me gustaría mucho ver una explicación clara de esto, porque la página web matplotlib solo describe la architecture, presenta casos simples, pero no explica situaciones como esta. También me sorprende que los métodos que aceptan o devuelvan coordenadas no digan en su documentación qué tipos de coordenadas son esos. Finalmente encontré muy útil este tutorial .

Solución

Al final, en lugar de meterme con las transformaciones, creé en GridSpec una fila adicional de altura cero y ejes invisibles (lo mismo se puede hacer con una columna de ancho cero para las tags del eje y). Luego agregué tags para estas subplots, estableciendo la alineación verticalalignment en la 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')