seaborn/matplotlib change number of columns in legend object - python

I've seen Creating multi column legend in python seaborn plot but I think my question is a bit different. In short, I've got a dataframe that I'm plotting in seaborn's lmplot and getting a FacetGrid. Trouble is, there are tons of values for hue so I get a super long, single column legend. Code example below:
ers = sns.lmplot(
data=emorb,
x="Pb",
y="Nd",
row="Ridge Sys",
hue="Seg Name",
scatter=True,
fit_reg=False,
scatter_kws={"alpha":0.7, "edgecolor": "w"},
palette=sns.color_palette("bright", 20),
legend=True
)
ers.set(ylim=(0.5122,0.5134))
I can access the legend object that is created by calling ers._legend and this returns an object with type Legend (basically, a matplotlib object). However, I can't then call to this legend object to change the number of columns, e.g., with:
l = ers._legend
l(ncols=9)
Any suggestions, or am I missing something perhaps more obvious, such as a way to redraw the legend and specify any parameters?
Thanks.

Whoops, figured it out:
The FacetGrid object has an attribute fig, i.e.
g = sns.lmplot()
parent_mpl_figure = g.fig
And so if I set legend=False in sns.lmplot(), I can then specify parent_mpl_figure.legend(labels=[], ncol=9, bbox_to_anchor=(1,1)).
Written cleanly:
g = sns.lmplot(legend = False)
parent_mpl_figure = g.fig
parent_mpl_figure.legend(labels = [], ncol = 9, bbox_to_anchor = (1,1))
Hope this is instructive for someone else / now to figure out how to have each Facet span the full color palette so that different hue groups within each Facet group are easier to distinguish...

Related

Hue and shared x-axis not working Seaborn facet grid

I'm aiming to plot a stacked chart that displays normalised values from a pandas df. Using below, each unique value in Item has it's own row. I then aim to plot a stacked chart containing normalised values from Label, with Num along the x-axis.
However, hue seems to pass a different set of colours for each individual Item. They aren't consistent, for ex, A in Up is blue, while A in Right is green.
I'm also hoping to share the x-axis for Num is consistent for each Item. The values aren't aligned with the respective x-axis.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'Num' : [1,2,1,2,3,2,1,3,2,2,1,2,3,3,1,3],
'Label' : ['A','B','C','B','B','C','C','B','B','A','C','A','B','A','C','A'],
'Item' : ['Up','Left','Up','Left','Down','Right','Up','Down','Right','Down','Right','Up','Up','Right','Down','Left'],
})
g = sns.FacetGrid(df,
row = 'Item',
row_order = ['Up','Right','Down','Left'],
aspect = 2,
height = 4,
sharex = True,
legend_out = True
)
g.map_dataframe(sns.histplot, x = 'Num', hue = 'Label', multiple = 'fill', shrink = 0.8, binwidth = 1)
g.add_legend()
Using FacetGrid directly can be tricky; it is basically doing a groupb-by and for loop over the axes, and it does not track any function-specific state that would be needed to make sure that the answer to questions like "what order should be used for each hue level" is the same in each facet. So you would need to supply that information somehow (i.e. hue_order or passing a palette dictionary). In fact, there is a warning in the documentation to this effect.
But you generally don't need to use FacetGrid directly; you can use one of the figure-level functions, which do all of the bookkeeping for you to make sure that information is aligned across facets. Here you would use displot:
sns.displot(
data=df, x="Num", hue="Label",
row='Item', row_order=['Up','Right','Down','Left'],
multiple="fill", shrink=.8, discrete=True,
aspect=4, height=2,
)
Note that I've made one other change to your code here, which is to use discrete=True instead of binwidth=1, which is what I think you want.

Changing subplot titles in seaborn's FacetGrid [duplicate]

