Using the 'tips' dataset as a toy model, I generate the following plot:
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset("tips")
cmap = sns.cubehelix_palette(dark=.3, light=.8, as_cmap=True)
g = sns.scatterplot(x="total_bill", y="sex", hue="smoker", size = 'tip',sizes=(320, 600), data=tips)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0., fontsize=13)
plt.show(g)
This image is exactly what I need. However, I want to remove the size = 'tip' from the legend and only keep the smoker. Essentially, remove those black circles labeled 0.0 to 12.0. How do I ensure my legend has only one variable of my choosing?
I was able to find a fix by indexing the labels in the legend.
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset("tips")
cmap = sns.cubehelix_palette(dark=.3, light=.8, as_cmap=True)
g = sns.scatterplot(x="total_bill", y="sex", hue="smoker", size = 'tip',sizes=(320, 600), data=tips)
h,l = g.get_legend_handles_labels()
plt.legend(h[0:3],l[0:3],bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0., fontsize=13)
plt.show(g)
Related
I want to see the values clearly, how can I fix that? How can I locate my values manually?
(values of gray and black colors (0.4) overlaps)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
df=pd.DataFrame({'Fiziki Durum':[173,76,1,1]},index=['Bina İçerisinde','Müstakil','Gezici','Prefabrik'])
labels='Bina İçerisinde','Müstakil','Gezici','Prefabrik'
colors=('#3a88e2','#5c9e1e','#708090','#0e1111')
fig1, ax=plt.subplots()
sizes=[173,76,1,1]
explode = (0.05,0.05,0.05,0.05)
patches, texts, autotexts = ax.pie(sizes, colors=colors, startangle=90,explode=explode, autopct='%1.1f%%' )
plt.legend(patches, labels, loc='upper center', bbox_to_anchor=(0.5, 0.08), ncol=4, frameon=False)
#draw circle
centre_circle = plt.Circle((0,0),0.70,fc='white')
fig = plt.gcf()
fig.gca().add_artist(centre_circle)
ax.axis('equal')
plt.tight_layout()
plt.show()
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 have codes below. You can see that a JointPlot is plotted.
But I want the size of dots to change with the values of the column "size".
So I changed the last line marker='o') to marker='o', s = "size"). Now I have error message AttributeError: 'Line2D' object has no property 's'.
I want each spot size to be different (i.e. similar to this). How can I modify my code to achieve this?
import seaborn as sns
import numpy as np
from itertools import product
sns.set(style="darkgrid")
tips = sns.load_dataset("tips")
g = sns.jointplot("total_bill", "tip", data=tips, kind="reg",
xlim=(0, 60), ylim=(0, 12), color='k', size = 7)
#Clear the axes containing the scatter plot
g.ax_joint.cla()
# #Plot each individual point separately
for i,row in enumerate(tips.values):
g.ax_joint.plot(row[0], row[1], color="blue", marker='o')
Updates:
I also tried to combine two plots directly, but still doesn't work. There is no error, but the scatterplot is just pasted to the right...
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
from itertools import product
sns.set(style="darkgrid")
tips = sns.load_dataset("tips")
fig, ax = plt.subplots()
g = sns.jointplot("total_bill", "tip", data=tips, kind="reg",
xlim=(0, 60), ylim=(0, 12), color='k', size = 7)
#Clear the axes containing the scatter plot
g.ax_joint.cla()
ax2 = ax.twinx()
sns.scatterplot(
data=tips, x="total_bill", y="tip", hue="size", size="size",
sizes=(20, 200), legend="full"
)
plt.show()
You can create a seaborn scatterplot on g.ax_joint. The following code has been tested with seaborn 0.11.2 (older versions may have a problem with a column named 'size').
import seaborn as sns
import numpy as np
sns.set(style="darkgrid")
tips = sns.load_dataset("tips")
g = sns.jointplot(x="total_bill", y="tip", data=tips, kind="reg",
xlim=(0, 60), ylim=(0, 12), color='k')
g.ax_joint.cla()
sns.scatterplot(data=tips, x='total_bill', y='tip', size='size', sizes=(10, 200),
ax=g.ax_joint)
As mentioned in the comments, to keep the regression line, sns.jointplot(..., scatter=False) can be used.
g = sns.jointplot(x="total_bill", y="tip", data=tips, kind="reg", scatter=False,
xlim=(0, 60), ylim=(0, 12), color='k')
sns.scatterplot(data=tips, x='total_bill', y='tip',
hue='size', palette='husl',
size='size', sizes=(10, 200), legend='full',
ax=g.ax_joint)
I have the following code to create a heatmap. However, it creates an overlap of the color bar and the right axis text. The text has no problems, I want it to be in that length.
How can I locate the colorbar on the right/left side of the heatmap with no overlap?
I tried with "pad" parameter in cbar_kws but it didn't help.enter image description here
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT=pd.DataFrame(np.random.randn(300,3), columns=list('ABC'))
miniPT=PT.iloc[:,:-1]
SMALL_SIZE = 8
MEDIUM_SIZE = 80
BIGGER_SIZE = 120
plt.rc('font', size=MEDIUM_SIZE) # controls default text sizes
plt.rc('axes', titlesize=MEDIUM_SIZE) # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
plt.rc('xtick', labelsize=MEDIUM_SIZE) # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc('legend', fontsize=MEDIUM_SIZE) # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
plt.figure(figsize=(10, miniPT.shape[0]/5.2))
ax =sns.heatmap(miniPT, annot=False, cmap='RdYlGn')
for _, spine in ax.spines.items():
spine.set_visible(True)
# second axis
asset_list=np.asarray(PT['C'])
asset_list=asset_list[::-1]
ax3 = ax.twinx()
ax3.set_ylim([0,ax.get_ylim()[1]])
ax3.set_yticks(ax.get_yticks())
ax3.set_yticklabels(asset_list, fontsize=MEDIUM_SIZE*0.6)
# colorbar
cbar = ax.collections[0].colorbar
cbar.ax.tick_params(labelsize=MEDIUM_SIZE)
One way to get the overlap automatically adjusted by matplotlib, is to explicitly create subplots: one for the heatmap and another for the colorbar. sns.heatmap's cbar_ax= parameter can be set to point to this subplot. gridspec_kws= is needed to set the relative sizes. At the end, plt.tight_layout() will adjust all the paddings to make everything fit nicely.
The question's code contains some strange settings (e.g. a fontsize of 80 is immense). Also, 300 rows will inevitably lead to overlapping text (the fontsize needs to be so small that non-overlapping text wouldn't be readable). Here is some more simplified example code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))
fig, (ax, cbar_ax) = plt.subplots(ncols=2, figsize=(10, len(PT) / 5.2), gridspec_kw={'width_ratios': [10, 1]})
sns.heatmap(PT.iloc[:, :-1], annot=False, cmap='RdYlGn', cbar_ax=cbar_ax, ax=ax)
for _, spine in ax.spines.items():
spine.set_visible(True)
# second axis
asset_list = np.asarray(PT['C'])
ax3 = ax.twinx()
ax3.set_ylim(ax.get_ylim())
ax3.set_yticks(np.arange(len(PT)))
ax3.set_yticklabels(asset_list, fontsize=80)
# colorbar
cbar_ax.tick_params(labelsize=80)
plt.tight_layout()
plt.show()
As the plot is quite large, here only the bottom part is pasted, with a link to the full plot.
This is how it would look like with:
fontsize 80 (Note that font sizes are measured in "points per inch", standard 72 points per inch);
figure width of 20 inches (instead of 10);
300 rows
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT = pd.DataFrame(np.random.randn(300, 3), columns=list('ABC'))
fig, (ax, cbar_ax) = plt.subplots(ncols=2, figsize=(20, len(PT) / 5.2), gridspec_kw={'width_ratios': [15, 1]})
sns.heatmap(PT.iloc[:, :-1], annot=False, cmap='RdYlGn', cbar_ax=cbar_ax, ax=ax)
for _, spine in ax.spines.items():
spine.set_visible(True)
# second axis
asset_list = np.asarray(PT['C'])
ax3 = ax.twinx()
ax3.set_ylim(ax.get_ylim())
ax3.set_yticks(np.arange(len(PT)))
ax3.set_yticklabels(asset_list, fontsize=80)
# colorbar
cbar_ax.tick_params(labelsize=80)
plt.tight_layout()
plt.show()
My solution was eventually move the colorbar to left side. This is the code and the output:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT = pd.DataFrame(np.random.randn(300, 3), columns=list('ABC'))
fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(10, len(PT) / 5.2), gridspec_kw={'width_ratios': [15, 15]})
sns.heatmap(PT.iloc[:, :-1], annot=False, cmap='RdYlGn', cbar_ax=ax0, ax=ax1)
for _, spine in ax1.spines.items():
spine.set_visible(True)
# second axis
asset_list = np.asarray(PT['C'])
ax3 = ax1.twinx()
ax3.set_ylim(ax1.get_ylim())
ax3.set_yticks(np.arange(len(PT)))
ax3.set_yticklabels(asset_list, fontsize=80)
# colorbar
ax0.tick_params(labelsize=80)
plt.tight_layout()
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: