remove overlapping tick marks on subplot in matplotlib - python

I've create the following set of subplots using the following function:
def create31fig(size,xlabel,ylabel,title=None):
fig = plt.figure(figsize=(size,size))
ax1 = fig.add_subplot(311)
ax2 = fig.add_subplot(312)
ax3 = fig.add_subplot(313)
plt.subplots_adjust(hspace=0.001)
plt.subplots_adjust(wspace=0.001)
ax1.set_xticklabels([])
ax2.set_xticklabels([])
xticklabels = ax1.get_xticklabels()+ ax2.get_xticklabels()
plt.setp(xticklabels, visible=False)
ax1.set_title(title)
ax2.set_ylabel(ylabel)
ax3.set_xlabel(xlabel)
return ax1,ax2,ax3
How do I make sure the top and bottom of subplot(312) do not overlap with their neighbours? Thanks.

In the ticker module there is a class called MaxNLocator that can take a prune kwarg.
Using that you can remove the topmost tick of the 2nd and 3rd subplots:
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator # added
def create31fig(size,xlabel,ylabel,title=None):
fig = plt.figure(figsize=(size,size))
ax1 = fig.add_subplot(311)
ax2 = fig.add_subplot(312)
ax3 = fig.add_subplot(313)
plt.subplots_adjust(hspace=0.001)
plt.subplots_adjust(wspace=0.001)
ax1.set_xticklabels([])
ax2.set_xticklabels([])
xticklabels = ax1.get_xticklabels() + ax2.get_xticklabels()
plt.setp(xticklabels, visible=False)
ax1.set_title(title)
nbins = len(ax1.get_xticklabels()) # added
ax2.yaxis.set_major_locator(MaxNLocator(nbins=nbins, prune='upper')) # added
ax2.set_ylabel(ylabel)
ax3.yaxis.set_major_locator(MaxNLocator(nbins=nbins,prune='upper')) # added
ax3.set_xlabel(xlabel)
return ax1,ax2,ax3
create31fig(5,'xlabel','ylabel',title='test')
Sample image after making those adjustments:
Aside: If the overlapping x- and y- labels in the lowest subplot are an issue consider "pruning" one of those as well.

Related

How to adjust the plot size in Matplotlib?

