tight_layout with gridspec and shared x-axis - python

Still new to python so I am still trying to learn and become better. The initial problem was that the y-labels for my gridspec did not stay within the figure size, so I tried to call the tight_layout which solved the initial problem but created another
The problem seems to be that my 1. gridspec which initially have a shared x-axis and stays in 1 "figure" separates into 2 figures when I call the tight_layout command. Can I still have tigh_layout or do I need to have another code for my y-labels to stay within figure size?
To produce the following figure I use the code:
colors = ["tab:blue", "#ed7d74", "#71bf82", "#000000", "C4", "C5", "C6"]
fig = plt.figure(figsize = (13,7))
#gs = fig.add_gridspec(2, hspace=0, height_ratios=[2,1])
gs1 = fig.add_gridspec(2, hspace=0, left=0.05, right=0.6, height_ratios=[2,1])
axs1 = gs1.subplots(sharex=True, sharey=False)
#fig.suptitle(lgn[idx])
mark_size = 3
axs1[0].plot(t, U, '.', markersize = mark_size, color = colors[1], alpha = 1)
axs1[0].plot(t0, U0, '.', markersize = mark_size, color = colors[0], alpha = 1)
axs1[0].plot(t1, U1, '.', markersize = mark_size, color = colors[0], alpha = 1)
axs1[0].plot(t2, U2, '.', markersize = mark_size, color = colors[0], alpha = 1)
axs1[0].plot(t3, U3, '.', markersize = mark_size, color = colors[0], alpha = 1)
axs1[0].plot(t4, U4, '.', markersize = mark_size, color = colors[0], alpha = 1)
axs1[0].set(ylabel="Potential [V]")
axs1[0].grid(alpha = 0.3)
axs1[0].set_ylim(min(U)-0.1, max(U)+0.1)
axs1[1].plot(t, j, '-', markersize = 2, color = colors[2], alpha = 1)
axs1[1].grid(alpha = 0.3)
axs1[1].set(ylabel="j [mA cm$^{-2}$]")
axs1[1].set(xlabel="Time [hours]")
axs1[1].set_ylim(0, max(j)+50)
gs2 = fig.add_gridspec(2, hspace=0.03, left=0.65, right=0.98, top=0.98, bottom=0.02, height_ratios=[2,1])
axs2 = gs2.subplots(sharex=True, sharey=False)
axs2[0].plot(t[IDX4_s-(4*35):IDX4_e+(12*35)+15], U[IDX4_s-(4*35):IDX4_e+(12*35)+15], '.', markersize = mark_size, color = colors[1], alpha = 0.6)
axs2[0].plot(t4, U4, '.', markersize = mark_size, color = colors[0], alpha = 0.6)
#axs2[0].set(ylabel="Potential [V]")
axs2[0].grid(alpha = 0.3)
axs2[0].set_xlim(t[IDX4_s-(4*35)], t[IDX4_e+(12*36)+10])
axs2[1].plot(t[IDX4_s-(4*35):IDX4_e+(12*35)+15], j[IDX4_s-(4*35):IDX4_e+(12*35)+15], '-', markersize = 2, color = colors[2], alpha = 0.6)
axs2[1].grid(alpha = 0.3)
#axs2[1].set(ylabel="j [mA cm$^{-2}$]")
axs2[1].set(xlabel="Time [hours]")
axs2[0].set_yticklabels([])
axs2[1].set_yticklabels([])
axs1[0].fill_between((t[IDX4_s-(4*35):IDX4_e+(12*35)+15]), axs2[0].get_ylim()[0], axs2[0].get_ylim()[1], facecolor=(0,0,0,0.1), edgecolor=(0,0,0,1), zorder = 20)
axs1[1].fill_between((t[IDX4_s-(4*35):IDX4_e+(12*35)+10]), axs2[1].get_ylim()[0]+10, axs2[1].get_ylim()[1], facecolor=(0,0,0,0.1), edgecolor=(0,0,0,1), zorder = 20)# facecolor=colors[3], alpha=0.1)
con1 = ConnectionPatch(xyA=(t[IDX4_s-(4*35)], axs2[0].get_ylim()[0]), coordsA=axs1[0].transData, xyB=(t[IDX4_s-(4*35)], axs2[0].get_ylim()[0]), coordsB=axs2[0].transData, color = colors[3])
fig.add_artist(con1)
con2 = ConnectionPatch(xyA=(t[IDX4_s-(4*35)], axs2[0].get_ylim()[1]), coordsA=axs1[0].transData, xyB=(t[IDX4_s-(4*35)], axs2[0].get_ylim()[1]), coordsB=axs2[0].transData, color = colors[3])
fig.add_artist(con2)
con3 = ConnectionPatch(xyA=(t[IDX4_s-(4*35)], axs2[1].get_ylim()[0]+10), coordsA=axs1[1].transData, xyB=(t[IDX4_s-(4*35)], axs2[1].get_ylim()[0]), coordsB=axs2[1].transData, color = colors[3])#, linestyle='--')
fig.add_artist(con3)
con4 = ConnectionPatch(xyA=(t[IDX4_s-(4*35)], axs2[1].get_ylim()[1]), coordsA=axs1[1].transData, xyB=(t[IDX4_s-(4*35)], axs2[1].get_ylim()[1]), coordsB=axs2[1].transData, color = colors[3])
fig.add_artist(con4)
When I add this code I get the following figure
gs1.tight_layout(fig, rect=[0, 0, 0.6, 1.0])
gs2.tight_layout(fig, rect=[0.65, 0.02, 0.98, 0.98])
As you can see the figure on left splits into 2 figures - can this be solved ?

