Using Seaborn, my legend keeps overlapping the data, no matter what dataframe I use, or whether I use pairplots or jointgrids. I know I can work around this by removing Seaborn's legend and making a custom legend, however, that's not the "cleanest" route. How can I get Seaborn to create non-overlapping legends ?
Here some code:
g = sns.pairplot(df, kind="reg", plot_kws={"marker": "+"}, hue="experiment", palette="Set2", x_vars=["alpha [%]", "shelter [%]", "beta [%]"], y_vars=["final [%]"])
plt.show()
(btw I'm on Mac OS, Pycharm, Python 3.6seaborn 0.10.0 and matplotlib 3.3.3)
It seems you can not do it with pairplot. In the docs they say:
This is a high-level interface for PairGrid that is intended to make it easy to draw a few common styles. You should use PairGrid directly if you need more flexibility.
Taking this PairGrid example from the docs, you can pass the loc parameter to the add_legend() method.
g = sns.PairGrid(penguins, hue="species")
g.map_diag(sns.histplot)
g.map_offdiag(sns.scatterplot)
g.add_legend(loc=(0.9,0.2)) # or g.add_legend(loc="upper right");
plt.show()
The arguments you can pass to the loc parameter are listed in the Matplotlib docs.
Related
In Matplotlib, I make dashed grid lines as follows:
fig = pylab.figure()
ax = fig.add_subplot(1,1,1)
ax.yaxis.grid(color='gray', linestyle='dashed')
however, I can't find out how (or even if it is possible) to make the grid lines be drawn behind other graph elements, such as bars. Changing the order of adding the grid versus adding other elements makes no difference.
Is it possible to make it so that the grid lines appear behind everything else?
According to this - http://matplotlib.1069221.n5.nabble.com/axis-elements-and-zorder-td5346.html - you can use Axis.set_axisbelow(True)
(I am currently installing matplotlib for the first time, so have no idea if that's correct - I just found it by googling "matplotlib z order grid" - "z order" is typically used to describe this kind of thing (z being the axis "out of the page"))
To me, it was unclear how to apply andrew cooke's answer, so this is a complete solution based on that:
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed')
If you want to validate the setting for all figures, you may set
plt.rc('axes', axisbelow=True)
or
plt.rcParams['axes.axisbelow'] = True
It works for Matplotlib>=2.0.
I had the same problem and the following worked:
[line.set_zorder(3) for line in ax.lines]
fig.show() # to update
Increase 3to a higher value if it does not work.
You can also set the zorder kwarg in matplotlib.pyplot.grid
plt.grid(which='major', axis='y', zorder=-1.0)
You can try to use one of Seaborn's styles. For instance:
import seaborn as sns
sns.set_style("whitegrid")
Not only the gridlines will get behind but the looks are nicer.
For some (like me) it might be interesting to draw the grid behind only "some" of the other elements. For granular control of the draw order, you can use matplotlib.artist.Artist.set_zorder on the axes directly:
ax.yaxis.grid(color='gray', linestyle='dashed')
ax.set_zorder(3)
This is mentioned in the notes on matplotlib.axes.Axes.grid.
The instructions from this question don't work for Seaborn FacetPlots. Would it be possible to get the method to do the same?
A facetgrid legend is not part of the axes, but part of the facetgrid object. The legend is still a standard matplotlib legend and can be manipulated as such.
plt.setp(g._legend.get_title(), fontsize=20)
Where g is your facetgrid object returned after you call the function making it.
If you're using a newer version of matplotlib there's an easier way to change legend font sizes -
plt.legend(fontsize='x-large', title_fontsize='40')
https://matplotlib.org/api/_as_gen/matplotlib.pyplot.legend.html
Might depend on the version of matplotlib you're using. I'm using 2.2.3 and it has the fontsize parameter but not the title_fontsize.
As in the linked answer you may use setp to set the properties (in this case the fontsize of the legend).
The only difference to the linked question is that you need to do that for each axes of the FacetGrid
g = FacetGrid( ... )
for ax in g.axes.flat:
plt.setp(ax.get_legend().get_texts(), fontsize=22) # for legend text
plt.setp(ax.get_legend().get_title(), fontsize=32) # for legend title
I'm plotting multiple ANOVA studies and I would like my error bar width and their cap thickness to be less than the trend line. When plotting a 3-way ANOVA, with error bars and caps, it can feel crowded. My current plot looks somethings like this:
My function for plotting this is:
sns.catplot(x="dose", y="somethings", hue="position", kind="point",
palette=sns.xkcd_palette(colors), capsize=.15, data=df, aspect=1.5)
I have tried to include keywords such as lw_conf (https://github.com/mwaskom/seaborn/pull/898), but I'm not sure what happened to it's functionality after factorplot was changed to catplot. I have also tried errorbar keywords from matplotlib such as, elinewidth and capthick. Catplot seems to take capsize as a keyword argument but I don't know what other keywords it might take or what happened to lw_conf. Any feedback about what may have happened to that keyword or a new way of accomplishing this task would be appreciated. Thanks!
catplot passes extra kwargs to the underlying plotting function. Since you are using kind="point", the plotting function is sns.pointplot(). The documentation for that function mentions the parameter:
errwidth : float, optional
Thickness of error bar lines (and caps).
Therefore you only need to include errwidth= in your call to catplot:
exercise = sns.load_dataset("exercise")
sns.catplot(x="time", y="pulse", hue="kind", kind="point",
capsize=.15, data=exercise, aspect=1.5, errwidth=0.5)
I am trying to plot data to a figure and respective axis in matplotlib and as new work comes up, recall the figure with the additional plot on the axis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
x=np.arange(0,20)
y=2*x
fig,ax=plt.subplots()
ax.scatter(x,x)
ax.scatter(x,y)
fig
Which works fine with matplotlib, if I however use seaborn's regplot:
fig2,ax2=plt.subplots()
sns.regplot(x,x,ax=ax2,fit_reg=False)
sns.regplot(x,y,ax=ax2,fit_reg=False)
fig2
fig2 generates the figure that I want but the regplot command generates an empty figure. Is there a way to suppress the regplot's empty output or have it display the updated ax2 without recalling fig2?
It seems you are using the jupyter notebook with the inline backend. In some circumstances regplot triggers the creation of a new figure even if the artists are being added to the previous one and this messes up the output. I don't know why this happens but I found a workaround that might help you, using plt.ioff to temporarily disable automatic display of figures.
plt.ioff()
fig, ax = plt.subplots()
sns.regplot(x, x, ax=ax)
fig
sns.regplot(x, 2 * x, ax=ax)
fig
You have to call plt.ioff before creating the figure for this to work. After that you have to explicitly display the figure. Then you can call plt.ion to restore the default behaviour.
regplot does not generate an empty figure. According to the documentation:
Understanding the difference between regplot() and lmplot() can be a
bit tricky. In fact, they are closely related, as lmplot() uses
regplot() internally and takes most of its parameters. However,
regplot() is an axes-level function, so it draws directly onto an axes
(either the currently active axes or the one provided by the ax
parameter), while lmplot() is a figure-level function and creates its
own figure, which is managed through a FacetGrid.
When I do the following:
fig2,ax2 = plt.subplots()
same_fig2 = sns.regplot(x,x,ax=ax2,fit_reg=False)
same_fig2.figure is fig2
>>> True
Using Pandas to plot in I-Python Notebook, I have several plots and because Matplotlib decides the Y axis it is setting them differently and we need to compare that data using the same range.
I have tried several variants on: (I assume I'll need to apply the limits to each plot.. but since I can't get one working... From the Matplotlib doc it seems that I need to set ylim, but can't figure the syntax to do so.
df2250.plot(); plt.ylim((100000,500000)) <<<< if I insert the ; I get int not callable and if I leave it out I get invalid syntax. anyhow, neither is right...
df2260.plot()
df5.plot()
I'm guessing this was a feature added after this answer was accepted in 2013; DataFrame.plot() now exposes a ylim parameter that sets the y axis limits:
df.plot(ylim=(0,200))
See pandas documentation for details.
Pandas plot() returns the axes, you can use it to set the ylim on it.
ax1 = df2250.plot()
ax2 = df2260.plot()
ax3 = df5.plot()
ax1.set_ylim(100000,500000)
ax2.set_ylim(100000,500000)
etc...
You can also pass an axes to Pandas plot, so plotting it in the same axes can be done like:
ax1 = df2250.plot()
df2260.plot(ax=ax1)
etc...
If you want a lot of different plots, defining the axes beforehand and within one figure might be the solution that gives you the most control:
fig, axs = plt.subplots(1,3,figsize=(10,4), subplot_kw={'ylim': (100000,500000)})
df2260.plot(ax=axs[0])
df2260.plot(ax=axs[1])
etc...