Matplotlib - Scientific notations overlap on multiple y-axis graph - python

I'm plotting a graph with 3 y-axis, and two of them have scietific notations. However, they overlap on the top-right of the graph. I'd like to have them separated, and if possible on top of their axis. Here's how i plot the graph, and a picture of the result, where you can clearly see the overlapping :
https://i.stack.imgur.com/G2K9A.png (The y-axis on the right overlap a bit too, but I know how to correct that)
import numpy as np, matplotlib.pyplot as plt
a = np.arange(-1*10**-5, 10**-5, (10**-5+10**-5)/10)
b = np.arange(-2*10**-7, 2*10**-7, (2*10**-7+2*10**-7)/10)
c = np.arange(-3*10**-6, 3*10**-6, (3*10**-6+3*10**-6)/10)
x = np.arange(0, 100, 100/10)
fig, ax = plt.subplots(num=1, figsize = (15, 10))
fig.subplots_adjust(right=0.75)
twin1 = ax.twinx()
twin2 = ax.twinx()
twin2.spines['right'].set_position(("axes", 1.1))
p1, = ax.plot(x, a, color = 'r', linewidth = 2, label="y1")
p2, = twin1.plot(x, b, color = 'g', linewidth = 2, label="y2")
p3, = twin2.plot(x, c, color = 'b', linewidth = 2, label="y3")
ax.set_xlabel("Time (s)", fontsize=35)
ax.set_ylabel("y1", fontsize=35)
twin1.set_ylabel("y2", fontsize=35)
twin2.set_ylabel("y3", fontsize=35)
ax.yaxis.label.set_color(p1.get_color())
twin1.yaxis.label.set_color(p2.get_color())
twin2.yaxis.label.set_color(p3.get_color())
ax.tick_params(axis='y', colors=p1.get_color(), labelsize=30)
twin1.tick_params(axis='y', colors=p2.get_color(), labelsize=30)
twin1.yaxis.offsetText.set_fontsize(30)
twin2.tick_params(axis='y', colors=p3.get_color(), labelsize=30)
twin2.yaxis.offsetText.set_fontsize(30)
ax.tick_params(axis='x', labelsize=30)
min_axis_x, max_axis_x = x.min(), x.max()
min_axis_y, max_axis_y = a.min(), a.max()
min_axis_y1, max_axis_y1 = b.min(), b.max()
min_axis_y2, max_axis_y2 = c.min(), c.max()
ax.legend(handles=[p1, p2, p3], fontsize=35)
plt.title("y1, y2, y3 = f(t)", fontsize=45)
plt.show()

Related

How to reduce the gap between a pcolormesh and a colorbar in matplotlib?

