¿Cómo puedo alinear las líneas de la cuadrícula para dos escalas del eje y utilizando Matplotlib?

Estoy trazando dos conjuntos de datos con diferentes unidades en el eje y. ¿Hay alguna forma de alinear los ticks y las líneas de cuadrícula en ambos ejes y?

La primera imagen muestra lo que obtengo, y la segunda muestra lo que me gustaría obtener.

Este es el código que estoy usando para trazar:

import seaborn as sns import numpy as np import pandas as pd np.random.seed(0) fig = plt.figure() ax1 = fig.add_subplot(111) ax1.plot(pd.Series(np.random.uniform(0, 1, size=10))) ax2 = ax1.twinx() ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r') 

Ejemplo de comportamiento no deseado.

Ejemplo de comportamiento deseado.

No estoy seguro de si esta es la forma más bonita de hacerlo, pero lo soluciona con una línea:

 import matplotlib.pyplot as plt import seaborn as sns import numpy as np import pandas as pd np.random.seed(0) fig = plt.figure() ax1 = fig.add_subplot(111) ax1.plot(pd.Series(np.random.uniform(0, 1, size=10))) ax2 = ax1.twinx() ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r') # ADD THIS LINE ax2.set_yticks(np.linspace(ax2.get_yticks()[0], ax2.get_yticks()[-1], len(ax1.get_yticks()))) plt.show() 

Escribí esta función que toma los objetos de ejes de Matplotlib ax1, ax2 y flotadores minresax1 minresax2:

 def align_y_axis(ax1, ax2, minresax1, minresax2): """ Sets tick marks of twinx axes to line up with 7 total tick marks ax1 and ax2 are matplotlib axes Spacing between tick marks will be a factor of minresax1 and minresax2""" ax1ylims = ax1.get_ybound() ax2ylims = ax2.get_ybound() ax1factor = minresax1 * 6 ax2factor = minresax2 * 6 ax1.set_yticks(np.linspace(ax1ylims[0], ax1ylims[1]+(ax1factor - (ax1ylims[1]-ax1ylims[0]) % ax1factor) % ax1factor, 7)) ax2.set_yticks(np.linspace(ax2ylims[0], ax2ylims[1]+(ax2factor - (ax2ylims[1]-ax2ylims[0]) % ax2factor) % ax2factor, 7)) 

Calcula y establece las marcas de modo que hay siete marcas. La marca más baja corresponde a la marca más baja actual y aumenta la marca más alta de modo que la separación entre cada marca sea múltiplos enteros de minrexax1 o minrexax2.

Para hacerlo general, puede establecer el número total de tics que desea cambiando cada vez que vea 7 al número total de tics, y cambiar 6 al número total de tics menos 1.

Puse una solicitud de extracción para incorporar algo de esto en matplotlib.ticker.LinearLocator:

https://github.com/matplotlib/matplotlib/issues/6142

En el futuro (¿Matplotlib 2.0 quizás?), Intente:

 import matplotlib.ticker nticks = 11 ax1.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks)) ax2.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks)) 

Eso debería funcionar y elegir marcas convenientes para ambos ejes y.

Podría resolverlo desactivando ax.grid(None) en uno de los ejes de la cuadrícula:

 import matplotlib.pyplot as plt import seaborn as sns import numpy as np import pandas as pd fig = plt.figure() ax1 = fig.add_subplot(111) ax1.plot(pd.Series(np.random.uniform(0, 1, size=10))) ax2 = ax1.twinx() ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r') ax2.grid(None) plt.show() 

Resultado de la figura

Este código garantizará que las cuadrículas de ambos ejes se alineen entre sí, sin tener que ocultar las cuadrículas de ninguno de los conjuntos. En este ejemplo, le permite hacer coincidir lo que tenga las líneas de cuadrícula más finas. Esto se basa en la idea de @Leo. ¡Espero eso ayude!

 import matplotlib.pyplot as plt import seaborn as sns import numpy as np import pandas as pd fig = plt.figure() ax1 = fig.add_subplot(111) ax1.plot(pd.Series(np.random.uniform(0,1,size=10))) ax2 = ax1.twinx() ax2.plot(pd.Series(np.random.uniform(10,20,size=10)),color='r') ax2.grid(None) # Determine which plot has finer grid. Set pointers accordingly l1 = len(ax1.get_yticks()) l2 = len(ax2.get_yticks()) if l1 > l2: a = ax1 b = ax2 l = l1 else: a = ax2 b = ax1 l = l2 # Respace grid of 'b' axis to match 'a' axis b_ticks = np.linspace(b.get_yticks()[0],b.get_yticks()[-1],l) b.set_yticks(b_ticks) plt.show() 

Esto ya se ha respondido correctamente hace un tiempo: problemas para alinear los tics para los ejes matplotlib twinx

(La respuesta que se da aquí no funciona en absoluto para un caso general)

Si está utilizando tags de eje, la solución de Leo puede empujarlas hacia un lado , debido a la precisión de los números en las marcas.

Así que además de algo como la solución de Leo (repetida aquí),

 ax2.set_yticks(np.linspace(ax2.get_yticks()[0],ax2.get_yticks()[-1],len(ax1.get_yticks()))) 

puede utilizar la configuración de autolayout , como se menciona en esta respuesta ; Por ejemplo, anteriormente en su script puede actualizar rcParams :

 from matplotlib import rcParams rcParams.update({'figure.autolayout': True}) 

En algunos casos de prueba, esto parece producir el resultado esperado, con marcas y marcas alineadas completamente contenidas en la salida.

Tuve el mismo problema, excepto que esto era para un eje x secundario. Resolví estableciendo mi eje x secundario igual al límite de mi eje primario. El siguiente ejemplo es sin establecer el límite del segundo eje igual al primero: ax2 = ax.twiny() introduzca la descripción de la imagen aquí

Una vez que establezca el límite del segundo eje igual al primer ax2.set_xlim(ax.get_xlim()) aquí está mi resultado: introduzca la descripción de la imagen aquí