Instead of using tight_layout() in this specific case, why don't you use subplots_adjust() to change the margins?
E.g. run
plt.subplots_adjust(left=.2, bottom=.2, right=.8, top=.8)
which will give you a lot of space around your panels, and then decrease / increase the values until it looks good.

Related

Matplotlib - Scientific notations overlap on multiple y-axis graph

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()

Matplotlib: correct placement of subplot annotations?

I want to create subplots with Matplotlib by looping over my data. However, I don't get the annotations into the correct position, apparently not even into the correct subplot. Also, the common x- and y-axis labels don't work.
My real data is more complex but here is an example that reproduces the error:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import seaborn as sns
# create data
distributions = []
first_values = []
second_values = []
for i in range(4):
distributions.append(np.random.normal(0, 0.5, 100))
first_values.append(np.random.uniform(0.7, 1))
second_values.append(np.random.uniform(0.7, 1))
# create subplot
fig, axes = plt.subplots(2, 2, figsize = (15, 10))
legend_elements = [Line2D([0], [0], color = '#76A29F', lw = 2, label = 'distribution'),
Line2D([0], [0], color = '#FEB302', lw = 2, label = '1st value', linestyle = '--'),
Line2D([0], [0], color = '#FF5D3E', lw = 2, label = '2nd value')]
# loop over data and create subplots
for data in range(4):
if data == 0:
position = axes[0, 0]
if data == 1:
position = axes[0, 1]
if data == 2:
position = axes[1, 0]
if data == 3:
position = axes[1, 1]
dist = distributions[data]
first = first_values[data]
second = second_values[data]
sns.histplot(dist, alpha = 0.5, kde = True, stat = 'density', bins = 20, color = '#76A29F', ax = position)
sns.rugplot(dist, alpha = 0.5, color = '#76A29F', ax = position)
position.annotate(f'{np.mean(dist):.2f}', (np.mean(dist), 0.825), xycoords = ('data', 'figure fraction'), color = '#76A29F')
position.axvline(first, 0, 0.75, linestyle = '--', alpha = 0.75, color = '#FEB302')
position.axvline(second, 0, 0.75, linestyle = '-', alpha = 0.75, color = '#FF5D3E')
position.annotate(f'{first:.2f}', (first, 0.8), xycoords = ('data', 'figure fraction'), color = '#FEB302')
position.annotate(f'{second:.2f}', (second, 0.85), xycoords = ('data', 'figure fraction'), color = '#FF5D3E')
position.set_xticks(np.arange(round(min(dist), 1) - 0.1, round(max(max(dist), max([first]), max([second])), 1) + 0.1, 0.1))
plt.xlabel("x-axis name")
plt.ylabel("y-axis name")
plt.legend(handles = legend_elements, bbox_to_anchor = (1.5, 0.5))
plt.show()
The resulting plot looks like this:
What I want is to have
the annotations in the correct subplot next to the vertical lines / the mean of the distribution
shared x- and y-labels for all subplot or at least for each row / column
Any help is highly appreciated!
If you use the function to make the subplot a single array (axes.flatten()) and modify it to draw the graph sequentially, you can draw the graph. The colors of the annotations have been partially changed for testing purposes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import seaborn as sns
np.random.seed(202000104)
# create data
distributions = []
first_values = []
second_values = []
for i in range(4):
distributions.append(np.random.normal(0, 0.5, 100))
first_values.append(np.random.uniform(0.7, 1))
second_values.append(np.random.uniform(0.7, 1))
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
legend_elements = [Line2D([0], [0], color = '#76A29F', lw = 2, label = 'distribution'),
Line2D([0], [0], color = '#FEB302', lw = 2, label = '1st value', linestyle = '--'),
Line2D([0], [0], color = '#FF5D3E', lw = 2, label = '2nd value')]
for i,ax in enumerate(axes.flatten()):
sns.histplot(distributions[i], alpha=0.5, kde=True, stat='density', bins=20, color='#76A29F', ax=ax)
sns.rugplot(distributions[i], alpha=0.5, color='#76A29F', ax=ax)
ax.annotate(f'{np.mean(distributions[i]):.2f}', (np.mean(distributions[i]), 0.825), xycoords='data', color='red')
ax.axvline(first_values[i], 0, 0.75, linestyle = '--', alpha = 0.75, color = '#FEB302')
ax.axvline(second_values[i], 0, 0.75, linestyle = '-', alpha = 0.75, color = '#FF5D3E')
ax.annotate(f'{first_values[i]:.2f}', (first_values[i], 0.8), xycoords='data', color='#FEB302')
ax.annotate(f'{second_values[i]:.2f}', (second_values[i], 0.85), xycoords='data', color = '#FF5D3E')
ax.set_xticks(np.arange(round(min(distributions[i]), 1) - 0.1, round(max(max(distributions[i]), max([first_values[i]]), max([second_values[i]])), 1) + 0.1, 0.1))
plt.xlabel("x-axis name")
plt.ylabel("y-axis name")
plt.legend(handles = legend_elements, bbox_to_anchor = (1.35, 0.5))
plt.show()