I am trying to create a FacetGrid in Seaborn
My code is currently:
g = sns.FacetGrid(df_reduced, col="ActualExternal", margin_titles=True)
bins = np.linspace(0, 100, 20)
g.map(plt.hist, "ActualDepth", color="steelblue", bins=bins, width=4.5)
This gives my the Figure
Now, instead of "ActualExternal =0.0" and "ActualExternal =1.0" I would like the titles "Internal" and "External"
And, instead of "ActualDepth" I would like the xlabel to say "Percentage Depth"
Finally, I would like to add a ylabel of "Number of Defects".
I've tried Googling and have tried a few things but so far no success. Please can you help me?
Thanks
Although you can iterate through the axes and set the titles individually using matplotlib commands, it is cleaner to use seaborn's built-in tools to control the title. For example:
# Add a column of appropriate labels
df_reduced['measure'] = df_reduced['ActualExternal'].replace({0: 'Internal',
1: 'External'}
g = sns.FacetGrid(df_reduced, col="measure", margin_titles=True)
g.map(plt.hist, "ActualDepth", color="steelblue", bins=bins, width=4.5)
# Adjust title and axis labels directly
g.set_titles("{col_name}") # use this argument literally
g.set_axis_labels(x_var="Percentage Depth", y_var="Number of Defects")
This has the benefit of not needing modification regardless of whether you have 1D or 2D facets.
You can access the axes of a FacetGrid (g = sns.FacetGrid(...)) via g.axes. With that you are free to use any matplotlib method you like to tweak the plot.
Change titles:
axes = g.axes.flatten()
axes[0].set_title("Internal")
axes[1].set_title("External")
Change labels:
axes = g.axes.flatten()
axes[0].set_ylabel("Number of Defects")
for ax in axes:
ax.set_xlabel("Percentage Depth")
Note that I prefer those above the FacetGrid's internal g.set_axis_labels and set_titles methods, because it makes it more obvious which axes is to be labelled.
The easiest way to set multiple titles would be:
titles = ['Internal','External']
for ax,title in zip(g.axes.flatten(),titles):
ax.set_title(title )

How to get color of most recent plotted line in pandas df.plot()

I would like to get the color of the my last plot
ax = df.plot()
df2.plot(ax=ax)
# how to get the color of this last plot,
#the plot is a single timeseries, there is therefore a single color.
I know how to do it in matplotlib.pyplot, for those interested see for instance here but I can't find a way to do it in pandas. Is there something acting like get_color() in pandas?
You cannot do the same with DataFrame.plot because it doesn't return a list of Line2D objects as pyplot.plot does. But ax.get_lines() will return a list of the lines plotted in the axes so you can look at the color of the last plotted line:
ax.get_lines()[-1].get_color()
Notice (don't know if it was implicit in the answer by Goyo) that calls to pandas objects' .plot() precisely return the ax you're looking for, as in:
plt1 = pd.Series(range(2)).plot()
color = plt1.lines[-1].get_color()
pd.Series(range(2, 4)).plot(color=color)
This is not much nicer, but might allow you to avoid importing matplotlib explicitly

Python, Seaborn FacetGrid change titles

I am trying to create a FacetGrid in Seaborn
My code is currently:
g = sns.FacetGrid(df_reduced, col="ActualExternal", margin_titles=True)
bins = np.linspace(0, 100, 20)
g.map(plt.hist, "ActualDepth", color="steelblue", bins=bins, width=4.5)
This gives my the Figure
Now, instead of "ActualExternal =0.0" and "ActualExternal =1.0" I would like the titles "Internal" and "External"
And, instead of "ActualDepth" I would like the xlabel to say "Percentage Depth"
Finally, I would like to add a ylabel of "Number of Defects".
I've tried Googling and have tried a few things but so far no success. Please can you help me?
Thanks
Although you can iterate through the axes and set the titles individually using matplotlib commands, it is cleaner to use seaborn's built-in tools to control the title. For example:
# Add a column of appropriate labels
df_reduced['measure'] = df_reduced['ActualExternal'].replace({0: 'Internal',
1: 'External'}
g = sns.FacetGrid(df_reduced, col="measure", margin_titles=True)
g.map(plt.hist, "ActualDepth", color="steelblue", bins=bins, width=4.5)
# Adjust title and axis labels directly
g.set_titles("{col_name}") # use this argument literally
g.set_axis_labels(x_var="Percentage Depth", y_var="Number of Defects")
This has the benefit of not needing modification regardless of whether you have 1D or 2D facets.
You can access the axes of a FacetGrid (g = sns.FacetGrid(...)) via g.axes. With that you are free to use any matplotlib method you like to tweak the plot.
Change titles:
axes = g.axes.flatten()
axes[0].set_title("Internal")
axes[1].set_title("External")
Change labels:
axes = g.axes.flatten()
axes[0].set_ylabel("Number of Defects")
for ax in axes:
ax.set_xlabel("Percentage Depth")
Note that I prefer those above the FacetGrid's internal g.set_axis_labels and set_titles methods, because it makes it more obvious which axes is to be labelled.
The easiest way to set multiple titles would be:
titles = ['Internal','External']
for ax,title in zip(g.axes.flatten(),titles):
ax.set_title(title )

set axis limits on individual facets of seaborn facetgrid

I'm trying to set the x-axis limits to different values for each facet a Seaborn facetgrid distplot. I understand that I can get access to all the axes within the subplots through g.axes, so I've tried to iterate over them and set the xlim with:
g = sns.FacetGrid(
mapping,
col=options.facetCol,
row=options.facetRow,
col_order=sorted(cols),
hue=options.group,
)
g = g.map(sns.distplot, options.axis)
for i, ax in enumerate(g.axes.flat): # set every-other axis for testing purposes
if i % 2 == 0[enter link description here][1]:
ax.set_xlim(-400, 500)
else:
ax.set_xlim(-200, 200)
However, when I do this, all axes get set to (-200, 200) not just every other facet.
What am I doing wrong?
mwaskom had the solution; posting here for completeness - just had to change the following line to:
g = sns.FacetGrid(
mapping,
col=options.facetCol,
row=options.facetRow,
col_order=sorted(cols),
hue=options.group,
sharex=False, # <- This option solved the problem!
)
As suggested by mwaskom you can simply use FacetGrid's sharex (respectively sharey) to allow plots to have independent axis scales:
share{x,y} : bool, ‘col’, or ‘row’ optional
If true, the facets will share y axes across columns and/or x axes across rows.
For example, with:
sharex=False each plot has its own axis
sharex='col' each column has its own axis
sharex='row' each row has its own axis (even if this one doesn't make too much sense to me)
sns.FacetGrid(data, ..., sharex='col')
If you use FacetGrid indirectly, for example via displot or relplot, you will have to use the facet_kws keyword argument:
sns.displot(data, ..., facet_kws={'sharex': 'col'})

Categories

Resources