I have a dataset that I want to plot as 4 panels (each a pcolormesh with its associated colorbar). This is the code I'm using to do this, with some mocked up data
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
xs = np.linspace(0.1, 0.2, 100)
ys = np.linspace(0, 2*np.pi*0.1, 400)
x_mesh, y_mesh = np.meshgrid(xs, ys)
# mocked up data arrays
A = np.full_like(x_mesh, 1.0)
B = np.full_like(x_mesh, 1.0)
C = np.full_like(x_mesh, 1.0)
D = np.full_like(x_mesh, 1.0)
fig = plt.figure()
gs = gridspec.GridSpec(nrows = 2, ncols = 4, height_ratios = (0.5, 0.5), width_ratios = (0.45, 0.05, 0.45, 0.05))
ax0 = fig.add_subplot(gs[0,0])
ax0_cbar = fig.add_subplot(gs[0,1])
ax1 = fig.add_subplot(gs[0,2])
ax1_cbar = fig.add_subplot(gs[0,3])
ax2 = fig.add_subplot(gs[1,0])
ax2_cbar = fig.add_subplot(gs[1,1])
ax3 = fig.add_subplot(gs[1,2])
ax3_cbar = fig.add_subplot(gs[1,3])
a = ax0.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, A, \
shading = 'auto')
cb1 = plt.colorbar(a, cax=ax0_cbar)
cb1.set_label(r"A")
b = ax1.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, B, \
shading = 'auto')
cb1 = plt.colorbar(b, cax=ax1_cbar)
cb1.set_label(r"B")
c = ax2.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, C, \
shading = 'auto')
cb1 = plt.colorbar(c, cax=ax2_cbar)
cb1.set_label(r"C")
d = ax3.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, D, \
shading = 'auto')
cb1 = plt.colorbar(d, cax=ax3_cbar)
cb1.set_label(r"D")
ax0.xaxis.set_ticklabels([])
ax1.xaxis.set_ticklabels([])
fig.tight_layout()
But when I actually do this, I find that there are really large gaps between the pcolormesh and the colorbars that are really unappealing (picture attached). How can I reduce these? I though I would be able to do it with fig.tight_layout() and width_ratios in gridspec
You don't require new axes elements for your colorbars, simply use the ax keyword argument to specify the colorbars for each subplot. The matplotlib documentation shows that using ax will produce a colorbar axis stolen from the parent axes ax (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.colorbar.html). The documentation should be your first port of call, always!
Here I have written a working version of your code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
xs = np.linspace(0.1, 0.2, 100)
ys = np.linspace(0, 2*np.pi*0.1, 400)
x_mesh, y_mesh = np.meshgrid(xs, ys)
# mocked up data arrays
A = np.full_like(x_mesh, 1.0)
B = np.full_like(x_mesh, 1.0)
C = np.full_like(x_mesh, 1.0)
D = np.full_like(x_mesh, 1.0)
fig = plt.figure(figsize=(6,4))
gs = gridspec.GridSpec(nrows = 2, ncols = 2, height_ratios = (0.5, 0.5), width_ratios = (0.5, 0.5))
ax0 = fig.add_subplot(gs[0,0])
ax1 = fig.add_subplot(gs[0,1])
ax2 = fig.add_subplot(gs[1,0])
ax3 = fig.add_subplot(gs[1,1])
a = ax0.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, A, \
shading = 'auto')
cb1 = plt.colorbar(a, ax=ax0)
cb1.set_label(r"A")
b = ax1.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, B, \
shading = 'auto')
cb1 = plt.colorbar(b, ax=ax1)
cb1.set_label(r"B")
c = ax2.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, C, \
shading = 'auto')
cb1 = plt.colorbar(c, ax=ax2)
cb1.set_label(r"C")
d = ax3.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, D, \
shading = 'auto')
cb1 = plt.colorbar(d, ax=ax3)
cb1.set_label(r"D")
ax0.xaxis.set_ticklabels([])
ax1.xaxis.set_ticklabels([])
fig.tight_layout()
plt.show()
Happy coding
The approach above is correct. It can break down if you have equal aspect axes, for which you can now use layout='compressed' for simple cases to remove white space:
fig, axs = plt.subplots(2, 2, layout='compressed', figsize=(6, 3))
for ax in axs.flat:
pc = ax.pcolormesh(np.random.randn(10, 10))
ax.set_aspect(1)
fig.colorbar(pc, ax=ax)
plt.show()
See also: https://matplotlib.org/stable/gallery/subplots_axes_and_figures/colorbar_placement.html

normal distribution curve doesn't fit well over histogram in subplots using matplotlib

I am using "plt.subplots(2, 2, sharex=True, sharey=True)" to draw a 2*2 subplots. Each subplot has two Y axis and contains normal distribution curve over a histogram. Noting I particularly set "sharex=True, sharey=True" here in order to make all subplots share the same X axis and Y axis.
After running my code, everything is fine except the second, three, and fourth subplots where the normal distribution curve doesn't fit the histogram very well (please see the figure here)
I did googling but failed to get this issue solved. However, if I set "sharex=True, sharey=False" in my code, then the figure looks correct, but all subplots use their own Y axix which isn't what I want. Please see the figure here
Hope this issue can be fixed by experts in StackOverflow. Many thanks in advance!
Below is my code:
import matplotlib.pyplot as plt
from scipy.stats import norm
def align_yaxis(ax1, v1, ax2, v2):
#adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1
_, y1 = ax1.transData.transform((0, v1))
_, y2 = ax2.transData.transform((0, v2))
inv = ax2.transData.inverted()
_, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2))
miny, maxy = ax2.get_ylim()
ax2.set_ylim(miny+dy, maxy+dy)
def drawSingle(myax, mydf , title, offset):
num_bins = 200
xs = mydf["gap"]
x = np.linspace(-1,1,1000)
mu =np.mean(x)
sigma =np.std(xs)
n, bins, patche = myax.hist(xs, num_bins, alpha=0.8, facecolor='blue', density=False)
myax.set_ylabel('frequency',color="black",fontsize=12, weight = "bold")
myax.set_xlabel('X', fontsize=12, weight = "bold",horizontalalignment='center')
ax_twin = myax.twinx()
y_normcurve = norm.pdf(bins, mu, sigma)
ax_twin.plot(bins, y_normcurve, 'r--')
align_yaxis(myax,0,ax_twin,0)
peakpoint = norm.pdf(mu,loc=mu,scale=sigma)
plt.vlines(mu, 0, peakpoint, 'y', '--', label='example')
ax_twin.set_ylabel("probablility dense",color="black",fontsize=12, weight = "bold")
def drawSubplots(mydf1,mydf2,mydf3,mydf4, pos1,pos2,pos3,pos4, title, filename):
plt.rcParams['figure.figsize'] = (18,15 )
my_x_ticks = np.arange(-0.8, 0.8,0.1)
rows, cols = 2, 2
fig, ax = plt.subplots(2, 2, sharex=True, sharey=True)
drawSingle(ax[0][0], mydf1, "Subplot1", pos1)
drawSingle(ax[0][1], mydf2, "Subplot2", pos2)
drawSingle(ax[1][0], mydf3, "Subplot3", pos3)
drawSingle(ax[1][1], mydf4, "Subplot4", pos4)
plt.text(-1, -1, title, horizontalalignment='center', fontsize=18)
plt.show()
drawSubplots(df1, df2,df3,df4,3.2,3.1,2.7,2.85,"test9", "test9")
Here is an attempt to:
have the left y-axes being "frequency" (which is very uninformative in the case of the current bin widths) and shared among the 4 subplots
have the right y-axes be a "probability density"; note how the top of all gaussians is around y=0.02 (the twin axes can only be set at the end because the shared y axes can be updated via later subplots)
have the histogram and the normal curve aligned
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from scipy.stats import norm
def drawSingle(myax, mydf, title):
num_bins = 200
xs = mydf["gap"]
x = np.linspace(-1, 1, 1000)
mu = np.mean(x)
sigma = np.std(xs)
n, bins, patches = myax.hist(xs, num_bins, alpha=0.8, facecolor='blue', density=False)
myax.set_ylabel('frequency', color="black", fontsize=12, weight="bold")
myax.set_xlabel('X', fontsize=12, weight="bold", horizontalalignment='center')
normalization_factor = len(xs) * (bins[1] - bins[0])
y_normcurve = norm.pdf(x, mu, sigma) * normalization_factor
myax.plot(x, y_normcurve, 'r--')
myax.vlines(mu, 0, y_normcurve.max(), 'y', '--', color='lime', label='example')
return normalization_factor
def drawSubplots(mydf1, mydf2, mydf3, mydf4, title):
plt.rcParams['figure.figsize'] = (18, 15)
fig, ax = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
dfs = [mydf1, mydf2, mydf3, mydf4]
norm_factors = [drawSingle(ax_i, df, title)
for ax_i, df, title in zip(ax.ravel(), dfs, ["Subplot1", "Subplot2", "Subplot3", "Subplot4"])]
for ax_i, norm_factor in zip(ax.ravel(), norm_factors):
ax_twin = ax_i.twinx()
ymax = ax_i.get_ylim()[1]
ax_twin.set_ylim(0, ymax / norm_factor)
plt.suptitle(title, fontsize=18)
plt.tight_layout()
plt.show()
df1, df2, df3, df4 = [pd.DataFrame({"gap": np.random.normal(0, 0.2, n)}) for n in [6000, 4000, 1800, 1200]]
drawSubplots(df1, df2, df3, df4, "Title")
Many thanks JohanC, you are amazing.
Based on your code, I just added a few lines of code within drawSubplots function in order to make 95% of the Gaussian curve area shaded between the lower bound and upper bound for each subplot. The following is my try. It seems that ax_twin.fill_between doesn't work normally here. As you could see from the figure that the shaded area is out of the Gaussian curve enter image description here. What I want is only to shade the area under the Gaussian curve between the lower bound and upper bound. If you don't mind, would you please check it out my mistake? Thank you very much!
import matplotlib.pyplot as plt
import math
from scipy.stats import norm
def align_yaxis(ax1, v1, ax2, v2):
#adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1
_, y1 = ax1.transData.transform((0, v1))
_, y2 = ax2.transData.transform((0, v2))
inv = ax2.transData.inverted()
_, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2))
miny, maxy = ax2.get_ylim()
ax2.set_ylim(miny+dy, maxy+dy)
def drawSingle(myax, mydf , title):
num_bins = 200
xs = mydf["gap"]
x = np.linspace(-1,1,1000)
mu =np.mean(xs)
sigma =np.std(xs)
n, bins, patches = myax.hist(xs, num_bins, alpha=0.8, facecolor='blue', density=False)
myax.set_ylabel('Frequency', color="black", fontsize=12, weight="bold")
myax.set_xlabel(title, fontsize=12, weight="bold", horizontalalignment='center')
normalization_factor = len(xs) * (bins[1] - bins[0])
y_normcurve = norm.pdf(x, mu, sigma) * normalization_factor
myax.plot(x, y_normcurve, 'r--')
myax.vlines(mu, 0, y_normcurve.max(), 'y', '--', color='lime', label='example')
plt.xlim(-0.8,0.8)
my_x_ticks = np.arange(-0.8, 0.8,0.1)
plt.xticks(my_x_ticks)
return normalization_factor, mu, sigma
def drawSubplots(mydf1,mydf2,mydf3,mydf4, title):
plt.rcParams['figure.figsize'] = (18,15 )
norm_factors = []
mus = []
sigmas = []
my_x_ticks = np.arange(-0.8, 0.8,0.1)
rows, cols = 2, 2
fig, ax = plt.subplots(nrows=rows, ncols=cols, sharex=True, sharey=True)
dfs = [mydf1, mydf2, mydf3, mydf4]
#norm_factors = [drawSingle(ax_i, df, title)
#for ax_i, df, title in zip(ax.ravel(), dfs, ["Subplot1", "Subplot2", "Subplot3", "Subplot4"])]
for ax_i, df, title in zip(ax.ravel(), dfs, ["Subplot1", "Subplot2", "Subplot3", "Subplot4"]):
norm_factor, mu, sigma = drawSingle(ax_i, df, title)
norm_factors.append(norm_factor)
mus.append(mu)
sigmas.append(sigma)
for ax_i, norm_factor, mu, sigma in zip(ax.ravel(), norm_factors, mus, sigmas ):
ax_twin = ax_i.twinx()
xmax = ax_i.get_xlim()[1]
ax_twin.set_ylim(0, xmax / norm_factor)
ax_twin.set_ylabel("probablility dense",color="black",fontsize=12, weight = "bold")
CI_95_lower = mu - (1.96*sigma)
CI_95_upper = mu + (1.96*sigma)
px_shaded = np.arange(CI_95_lower,CI_95_upper,0.1)
ax_twin.fill_between(px_shaded,norm.pdf(px_shaded,loc=mu,scale=sigma) * norm_factor,alpha=0.75, color='pink')
area_shaded_95_CI = norm.cdf(x=CI_95_upper, loc=mu, scale=sigma)-norm.cdf(x=CI_95_lower, loc=mu, scale=sigma)
ax_twin.text(-0.06,0.01,str(round(area_shaded_95_CI*100,1))+"%", fontsize=20)
ax_twin.annotate(s=f'lower bound= {CI_95_lower:.3f}',xy=(CI_95_lower,norm.pdf(CI_95_lower,loc=mu,scale=sigma)),xytext=(-0.75,0.01),weight='bold',color='blue',\
arrowprops=dict(arrowstyle='-|>',connectionstyle='arc3',color='green'),\
fontsize=12
)
ax_twin.annotate(s=f'upper bound= {CI_95_upper:.3f}',xy=(CI_95_upper,norm.pdf(CI_95_upper,loc=mu,scale=sigma)),xytext=(0.28,0.01),weight='bold',color='blue',\
arrowprops=dict(arrowstyle='-|>',connectionstyle='arc3',color='green'),\
fontsize=12
)
ax_twin.text(0.05, 0.03, r"$\mu=" + f'{mu:.6f}' + ", \sigma=" + f'{sigma:.6f}' + "$" + ", confidence interval=95%" ,
horizontalalignment='center', fontsize=15)
plt.suptitle(title, fontsize=18)
plt.tight_layout()
plt.show()
df1, df2, df3, df4 = [pd.DataFrame({"gap": np.random.normal(0, 0.2, n)}) for n in [6000, 4000, 1800, 1200]]
drawSubplots(df1, df2, df3, df4, "Title")

