How to shrink a subplot colorbar - matplotlib

starting from this code:
import numpy as np
import matplotlib.pyplot as pl
import matplotlib
from matplotlib.gridspec import GridSpec
x=np.linspace(0.0,1.0,100)
y=np.linspace(0.0,1.0,100)
xv,yv=np.meshgrid(x,y)
gs = GridSpec(2, 2,hspace=0.00,wspace=0.1,width_ratios=[25,1])
ax1 = pl.subplot(gs[0,0])
im=ax1.imshow(xv.T, origin='lower', cmap=matplotlib.cm.jet,extent=(0,100,0,1.0),aspect='auto')
xax1=ax1.get_xaxis()
xax1.set_ticks([])
ax3 = pl.subplot(gs[0,1])
#cbar=pl.colorbar(im,cax=ax3,shrink=0.5)
cbar=pl.colorbar(im,cax=ax3)
ax2 = pl.subplot(gs[1,0])
ax2.plot(np.sin(x))
pl.savefig('test.pdf')
I would like to keep the two plots sharing the same x-axis but I would like to
shrink the colorbar as well. If I use the commented line it does not work. What is the
better, most elegant, way to do that? I think I should use make_axes_locatable at some point, but I do not know how to use it in the proper way without changing the imshow
x-axis length.
Thank you.

You can do it with a lot of control about positioning, using the inset_axes.
import numpy as np
import matplotlib.pyplot as pl
import matplotlib
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
x=np.linspace(0.0,1.0,100)
y=np.linspace(0.0,1.0,100)
xv,yv=np.meshgrid(x,y)
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212, sharex = ax1)
im = ax1.imshow(xv.T, origin='lower',
cmap=matplotlib.cm.jet,extent=(0,100,0,1.0),aspect='auto')
ax2.plot(np.sin(x))
cax = inset_axes(ax1,
width="5%",
height="70%",
bbox_transform=ax1.transAxes,
bbox_to_anchor=(0.025, 0.1, 1.05, 0.95),
loc= 1)
norm = mpl.colors.Normalize(vmin=xv.min(), vmax=xv.max())
cb1 = mpl.colorbar.ColorbarBase(cax,
cmap=matplotlib.cm.jet, norm=norm,
orientation='vertical')
cb1.set_label(u'some cbar')
This is what I get then. Does that help your question?

Related

In Matplotlib, adding `trantsform` breaks rectangles [duplicate]

I wanted to rotate a Rectangle in matplotlib but when I apply the transformation, the rectangle doesn't show anymore:
rect = mpl.patches.Rectangle((0.0120,0),0.1,1000)
t = mpl.transforms.Affine2D().rotate_deg(45)
rect.set_transform(t)
is this a known bug or do I make a mistake?
The patch in the provided code makes it hard to tell what's going on, so I've made a clear demonstration that I worked out from a matplotlib example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
fig = plt.figure()
ax = fig.add_subplot(111)
r1 = patches.Rectangle((0,0), 20, 40, color="blue", alpha=0.50)
r2 = patches.Rectangle((0,0), 20, 40, color="red", alpha=0.50)
t2 = mpl.transforms.Affine2D().rotate_deg(-45) + ax.transData
r2.set_transform(t2)
ax.add_patch(r1)
ax.add_patch(r2)
plt.xlim(-20, 60)
plt.ylim(-20, 60)
plt.grid(True)
plt.show()
Apparently the transforms on patches are composites of several transforms for dealing with scaling and the bounding box. Adding the transform to the existing plot transform seems to give something more like what you'd expect. Though it looks like there's still an offset to work out.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
fig = plt.figure()
ax = fig.add_subplot(111)
rect = patches.Rectangle((0.0120,0),0.1,1000)
t_start = ax.transData
t = mpl.transforms.Affine2D().rotate_deg(-45)
t_end = t_start + t
rect.set_transform(t_end)
print repr(t_start)
print repr(t_end)
ax.add_patch(rect)
plt.show()

Show exponentiated values along opposite side of log color scale

With a horizontal log-scaled color bar and logged labels along the bottom, is it possible to show the exponentiated (original) values along the top?
So in this example, there should be ticks and labels along the top of the color bar going from mat.min() = 0.058 to mat.max() = 13.396
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
mat = np.exp(np.random.randn(20, 20))
plt.matshow(mat)
norm = mpl.colors.Normalize(1, np.log(mat.max()))
plt.colorbar(plt.cm.ScalarMappable(norm=norm), orientation="horizontal")
plt.savefig("rand_mat.png", dpi=200)
Here is the best answer for your response. I've customized it based on that. Does this result match the intent of your question? The color bar and the size of the figure are not the same, so I adjusted them.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(20210404)
mat = np.exp(np.random.randn(20, 20))
norm = mpl.colors.Normalize(1, np.log(mat.max()))
fig, (ax, cax) = plt.subplots(nrows=2, gridspec_kw=dict(height_ratios=[15,1],hspace=0.5))
im = ax.matshow(mat)
cbar = plt.colorbar(plt.cm.ScalarMappable(norm=norm), orientation="horizontal", cax=cax)
cax2 = cax.twiny()
cbar.ax.xaxis.set_label_position("bottom")
iticks = np.arange(mat.min(), mat.max(), 2)
cax2.set_xticks(iticks)
ax_pos = ax.get_position()
cax_pos = cbar.ax.get_position()
new_size = [ax_pos.x0, cax_pos.y0, ax_pos.x1 - ax_pos.x0, cax_pos.y1 - cax_pos.y0]
cbar.ax.set_position(new_size)
plt.show()
At the risk of committing a faux pas, I'll answer my own question with the solution that best suits my needs:
cb.ax.secondary_xaxis("top", functions=(np.exp, np.log))
which gives
Full Code
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
mat = np.exp(np.random.randn(20, 20))
plt.matshow(mat)
norm = mpl.colors.Normalize(np.log(mat.min()), np.log(mat.max()))
cb = plt.colorbar(plt.cm.ScalarMappable(norm=norm), orientation="horizontal")
cb_ax_top = cb.ax.secondary_xaxis("top", functions=(np.exp, np.log))
cb_ax_top.set_xticks([0.1, 0.5, 1, 4, 10, 20])

Seaborn boxplot custom lables aside box

I have the code segment given below, and it generates the provided boxplot. I would like to know how to add custom labels aside each box, so that the boxplot is even more digestible to the readers of my result. The expected diagram is also provided. I reckon there should be an easy way to get this done in Seaborn/Matplotlib.
What I exactly want is to add the following labels to each box (on left hand side as in shown in the example provided)
The code use to generate boxplot
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as MaxNLocator
from matplotlib import rcParams
from matplotlib.ticker import ScalarFormatter, FuncFormatter,FormatStrFormatter, EngFormatter#, mticker
%matplotlib inline
import seaborn as sns
range_stats = pd.read_csv(f'{snappy_data_dir}range_searcg_snappy_stats.csv')
data_stats_rs_txt = range_stats[range_stats['category'] == "t"]
data_stats_rs_seq = range_stats[range_stats['category'] == "s"]
fig, ax =plt.subplots(1,2)
rcParams['figure.figsize'] =8, 6
flierprops = dict(marker='x')
labels1 = ['R1', 'R2', 'R3', 'R4', 'R5']
sns.boxplot(x='Interval',y='Total',data=data_stats_rs_txt,palette='rainbow', ax=ax[0])
sns.boxplot(x='Interval',y='Total',data=data_stats_rs_seq,palette='rainbow', ax=ax[1])
ax[0].set(xlabel='Interval (s)', ylabel='query execution time (s)', title='Text format', ylim=(0, 290))
ax[1].set(xlabel='Interval (s)', ylabel='', title='Proposed format',ylim=(0, 290), yticklabels=[])
plt.savefig("range-query-corrected.svg")
plt.savefig('snappy_compressed_rangesearch.pdf')
Resulted figure:
Expected figure with labels
This might help you, although it is not a fully correct way and is not a complete solution.
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
tips = sns.load_dataset('tips')
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.set_context('poster',font_scale=0.5)
sns.boxplot(x="day", y="total_bill", data=tips,palette='rainbow', ax=axes[0], zorder=0)
axes[0].text(0, 45, r"$B1$", fontsize=20, color="blue")
axes[0].text(0.9, 45, r"$B2$", fontsize=20, color="blue")
axes[0].text(2.2, 45, r"$B3$", fontsize=20, color="blue")
axes[0].text(3.1, 45, r"$B4$", fontsize=20, color="blue");
sns.boxplot(x="day", y="tip", data=tips,palette='rainbow', ax=axes[1], zorder=10)
iris = sns.load_dataset("iris")
x_var = 'species'
y_var = 'sepal_width'
x_order = ['setosa', 'versicolor', 'virginica']
labels = ['R1','R2','R3']
max_vals = iris.groupby(x_var).max()[y_var].reindex(x_order)
ax = sns.boxplot(x=x_var, y=y_var, data=iris)
for x,y,l in zip(range(len(x_order)), max_vals, labels):
ax.annotate(l, xy=[x,y], xytext=[0,5], textcoords='offset pixels', ha='center', va='bottom')

matplotlib: shorten a colorbar by half when the colorbar is created using axes_grid1

I am trying to shorten a colorbar by half. Does anyone know how to do this? I tried cax.get_position() and then cax.set_position(), but this method did not work.
Besides, it seems that axes created by axes_grid1 has the same bbox positions as the original axes. Is this a bug?
PS. I have to use axes_grid1 to create colorbar axes, because I need to use tight_layout() afterwards, and tight_layout() only applies to axes created by axes_grid1 but not ones created by add_axes().
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
bbox1 = ax.get_position()
print(bbox1)
bbox1 = cax.get_position()
print(bbox1)
plt.colorbar(im, cax=cax)
plt.show()
The whole point of the axes_divider is to divide the axes to make space for a new axes. This ensures that all axes have the same surrounding box. And that is the box you see being printed.
Some of the usual ways to create a colorbar, at a certain location in the figue are shown in this question. Here the problem seems to be to be able to call tight_layout. This is achievable with the following two options. (There might be others still.)
A. using gridspec
I'm not too sure about the exact requirements here, but it seems that using a normal grid layout would be more in the direction of what you need here.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure()
gs = gridspec.GridSpec(2, 2, width_ratios=[95,5],)
ax = fig.add_subplot(gs[:, 0])
im = ax.imshow(np.arange(100).reshape((10,10)))
cax = fig.add_subplot(gs[1, 1])
fig.colorbar(im, cax=cax, ax=ax)
plt.tight_layout()
plt.show()
B. Using axes_grid1
If you really need to use axes_grid1, it might become a little bit more complicated.
import matplotlib.pyplot as plt
import matplotlib.axes
from mpl_toolkits.axes_grid1 import make_axes_locatable, Size
import numpy as np
fig, ax = plt.subplots()
im = ax.imshow(np.arange(100).reshape((10,10)))
divider = make_axes_locatable(ax)
pad = 0.03
pad_size = Size.Fraction(pad, Size.AxesY(ax))
xsize = Size.Fraction(0.05, Size.AxesX(ax))
ysize = Size.Fraction(0.5-pad/2., Size.AxesY(ax))
divider.set_horizontal([Size.AxesX(ax), pad_size, xsize])
divider.set_vertical([ysize, pad_size, ysize])
ax.set_axes_locator(divider.new_locator(0, 0, ny1=-1))
cax = matplotlib.axes.Axes(ax.get_figure(),
ax.get_position(original=True))
locator = divider.new_locator(nx=2, ny=0)
cax.set_axes_locator(locator)
fig.add_axes(cax)
fig.colorbar(im, cax=cax)
plt.tight_layout()
plt.show()

matplotlib different size of unit along x-axis

everyone. I want to generate a x-axis like the picture showing below.
Except make several different-sized subplots then merged to a single one.
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
axes=[]
ax1 = plt.subplot2grid((1,10),(0,0),colspan=4,rowspan=1)
ax1.plot([0,1],[2,3])
ax2 = plt.subplot2grid((1,10),(0,4),colspan=1,rowspan=1)
ax2.plot([1,2],[3,4])
ax3 = plt.subplot2grid((1,10),(0,5),colspan=3,rowspan=1)
ax3.plot([2,3],[4,5])
ax4 = plt.subplot2grid((1,10),(0,8),colspan=2,rowspan=1)
ax4.plot([3,4],[5,6])
axes=[ax1,ax2,ax3,ax4]
ax1.spines['right'].set_visible(False)
ax1.set_xticks([0,1])
ax1.set_xticklabels(['0','1'])
ax2.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax2.yaxis.set_major_locator(ticker.NullLocator())
ax2.set_xticks([2])
ax2.set_xticklabels(['2'])
ax3.spines['right'].set_visible(False)
ax3.spines['left'].set_visible(False)
ax3.yaxis.set_major_locator(ticker.NullLocator())
ax3.set_xticks([3])
ax3.set_xticklabels(['3'])
ax4.spines['left'].set_visible(False)
ax4.yaxis.set_major_locator(ticker.NullLocator())
ax4.set_xticks([4])
ax4.set_xticklabels(['4'])
[plt.setp(axes[i],xlim=[i+0,i+1]) for i in range(4)]
[plt.setp(axes[i],ylim=[2,6]) for i in range(4)]
plt.subplots_adjust(wspace=0,)
plt.savefig('xx.png',format='png',dpi=300)
I wonder is there other way to do this?