Turning of exponent label in plot and gaining exponent information

Hello I'm looking for a way to turn off the exponent on an matplot figure. Is there an easy way to turn that off?
I'm also looking for a possibility get that exponent value. I tried already
ax1.yaxis.get_offset_text().get_text()
But that only results in a string with latex format. And I'd prefer to have a float.
In the end I'd like to have an easy possibility to position the Exponent anywhere on the plot.
I hope that concludes what I'm looking for.
Thank you for your help in advance :)
edit:
Some more code:
fig = plt.figure(figsize = size)
gs = gridspec.GridSpec(1,2)
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharey=ax1)
plt.setp(ax2.get_yticklabels(), visible=False)
ax1.tick_params(direction='in',labelsize=fontsize)
ax2.tick_params(direction='in',labelsize=fontsize)
ax2.yaxis.tick_right()
ax2.yaxis.set_label_position("right")
ax1.set_ylabel('Intensität / willk. Einh.')
ax1.set_xlabel('Messpunkt')
ax2.set_xlabel('Messpunkt')
test = ax1.yaxis.get_offset_text().get_text()
ax1.plot([],[], linestyle = 'None',label = test + name + r' bei $p_0 =\,$'+ str(pressure[0]) + r'$\,$ mbar')
ax2.plot([],[], linestyle = 'None',label = name + r' bei $p_0 =\,$'+ str(pressure[1])+ r'$\,$ mbar')
plt.setp([ax1, ax2], visible = True)
gs.tight_layout(fig)
y0 = file(Messung['Pfad'][key[0]],Messung['Name'][key[0]])
y1 = file(Messung['Pfad'][key[1]],Messung['Name'][key[1]])
x = np.arange(0,len(y0),1)
ax1.plot(x,y1, 's', label = 'Messpunkt', markersize = 3, color = colors(T0))
ax2.plot(x,y0 , 's', label = 'Messpunkt', markersize = 3, color = colors(T0))
#format_label_string_with_exponent(ax1, axis='both')
#format_label_string_with_exponent(ax2, axis='both')
ax1.legend(bbox_to_anchor=(0.3, 1, 0, 0.08), loc=1, ncol= 2, mode="expand",borderaxespad=0.5,frameon=False, fontsize = fontsize)
ax2.legend(bbox_to_anchor=(0.3, 1, 0, 0.08), loc=1, ncol= 2, mode="expand",borderaxespad=0.5,frameon=False, fontsize = fontsize)
plt.show()
I found a way to turn the exponent off, in case someone is looking for that:
ax.yaxis.offsetText.set_visible(False)
ax is the name of the axis and its possible to turn it separately on or off x and y axis

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

