使用pandas绘图时,图例只显示一个标签

37

我有两个Pandas数据框,希望将它们绘制在同一图中。我正在使用IPython笔记本。

我希望图例显示两个数据框的标签,但到目前为止,我只能显示后一个数据框的标签。如果有任何关于如何更明智地编写代码的建议,我会很感激。我对面向对象绘图一窍不通。

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

图例缺少一个标签

3个回答

58

这确实有点令人困惑。我认为问题在于Matplotlib如何处理次坐标轴。Pandas可能会在某个地方调用ax.twinx(),在第一个坐标轴上叠加一个次坐标轴,但这实际上是一个独立的坐标轴。因此也有单独的线条和标签以及独立的图例。调用plt.legend()仅适用于其中一个坐标轴(活动坐标轴),在您的示例中是第二个坐标轴。

幸运的是,Pandas确实存储了两个坐标轴,因此您可以从它们两个中获取所有线条对象,并将它们传递给.legend()命令。根据您的示例数据,可以像您所做的那样绘制:

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

通过ax(左轴)和ax.right_ax,可以使用两个轴对象,从中获取线条对象。Matplotlib的.get_lines()返回一个列表,因此您可以通过简单的加法将它们合并。

lines = ax.get_lines() + ax.right_ax.get_lines()
线对象有一个标签属性,可用于读取并将标签传递给.legend()命令。
ax.legend(lines, [l.get_label() for l in lines], loc='upper center')

剩下的绘图:

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

enter image description here

编辑:

如果你更加严格地分离Pandas(数据)和Matplotlib(绘图)部分,避免使用Pandas内置的绘图功能(它只是包装了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()

5

当绘制多个数据系列时,默认情况下不显示图例。
显示自定义图例的简单方法是只使用最后一个绘制的数据系列/数据框的轴(来自我的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


7
无法与此问题特别涉及的次要轴一起使用。 - Brad Solomon

3
你可以使用pd.concat合并这两个数据框,然后使用辅助y轴绘制图表:
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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接