Align colorbar with GeoAxes subplot edges

I have a figure with 3 subplots, two of which share a colorbar and the third has has it's own colorbar.
I would like the colorbars to align with the vertical limits of their respective plots, and for the top two plots to have the same vertical limits.
Googling, I have found ways to do this with a single plot, but am stuck trying to make it work for my fig. My figure currently looks like this:
The code for which is as follows:
import cartopy.io.shapereader as shpreader
import cartopy.crs as ccrs
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
shpfilename = shpreader.natural_earth(resolution='50m',
category='cultural',
name='admin_0_countries')
reader = shpreader.Reader(shpfilename)
countries = reader.records()
projection = ccrs.PlateCarree()
fig = plt.figure()
axs = [plt.subplot(2, 2, x + 1, projection = projection) for x in range(2)]\
+ [plt.subplot(2, 2, (3, 4), projection = projection)]
def cmap_seg(cmap, value, k):
cmaplist = [cmap(i) for i in range(cmap.N)]
cmap = mpl.colors.LinearSegmentedColormap.from_list(
'Custom cmap', cmaplist, cmap.N)
bounds = np.linspace(0, k, k + 1)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
color = cmap(norm(value))
return color, cmap
for country in countries:
c_name = country.attributes["SOVEREIGNT"]
country_dat = df.loc[c_name]
cmap = matplotlib.cm.get_cmap("plasma")
cmap_blues = matplotlib.cm.get_cmap("Blues")
ax_extent = [-170, 180, -65, 85]
alpha = 1.0
edgecolor = "k"
linewidth = 0.5
ax = axs[0]
value = country_dat.loc["wgi_bin"]
ax.add_geometries([country.geometry],
projection,
facecolor = cmap_seg(cmap, value, 5)[0],
alpha = alpha,
edgecolor = edgecolor,
linewidth = linewidth)
ax.set_xlabel("WGI group")
ax.set_extent(ax_extent)
ax = axs[1]
value = country_dat.loc["epi_bin"]
ax.add_geometries([country.geometry],
projection,
facecolor = cmap_seg(cmap, value, 5)[0],
alpha = alpha,
edgecolor = edgecolor,
linewidth = linewidth)
ax.set_xlabel("EPI group")
ax.set_extent(ax_extent)
ax = axs[2]
value = country_dat.loc["diff"]
ax.add_geometries([country.geometry],
projection,
facecolor = cmap_seg(cmap_blues, value, 4)[0],
alpha = alpha,
edgecolor = edgecolor,
linewidth = linewidth)
ax.set_xlabel("difference")
ax.set_extent(ax_extent)
subplot_labels = ["WGI group", "EPI group", "Metric difference"]
for i, ax in enumerate(axs):
ax.text(0.5, -0.07, subplot_labels[i], va='bottom', ha='center',
rotation='horizontal', rotation_mode='anchor',
transform=ax.transAxes)
sm = plt.cm.ScalarMappable(cmap=cmap_seg(cmap, 5, 5)[1], norm = plt.Normalize(0, 5))
sm._A = []
cb = plt.colorbar(sm, ax = axs[1], values = [1,2,3,4, 5], ticks = [1,2,3,4,5])
sm2 = plt.cm.ScalarMappable(cmap=cmap_seg(cmap_blues, 5, 4)[1], norm = plt.Normalize(0, 4))
sm2._A = []
cb2 = plt.colorbar(sm2, ax = axs[2], values = [0,1,2,3], ticks = [0,1,2,3])
Try this:
# update your code for this specific line (added shrink option)
cb = plt.colorbar(sm, ax=axs[1], values=[1,2,3,4,5], ticks=[1,2,3,4,5], shrink=0.6)
And add these lines of code towards the end:
p00 = axs[0].get_position()
p01 = axs[1].get_position()
p00_new = [p00.x0, p01.y0, p00.width, p01.height]
axs[0].set_position(p00_new)
The plot should be similar to this:

Basemap is returning blank after add meridians, paralles and scale

I am trying to plot a depth map using Basemap in python. The contour and pcolormesh are working, but them when I add meridians, parallels and scale is returning a blank image.
I have tried to plot one by one, excluding meridians and paralles, and adding just scale, but returns a blank map and it is the same with the others. I used the same code before and it was working...
import netCDF4 as nc
from netCDF4 import Dataset
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib import ticker
grid = nc.Dataset('remo_grd.nc', mode='r')
h = grid.variables['h'][:]
h = h.astype(int)
h=-h
lon= grid.variables['lon_rho'][:]
lat= grid.variables['lat_rho'][:]
latmin = np.min(lat)
latmax= np.max(lat)
lonmax= np.max(lon)
lonmin= np.min(lon)
fig = plt.figure(1, figsize = (7,5.4), dpi = 100)
ax = fig.add_subplot(111)
m = Basemap(projection='merc', llcrnrlon=lonmin-0.2, urcrnrlon=lonmax+0.2, llcrnrlat=latmin-0.2, urcrnrlat=latmax+0.2, lat_ts=0, resolution='i')
xi, yi = m(lon,lat)
m.ax = ax
cs= m.pcolormesh(xi, yi, np.squeeze(h), shading = 'flat', zorder = 2)
levels = [-1000, -200]
a = m.contour(xi, yi, np.squeeze(h), levels, colors = 'black', linestyles = 'solid', linewidth= 1.5, extend = 'both', zorder = 3 )
plt.clabel(a, inline=2, fontsize= 10, linewidth= 1.0, fmt = '%.f', zorder= 4)
ax.text(0.5, -0.07, 'Longitude', transform=ax.transAxes, ha='center', va='center', fontsize = '10')
ax.text(-0.15, 0.5, 'Latitude', transform=ax.transAxes, ha= 'center', va='center', rotation='vertical', fontsize = '10')
m.drawcoastlines(linewidth=1.5, color = '0.1',zorder=5)
m.fillcontinents(color=('gray'),zorder=5 )
m.drawstates(linewidth = 0.5, zorder = 7)
m.drawmapboundary(color = 'black', zorder = 8, linewidth =1.2)
m.drawparallels(np.arange(int(latmin),int(latmax),3),labels=[1,0,0,0], linewidth=0.0, zorder =0)
m.drawmeridians(np.arange(int(lonmin),int(lonmax),3),labels=[0,0,0,1], linewidth=0.0)
cbar = plt.colorbar(cs, shrink=0.97, extend = 'both')
cbar.set_ticks([-10, -250, -500, -750, -1000, -1250, -1500, -1750, -2000, -2250, -2500])
cbar.set_ticklabels([-10, -250, -500, -750, -1000, -1250, -1500, -1750, -2000, -2250, -2500])
cbar.set_label('Meters (m)' , size = 10, labelpad = 20, rotation = 270)
ax = cbar.ax.tick_params(labelsize = 9)
titulo='Depth'
plt.title(titulo, va='bottom', fontsize='12')
#plot scale
dref=200
# Coordinates
lat0=m.llcrnrlat+0.9
lon0=m.llcrnrlon+1.9
#Tricked distance to provide to the the function
distance=dref/np.cos(lat0*np.pi/180.)
# Due to the bug, the function will draw a bar of length dref
scale=m.drawmapscale(lon0,lat0,lon0,lat0,distance, barstyle='fancy', units='km', labelstyle='simple',fillcolor1='w', fillcolor2='#555555', fontcolor='#555555', zorder = 8)
#Modify the labels with dref instead of distance
scale[12].set_text(dref/2)
scale[13].set_text(dref)
plt.show()
I've solved the problem! I was setting a specific order to plot each one of the details using the function zorder, so I was overlapping the data

adjust the position of colorbar and equalize the size of subplots

Following my previous question that didn't get any answer, I tried to solve my problem of adding colorbar instead of legend to my plots. There are couple of problems that I couldn't solve yet.
Update:
I want to move the colorbar to the proper position on the right of the plot.
I generate two plots with the same instruction but the second one looks completely different and I couldn't understand what caused this problem.
Here is my code:
import numpy as np
import pylab as plt
from matplotlib import rc,rcParams
rc('text',usetex=True)
rcParams.update({'font.size':10})
import matplotlib.cm as cm
from matplotlib.ticker import NullFormatter
import matplotlib as mpl
def plot(Z_s,CWL,filter_id,spectral_type,model_mag,mag,plot_name):
f= ['U38','B','V','R','I','MB420','MB464','MB485','MB518','MB571','MB604','MB646','MB696','MB753','MB815','MB856','MB914']
wavetable=CWL/(1+Z_s)
dd=model_mag-mag
nplist=['E', 'Sbc', 'Scd', 'Irr', 'SB3', 'SB2']
minimum,maximum=(0.,16.)
Z = [[0,0],[0,0]]
levels = list(np.linspace(0, 1, len(f)))
NUM_COLORS = len(f)
cm = plt.get_cmap('gist_rainbow')
mycolor=[]
for i in range(NUM_COLORS):
mycolor.append( cm(1.*i/NUM_COLORS)) # color will now be an RGBA tuple
mymap = mpl.colors.LinearSegmentedColormap.from_list('mycolors',mycolor)
CS3 = plt.contourf(Z, levels, cmap=mymap)
plt.clf()
FILTER=filter_id
SED=spectral_type
for (j,d) in enumerate(nplist):
bf=(SED==j)
if (j<3):
k=j
i_subplot = k + 1
fig = plt.figure(1, figsize=(5,5))
ax = fig.add_subplot(3,1,i_subplot)
for i in range(len(f)):
bb=np.where(FILTER[bf]==i)[0]
r=mycolor[i][0]
g=mycolor[i][1]
b=mycolor[i][2]
ax.scatter(wavetable[bb], dd[bb], s=1, color=(r,g,b))
if (k<2):
ax.xaxis.set_major_formatter( NullFormatter() )
ax.set_ylabel(r'$\Delta$ MAG',fontsize=10)
else:
ax.set_xlabel(r'WL($\AA$)',fontsize=10)
ax.set_ylabel(r'$\Delta$ MAG',fontsize=10)
fig.subplots_adjust(wspace=0,hspace=0)
ax.axhline(y=0,color='k')
ax.set_xlim(1000,9000)
ax.set_ylim(-3,3)
ax.set_xticks(np.linspace(1000, 9000, 16, endpoint=False))
ax.set_yticks(np.linspace(-3, 3, 4, endpoint=False))
ax.text(8500,2.1,nplist[j], {'color': 'k', 'fontsize': 10})
fontsize=8
for tick in ax.xaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
for tick in ax.yaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
if (j==2):
cbar_ax = fig.add_axes([0.9, 0.15, 0.05, 0.7])
cbar=plt.colorbar(CS3, cax=cbar_ax, ticks=range(0,len(f)),orientation='vertical')
cbar.ax.get_yaxis().set_ticks([])
for s, lab in enumerate(f):
cbar.ax.text( 0.08,(0.95-0.01)/float(len(f)-1) * s, lab, fontsize=8,ha='left')
fname = plot_name+'.'+nplist[0]+'.'+nplist[1]+'.'+nplist[2]+'.pdf'
plt.savefig(fname)
plt.close()
else:
k=j-3
i_subplot = k + 1
fig = plt.figure(1, figsize=(5,5))
ax = fig.add_subplot(3,1,i_subplot)
for i in range(len(f)):
bb=np.where(FILTER[bf]==i)[0]
r=mycolor[i][0]
g=mycolor[i][1]
b=mycolor[i][2]
ax.scatter(wavetable[bb], dd[bb], s=1, color=(r,g,b))
if (k<2):
ax.xaxis.set_major_formatter( NullFormatter() )
ax.set_ylabel(r'$\Delta$ MAG',fontsize=10)
else:
ax.set_xlabel(r'WL($\AA$)',fontsize=10)
ax.set_ylabel(r'$\Delta$ MAG',fontsize=10)
fig.subplots_adjust(wspace=0,hspace=0)
ax.axhline(y=0,color='k')
ax.set_xlim(1000,9000)
ax.set_ylim(-3,3)
ax.set_xticks(np.linspace(1000, 9000, 16, endpoint=False))
ax.set_yticks(np.linspace(-3, 3, 4, endpoint=False))
ax.text(8500,2.1,nplist[j], {'color': 'k', 'fontsize': 10})
fontsize=8
for tick in ax.xaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
for tick in ax.yaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
if (j==5):
cbar_ax = fig.add_axes([0.9, 0.15, 0.05, 0.7])
cbar=plt.colorbar(CS3, cax=cbar_ax, ticks=range(0,len(f)),orientation='vertical')
cbar.ax.get_yaxis().set_ticks([])
for s, lab in enumerate(f):
cbar.ax.text( 0.08,(0.95-0.01)/float(len(f)-1) * s, lab , fontsize=8,ha='left')
fname = plot_name+'.'+nplist[3]+'.'+nplist[4]+'.'+nplist[5]+'.pdf'
plt.savefig(fname)
plt.close()
a=np.loadtxt('calibration.photometry.information.capak.cat')
Z_s=a[:,0]
CWL=a[:,1]
filter_id=a[:,2]
spectral_type=a[:,3]
model_mag=a[:,4]
mag=a[:,5]
plot_name='test'
plot(Z_s,CWL,filter_id,spectral_type,model_mag,mag,plot_name)
you can also download the data from here.
I will appreciate to get any help.
You can use plt.subplots() passing the gridspec_kw parameter to adjust the axes' aspect ratio in a very flexible way, and then select the top axes to include the colorbar.
I've worked on your code simplifying it quite a bit. Furthermore, I've changed many things in your code such as: PEP8, removed repeated calls to plt.savefig()and ax methods. The result is:
import numpy as np
import pylab as plt
from matplotlib import rc, rcParams, colors
rc('text', usetex=True)
rcParams['font.size'] = 10
rcParams['axes.labelsize'] = 8
def plot(Z_s, CWL, filter_id, spectral_type, model_mag, mag, plot_name):
f= ['U38', 'B', 'V', 'R', 'I', 'MB420', 'MB464', 'MB485', 'MB518',
'MB571', 'MB604', 'MB646', 'MB696', 'B753', 'MB815', 'MB856',
'MB914']
wavetable = CWL/(1+Z_s)
dd = model_mag-mag
nplist = ['E', 'Sbc', 'Scd', 'Irr', 'SB3', 'SB2']
minimum, maximum = (0., 16.)
Z = [[0, 0],[0, 0]]
levels = list(np.linspace(0, 1, len(f)+1))
NUM_COLORS = len(f)
cmap = plt.get_cmap('gist_rainbow')
mycolor = []
for i in range(NUM_COLORS):
mycolor.append(cmap(1.*i/NUM_COLORS))
mymap = colors.LinearSegmentedColormap.from_list('mycolors', mycolor)
CS3 = plt.contourf(Z, levels, cmap=mymap)
coords = CS3.get_array()
coords = coords[:-1] + np.diff(coords)/2.
FILTER = filter_id
SED = spectral_type
dummy = 2
xmin = 1000
xmax = 9000
ymin = -3
ymax = 3
fig, axes = plt.subplots(nrows=5, figsize=(5, 6),
gridspec_kw=dict(height_ratios=[0.35, 0.05, 1, 1, 1]))
fig2, axes2 = plt.subplots(nrows=5, figsize=(5, 6),
gridspec_kw=dict(height_ratios=[0.35, 0.05, 1, 1, 1]))
fig.subplots_adjust(wspace=0, hspace=0)
fig2.subplots_adjust(wspace=0, hspace=0)
axes_all = np.concatenate((axes[dummy:], axes2[dummy:]))
dummy_axes = np.concatenate((axes[:dummy], axes2[:dummy]))
for ax in axes_all:
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
ax.axhline(y=0, color='k')
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
ax.set_xticks([])
ax.set_yticks(np.linspace(ymin, ymax, 4, endpoint=False))
ax.set_ylabel(r'$\Delta$ MAG', fontsize=10)
axes[-1].set_xticks(np.linspace(xmin, xmax, 16, endpoint=False))
axes2[-1].set_xticks(np.linspace(xmin, xmax, 16, endpoint=False))
plt.setp(axes[-1].xaxis.get_majorticklabels(), rotation=30)
plt.setp(axes2[-1].xaxis.get_majorticklabels(), rotation=30)
axes[-1].set_xlabel(r'WL($\AA$)', fontsize=10)
axes2[-1].set_xlabel(r'WL($\AA$)', fontsize=10)
for ax in dummy_axes:
for s in ax.spines.values():
s.set_visible(False)
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
ax.set_xticks([])
ax.set_yticks([])
for axes_i in [axes, axes2]:
cbar = plt.colorbar(CS3, ticks=[], orientation='horizontal',
cax=axes_i[0])
for s, lab in enumerate(f):
cbar.ax.text(coords[s], 0.5, lab, fontsize=8, va='center',
ha='center', rotation=90,
transform=cbar.ax.transAxes)
for (j, d) in enumerate(nplist):
bf = (SED==j)
if (j<3):
k = j
ax = axes[k+dummy]
ax.text(8500, 2.1, nplist[j], {'color': 'k', 'fontsize': 10})
for i in range(len(f)):
bb = np.where(FILTER[bf]==i)[0]
ax.scatter(wavetable[bb], dd[bb], s=1, color=mycolor[i])
else:
k = j-3
ax = axes2[k+dummy]
ax.text(8500, 2.1, nplist[j], {'color': 'k', 'fontsize': 10})
for i in range(len(f)):
bb = np.where(FILTER[bf]==i)[0]
ax.scatter(wavetable[bb], dd[bb], s=1, color=mycolor[i])
fname = '.'.join([plot_name, nplist[0], nplist[1], nplist[2], 'png'])
fig.savefig(fname)
fname = '.'.join([plot_name, nplist[3], nplist[4], nplist[5], 'png'])
fig2.savefig(fname)
if __name__=='__main__':
a = np.loadtxt('calibration.photometry.information.capak.cat')
Z_s = a[:, 0]
CWL = a[:, 1]
filter_id = a[:, 2]
spectral_type = a[:, 3]
model_mag = a[:, 4]
mag = a[:, 5]
plot_name = 'test'
plot(Z_s, CWL, filter_id, spectral_type, model_mag, mag, plot_name)
which gives:

Categories

Resources