How to put the axis for two subplots separately?

I am trying to have two sub-plots in the figure in the Python Script. But I am not able to set the axis separately according to my inputs. Can anybody help me to set the x-axis, y-axis for each of the sub-plot separately?
I am including the piece of code that I have done, which was not giving me the result.
fig = plt.figure()
ax = plt.subplot(121) # To show the ascending order
plt.xlabel ('RF Input Power (dBm)', fontsize = 'small')
plt.ylabel ('Gain (dB)', fontsize = 'small')
tx = plt.title('Gain Vs Ascending RFInputAmpl for ' + str(measuredFrequencyUnderTest) + 'MHz', fontsize = 'small')
axPlotAxis = plt.axis([rfInputMin, rfInputMax, -20.0, 30.0])
# Now, Plot all the gain stages across the RFInput
ax.plot(rfInput_Plot, lna_Pre_Plot, color = 'r', marker = '+', label = 'lna_Pre')
ax.plot(rfInput_Plot, lna_Post_Plot, color = 'm', marker = 'x', label = 'lna_Post')
ax.plot(rfInput_Plot, sampler1_Plot, color = 'k', marker = '*', label = 'Sampler1')
ax.plot(rfInput_Plot, sampler2_Plot, color = 'c', marker = 's', label = 'Sampler2')
ax.plot(rfInput_Plot, vga_Plot, color = 'b', marker = 'p', label = 'VGA')
ax.plot(rfInput_Plot, dagc1_Plot, color = 'g', marker = 'H', label = 'DAGC1')
ax.plot(rfInput_Plot, dagc2_Plot, color = 'y', marker = 'v', label = 'DAGC2')
# Put the Legend
ax.legend(loc='upper center', bbox_to_anchor = (1.3, -0.05), shadow=True,numpoints = 1, prop = legend_font_props, ncol = 3)
# Now, come to the second plot
ay = plt.subplot(122) # To show the descending order
plt.xlabel ('RF Input Power (dBm)', fontsize = 'small')
plt.ylabel ('Gain (dB)', fontsize = 'small', horizontalalignment = 'left')
ty = plt.title('Gain Vs Descending RF Input for '+ str(measuredFrequencyUnderTest)+ 'MHz', fontsize = 'small')
# Now, fix the x axis here in descending order
plt.axis([rfInputMax, rfInputMin, y_Min_Value, y_Max_Value])
plt.minorticks_on()
Is there something wrong that I am performing? Pls help me to correct it.
You can set independent scales very easily (from an ipython -pylab session):
In [8]: ax = plt.subplot(121)
In [9]: ay = plt.subplot(122)
In [10]: ax.set_xlim((-1,2))
In [11]: ax.set_ylim((10,20))
In [12]: ay.set_xlim((-10,4))
In [13]: ay.set_ylim((3,5))

Categories

Resources