How do I increase the spacing between subplots with subplot2grid? - python

I use subplot2grid to make a subplot like the following:
In order to make the ticks sufficiently large for publication, I need to increase the vertical and horizontal spacing between axes.
Normally, I would use something like subplot_adjust(hspace = 0.5), but that doesn't seem to work with subplot2grid.
Could anyone please recommend a solution?
Here is the code I use to plot things and create the axes:
import matplotlib.pyplot as plt
ax1 = plt.subplot2grid((2,2),(0,0), colspan = 2)
ax2 = plt.subplot2grid((2,2),(1,0), colspan = 1)
ax3 = plt.subplot2grid((2,2),(1,1), colspan = 1)
df.plot( ax = ax1)
plt.show()

I've found the solution here
The code is as follows:
AX = gridspec.GridSpec(2,2)
AX.update(wspace = 0.5, hspace = 0.5)
ax1 = plt.subplot(AX[0,:])
ax2 = plt.subplot(AX[1,0])
ax3 = plt.subplot(AX[1,1])
Which produces the same subplots with increased horizontal and vertical spacing.

You can add the following line:
plt.subplots_adjust(hspace=0.8)
under this one:
ax3 =plt.subplot2grid((2,2),(1,1), colspan = 1)
You can play with all sorts of parameters this way.

Related

How do I remove labels from one axis in a subplot?

I am using Python 3.9 on MacOS. Shortly, I have to make a plot with 4 subplots, and they share axis. The code looks like this:
#take some data
gs = gridspec.GridSpec(2, 2, height_ratios = [3, 1])
ax0 = plt.subplot(gs[0])
#plot data, make legend, etc.
ax2 = plt.subplot(gs[2], sharex = ax0)
#plot data, make legend, etc.
#take more data
ax1 = plt.subplot(gs[1], sharey = ax0)
#plot data, make legend, etc.
ax3 = plt.subplot(gs[3], sharex = ax1, sharey = ax2)
#plot data, make legend, etc.
plt.show()
As you can see, some plots share an axis with each other. The problem is that on the x-axis everything is fine, while it is not on the y-axis (see picture). Getting to the point: how can I remove the numbers on the vertical axis of the right plot but not on the left? I've seen many posts in which the problem was solved with things like
ax.set_yticklabels([])
but that removes the numbers from the left plot as well.
Try this:
ax1.tick_params('y', labelleft=False)

Having python plot two different x-axes with different scales

Here's my code so far:
QE_ellip_fixed = [-1.04e-3,-1.04e-2,-0.1,-0.76,-2.34,-2.54]
QL_ellip_fixed = [1.77e-4,9.89e-4,-6e-2,-2.9,-4.45,-2.74]
QP_ellip_fixed = [1.26e-3,1.45e-2,0.14,0.98,2.6,2.5]
QE_ellip_varied = [-1.73e-4,-1.73e-3,-1.71e-2,-0.15,-0.86,-3.16]
QL_ellip_varied = [7.57e-5,7.53e-4,5.4e-3,-0.13,-4.15,-7.3]
QP_ellip_varied = [1.41e-3,1.77e-3,2.34e-2,0.22,1.33,3.14]
RHScalls_ellip = [764021,76388,7625,750,63,3]
RHScalls_circ = [629171,62864,6234,577,41,5]
QE_circ_fixed= [-1.26e-4,-1.26e-3,-1.24e-2,-0.11,-0.57,-2.98]
QL_circ_fixed = [-1.32e-4,5.89e-4,1.5e-3,-0.51,0.4,-9.57]
QP_circ_fixed = [1.45e-2,9.25e-3,4.62e-2,0.58,3.5,8.54]
QE_circ_varied = [-1.26e-4,-1.25e-3,-1.24e-2,-0.11,-0.56,-2.13]
QL_circ_varied = [-1.33e-4,5.88e-4,1.69e-3,-0.45,-0.64,-6.58]
QP_circ_varied = [1.45e-2,9.32e-3,5.2e-2,0.55,3.11,13.05]
alp = [1e-5,1e-4,1e-3,1e-2,1e-1,1]
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(alp,np.abs(QE_ellip_varied),label='$|Q_E|$')
ax1.plot(alp,np.abs(QL_ellip_varied),label='$|Q_L|$')
ax1.plot(alp,np.abs(QP_ellip_varied),label='$|Q_P|$')
ax2 = ax1.twiny()
ax2.set_xticks([1e-5,1e-4,1e-3,1e-2,1e-1,1])
ax2.set_xticklabels(RHScalls_ellip)
ax1.set_xscale('log')
plt.yscale('log')
ax1.grid()
ax1.set_xlabel('alpha')
ax1.set_ylabel('Score (unitless)')
ax1.legend()
plt.show()
And here's the outputted image:
I want to have the values on the top axis have ticklabels in line with the grid lines already imposed, as they actually correspond to those gridlines, but I can't seem to make the top axis not behave in this annoying logarithmic way. I've only specified for axis 1 to have a logarithmic scale, but it seems to have applied to axis 2 as well...
it's because the axes share the y-axis, but not the x-axis. The xlimits are different for each..the following worked for me:
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
ax1.set_xscale('log')
ax2.set_xscale('log') #make sure both log
plt.yscale('log')
ax1.plot(alp,np.abs(QE_ellip_varied),label='$|Q_E|$')
ax1.plot(alp,np.abs(QL_ellip_varied),label='$|Q_L|$')
ax1.plot(alp,np.abs(QP_ellip_varied),label='$|Q_P|$')
ax2.set_xlim(ax1.get_xlim()) #make sure same limits
ax2.set_xticks([1e-5,1e-4,1e-3,1e-2,1e-1,1])
ax2.set_xticklabels(RHScalls_ellip)
ax1.grid()
ax1.set_xlabel('alpha')
ax1.set_ylabel('Score (unitless)')
ax1.legend()

