Set xlim in heatmap with subplots and annotation - python

I would like to plot several heatmaps side by side, with annotations.
For this, I use subplots and I can plot each heatmap in its axes using the ax kwarg.
The issue is when I use xlim : it's applied to the heatmap, but not the annotation :
Here is the code :
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
values = np.random.random((7,24)) # create (7,24) shape array
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(30,10)) # create 2 columns for subplots
ax1 = sns.heatmap(values, annot=True, ax=ax1) # heatmap with annotation
ax1.set(xlim=(12,22)) # works fine with this line commented
# ax1.set_xlim(12,22)
# ax2 = sns.heatmap(values, annot=True, ax=ax2) # second heatmap
plt.show()
And it gets worse with a second heatmap, because the annotation from the second heatmap are ploted on the first heatmap.
How can I limit x axis to (12,22) while using annotation ?
matplotlib 2.2.2
seaborn 0.9.0
python 3.6.5

Why not providing the slice of interest in the first place and relabel the x-axis?
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(1234)
values = np.random.random((7,24)) # create (7,24) shape array # create (7,24) shape array ) # create (7,24) shape array
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(21,7)) # create 2 columns for subplots
#full heatmap
sns.heatmap(values, annot=True, ax=ax1)
#slice of interest
start=12
stop=22
sns.heatmap(values[:, start:stop+1], annot=True, ax=ax2, xticklabels = np.arange(start, stop+1)) # second heatmap
plt.show()
Sample output

After posting this issue on seaborn github, here is the official answer :
matplotlib text objects are not automatically clipped when they are
placed outside of the axes limits; you can turn that on by passing
annot_kws=dict(clip_on=True) to heatmap, though.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
values = np.random.random((7,24)) # create (7,24) shape array
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(30,10)) # create 2 columns for subplots
ax1 = sns.heatmap(values, annot=True, ax=ax1, annot_kws=dict(clip_on=True)) # heatmap with annotation
ax1.set(xlim=(12,22)) # works fine with this line commented
# ax1.set_xlim(12,22)
ax2 = sns.heatmap(values, annot=True, ax=ax2, annot_kws=dict(clip_on=True)) # second heatmap
ax2.set(xlim=(12,22))
plt.show()
clip_on=True will remove everithing that is outside the axe

Related

Combine 2 kde-functions in one plot in seaborn

I have the following code for plotting the histogram and the kde-functions (Kernel density estimation) of a training and validation dataset:
#Plot histograms
import matplotlib.pyplot as plt
import matplotlib
import seaborn as sns
displot_dataTrain=sns.displot(data_train, bins='auto', kde=True)
displot_dataTrain._legend.remove()
plt.ylabel('Count')
plt.xlabel('Training Data')
plt.title("Histogram Training Data")
plt.show()
displot_dataValid =sns.displot(data_valid, bins='auto', kde=True)
displot_dataValid._legend.remove()
plt.ylabel('Count')
plt.xlabel('Validation Data')
plt.title("Histogram Validation Data")
plt.show()
# Try to plot the kde-functions together --> yields an AttributeError
X1 = np.linspace(data_train.min(), data_train.max(), 1000)
X2 = np.linspace(data_valid.min(), data_valid.max(), 1000)
fig, ax = plt.subplots(1,2, figsize=(12,6))
ax[0].plot(X1, displot_dataTest.kde.pdf(X1), label='train')
ax[1].plot(X2, displot_dataValid.kde.pdf(X1), label='valid')
The plotting of the histograms and kde-functions inside one plot works without problems. Now I would like to have the 2 kde-functions inside one plot but when using the posted code, I get the following error AttributeError: 'FacetGrid' object has no attribute 'kde'
Do you have any idea, how I can combined the 2 kde-functions inside one plot (without the histogram)?
sns.displot() returns a FacetGrid. That doesn't work as input for ax.plot(). Also, displot_dataTest.kde.pdf is never valid. However, you can write sns.kdeplot(data=data_train, ax=ax[0]) to create a kdeplot inside the first subplot. See the docs; note the optional parameters cut= and clip= that can be used to adjust the limits.
If you only want one subplot, you can use fig, ax = plt.subplots(1, 1, figsize=(12,6)) and use ax=ax instead of ax=ax[0] as in that case ax is just a single subplot, not an array of subplots.
The following code has been tested using the latest seaborn version:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
fig, ax = plt.subplots(figsize=(12, 6))
sns.kdeplot(data=np.random.normal(0.1, 1, 100).cumsum(),
color='crimson', label='train', fill=True, ax=ax)
sns.kdeplot(data=np.random.normal(0.1, 1, 100).cumsum(),
color='limegreen', label='valid', fill=True, ax=ax)
ax.legend()
plt.tight_layout()
plt.show()

