Легенда показывает только одну метку при построении с помощью pandas
У меня есть два Pandas DataFrames, которые я надеюсь построить в одной фигуре. Я использую ноутбук IPython.
Мне хотелось бы, чтобы легенда показывала ярлык для обоих DataFrames, но до сих пор мне удалось получить только последний, чтобы показать. Также будут оценены любые предложения относительно того, как писать код более разумным образом. Я новичок во всем этом и на самом деле не понимаю объектно-ориентированное построение.
%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]()
Ответы
Ответ 1
Это действительно немного запутанно. Я думаю, это сводится к тому, как 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 (data) и Matplotlib (plotting), поэтому избегайте создания встроенного графика 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()
Ответ 2
При построении нескольких серий легенда не отображается по умолчанию.
Легкий способ отображать пользовательские легенды - это просто использовать ось из последней построенной серии /dataframes (мой код из 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
Вы можете использовать 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]()