Skip to content

Thoughts on PR #418 about legend placement #459

@gepcel

Description

@gepcel

I like what you have done in PR #418. But there's a little more I'd like to discuss if you don't mind. Please excuse me if I'm mistaken.

I've been exploring the ax/row(s)/col(s)/span arguments of legend. These arguments are useful when someone wants the legend placed based on specific axes rather than the whole figure. However, when using this feature, two questions naturally arise: which axes' elements to include and where to position the legend. This leads to some confusion. I don't have definite answers, but I'd like to highlight this for your consideration.

Take the following code (without the fig.legend part) as an example, and let's assume loc="bottom throughout the discussion.

import ultraplot as pplt
fig, axs = pplt.subplots(ncols=4, nrows=4, share='all', width='10cm')
axs.format(yticklabels=[], xticklabels=[])
cycle = pplt.Cycle("default", N=16)
for n, ax in enumerate(axs):
    ax.text(.5, .5,f'ax:{n}\ncols:{n%4+1}', transform=ax.transAxes, ha='center',va='center')
    ax.scatter(.5, .25, transform=ax.transAxes, color=cycle.get_next()['color'], label=f"ax:{n}")

# Now the fig.legend() part.
  1. fig.legend(ax=axs[4:6], loc='b', span=(1,2)) works as expected:

    1. axs[4:6] → includes elements from axs[4] and axs[5].
    2. span=(1,2) → merges them into a single legend and places it below columns 1 and 2, i.e., below axs[4] and axs[5].
  2. fig.legend(ax=axs[4:6], loc="bottom", span=(3,4)) works, though seems odd but makes sense with complex layout. It includes elements from axs[4] and axs[5] but places the legend below columns 3 and 4, i.e., below axs[6] and axs[7]. What about other rows? Since ax and cols can be disrelevant, then how to put legend under 1st row, i.e., below axs[2] and axs[3]

Currently, ax accepts a zero‑based slice but not an array. [4:7] is valid, while [4,5] is not. Meanwhile, span/cols accept a one‑based tuple but not a slice or array. In the example above, for the second row, cols=(1,3) essentially corresponds to axs[4:7].

If ax controls which elements are included, it should accept a scalar, slice, or list. And if span=(1,3) means “from column 1 to column 3,” that makes sense. But why does axs start at 0 while cols start at 1?

Trying fig.legend(ax=axs[0,3,6], loc='bottom', span=(3,4)) # or axs[[0,3,6]] results in only axs[3] being included, which doesn't seem right.

Sub-issues

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions