How to dynamically re-order items in matplotlib legend?

Viewed 3340

I am trying to create a simple bump chart where the legend order will be determined by the ranking of the most recent observations. I have a pandas DataFrame "y" with rankings across rows that looks like this.

In[231]: y
Out[231]: 
Currency    AUD  CAD   CHF  EUR  GBP  JPY  NOK  NZD  SEK   USD
2017-09-08  5.0  1.0   8.0  3.0  9.0  7.0  4.0  6.0  2.0  10.0
2017-09-22  8.0  2.0  10.0  1.0  5.0  9.0  4.0  6.0  3.0   7.0
2017-10-06  4.0  5.0  10.0  2.0  7.0  9.0  3.0  8.0  1.0   6.0
2017-10-20  3.0  4.0  10.0  1.0  7.0  5.0  6.0  9.0  2.0   8.0
2017-11-03  5.0  3.0  10.0  1.0  9.0  7.0  2.0  8.0  4.0   6.0
2017-11-17  5.0  4.0  10.0  1.0  9.0  7.0  3.0  8.0  2.0   6.0

The code I have used so far:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

fig = plt.figure(figsize=(8.5,8.5))
ax = fig.add_subplot(111)

l1,l2,l3,l4,l5,l6,l7,l8,l9,l10 = ax.plot_date(y.index.to_pydatetime(), y, '*-') 
ax.xaxis.set_major_locator(mdates.WeekdayLocator(byweekday=(4),interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
ax.yaxis.set_ticks(np.arange(1, 11, 1))
ax.set_title('Sample Chart', fontsize=20)
plt.gca().invert_yaxis()
fig.legend([l1,l2,l3,l4,l5,l6,l7,l8,l9,l10], ['AUD','CAD','CHF','EUR','GBP','JPY','NOK','NZD','SEK','USD'], 'right', fontsize=16, frameon=False, labelspacing=1.4)
plt.tight_layout(pad=10) 

Which generates an output of fig.png

My question is how to reorder the legend based on the last observation from '2017-11-17'? i.e. I want EUR, SEK, NOK, etc... I do not want a static alphabetical ordering. Essentially in fig.legend I need [l4,l9,l7,...,l3], although this list will reorder as rows are added to 'y'. I can easily generate a list with the proper ordering the ordering but fig.legend wont accept a list where the elements are in quotations, ie ['l4','l9','l7',...,'l3'].

I may be approaching this completely wrong so any help would be great.

1 Answers

You will need to order the list of lines to be used as legend handles as well as the list of labels by the result of the sorting of the last (or any other) row of the dataframe.

lines = ax.plot_date(...) 
lastrow = y.iloc[-1,:].values 
order = np.argsort(lastrow)
fig.legend(np.array(lines)[order], y.columns[order])

Complete example:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

fig, ax = plt.subplots(figsize=(6.4,3))

lines = ax.plot_date(y.index.to_pydatetime(), y, '*-') 
ax.xaxis.set_major_locator(mdates.WeekdayLocator(byweekday=(4),interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
ax.yaxis.set_ticks(np.arange(1, 11, 1))

plt.gca().invert_yaxis()

lastrow = y.iloc[-1,:].values 
order = np.argsort(lastrow)
fig.legend(np.array(lines)[order], y.columns[order], loc='right', frameon=False)
plt.subplots_adjust(right=0.85)
plt.show()

enter image description here

Related