Reputation: 1395
I use legend(loc='best')
a lot. I noticed that it seems to default to the upper right corner, if a lot of space is available. However, I would like loc='best'
to default to the upper left corner. (How) is it possible to change that behavior?
Upvotes: 4
Views: 465
Reputation: 10650
You might be able to redefine the method in matplotlib which finds the best location,
It's in here.
As @BrenBarn comments below, and mentions in his answer, the magic happens in _get_anchored_bbox
, on line 875.
Add this:
from matplotlib.legend import Legend
def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer):
assert loc in range(1, 11) # called only internally
BEST, UL, UR, LL, LR, R, CL, CR, LC, UC, C = list(xrange(11))
anchor_coefs = {UR: "NE", UL: "NW", LL: "SW", LR: "SE", R: "E",
CL: "W", CR: "E", LC: "S", UC: "N", C: "C"}
c = anchor_coefs[loc]
fontsize = renderer.points_to_pixels(self._fontsize)
container = parentbbox.padded(-(self.borderaxespad) * fontsize)
anchored_box = bbox.anchored(c, container=container)
return anchored_box.x0, anchored_box.y0
Legend._get_anchored_bbox = _get_anchored_bbox
To the top of your code, and it changes the behaviour. I've removed the comments and changed the spacing a little to make it take up less space. Alternatively you could just dump it in another file, call it legendLocationHack.py
and call it whenever you want the behaviour.
Upvotes: 3
Reputation: 251568
You could monkeypatch matplotlib.legend.Legend._get_anchored_bbox
. This function contains this code:
BEST, UR, UL, LL, LR, R, CL, CR, LC, UC, C = list(xrange(11))
anchor_coefs = {UR: "NE",
UL: "NW",
LL: "SW",
LR: "SE",
R: "E",
CL: "W",
CR: "E",
LC: "S",
UC: "N",
C: "C"}
This maps the sequential "position codes" to the spatial locations. The badness-calculating code in _find_best_position
breaks badness ties by taking the earliest position in the sequence, so upper-right will win unless another position is actually better. I think you can safely swap the codes by writing a new version of the function changing the first line to:
BEST, UL, UR, LL, LR, R, CL, CR, LC, UC, C = list(xrange(11))
Note that I switched UL and UR. When I try this on a simple V-shaped graph, it puts the legend in the upper right before the change, but upper left after the change.
To monkeypatch in the change, you should redefine your own version of _get_anchored_bbox
and then do matplotlib.legend.Legend._get_anchored_bbox = my_get_anchored_bbox
.
Any such monkeypatched solution should be treated as fragile. It may break if something changes in a later matplotlib version, since it involves overriding internal functions that you're not supposed to mess with. It might be a better idea to ask on the matplotlib mailing list, or raise an issue in the bug tracker, asking for a configurable paramater to set the tie-breaking order.
Upvotes: 3