gboffi
gboffi

Reputation: 25093

Matplotlib's legend: how to order entries by row first, rather than by column

By default Matplotlib orders the entries in the legend by column, e.g.,

In [48]: from matplotlib.pyplot import subplots, show
    ...: f, ax = subplots(figsize=(6, 2), constrained_layout=True)
    ...: for i in range(1, 10):
    ...:     ax.plot((0,1),(0,1), label=str(i))
    ...: ax.legend(ncol=4, loc=3)
    ...: show()

enter image description here

but I'd like better to have the entries listed by rows, like

1  2  3  4
5  6  7  8
9

I could reorder the handles and labels and invoke ax.legend passing the reordered lists (I'll post that as an answer if requested in comments) but I'd like to know if there is a more direct and straightforward way to instruct Matplotlib to do as I want, so that I don't have to copy and paste the reorder solution in 50% of the Matplotlib scripts I'll write…

Upvotes: 2

Views: 1966

Answers (1)

gboffi
gboffi

Reputation: 25093

You can get the lists of handles to artists and the labels using the Axes method get_legend_handles_labels, then you call legend passing the reordered lists.

To reorder the lists, we observe that

  • legend places in the first column the first elements in the lists, then proceeds with the following columns/items,
  • the columns have variable lengths but all the rows must have the same length (i.e., ncol) except the last one, moreover in each column the increment betweeen items must equal ncol.

That said, it is possible to generate a sublist for each column using slicing, the length of the column is automatically the right one..., and eventually use sum to join the sub lists in the reordered one.

In [59]: from matplotlib.pyplot import subplots, show
    ...: reorder = lambda l, nc: sum((l[i::nc] for i in range(nc)), [])
    ...: 
    ...: f, ax = subplots(figsize=(6, 2), constrained_layout=True)
    ...: for i in range(1, 10):
    ...:     ax.plot((0,1),(0,1), label=str(i))
    ...: h, l = ax.get_legend_handles_labels()
    ...: ax.legend(reorder(h, 4), reorder(l, 4), ncol=4, loc=3)
    ...: show()

enter image description here


A few years later...


Slightly more compact in its usage when calling legend

reorder=lambda hl,nc:(sum((lis[i::nc]for i in range(nc)),[])for lis in hl)
...
h_l = ax.get_legend_handles_labels()
ax.legend(*reorder(h_l, 4), ncol=4)

(but you must remember to unpack the values returned by reorder).

Upvotes: 1

Related Questions