web-dev-qa-db-fra.com

Comment aligner les lignes de la grille pour deux échelles d'axe des y avec Matplotlib?

Je trace deux ensembles de données avec des unités différentes sur l'axe des ordonnées. Existe-t-il un moyen d’aligner les graduations et le quadrillage sur les deux axes des y?

La première image montre ce que je reçois et la deuxième image ce que je voudrais obtenir.

C'est le code que j'utilise pour tracer:

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

Example of unwanted behavior

Example of wanted behavior

26
Artturi Björk

Je ne suis pas sûr que ce soit la meilleure façon de le faire, mais cela se règle avec une seule ligne:

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

J'ai écrit cette fonction qui prend les objets axes Matplotlib ax1, ax2 et floats 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))

Il calcule et définit les ticks de telle sorte qu'il y ait sept ticks. Le tick le plus bas correspond au tick le plus bas actuel et augmente le tick le plus haut de sorte que la séparation entre chaque tick soit un multiple entier de minrexax1 ou de minrexax2.

Pour le rendre plus général, vous pouvez définir le nombre total de ticks que vous voulez en changeant chaque fois que 7 vous voyez par le nombre total de ticks, et changez 6 en nombre total de ticks moins 1.

J'ai mis une requête pull pour incorporer cela dans matplotlib.ticker.LinearLocator:

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

Dans le futur (peut-être Matplotlib 2.0?), Essayez:

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

Cela devrait simplement fonctionner et choisir des repères pratiques pour les deux axes des ordonnées.

8
Scott Howard

Je pourrais le résoudre en désactivant ax.grid(None) dans l’un des axes de la grille:

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

Figure Result

7
arnaldo

ceci a déjà été répondu correctement il y a quelque temps: difficulté à aligner les graduations pour les axes matplotlib twinx

(la réponse donnée ici ne fonctionne pas du tout pour un cas général)

1
raphael

Ce code garantit que les grilles des deux axes sont alignées sans qu'il soit nécessaire de masquer les lignes de la grille. Dans cet exemple, cela vous permet de faire correspondre celle qui a les lignes de grille les plus fines. Cela découle de l’idée de @Leo. J'espère que ça aide!

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

Si vous utilisez des étiquettes d'axes, la solution de Leo peut Les pousser hors du côté , en raison de la précision des nombres dans les graduations.

Donc, en plus de quelque chose comme la solution de Leo (répété ici),

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

vous pouvez utiliser le paramètre autolayout, comme indiqué dans cette réponse ; Par exemple, plus tôt dans votre script, vous pouvez mettre à jour rcParams:

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

Dans quelques cas de test, cela semble produire le résultat attendu, les deux graduations et les étiquettes alignées étant entièrement contenues dans la sortie.

0
Jonathan W.

J'ai eu le même problème, sauf que c'était pour un axe x secondaire. J'ai résolu le problème en réglant mon axe x secondaire sur la limite de mon axe principal. L'exemple ci-dessous permet de ne pas définir la limite du deuxième axe sur le premier: ax2 = ax.twiny()enter image description here

Une fois que je règle la limite du deuxième axe sur le premier ax2.set_xlim(ax.get_xlim()), voici mon résultat: enter image description here

0
Hugo Alain Oliva