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

La première image montre ce que j'obtiens et la deuxième image montre ce que j'aimerais obtenir.

Voici 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

35
Artturi Björk 5 nov. 2014 à 11:18

7 réponses

Meilleure réponse

Je ne suis pas sûr que ce soit la meilleure façon de le faire, mais cela le corrige 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()
28
Peter Mortensen 8 janv. 2018 à 23:14

Ce code garantira que les grilles des deux axes s'alignent les unes sur les autres, sans avoir à masquer le quadrillage des deux ensembles. Dans cet exemple, il vous permet de faire correspondre celui qui a les lignes de grille les plus fines. Cela se fonde sur 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()
2
John 11 juil. 2017 à 23:56

Cela a déjà été répondu correctement il y a un certain temps: difficulté à aligner les graduations pour les axes twinpl de matplotlib

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

1
raphael 12 juil. 2018 à 09:51

J'ai eu le même problème, sauf que c'était pour un axe x secondaire. J'ai résolu en définissant mon axe x secondaire égal à la limite de mon axe principal. L'exemple ci-dessous est sans définir la limite du deuxième axe égale au premier: ax2 = ax.twiny() entrez la description de l'image ici

Une fois que j'ai défini la limite du deuxième axe égale au premier ax2.set_xlim(ax.get_xlim()) voici mon résultat: entrez la description de l'image ici

1
Hugo Alain Oliva 16 févr. 2018 à 17:27

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

10
Peter Mortensen 8 janv. 2018 à 23:15

J'ai écrit cette fonction qui prend les objets d'axes Matplotlib ax1, ax2 et flotte 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 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 élevé de sorte que la séparation entre chaque tick soit des multiples entiers de minrexax1 ou minrexax2.

Pour le rendre général, vous pouvez définir le nombre total de ticks que vous souhaitez en changeant chaque 7 que vous voyez en nombre total de ticks, et changer 6 le nombre total de ticks moins 1.

J'ai mis une demande de tirage pour en incorporer un peu dans matplotlib.ticker.LinearLocator:

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

À l'avenir (Matplotlib 2.0 peut-être?), 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 graduations pratiques pour les deux axes y.

8
Peter Mortensen 8 janv. 2018 à 23:18

Si vous utilisez des étiquettes d'axe, la solution de Leo peut les repousser sur le côté, en raison à la précision des nombres dans les ticks.

Donc, en plus de quelque chose comme la solution de Leo (répétée 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, avec à la fois des graduations alignées et des étiquettes entièrement contenues dans la sortie.

0
Community 23 mai 2017 à 11:55