set axis limits on individual facets of seaborn facetgrid - python

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'})

Related

seaborn distplot different bar width on each figure

Sorry for giving an image however I think it is the best way to show my problem.
As you can see all of the bin width are different, from my understanding it shows range of rent_hours. I am not sure why different figure have different bin width even though I didn't set any.
My code looks is as follows:
figure, axes = plt.subplots(nrows=4, ncols=3)
figure.set_size_inches(18,14)
plt.subplots_adjust(hspace=0.5)
for ax, age_g in zip(axes.ravel(), age_cat):
group = total_usage_df.loc[(total_usage_df.age_group == age_g) & (total_usage_df.day_of_week <= 4)]
sns.distplot(group.rent_hour, ax=ax, kde=False)
ax.set(title=age_g)
ax.set_xlim([0, 24])
figure.suptitle("Weekday usage pattern", size=25);
additional question:
Seaborn : How to get the count in y axis for distplot using PairGrid for here it says that kde=False makes y-axis count however http://seaborn.pydata.org/generated/seaborn.distplot.html in the doc, it uses kde=False and still seems to show something else. How can I set y-axis to show count?
I've tried
sns.distplot(group.rent_hour, ax=ax, norm_hist=True) and it still seems to give something else rather than count.
sns.distplot(group.rent_hour, ax=ax, kde=False) gives me count however I don't know why it is giving me count.
Answer 1:
From the documentation:
norm_hist : bool, optional
If True, the histogram height shows a density rather than a count.
This is implied if a KDE or fitted density is plotted.
So you need to take into account your bin width as well, i.e. compute the area under the curve and not just the sum of the bin heights.
Answer 2:
# Plotting hist without kde
ax = sns.distplot(your_data, kde=False)
# Creating another Y axis
second_ax = ax.twinx()
#Plotting kde without hist on the second Y axis
sns.distplot(your_data, ax=second_ax, kde=True, hist=False)
#Removing Y ticks from the second axis
second_ax.set_yticks([])

seaborn/matplotlib change number of columns in legend object

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...

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 )

matplotlib: align y-ticks in twinx [duplicate]

I created a matplotlib plot that has 2 y-axes. The y-axes have different scales, but I want the ticks and grid to be aligned. I am pulling the data from excel files, so there is no way to know the max limits beforehand. I have tried the following code.
# creates double-y axis
ax2 = ax1.twinx()
locs = ax1.yaxis.get_ticklocs()
ax2.set_yticks(locs)
The problem now is that the ticks on ax2 do not have labels anymore. Can anyone give me a good way to align ticks with different scales?
Aligning the tick locations of two different scales would mean to give up on the nice automatic tick locator and set the ticks to the same positions on the secondary axes as on the original one.
The idea is to establish a relation between the two axes scales using a function and set the ticks of the second axes at the positions of those of the first.
import matplotlib.pyplot as plt
import matplotlib.ticker
fig, ax = plt.subplots()
# creates double-y axis
ax2 = ax.twinx()
ax.plot(range(5), [1,2,3,4,5])
ax2.plot(range(6), [13,17,14,13,16,12])
ax.grid()
l = ax.get_ylim()
l2 = ax2.get_ylim()
f = lambda x : l2[0]+(x-l[0])/(l[1]-l[0])*(l2[1]-l2[0])
ticks = f(ax.get_yticks())
ax2.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(ticks))
plt.show()
Note that this is a solution for the general case and it might result in totally unreadable labels depeding on the use case. If you happen to have more a priori information on the axes range, better solutions may be possible.
Also see this question for a case where automatic tick locations of the first axes is sacrificed for an easier setting of the secondary axes tick locations.
To anyone who's wondering (and for my future reference), the lambda function f in ImportanceofBeingErnest's answer maps the input left tick to a corresponding right tick through:
RHS tick = Bottom RHS tick + (% of LHS range traversed * RHS range)
Refer to this question on tick formatting to truncate decimal places:
from matplotlib.ticker import FormatStrFormatter
ax2.yaxis.set_major_formatter(FormatStrFormatter('%.2f')) # ax2 is the RHS y-axis

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 )

Categories

Resources