Two subplots coming out too long (length)

I'm attempting to plot two bar charts using matplotlib.pyplot.subplots. I've created subplots within a function, but when I output the subplots they are too long in height and not long enough in width.
Here's the function that I wrote:
def corr_bar(data1, data2, method='pearson'):
# Basic configuration.
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(7, 7))
ax1, ax2 = axes
corr_matrix1 = data1.corr(method=method)
corr_matrix2 = data2.corr(method=method)
cmap = cm.get_cmap('coolwarm')
major_ticks = np.arange(0, 1.1, 0.1)
minor_ticks = np.arange(0, 1.1, 0.05)
# Values for plotting.
x1 = corr_matrix1['price'].sort_values(ascending=False).index
x2 = corr_matrix2['price'].sort_values(ascending=False).index
values1 = corr_matrix1['price'].sort_values(ascending=False).values
values2 = corr_matrix2['price'].sort_values(ascending=False).values
im1 = ax1.bar(x1, values1, color=cmap(values1))
im2 = ax2.bar(x2, values2, color=cmap(values2))
# Formatting for plot 1.
ax1.set_yticks(major_ticks)
ax1.set_yticks(minor_ticks, minor=True)
plt.setp(ax1.get_xticklabels(), rotation=45, ha='right', rotation_mode='anchor')
ax1.grid(which='both')
ax1.grid(which='minor', alpha=0.4)
ax1.grid(which='major', alpha=0.7)
ax1.xaxis.grid(False)
# Formatting for plot 2.
ax2.set_yticks(major_ticks)
ax2.set_yticks(minor_ticks, minor=True)
plt.setp(ax2.get_xticklabels(), rotation=45, ha='right', rotation_mode='anchor')
ax2.grid(which='both')
ax2.grid(which='minor', alpha=0.4)
ax2.grid(which='major', alpha=0.7)
ax2.xaxis.grid(False)
fig.tight_layout()
plt.show()
This function (when run with two Pandas DataFrames) outputs an image like the following:
I purposely captured the blank right side of the image as well in an attempt to better depict my predicament. What I want is for the bar charts to be appropriately sized in height and width as to take up the entire space, rather than be elongated and pushed to the left.
I've tried to use the ax.set(aspect='equal') method but it "scrunches up" the bar chart. Would anybody happen to know what I could do to solve this issue?
Thank you.
When you define figsize=(7,7) you are setting the size of the entire figure and not the subplots. So your entire figure must be a square in this case. You should change it to figsize=(14,7) or use a number larger than 14 to get a little bit of extra space.

Multiple GridSpecs in one Figure

