pythonmatplotlibseabornseaborn-objects

How can I use .on(fig) without distorting the legend position in seaborn.objects?


I am creating a plot in seaborn.objects. This plot has a legend, and I would also like to change its size.

This can be done using the .theme() method, which affects the matplotlib rcParams:

import matplotlib.pyplot as plt
import seaborn.objects as so
import pandas as pd

dat = pd.DataFrame({'group':['a','a','b','b'],
                    'x': [1, 2, 1, 2],
                    'y': [4, 3, 2, 1]})

# Choosing a very distorted figure size here so you can see when it works
(so.Plot(dat, x = 'x', y = 'y', color = 'group')
 .add(so.Line())
 .theme({'figure.figsize': (8,2)}))

Line graph with legend and (8,2) figure size

However, in order to solve the problem outlined in this post, I need to create a matplotlib figure object and then graph .on() that. When I do this, the 'figure.figsize' setting in .theme() is ignored (some other .theme() settings do still work, but not this one or a couple others I've tried). Also if you look closely you can see the right edge of the legend being pushed off the edge of the image.

(Note also that the 'legend.loc' rcParam is ignored with or without .on(fig): seaborn.objects has its own legend placement system I think.)

fig = plt.figure()

# Choosing a very distorted figure size here so you can see when it works
(so.Plot(dat, x = 'x', y = 'y', color = 'group')
 .on(fig)
 .add(so.Line())
 .theme({'figure.figsize': (8,2)}))

Line graph with legend and a figure size that is not (8,2)

I can, however, now set figsize in the plt.figure() function. But when I do this, the legend positioning is thrown much further out of whack and is largely cut off.

fig = plt.figure(figsize = (8,2))

# Choosing a very distorted figure size here so you can see when it works
(so.Plot(dat, x = 'x', y = 'y', color = 'group')
 .on(fig)
 .add(so.Line()))

Line graph with misplaced legend and (8,2) figure size

How can I include both .on(fig) with a legend without pushing the legend away? As pointed out in this other question standard tools designed for legend movement in regular matplotlib/seaborn don't work in the same way for seaborn.objects. Although to be clear, my question isn't really about how to move the legend (while that would be one way to fix this problem) - seaborn.objects already knows how to properly place the legend for a resized figure if it's through .theme(), I ideally just want that to work through plt.figure() too.

(Edit: immediately after posting it occurred to me to try changing the figsize using the rcParams but from within matplotlib but this doesn't matter: import matplotlib as mpl; mpl.rcParams['figure.figsize'] = (8,2) produces the same result as the fig = plt.figure(figsize = (8,2)) attempt)


Solution

  • I'm not completely sure what's happening here but it looks like it can be fixed by using layout="tight" when you create the figure:

    fig = plt.figure(figsize=(8, 2), layout="tight")
    
    (so.Plot(dat, x = 'x', y = 'y', color = 'group')
     .on(fig)
     .add(so.Line())
    )
    

    enter image description here