discontinous axis in subplot - python matplotlib - python

I would like to have plot with an y axis that is devided into two parts. The lower part should have a normal scale while the upper one should scale with a factor of 10.
I already found some examples on how to make plots with broken x or y axes, for example:
http://matplotlib.org/examples/pylab_examples/broken_axis.html
But I do not understand how to achieve this, when I want to apply this to one single subplot inside a 2x2 grid of plots. If it is important, I set up the plots like this:
fig = plt.figure()
fig.set_size_inches(8, 6)
fig.add_subplot(221)
[...]
fig.add_subplot(222)
[...]

You could use gridspec to layout the shape and location of the axes:
import numpy as np
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
gs = gridspec.GridSpec(4, 2)
ax00 = plt.subplot(gs[:2, 0])
ax01 = plt.subplot(gs[:2, 1])
ax10a = plt.subplot(gs[2, 0])
ax10b = plt.subplot(gs[3, 0])
ax11 = plt.subplot(gs[2:, 1])
x = np.linspace(-1, 1, 500)
y = 100*np.cos(10*x)**2*np.exp(-x**2)
for ax in (ax00, ax01, ax10a, ax10b, ax11):
ax.plot(x, y)
ax10a.set_ylim(60, 110)
ax10b.set_ylim(0, 10)
ax10a.spines['bottom'].set_visible(False)
ax10b.spines['top'].set_visible(False)
ax10a.xaxis.tick_top()
ax10a.tick_params(labeltop='off') # don't put tick labels at the top
ax10b.xaxis.tick_bottom()
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax10a.transAxes, color='k', clip_on=False)
ax10a.plot((-d,+d),(-d,+d), **kwargs) # top-left diagonal
ax10a.plot((1-d,1+d),(-d,+d), **kwargs) # top-right diagonal
kwargs.update(transform=ax10b.transAxes) # switch to the bottom axes
ax10b.plot((-d,+d),(1-d,1+d), **kwargs) # bottom-left diagonal
ax10b.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-right diagonal
plt.tight_layout()
plt.show()

Couldn't you set up a 4x4 grid of axes, and have 3 of the axes span 2x2 of that space? Then the plot you want to have broken axes on can just cover the remaining 2x2 space as parts ax4_upper and ax4_lower.
ax1 = plt.subplot2grid((4, 4), (0, 0), colspan=2, rowspan=2)
ax2 = plt.subplot2grid((4, 4), (0, 2), colspan=2, rowspan=2)
ax3 = plt.subplot2grid((4, 4), (2, 0), colspan=2, rowspan=2)
ax4_upper = plt.subplot2grid((4, 4), (2, 2), colspan=2, rowspan=1)
ax4_lower = plt.subplot2grid((4, 4), (3, 2), colspan=2, rowspan=1)
You can then set the ylim values for ax4_upper and ax4_lower, and continue as your example showed:
# hide the spines between ax4 upper and lower
ax4_upper.spines['bottom'].set_visible(False)
ax4_lower.spines['top'].set_visible(False)
ax4_upper.xaxis.tick_top()
ax4_upper.tick_params(labeltop='off') # don't put tick labels at the top
ax4_lower.xaxis.tick_bottom()
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax4_upper.transAxes, color='k', clip_on=False)
ax4_upper.plot((-d,+d),(-d,+d), **kwargs) # top-left diagonal
ax4_upper.plot((1-d,1+d),(-d,+d), **kwargs) # top-right diagonal
kwargs.update(transform=ax4_lower.transAxes) # switch to the bottom axes
ax4_lower.plot((-d,+d),(1-d,1+d), **kwargs) # bottom-left diagonal
ax4_lower.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-right diagonal
plt.show()

Related

Python Subplot2Grid - controlling axis labels