I was working through matplotlib's documentation (http://matplotlib.org/users/gridspec.html#adjust-gridspec-layout), and in this particular example I do not understand the logic behind the layout of two GridSpecs in one figure. The significant part of the code they use (leaving out text, titles and labels) is
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
f = plt.figure()
gs1 = GridSpec(3, 3)
gs1.update(left=0.05, right=0.48, wspace=0.05)
ax1 = plt.subplot(gs1[:-1, :])
ax2 = plt.subplot(gs1[-1, :-1])
ax3 = plt.subplot(gs1[-1, -1])
gs2 = GridSpec(3, 3)
gs2.update(left=0.55, right=0.98, hspace=0.05)
ax4 = plt.subplot(gs2[:, :-1])
ax5 = plt.subplot(gs2[:-1, -1])
ax6 = plt.subplot(gs2[-1, -1])
This gives the following result (http://matplotlib.org/_images/demo_gridspec03.png):
These two GridSpecs seem to be aligned next to each other by default. Do I miss something in the code, that does this explicitly?
I tried to add a third GridSpec, like so:
gs3 = gridspec.GridSpec(3, 3)
ax7 = plt.subplot(gs3[:, 0])
ax8 = plt.subplot(gs3[:, 1:])
but this just fills the whole figure and the first two GridSpecs are "overpainted".
To restate my question, is there some implicit logic for the layout of two GridSpecs in a figure (note that I know of the method GridSpecFromSubplotSpec, but here it is not being used)?
The GridSpec extent can be adjusted with the update command. With this line you limit the first GridSpec to the left side (48%) of the Figure.
gs1.update(left=0.05, right=0.48, wspace=0.05)
The second GridSpec is then limited to the right side of the Figure with
gs1.update(left=0.55, right=0.98, hspace=0.05)
Similarly you can limit the vertical extent with the keywords top and bottom.

Change grid interval and specify tick labels in Matplotlib

I am trying to plot counts in gridded plots, but I haven't been able to figure out how to go about it.
I want:
to have dotted grids at an interval of 5;
to have major tick labels only every 20;
for the ticks to be outside the plot; and
to have "counts" inside those grids.
I have checked for potential duplicates, such as here and here, but have not been able to figure it out.
This is my code:
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
for key, value in sorted(data.items()):
x = value[0][2]
y = value[0][3]
count = value[0][4]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.annotate(count, xy = (x, y), size = 5)
# overwrites and I only get the last data point
plt.close()
# Without this, I get a "fail to allocate bitmap" error.
plt.suptitle('Number of counts', fontsize = 12)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.axes().set_aspect('equal')
plt.axis([0, 1000, 0, 1000])
# This gives an interval of 200.
majorLocator = MultipleLocator(20)
majorFormatter = FormatStrFormatter('%d')
minorLocator = MultipleLocator(5)
# I want the minor grid to be 5 and the major grid to be 20.
plt.grid()
filename = 'C:\Users\Owl\Desktop\Plot.png'
plt.savefig(filename, dpi = 150)
plt.close()
This is what I get.
I also have a problem with the data points being overwritten.
Could anybody PLEASE help me with this problem?
There are several problems in your code.
First the big ones:
You are creating a new figure and a new axes in every iteration of your loop →
put fig = plt.figure and ax = fig.add_subplot(1,1,1) outside of the loop.
Don't use the Locators. Call the functions ax.set_xticks() and ax.grid() with the correct keywords.
With plt.axes() you are creating a new axes again. Use ax.set_aspect('equal').
The minor things:
You should not mix the MATLAB-like syntax like plt.axis() with the objective syntax.
Use ax.set_xlim(a,b) and ax.set_ylim(a,b)
This should be a working minimal example:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Major ticks every 20, minor ticks every 5
major_ticks = np.arange(0, 101, 20)
minor_ticks = np.arange(0, 101, 5)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, minor=True)
ax.set_yticks(major_ticks)
ax.set_yticks(minor_ticks, minor=True)
# And a corresponding grid
ax.grid(which='both')
# Or if you want different settings for the grids:
ax.grid(which='minor', alpha=0.2)
ax.grid(which='major', alpha=0.5)
plt.show()
Output is this:
A subtle alternative to MaxNoe's answer where you aren't explicitly setting the ticks but instead setting the cadence.
import matplotlib.pyplot as plt
from matplotlib.ticker import (AutoMinorLocator, MultipleLocator)
fig, ax = plt.subplots(figsize=(10, 8))
# Set axis ranges; by default this will put major ticks every 25.
ax.set_xlim(0, 200)
ax.set_ylim(0, 200)
# Change major ticks to show every 20.
ax.xaxis.set_major_locator(MultipleLocator(20))
ax.yaxis.set_major_locator(MultipleLocator(20))
# Change minor ticks to show every 5. (20/4 = 5)
ax.xaxis.set_minor_locator(AutoMinorLocator(4))
ax.yaxis.set_minor_locator(AutoMinorLocator(4))
# Turn grid on for both major and minor ticks and style minor slightly
# differently.
ax.grid(which='major', color='#CCCCCC', linestyle='--')
ax.grid(which='minor', color='#CCCCCC', linestyle=':')

Categories