I'm trying to remove the white space from the plot that I created:
As it is possible to see, there a big white spot on the right and also on the bottom, how to fix it? Here is my script:
fig = plt.figure(figsize=(7,7))
ax1 = plt.subplot2grid((4,3), (0,0),)
ax2 = plt.subplot2grid((4,3), (1,0),)
ax3 = plt.subplot2grid((4,3), (0,1),)
ax4 = plt.subplot2grid((4,3), (1,1),)
data = self.dframe[i]
tes = print_data(data, self.issues, self.color, self.type_user)
tes.print_top(data=data, top=10, ax=ax1, typegraph="hbar", problem=self.issues[i], tone=self.color[i])
tes.print_top(data=data, top=10, ax=ax2, typegraph="prod_bar", problem=self.issues[i], tone=self.color[i])
tes.print_top(data=data, top=10, ax=ax3, typegraph="reg_hbar", problem=self.issues[i], tone=self.color[i])
tes.print_top(data=data, top=10, ax=ax4, typegraph=self.type_user, problem=self.issues[i], tone=self.color[i])
problem = self.issues[i]
plt.tight_layout()
name = problem + str('.PNG')
plt.close(fig)
fig.savefig(name)
You are creating too many subplots!
If we look at this line:
ax1 = plt.subplot2grid((4,3), (0,0),)
We can see the first argument given to subplot2grid are the dimensions of the subplot grid to be made, in this case 4 rows, and 3 columns. You are then plotting in the subplots in the top left of your figure (the second argument given) which leaves a lot of space that's not used.
So to solve this, reduce the number of subplots by using:
ax1 = plt.subplot2grid((2,2), (0,0),)
Full example:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(25)
fig = plt.figure(figsize=(7,7))
ax1 = plt.subplot2grid((2,2), (0,0),)
ax2 = plt.subplot2grid((2,2), (1,0),)
ax3 = plt.subplot2grid((2,2), (0,1),)
ax4 = plt.subplot2grid((2,2), (1,1),)
ax1.plot(data)
ax2.plot(data)
ax3.plot(data)
ax4.plot(data)
plt.show()
Giving:
you can use
plt.subplots_adjust(left=0.09, bottom=0.07, right=0.98, top=0.97, wspace=0.2 , hspace=0.17 ) to adjust the window.
But the issue is that a lot of the space in your plot is empty
maybe you should change
plt.subplot2grid((4,3)... to plt.subplot2grid((2,2)

Dynamic Subplots in Matplotlib GUI [duplicate]

I've got a figure that contains three subplots which are arranged vertically. Once I click into the figure, I want the second subplot ax2 to be hidden and the other plots to fill the space. A second click into the figure should restore the original plot and layout.
Hiding the subplot ax2 isn't a problem, but how can I rearrange the positions of the other subplots?
I've tried creating a new GridSpec, using the set_position and set_subplotspec methods, but nothing worked out. I'm sure I'm missing something here, any help would be appreciated.
This is my code:
import matplotlib.pyplot as plt
from matplotlib import gridspec
fig = plt.figure()
gs = gridspec.GridSpec(3, 1, height_ratios=[5, 2, 1])
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
ax3 = fig.add_subplot(gs[2], sharex=ax2)
visible = True
def toggle_ax2(event):
global visible
visible = not visible
ax2.set_visible(visible)
plt.draw()
fig.canvas.mpl_connect('button_press_event', toggle_ax2)
plt.show()
You can define two different GridSpecs. One would have 3 subplots, the other 2. Depending on the visibility of the middle axes, you change the position of the other two axes to obey to the first or second GridSpec.
(There is no need for any dummy figure or so, like other answers might suggest.)
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure()
gs = gridspec.GridSpec(3, 1, height_ratios=[5, 2, 1], hspace=0.3)
gs2 = gridspec.GridSpec(2,1, height_ratios=[5,3])
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
ax3 = fig.add_subplot(gs[2], sharex=ax2)
ax1.plot([1,2,3], [1,2,3], color="crimson")
ax2.plot([1,2,3], [2,3,1], color="darkorange")
ax3.plot([1,2,3], [3,2,1], color="limegreen")
visible = True
def toggle_ax2(event):
global visible
visible = not visible
ax2.set_visible(visible)
if visible:
ax1.set_position(gs[0].get_position(fig))
ax3.set_position(gs[2].get_position(fig))
else:
ax1.set_position(gs2[0].get_position(fig))
ax3.set_position(gs2[1].get_position(fig))
plt.draw()
fig.canvas.mpl_connect('button_press_event', toggle_ax2)
plt.show()
Left: original; right: after clicking
You can create a new gridspec instance, and use that to create some dummy figures in a second figure (you can close this before you plt.show, so you never actually see it, we just want to grab some positions from the axes here).
By storing the two possible positions for ax1 and ax3 from that dummy figure and the original figure, then you can use ax.set_position() in your toggle_ax2 function to change the positions of the remaining two axes.
import matplotlib.pyplot as plt
from matplotlib import gridspec
fig = plt.figure()
gs = gridspec.GridSpec(3, 1, height_ratios=[5, 2, 1])
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
ax3 = fig.add_subplot(gs[2], sharex=ax2)
# Store the original positions of ax1 and ax3
pos1_1 = ax1.get_position()
pos3_1 = ax3.get_position()
# Create a second gridspec for when ax2 is hidden. Keep 5:1 ratio
gs2 = gridspec.GridSpec(2, 1, height_ratios=[5, 1])
fig2 = plt.figure()
ax1_2 = fig2.add_subplot(gs2[0])
ax3_2 = fig2.add_subplot(gs2[1])
# Store the positions of ax1 and ax3 in the new gridspec
pos1_2 = ax1_2.get_position()
pos3_2 = ax3_2.get_position()
# Close the dummy figure2
plt.close(fig2)
visible = True
def toggle_ax2(event):
global visible
visible = not visible
ax2.set_visible(visible)
# Use the stored positions to switch between
# different arrangements of ax1 and ax3
if visible:
ax1.set_position(pos1_1)
ax3.set_position(pos3_1)
else:
ax1.set_position(pos1_2)
ax3.set_position(pos3_2)
plt.draw()
fig.canvas.mpl_connect('button_press_event', toggle_ax2)
plt.show()
Original configuration:
After removing ax2:

matplotlib: hide subplot and fill space with other subplots

I've got a figure that contains three subplots which are arranged vertically. Once I click into the figure, I want the second subplot ax2 to be hidden and the other plots to fill the space. A second click into the figure should restore the original plot and layout.
Hiding the subplot ax2 isn't a problem, but how can I rearrange the positions of the other subplots?
I've tried creating a new GridSpec, using the set_position and set_subplotspec methods, but nothing worked out. I'm sure I'm missing something here, any help would be appreciated.
This is my code:
import matplotlib.pyplot as plt
from matplotlib import gridspec
fig = plt.figure()
gs = gridspec.GridSpec(3, 1, height_ratios=[5, 2, 1])
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
ax3 = fig.add_subplot(gs[2], sharex=ax2)
visible = True
def toggle_ax2(event):
global visible
visible = not visible
ax2.set_visible(visible)
plt.draw()
fig.canvas.mpl_connect('button_press_event', toggle_ax2)
plt.show()
You can define two different GridSpecs. One would have 3 subplots, the other 2. Depending on the visibility of the middle axes, you change the position of the other two axes to obey to the first or second GridSpec.
(There is no need for any dummy figure or so, like other answers might suggest.)
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure()
gs = gridspec.GridSpec(3, 1, height_ratios=[5, 2, 1], hspace=0.3)
gs2 = gridspec.GridSpec(2,1, height_ratios=[5,3])
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
ax3 = fig.add_subplot(gs[2], sharex=ax2)
ax1.plot([1,2,3], [1,2,3], color="crimson")
ax2.plot([1,2,3], [2,3,1], color="darkorange")
ax3.plot([1,2,3], [3,2,1], color="limegreen")
visible = True
def toggle_ax2(event):
global visible
visible = not visible
ax2.set_visible(visible)
if visible:
ax1.set_position(gs[0].get_position(fig))
ax3.set_position(gs[2].get_position(fig))
else:
ax1.set_position(gs2[0].get_position(fig))
ax3.set_position(gs2[1].get_position(fig))
plt.draw()
fig.canvas.mpl_connect('button_press_event', toggle_ax2)
plt.show()
Left: original; right: after clicking
You can create a new gridspec instance, and use that to create some dummy figures in a second figure (you can close this before you plt.show, so you never actually see it, we just want to grab some positions from the axes here).
By storing the two possible positions for ax1 and ax3 from that dummy figure and the original figure, then you can use ax.set_position() in your toggle_ax2 function to change the positions of the remaining two axes.
import matplotlib.pyplot as plt
from matplotlib import gridspec
fig = plt.figure()
gs = gridspec.GridSpec(3, 1, height_ratios=[5, 2, 1])
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
ax3 = fig.add_subplot(gs[2], sharex=ax2)
# Store the original positions of ax1 and ax3
pos1_1 = ax1.get_position()
pos3_1 = ax3.get_position()
# Create a second gridspec for when ax2 is hidden. Keep 5:1 ratio
gs2 = gridspec.GridSpec(2, 1, height_ratios=[5, 1])
fig2 = plt.figure()
ax1_2 = fig2.add_subplot(gs2[0])
ax3_2 = fig2.add_subplot(gs2[1])
# Store the positions of ax1 and ax3 in the new gridspec
pos1_2 = ax1_2.get_position()
pos3_2 = ax3_2.get_position()
# Close the dummy figure2
plt.close(fig2)
visible = True
def toggle_ax2(event):
global visible
visible = not visible
ax2.set_visible(visible)
# Use the stored positions to switch between
# different arrangements of ax1 and ax3
if visible:
ax1.set_position(pos1_1)
ax3.set_position(pos3_1)
else:
ax1.set_position(pos1_2)
ax3.set_position(pos3_2)
plt.draw()
fig.canvas.mpl_connect('button_press_event', toggle_ax2)
plt.show()
Original configuration:
After removing ax2:

2 subplots sharing y-axis (no space between) with single color bar

Does anyone have a matplotlib example of two plots sharing the y-axis (with no space between the plots) with a single color bar pertaining to both subplots? I have not been able to find examples of this yet.
I created the following code based on your question. Personally I do not like it to have no space between the subplots at all. If you do want to change this at some point all you need to do is to replace plt.subplots_adjust(wspace = -.059) with plt.tight_layout().
Hope this helps
import numpy
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
#Random data
data = numpy.random.random((10, 10))
fig = plt.figure()
ax1 = fig.add_subplot(1,2,1, aspect = "equal")
ax2 = fig.add_subplot(1,2,2, aspect = "equal", sharey = ax1) #Share y-axes with subplot 1
#Set y-ticks of subplot 2 invisible
plt.setp(ax2.get_yticklabels(), visible=False)
#Plot data
im1 = ax1.pcolormesh(data)
im2 = ax2.pcolormesh(data)
#Define locations of colorbars for both subplot 1 and 2
divider1 = make_axes_locatable(ax1)
cax1 = divider1.append_axes("right", size="5%", pad=0.05)
divider2 = make_axes_locatable(ax2)
cax2 = divider2.append_axes("right", size="5%", pad=0.05)
#Create and remove the colorbar for the first subplot
cbar1 = fig.colorbar(im1, cax = cax1)
fig.delaxes(fig.axes[2])
#Create second colorbar
cbar2 = fig.colorbar(im2, cax = cax2)
#Adjust the widths between the subplots
plt.subplots_adjust(wspace = -.059)
plt.show()
The result is the following:

How to add a single colobar that will show the data from 2 different subplot

What i wanna do is adding a single colorbar (at the right side of the figure shown below), that will show the colorbar for both subplots (they are at the same scale).
Another thing doesn't really make sense for me is why the lines I try to draw on the end of the code are not drawn (they are supposed to be horizontal lines on the center of both plots)
Thanks for the help.
Here are the code:
idx=0
b=plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B=np.zeros((2*len(self.Chan),len(b[0])))
B[idx,:]=20*log10(b[0])
c=plt.psd(dOD_filt[:,idx],Fs=self.fs,NFFT=512)
C=np.zeros((2*len(self.Chan),len(b[0])))
C[idx,:]=20*log10(c[0])
for idx in range(2*len(self.Chan)):
b=plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B[idx,:]=20*log10(b[0])
c=plt.psd(dOD_filt[:,idx],Fs=self.fs,NFFT=512)
C[idx,:]=20*log10(c[0])
## Calculate the color scaling for the imshow()
aux1 = max(max(B[i,:]) for i in range(size(B,0)))
aux2 = min(min(B[i,:]) for i in range(size(B,0)))
bux1 = max(max(C[i,:]) for i in range(size(C,0)))
bux2 = min(min(C[i,:]) for i in range(size(C,0)))
scale1 = 0.75*max(aux1,bux1)
scale2 = 0.75*min(aux2,bux2)
fig, axes = plt.subplots(nrows=2, ncols=1,figsize=(7,7))#,sharey='True')
fig.subplots_adjust(wspace=0.24, hspace=0.35)
ii=find(c[1]>=frange)[0]
## Making the plots
cax=axes[0].imshow(B, origin = 'lower',vmin=scale2,vmax=scale1)
axes[0].set_ylim((0,2*len(self.Chan)))
axes[0].set_xlabel(' Frequency (Hz) ')
axes[0].set_ylabel(' Channel Number ')
axes[0].set_title('Pre-Filtered')
cax2=axes[1].imshow(C, origin = 'lower',vmin=scale2,vmax=scale1)
axes[1].set_ylim(0,2*len(self.Chan))
axes[1].set_xlabel(' Frequency (Hz) ')
axes[1].set_ylabel(' Channel Number ')
axes[1].set_title('Post-Filtered')
axes[0].annotate('690nm', xy=((ii+1)/2, len(self.Chan)/2-1),
xycoords='data', va='center', ha='right')
axes[0].annotate('830nm', xy=((ii+1)/2, len(self.Chan)*3/2-1 ),
xycoords='data', va='center', ha='right')
axes[1].annotate('690nm', xy=((ii+1)/2, len(self.Chan)/2-1),
xycoords='data', va='center', ha='right')
axes[1].annotate('830nm', xy=((ii+1)/2, len(self.Chan)*3/2-1 ),
xycoords='data', va='center', ha='right')
axes[0].axis('tight')
axes[1].axis('tight')
## Set up the xlim to aprox frange Hz
axes[0].set_xlim(left=0,right=ii)
axes[1].set_xlim(left=0,right=ii)
## Make the xlabels become the actual frequency number
ticks = linspace(0,ii,10)
tickslabel = linspace(0.,frange,10)
for i in range(10):
tickslabel[i]="%.1f" % tickslabel[i]
axes[0].set_xticks(ticks)
axes[0].set_xticklabels(tickslabel)
axes[1].set_xticks(ticks)
axes[1].set_xticklabels(tickslabel)
## Draw a line to separate the two different wave lengths, and name each region
l1 = Line2D([0,frange],[28,28],ls='-',color='black')
axes[0].add_line(l1)
axes[1].add_line(l1)
And here the figure it makes:
If any more info are needed, just ask.
Basically, figure.colorbar() is good for both images, as long as their are not with too different scales. So you could let matplotlib do it for you... or you manually position your colorbar on axes inside the images. Here is how to control the location of the colorbar:
import numpy as np
from matplotlib import pyplot as plt
A = np.random.random_integers(0, 10, 100).reshape(10, 10)
B = np.random.random_integers(0, 10, 100).reshape(10, 10)
fig = plt.figure()
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
mapable = ax1.imshow(A, interpolation="nearest")
cax = ax2.imshow(A, interpolation="nearest")
# set the tickmarks *if* you want cutom (ie, arbitrary) tick labels:
cbar = fig.colorbar(cax, ax=None)
fig = plt.figure(2)
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
mapable = ax1.imshow(A, interpolation="nearest")
cax = ax2.imshow(A, interpolation="nearest")
# on the figure total in precent l b w , height
ax3 = fig.add_axes([0.1, 0.1, 0.8, 0.05]) # setup colorbar axes.
# put the colorbar on new axes
cbar = fig.colorbar(mapable,cax=ax3,orientation='horizontal')
plt.show()
Note ofcourse you can position ax3 as you wish, on the side, on the top, where ever,
as long as it is in the boundaries of the figure.
I don't know why your line2D is not appearing.
I added to my code before plt.show() the following and everything is showing:
from mpl_toolkits.axes_grid1 import anchored_artists
from matplotlib.patheffects import withStroke
txt = anchored_artists.AnchoredText("SC",
loc=2,
frameon=False,
prop=dict(size=12))
if withStroke:
txt.txt._text.set_path_effects([withStroke(foreground="w",
linewidth=3)])
ax1.add_artist(txt)
## Draw a line to separate the two different wave lengths, and name each region
l1 = plt.Line2D([-1,10],[5,5],ls='-',color='black',lineswidth=10)
ax1.add_line(l1)

Categories

Resources