I am using the Subplot2Grid functionality within Matplotlib to combine two figures with different orientations, 4 bar plots (full width) and then 3 scatter plots splitting the full width into 3 columns, plus an extra space for a legend. The different sections have axes that need to align so I have used sharex = ax1 and sharey = ax1 within Subplot2Grid to implement this successfully.
However, I now cannot seem to control the axis labels the same as I would just using regular subplots function, having the x-axis tick labels showing only on the final bar plot and the y-axis tick labels showing only on the left-most scatter plot.
Plotting using Subplot2Grid, extra axis labels showing
I have tried the ax.set_xticklabels('') to try and switch them off, but the sharex/sharey seems to override them? I have also put the ax.set_xticklabels('') at the end of the code (after they are defined in ax4) and it switches them all off, not just the axis the one that is called (ax1, ax2 or ax3)
Relevant parts of the code are below:
# figure setup
fig = plt.figure()
fig.set_figheight(15)
fig.set_figwidth(9)
ax1 = plt.subplot2grid(shape=(6, 3), loc=(0, 0), colspan=3)
ax2 = plt.subplot2grid(shape=(6, 3), loc=(1, 0), colspan=3,sharex=ax1)
ax3 = plt.subplot2grid(shape=(6, 3), loc=(2, 0), colspan=3,sharex=ax1)
ax4 = plt.subplot2grid(shape=(6, 3), loc=(3, 0), colspan=3,sharex=ax1)
ax5 = plt.subplot2grid(shape=(6, 3), loc=(5, 0))
ax6 = plt.subplot2grid(shape=(6, 3), loc=(5, 1),sharex=ax5,sharey=ax5)
ax7 = plt.subplot2grid(shape=(6, 3), loc=(5, 2),sharex=ax5,sharey=ax5)
# plotting bars here
# first bar plot
ax1.set_title('Inundation area')
ax1.set_xticklabels('')
ylbl0 = 'Inundation area \n' + r'$(km^2)$'
ax1.set_ylabel(ylbl0)
# repeat for ax2 & ax3
# last bar plot
ax4.set_title(r'$\Delta$ Shear Stress')
ax4.set_xticks(np.arange(len(df_bars)))
ax4.set_xticklabels(df_bars['Reach Number'])
ax4.invert_xaxis()
ax4.axhline(y=0,c='k',lw = 0.5)
ax4.set_xlabel('Reach number')
ax4.set_ylabel('% change \n (2019-2020)')
Same occurs when using sharey for the 3 scatter plots and ax.set_yticklabels('')
ax1.tick_params(labelbottom=False)
does what you want.
This example works for me:
fig = plt.figure()
fig.set_figheight(15)
fig.set_figwidth(9)
ax1 = plt.subplot2grid(shape=(2, 1), loc=(0, 0), colspan=3)
ax1.plot(np.random.rand(10))
ax2 = plt.subplot2grid(shape=(2, 1), loc=(1, 0), colspan=3,sharex=ax1)
ax2.plot(np.random.rand(10))
ax1.tick_params(labelbottom=False)
plt.show()

Matplotlib get subplot (axes) size?

Just wandering - how can one obtain the size of a subplot (axes?) in Matplotlib?
If I do Ctrl-F "size" in https://matplotlib.org/3.1.1/api/axes_api.html - there is only one match, in context: "... with varying marker size and/or ...", so it does not really tell me how to find the size of the axes.
Say, I have the same code as in Interactively resize figure and toggle plot visibility in Matplotlib?
#!/usr/bin/env python3
import matplotlib
print("matplotlib.__version__ {}".format(matplotlib.__version__))
import matplotlib.pyplot as plt
import numpy as np
default_size_inch = (9, 6)
showThird = False
def onpress(event):
global fig, ax1, ax2, ax3, showThird
if event.key == 'x':
showThird = not showThird
if showThird:
fig.set_size_inches(default_size_inch[0]+3, default_size_inch[1], forward=True)
plt.subplots_adjust(right=0.85) # leave a bit of space on the right
ax3.set_visible(True)
ax3.set_axis_on()
else:
fig.set_size_inches(default_size_inch[0], default_size_inch[1], forward=True)
plt.subplots_adjust(right=0.9) # default
ax3.set_visible(False)
ax3.set_axis_off()
fig.canvas.draw()
def main():
global fig, ax1, ax2, ax3
xdata = np.arange(0, 101, 1) # 0 to 100, both included
ydata1 = np.sin(0.01*xdata*np.pi/2)
ydata2 = 10*np.sin(0.01*xdata*np.pi/4)
fig = plt.figure(figsize=default_size_inch, dpi=120)
ax1 = plt.subplot2grid((3,3), (0,0), colspan=2, rowspan=2)
ax2 = plt.subplot2grid((3,3), (2,0), colspan=2, sharex=ax1)
ax3 = plt.subplot2grid((3,3), (0,2), rowspan=3)
ax3.set_visible(False)
ax3.set_axis_off()
ax1.plot(xdata, ydata1, color="Red")
ax2.plot(xdata, ydata2, color="Khaki")
fig.canvas.mpl_connect('key_press_event', lambda event: onpress(event))
plt.show()
# ENTRY POINT
if __name__ == '__main__':
main()
How do I find the size of the subplots represented by ax1 and ax2 axes?
For the full explanation of how bbox works refer to here. Each of your axes object fits in a bounding box. All you need to do is to get the height and width of your axis bounding box.
ax_h, ax_w = ax.bbox.height, ax.bbox.width
You can transform to figure coordinates by using bbox.transformed method. For example:
ax_h = ax.bbox.transformed(fig.gca().transAxes).height

Can you create non-uniform iterable plots in matplotlib

I'm trying to create a figure with a number of non-uniform subplots. I would like to be able to create the plots using an iterable index so that I do not have to create each plot individually.
I can create a series of uniform subplots using fig, ax = plt.subplots(5) where I can plot to the various axes using ax[i].
fig, ax = plt.subplots(5)
Going forward I can plot to each plot using ax[i] using ax[0].plt etc.
However I would like to be able to create a series of plots that looks like:
gridsize = (10,3)
fig = plt.figure(figsize=(5,3))
ax0 = plt.subplot2grid(gridsize, (0, 0), colspan=3, rowspan=1)
for i in range(1,5):
ax1 = plt.subplot2grid(gridsize, (i, 0), colspan=2, rowspan=1)
ax2 = plt.subplot2grid(gridsize, (i, 2), colspan=2, rowspan=1)
where I can call each plot using ax[i] as above.
Does anyone have any ideas? Thanks.
You may append the axes to a list from which to index the respective item or over which to iterate.
import numpy as np
import matplotlib.pyplot as plt
gridsize = (10,3)
fig = plt.figure(figsize=(5,3))
ax0 = plt.subplot2grid(gridsize, (0, 0), colspan=3, rowspan=1)
ax = [ax0]
for i in range(1,5):
ax.append(plt.subplot2grid(gridsize, (i, 0), colspan=2, rowspan=1))
ax.append(plt.subplot2grid(gridsize, (i, 2), colspan=2, rowspan=1))
## Now plot to those axes:
for i in range(2*4+1):
ax[i].plot(np.arange(14),np.random.randn(14))
for axi in ax:
axi.plot(np.arange(14),np.random.randn(14))
plt.show()

bunch of histograms next to each other

In this figure, in the 1st plot, the grid divides the plot in "windows" and each window is divided in subwindows (made with let's say 5 data).
Then the slope of each subwindow is calculated and saved.
Next I divide the polar plane in 16 quadrants and calculate which quadrant correspond to each slope. So, I get something like this:
1,1,1,1,1,1,1,1,1,1,1,2,2,2,3,4
4,1,-1,-2,...
In the dataset above, each number is the quadrant that represents the slope of a subwindow and each row represents a window (The histograms are calculated with this dataset).
What I'm looking for is that the figure at the top, the 2nd plot shows the histogram of each window under its corresponding window.
All I could get is this from the matplotlib page but none of those examples are close to what I need because I need the histograms next to each other without blocking each other.
Sometimes, depending on the parameters used, it could be more than 800 histograms in the same plot.
Here's an example of how you can display multiple plots side-by-side below a larger one using Gridspec:
import numpy as np
import matplotlib.pyplot as plt
# generate some data
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# plot it
fig = plt.figure(figsize=(8, 6))
ax0 = plt.subplot2grid((2, 4), (0, 0), colspan=4)
ax0.plot(x, y)
ax1 = plt.subplot2grid((2, 4), (1, 0))
ax1.hist(y)
ax2 = plt.subplot2grid((2, 4), (1, 1))
ax2.hist(y)
ax2.set_yticklabels([])
ax2.set_yticks([])
ax3 = plt.subplot2grid((2, 4), (1, 2))
ax3.hist(y)
ax3.set_yticklabels([])
ax3.set_yticks([])
ax4 = plt.subplot2grid((2, 4), (1, 3))
ax4.hist(y)
ax4.set_yticklabels([])
ax4.set_yticks([])
plt.subplots_adjust(wspace=0) # no space left between hists in 2nd row
Results in:

Using passed axis objects in a matplotlib.pyplot figure?

I am currently attempting to use passed axis object created in function, e.g.:
def drawfig_1():
import matplotlib.pyplot as plt
# Create a figure with one axis (ax1)
fig, ax1 = plt.subplots(figsize=(4,2))
# Plot some data
ax1.plot(range(10))
# Return axis object
return ax1
My question is, how can I use the returned axis object, ax1, in another figure? For example, I would like to use it in this manner:
# Setup plots for analysis
fig2 = plt.figure(figsize=(12, 8))
# Set up 2 axes, one for a pixel map, the other for an image
ax_map = plt.subplot2grid((3, 3), (0, 0), rowspan=3)
ax_image = plt.subplot2grid((3, 3), (0, 1), colspan=2, rowspan=3)
# Plot the image
ax_psf.imshow(image, vmin=0.00000001, vmax=0.000001, cmap=cm.gray)
# Plot the map
???? <----- #I don't know how to display my passed axis here...
I've tried statements such as:
ax_map.axes = ax1
and although my script does not crash, my axis comes up empty. Any help would be appreciated!
You are trying to make a plot first and then put that plot as a subplot in another plot (defined by subplot2grid). Unfortunately, that is not possible. Also see this post: How do I include a matplotlib Figure object as subplot?.
You would have to make the subplot first and pass the axis of the subplot to your drawfig_1() function to plot it. Of course, drawfig_1() will need to be modified. e.g:
def drawfig_1(ax1):
ax1.plot(range(10))
return ax1
# Setup plots for analysis
fig2 = plt.figure(figsize=(12, 8))
# Set up 2 axes, one for a pixel map, the other for an image
ax_map = plt.subplot2grid((3, 3), (0, 0), rowspan=3)
ax_image = plt.subplot2grid((3, 3), (0, 1), colspan=2, rowspan=3)
# Plot the image
ax_image.imshow(image, vmin=0.00000001, vmax=0.000001, cmap=cm.gray)
# Plot the map:
drawfig_1(ax_map)

Categories

Resources