I'm using factorplot(kind="bar").
How do I scale the y-axis, for example with log-scale?
I tried tinkering with the plots' axes, but that always messed up the bar plot in one way or another, so please try your solution first to make sure it really works.
Considering your question mentions barplot I thought I would add in a solution for that type of plot also as it differs from the factorplot in #Jules solution.
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="whitegrid")
xs = ["First", "First", "Second", "Second", "Third", "Third"]
hue = ["Female", "Male"] * 3
ys = [1988, 301, 860, 77, 13, 1]
g = sns.barplot(x=xs, y=ys, hue=hue)
g.set_yscale("log")
_ = g.set(xlabel="Class", ylabel="Survived")
And if you want to label the y-axis with non-logarithmic labels you can do the following.
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="whitegrid")
xs = ["First", "First", "Second", "Second", "Third", "Third"]
hue = ["Female", "Male"] * 3
ys = [1988, 301, 860, 77, 13, 1]
g = sns.barplot(x=xs, y=ys, hue=hue)
g.set_yscale("log")
# the non-logarithmic labels you want
ticks = [1, 10, 100, 1000]
g.set_yticks(ticks)
g.set_yticklabels(ticks)
_ = g.set(xlabel="Class", ylabel="Survived")
Note that seaborn.factorplot was renamed to seaborn.catplot
import seaborn as sns
import matplotlib.pyplot as plt
titanic = sns.load_dataset("titanic")
g = sns.catplot(x="class", y="survived", hue="sex",
data=titanic, kind="bar",
height=5, palette="muted", legend=False, log=True)
plt.show()
You can use Matplotlib commands after calling factorplot.
For example:
g = sns.factorplot(x="class", y="survived", hue="sex",
data=titanic, kind="bar",
height=5, palette="muted", legend=False)
g.fig.get_axes()[0].set_yscale('log')
plt.show()
If you are facing the problem of vanishing bars upon setting log-scale using the previous solutions, try adding log=True to the seaborn function call instead. (I'm lacking reputation to comment on the other answers).
Using sns.factorplot:
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid")
titanic = sns.load_dataset("titanic")
g = sns.factorplot(x="class", y="survived", hue="sex", kind='bar',
data=titanic, palette="muted", log=True)
g.ax.set_ylim(0.05, 1)
Using sns.barplot:
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid")
titanic = sns.load_dataset("titanic")
g = sns.barplot(x="class", y="survived", hue="sex",
data=titanic, palette="muted", log=True)
g.set_ylim(0.05, 1)
Seaborn's catplot does not have anymore the log parameter.
For those looking for an updated answer, here's the quickest fix I've used: you have to use matplotlib's built-in support by accessing the axes object.
g = sns.catplot(data=df, <YOUR PARAMETERS>)
for ax in g.fig.axes:
ax.set_yscale('log')
Related
I'm using factorplot(kind="bar") with seaborn.
The plot is fine except the legend is misplaced: too much to the right, text goes out of the plot's shaded area.
How do I make seaborn place the legend somewhere else, such as in top-left instead of middle-right?
Building on #user308827's answer: you can use legend=False in factorplot and specify the legend through matplotlib:
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid")
titanic = sns.load_dataset("titanic")
g = sns.factorplot("class", "survived", "sex",
data=titanic, kind="bar",
size=6, palette="muted",
legend=False)
g.despine(left=True)
plt.legend(loc='upper left')
g.set_ylabels("survival probability")
plt acts on the current axes. To get axes from a FacetGrid use fig.
g.fig.get_axes()[0].legend(loc='lower left')
For seaborn >= 0.11.2 use seaborn.move_legend, which applies to Axes and Figure level plots, and it accepts kwargs, like title
See matplotlib.axes.Axes.legend and How to put the legend out of the plot for parameters and their usage.
The original question asked about sns.factorplot, which has been renamed to seaborn.catplot, a figure-level plot.
For g = sns.jointplot or g = sns.JointGrid, the legend is in g.ax_joint, not g.
sns.move_legend(g.ax_joint)
See How to move or remove the legend from a seaborn JointGrid or jointplot.
Tested in python 3.10, pandas 1.5.0, matplotlib 3.5.2, seaborn 0.12.0
There isn't a current solution to relocate legends using the new seaborn.object interface, which debuted in seaborn 0.12.0.
import matplotlib.pyplot as plt
import seaborn as sns
# load the data
penguins = sns.load_dataset('penguins', cache=False)
Figure Level Plot
g = sns.displot(penguins, x="bill_length_mm", hue="species", col="island", col_wrap=2, height=3)
sns.move_legend(g, "upper left", bbox_to_anchor=(.55, .45), title='Species')
plt.show()
Axes Level Plot
ax = sns.histplot(penguins, x="bill_length_mm", hue="species")
sns.move_legend(ax, "lower center", bbox_to_anchor=(.5, 1), ncol=3, title=None, frameon=False)
plt.show()
Check out the docs here: https://matplotlib.org/users/legend_guide.html#legend-location
adding this simply worked to bring legend out of the plot:
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
Modifying the example here:
You can use legend_out = False
import seaborn as sns
sns.set(style="whitegrid")
titanic = sns.load_dataset("titanic")
g = sns.factorplot("class", "survived", "sex",
data=titanic, kind="bar",
size=6, palette="muted",
legend_out=False)
g.despine(left=True)
g.set_ylabels("survival probability")
This is how I was able to move the legend to a particular place inside the plot and change the aspect and size of the plot:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
matplotlib.style.use('ggplot')
import seaborn as sns
sns.set(style="ticks")
figure_name = 'rater_violinplot.png'
figure_output_path = output_path + figure_name
viol_plot = sns.factorplot(x="Rater",
y="Confidence",
hue="Event Type",
data=combo_df,
palette="colorblind",
kind='violin',
size = 10,
aspect = 1.5,
legend=False)
viol_plot.ax.legend(loc=2)
viol_plot.fig.savefig(figure_output_path)
This worked for me to change the size and aspect of the plot as well as move the legend outside the plot area.
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
matplotlib.style.use('ggplot')
import seaborn as sns
sns.set(style="ticks")
figure_name = 'rater_violinplot.png'
figure_output_path = output_path + figure_name
viol_plot = sns.factorplot(x="Rater",
y="Confidence",
hue="Event Type",
data=combo_df,
palette="colorblind",
kind='violin',
size = 10,
aspect = 1.5,
legend_out=True)
viol_plot.fig.savefig(figure_output_path)
I figured this out from mwaskom's answer here and Fernando Hernandez's answer here.
it seems you can directly call:
g = sns.factorplot("class", "survived", "sex",
data=titanic, kind="bar",
size=6, palette="muted",
legend_out=False)
g._legend.set_bbox_to_anchor((.7, 1.1))
If you wish to customize your legend, just use the add_legend method. It takes the same parameters as matplotlib plt.legend.
import seaborn as sns
sns.set(style="whitegrid")
titanic = sns.load_dataset("titanic")
g = sns.factorplot("class", "survived", "sex",
data=titanic, kind="bar",
size=6, palette="muted",
legend_out=False)
g.despine(left=True)
g.set_ylabels("survival probability")
g.add_legend(bbox_to_anchor=(1.05, 0), loc=2, borderaxespad=0.)
Using object oriented API:
fig,ax = plt.subplots(1,1)
sns.someplot(...,ax=ax)
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels,loc="upper left")
source: https://matplotlib.org/stable/tutorials/intermediate/legend_guide.html
I would like to create a plot where dots are overlaid depending on whether or not they are within the 1st-3rd quartiles in seaborn. What function to use?
Something similar to the figure:
The following code creates a Seaborn swarmplot and then recolors the dots depending on their quartile. Looping through the collections created by the swarmplot, the y-data are retrieved. np.percentile calculates the borders of the quartiles and np.digitize calculates the corresponding quartiles. These quartiles can be used to define the color.
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from matplotlib.colors import ListedColormap
sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
# cmap = plt.get_cmap('tab10')
cmap = ListedColormap(['gold', 'crimson', 'teal', 'orange'])
ax = sns.swarmplot(x="day", y="total_bill", data=tips)
for col in ax.collections:
y = col.get_offsets()[:,1]
perc = np.percentile(y, [25, 50, 75])
col.set_cmap(cmap)
col.set_array(np.digitize(y, perc))
plt.show()
The same approach can be used for a stripplot (optionally without jitter) to create a plot similar to the one in the question.
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from matplotlib.colors import ListedColormap
sns.set(style="whitegrid")
N = 200
x = np.repeat(list('abcdefg'), N)
y = np.random.normal(np.repeat(np.random.uniform(11, 15, 7), N), 1)
cmap = ListedColormap(['grey', 'turquoise', 'grey'])
ax = sns.stripplot(x=x, y=y, jitter=False, alpha=0.2)
for col in ax.collections:
y = col.get_offsets()[:, 1]
perc = np.percentile(y, [25, 75])
col.set_cmap(cmap)
col.set_array(np.digitize(y, perc))
plt.show()
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import seaborn as sns
import pandas as pd
sns.set(style="darkgrid")
fig, ax = plt.subplots(figsize=(8, 5))
palette = sns.color_palette("bright", 6)
g = sns.scatterplot(ax=ax, x="Area", y="Rent/Sqft", hue="Region", marker='o', data=df, s=100, palette= palette)
g.legend(bbox_to_anchor=(1, 1), ncol=1)
g.set(xlim = (50000,250000))
How can I can change the axis format from a number to custom format? For example, 125000 to 125.00K
IIUC you can format the xticks and set these:
In[60]:
#generate some psuedo data
df = pd.DataFrame({'num':[50000, 75000, 100000, 125000], 'Rent/Sqft':np.random.randn(4), 'Region':list('abcd')})
df
Out[60]:
num Rent/Sqft Region
0 50000 0.109196 a
1 75000 0.566553 b
2 100000 -0.274064 c
3 125000 -0.636492 d
In[61]:
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import seaborn as sns
import pandas as pd
sns.set(style="darkgrid")
fig, ax = plt.subplots(figsize=(8, 5))
palette = sns.color_palette("bright", 4)
g = sns.scatterplot(ax=ax, x="num", y="Rent/Sqft", hue="Region", marker='o', data=df, s=100, palette= palette)
g.legend(bbox_to_anchor=(1, 1), ncol=1)
g.set(xlim = (50000,250000))
xlabels = ['{:,.2f}'.format(x) + 'K' for x in g.get_xticks()/1000]
g.set_xticklabels(xlabels)
Out[61]:
The key bit here is this line:
xlabels = ['{:,.2f}'.format(x) + 'K' for x in g.get_xticks()/1000]
g.set_xticklabels(xlabels)
So this divides all the ticks by 1000 and then formats them and sets the xtick labels
UPDATE
Thanks to #ScottBoston who has suggested a better method:
ax.xaxis.set_major_formatter(ticker.FuncFormatter(lambda x, pos: '{:,.2f}'.format(x/1000) + 'K'))
see the docs
The canonical way of formatting the tick labels in the standard units is to use an EngFormatter. There is also an example in the matplotlib docs.
Also see Tick locating and formatting
Here it might look as follows.
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import seaborn as sns
import pandas as pd
df = pd.DataFrame({"xaxs" : np.random.randint(50000,250000, size=20),
"yaxs" : np.random.randint(7,15, size=20),
"col" : np.random.choice(list("ABC"), size=20)})
fig, ax = plt.subplots(figsize=(8, 5))
palette = sns.color_palette("bright", 6)
sns.scatterplot(ax=ax, x="xaxs", y="yaxs", hue="col", data=df,
marker='o', s=100, palette="magma")
ax.legend(bbox_to_anchor=(1, 1), ncol=1)
ax.set(xlim = (50000,250000))
ax.xaxis.set_major_formatter(ticker.EngFormatter())
plt.show()
Using Seaborn without importing matplotlib:
import seaborn as sns
sns.set()
chart = sns.relplot(x="x_val", y="y_val", kind="line", data=my_data)
ticks = chart.axes[0][0].get_xticks()
xlabels = ['$' + '{:,.0f}'.format(x) for x in ticks]
chart.set_xticklabels(xlabels)
chart.fig
Thank you to EdChum's answer above for getting me 90% there.
Here's how I'm solving this: (similar to ScottBoston)
from matplotlib.ticker import FuncFormatter
f = lambda x, pos: f'{x/10**3:,.0f}K'
ax.xaxis.set_major_formatter(FuncFormatter(f))
We could used the APIs: ax.get_xticklabels() , get_text() and ax.set_xticklabels do it.
e.g,
xlabels = ['{:.2f}k'.format(float(x.get_text().replace('−', '-')))/1000 for x in g.get_xticklabels()]
g.set_xticklabels(xlabels)
In the figure the y-axis labels are in decimals from (0 to 1) i.e (0.1, 0.2, 0.4 etc). How can I convert this into a % format (10%, 20%, 40% etc). Just 10, 20, 40 also will do.
Thanks, John
g = sns.catplot(x="who", y="survived", col="class",
... data=titanic, saturation=.5,
... kind="bar", ci=None, aspect=.6)
You may use a PercentFormatter on the axes of the grid.
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter
titanic = sns.load_dataset("titanic")
g = sns.catplot(x="who", y="survived", col="class",
data=titanic, saturation=.5,
kind="bar", ci=None, aspect=.6)
for ax in g.axes.flat:
ax.yaxis.set_major_formatter(PercentFormatter(1))
plt.show()
Let's say I want to make a bar plot where the hue of the bars represents some continuous quantity. e.g.
import seaborn as sns
titanic = sns.load_dataset("titanic")
g = titanic.groupby('pclass')
survival_rates = g['survived'].mean()
n = g.size()
ax = sns.barplot(x=n.index, y=n,
hue=survival_rates, palette='Reds',
dodge=False,
)
ax.set_ylabel('n passengers')
The legend here is kind of silly, and gets even worse the more bars I plot. What would make most sense is a colorbar (such as are used when calling sns.heatmap). Is there a way to make seaborn do this?
The other answer is a bit hacky. So a more stringent solution, without producing plots that are deleted afterwards, would involve the manual creation of a ScalarMappable as input for the colorbar.
import matplotlib.pyplot as plt
import seaborn as sns
titanic = sns.load_dataset("titanic")
g = titanic.groupby('pclass')
survival_rates = g['survived'].mean()
n = g.size()
norm = plt.Normalize(survival_rates.min(), survival_rates.max())
sm = plt.cm.ScalarMappable(cmap="Reds", norm=norm)
sm.set_array([])
ax = sns.barplot(x=n.index, y=n, hue=survival_rates, palette='Reds',
dodge=False)
ax.set_ylabel('n passengers')
ax.get_legend().remove()
ax.figure.colorbar(sm)
plt.show()
You can try this:
import matplotlib.pyplot as plt
import seaborn as sns
titanic = sns.load_dataset("titanic")
g = titanic.groupby('pclass')
survival_rates = g['survived'].mean()
n = g.size()
plot = plt.scatter(n.index, n, c=survival_rates, cmap='Reds')
plt.clf()
plt.colorbar(plot)
ax = sns.barplot(x=n.index, y=n, hue=survival_rates, palette='Reds', dodge=False)
ax.set_ylabel('n passengers')
ax.legend_.remove()
Output: