web-dev-qa-db-fra.com

La légende ne montre qu'une seule étiquette lorsque vous traitez avec des pandas

J'ai deux Pandas DataFrames que j'espère tracer en un seul chiffre. J'utilise le cahier IPython.

J'aimerais que la légende montre l'étiquette pour les deux DataFrames, mais jusqu'à présent, je n'ai pu obtenir que le dernier. Nous vous serions également reconnaissants de bien vouloir rédiger le code de manière plus judicieuse. Je suis nouveau dans tout cela et je ne comprends pas vraiment le traçage orienté objet.

%pylab inline
import pandas as pd

#creating data

prng = pd.period_range('1/1/2011', '1/1/2012', freq='M')
var=pd.DataFrame(randn(len(prng)),index=prng,columns=['total'])
shares=pd.DataFrame(randn(len(prng)),index=index,columns=['average'])

#plotting

ax=var.total.plot(label='Variance')
ax=shares.average.plot(secondary_y=True,label='Average Age')
ax.left_ax.set_ylabel('Variance of log wages')
ax.right_ax.set_ylabel('Average age')
plt.legend(loc='upper center')
plt.title('Wage Variance and Mean Age')
plt.show()

Legend is missing one of the labels

17
Artturi Björk

C'est en effet un peu déroutant. Je pense que cela se résume à la façon dont Matplotlib gère les axes secondaires. Les pandas appellent probablement ax.twinx() quelque part qui superpose un axe secondaire sur le premier, mais il s’agit en fait d’un axe séparé. Par conséquent également avec des lignes et des étiquettes séparées et une légende séparée. L'appel de plt.legend() s'applique uniquement à l'un des axes (celui qui est actif), qui dans votre exemple correspond au deuxième axe.

Heureusement, les pandas stockent les deux axes. Vous pouvez donc saisir tous les objets de la ligne et les transmettre à la commande .legend(). Compte tenu de vos données d'exemple:

Vous pouvez tracer exactement comme vous l'avez fait:

ax = var.total.plot(label='Variance')
ax = shares.average.plot(secondary_y=True, label='Average Age')

ax.set_ylabel('Variance of log wages')
ax.right_ax.set_ylabel('Average age')

Les deux objets d'axes sont disponibles avec ax (axe gauche) et ax.right_ax. Vous pouvez ainsi récupérer les objets de ligne. La fonction .get_lines() de Matplotlib renvoie une liste afin que vous puissiez les fusionner par simple addition.

lines = ax.get_lines() + ax.right_ax.get_lines()

Les objets de ligne ont une propriété label qui peut être utilisée pour lire et transmettre le label à la commande .legend().

ax.legend(lines, [l.get_label() for l in lines], loc='upper center')

Et le reste du complot:

ax.set_title('Wage Variance and Mean Age')
plt.show()

enter image description here

modifier:

Si vous séparez plus strictement les parties Pandas (données) et Matplotlib (traçage), évitez d'utiliser le traçage Pandas intégré (qui n'englobe que Matplotlib):

fig, ax = plt.subplots()

ax.plot(var.index.to_datetime(), var.total, 'b', label='Variance')
ax.set_ylabel('Variance of log wages')

ax2 = ax.twinx()
ax2.plot(shares.index.to_datetime(), shares.average, 'g' , label='Average Age')
ax2.set_ylabel('Average age')

lines = ax.get_lines() + ax2.get_lines()
ax.legend(lines, [line.get_label() for line in lines], loc='upper center')

ax.set_title('Wage Variance and Mean Age')
plt.show()
32
Rutger Kassies

Lorsque plusieurs séries sont tracées, la légende ne s'affiche pas par défaut.
Le moyen le plus simple d’afficher des légendes personnalisées consiste à utiliser l’axe des dernières séries/images tracées (mon code de IPython Notebook ):

%matplotlib inline  # Embed the plot
import matplotlib.pyplot as plt

...
rates[rates.MovieID <= 25].groupby('MovieID').Rating.count().plot()  # blue
(rates[rates.MovieID <= 25].groupby('MovieID').Rating.median() * 1000).plot()  # green
(rates[rates.MovieID <= 25][rates.RateDelta <= 10].groupby('MovieID').Rating.count() * 2000).plot()  # red
ax = (rates[rates.MovieID <= 25][rates.RateDelta <= 10].groupby('MovieID').Rating.median() * 1000).plot()  # cyan

ax.legend(['Popularity', 'RateMedian', 'FirstPpl', 'FirstRM'])

The plot with custom legends

3
luart

Vous pouvez utiliser pd.concat pour fusionner les deux images, puis le tracé utilise un axe y secondaire:

import numpy as np  # For generating random data.
import pandas as pd

# Creating data.
np.random.seed(0)
prng = pd.period_range('1/1/2011', '1/1/2012', freq='M')
var = pd.DataFrame(np.random.randn(len(prng)), index=prng, columns=['total'])
shares = pd.DataFrame(np.random.randn(len(prng)), index=prng, columns=['average'])

# Plotting.
ax = (
    pd.concat([var, shares], axis=1)
    .rename(columns={
        'total': 'Variance of Low Wages',
        'average': 'Average Age'
    })
    .plot(
        title='Wage Variance and Mean Age',
        secondary_y='Average Age')
)
ax.set_ylabel('Variance of Low Wages')
ax.right_ax.set_ylabel('Average Age', rotation=-90)

 chart

0
Alexander