How to set ticklabel rotation and add bar annotations

I would like to draw the following bar plot with annotation and I want to keep the x-label 45 degree so that it is easily readable. I am not sure why my code is not working. I have added the sample data and desired bar plots as a attachment. I appreciate your suggestions! Thanks!
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
#sns.set(rc={"figure.dpi":300, 'savefig.dpi':300})
sns.set_context('notebook')
sns.set_style("ticks")
#sns.set_style('white')
sns.set_context("paper", font_scale = 2)
colors = ['b', 'g', 'r', 'c', 'm']
#sns.set(style="whitegrid")
#sns.set_palette(sns.color_palette(colors))
#fig, (ax1,ax2) = plt.subplots(1, 2, figsize=(16, 8))
#fig.subplots_adjust(wspace=0.3)
plots1 = sns.barplot(x="Model", y="G-mean", data=df_Aussel2014_5features, ax=ax1,palette='Spectral')
# Iterrating over the bars one-by-one
for bar in plots1.patches:
# Using Matplotlib's annotate function and
# passing the coordinates where the annotation shall be done
plots1.annotate(format(bar.get_height(), '.2f'),
(bar.get_x() + bar.get_width() / 2,
bar.get_height()), ha='center', va='center',
size=10, xytext=(0, 5),
textcoords='offset points')
plt.show()
# Save figure
#plt.savefig('Aussel2014_5features.png', dpi=300, transparent=False, bbox_inches='tight')
I got the following image.
You are using the object oriented interface (e.g. axes) so don't mix plt. and axes. methods
seaborn.barplot is an axes-level plot, which returns a matplotlib axes, p1 in this case.
Use the matplotlib.axes.Axes.tick_params to set the rotation of the axis, or a number of other parameters, as shown in the documentation.
Use matplotlib.pyplot.bar_label to add bar annotations.
See this answer with additional details and examples for using the method.
Adjust the nrow, ncols and figsize as needed, and set sharex=False and sharey=False.
Tested in python 3.8.12, pandas 1.3.4, matplotlib 3.4.3, seaborn 0.11.2
import seaborn as sns
import matplotlib.pyplot as plot
import pandas as pd
# data
data = {'Model': ['QDA', 'LDA', 'DT', 'Bagging', 'NB'],
'G-mean': [0.703780, 0.527855, 0.330928, 0.294414, 0.278713]}
df = pd.DataFrame(data)
# create figure and axes
fig, ax1 = plt.subplots(nrows=1, ncols=1, figsize=(8, 8), sharex=False, sharey=False)
# plot
p1 = sns.barplot(x="Model", y="G-mean", data=df, palette='Spectral', ax=ax1)
p1.set(title='Performance Comparison based on G-mean')
# add annotation
p1.bar_label(p1.containers[0], fmt='%0.2f')
# add a space on y for the annotations
p1.margins(y=0.1)
# rotate the axis ticklabels
p1.tick_params(axis='x', rotation=45)
import matplotlib.pyplot as plt. plt.xticks(rotation=‌​45)
Example :
import matplotlib.pyplot as plt
plt.xticks(rotation=‌​45)

Matplotlib: Is there a way to get a colorbar axis from a parent axis?

I am doing a plot something like this:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
fig = plt.gcf()
ax = plt.gca()
ax.pcolormesh(np.random.rand(10, 10))
fig.colorbar(mpl.cm.ScalarMappable(), ax=ax)
The last line adds a colorbar and a second axis
fig.axes
>>> [<AxesSubplot:>, <AxesSubplot:label='<colorbar>'>]
My question:
Is there any relation between the two axes that can be used to get the axis of the colorbar (second in the list above) using only the axis returned by ax = plt.gca() (first returned in the list above)?
As far as I know, if you define pcolormesh and colorbar that way, no.
Anyway, you can define an ax for the pcolormesh and a cax for the colorbar beforehand. Then you can pass cax as parameter to matplotlib.pyplot.colorbar. In this way you can access to both axis ax and cax as you need.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
grid_kws = {'width_ratios': (0.9, 0.05), 'wspace': 0.2}
fig, (ax, cax) = plt.subplots(1, 2, gridspec_kw = grid_kws, figsize = (10, 8))
ax.pcolormesh(np.random.rand(10, 10))
plt.colorbar(mpl.cm.ScalarMappable(), cax=cax)
plt.show()
In general, focusing on your code:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
fig = plt.gcf()
ax = plt.gca()
ax.pcolormesh(np.random.rand(10, 10))
fig.colorbar(mpl.cm.ScalarMappable(), ax=ax)
starting from ax, you can get its figure with ax.figure. From there, you can get the list of all figure axes with ax.figure.axes. So, if you want to get colobar's axis using only pcolormesh' axis, you should use:
ax.figure.axes[1]
The parent figure, as far as I know, is the only relation between the two axes.

Adding seaborn clustermap to figure with other plots

I am trying to put the following two plots onto the same figure:
import seaborn as sns; sns.set(color_codes=True)
import matplotlib.pyplot as plt
f, (ax1, ax2) = plt.subplots(1, 2, sharey=True)
iris = sns.load_dataset("iris")
sns.boxplot(data=iris, orient="h", palette="Set2", ax = ax1)
species = iris.pop("species")
lut = dict(zip(species.unique(), "rbg"))
row_colors = species.map(lut)
sns.clustermap(iris, row_colors=row_colors, ax = ax2)
I understand that clustermap returns a figure, so this doesn't work. However, I still need a way to present these plots next to each other (horizontal). sns.heatmap returns an axes, but it does not support clustering or color annotation .
What is the best way to do this ?
Indeed, clustermap, as some other seaborn functions, creates its own figure. There is nothing you can do about that but as long as all other content you want to have in the final figure can be created inside axes, like in this case the boxplot, the solution is relatively easy.
You can simply work with the figure that clustermap has created for you. The idea would then be to manipulate the gridspec of the axes such that there is some place left for the other axes.
import seaborn as sns; sns.set(color_codes=True)
import matplotlib.pyplot as plt
import matplotlib.gridspec
iris = sns.load_dataset("iris")
species = iris.pop("species")
lut = dict(zip(species.unique(), "rbg"))
row_colors = species.map(lut)
#First create the clustermap figure
g = sns.clustermap(iris, row_colors=row_colors, figsize=(13,8))
# set the gridspec to only cover half of the figure
g.gs.update(left=0.05, right=0.45)
#create new gridspec for the right part
gs2 = matplotlib.gridspec.GridSpec(1,1, left=0.6)
# create axes within this new gridspec
ax2 = g.fig.add_subplot(gs2[0])
# plot boxplot in the new axes
sns.boxplot(data=iris, orient="h", palette="Set2", ax = ax2)
plt.show()
For the case when having multiple figure-level functions to combine the solution is much more complicated, as seen e.g. in this question.

How can I make a barplot and a lineplot in the same seaborn plot with different Y axes nicely?

I have two different sets of data with a common index, and I want to represent the first one as a barplot and the second one as a lineplot in the same graph. My current approach is similar to the following.
ax = pt.a.plot(alpha = .75, kind = 'bar')
ax2 = ax.twinx()
ax2.plot(ax.get_xticks(), pt.b.values, alpha = .75, color = 'r')
And the result is similar to this
This image is really nice and almost right. My only problem is that ax.twinx() seems to create a new canvas on top of the previous one, and the white lines are clearly seen on top of the barplot.
Is there any way to plot this without including the white lines?
You can use twinx() method along with seaborn to create a seperate y-axis, one for the lineplot and the other for the barplot. To control the style of the plot (default style of seaborn is darkgrid), you can use set_style method and specify the preferred theme. If you set style=None it resets to white background without the gridlines. You can also try whitegrid. If you want to further customize the gridlines, you can do it on the axis level using the ax2.grid(False).
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
matplotlib.rc_file_defaults()
ax1 = sns.set_style(style=None, rc=None )
fig, ax1 = plt.subplots(figsize=(12,6))
sns.lineplot(data = df['y_var_1'], marker='o', sort = False, ax=ax1)
ax2 = ax1.twinx()
sns.barplot(data = df, x='x_var', y='y_var_2', alpha=0.5, ax=ax2)
You have to remove grid lines of the second axis. Add to the code ax2.grid(False). However y-ticks of the second axis will be not align to y-ticks of the first y-axis, like here:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(pd.Series(np.random.uniform(0,1,size=10)), color='g')
ax2 = ax1.twinx()
ax2.plot(pd.Series(np.random.uniform(0,17,size=10)), color='r')
ax2.grid(False)
plt.show()

Categories

Resources