probelm with subplots in matplotlib - matplotlib

I have the following code which works just fine:
plt.rcParams["figure.figsize"] = (5,5) # V1.0b
fig, axes = plt.subplots(ncols = 2, nrows = 2) # V1.0b
ax1, ax2, ax3, ax4 = axes.flatten()
plt.subplot(2, 2, 1)
ax1.plot(x1, y1)
ax1.plot(x2, y2)
(etc)
Exactly as expected, I get 2 plots in row 1, 2 plots in row 2.
Now, I want 2 rows by 3 cols and 4 plots (from exactly the same data):
plt.rcParams["figure.figsize"] = (6,4)
fig, axes = plt.subplots(ncols = 3, nrows = 2)
ax1, ax2, ax3, ax4 = axes.flatten()
plt.subplot(2, 3, 1)
ax1.plot(x1, y1)
(etc)
And I get an error from the line:
---> 12 ax1, ax2, ax3, ax4 = axes.flatten()
The error message is:
ValueError: too many values to unpack (expected 4)
Surely ax1, ax2, ax3, ax4 are the 4 values? But, evidently not; what's going wrong here?

I've found this works. As you say, no need for subplots:
figure, axis = plt.subplots(3, 3)
axis[0, 0]
axis[0, 0].set_title("NGC0628")
axis[0, 0].plot(x0,y0)
axis[0, 1]
axis[0, 0].plot(x1,y1)
axis[0, 2]
axis[0, 0].plot(x2,y2)
(etc)
BTW I need control over each plot, i.e. as in
axis[0, 0].set_title("NGC0628")
Thanks for the steer

Related

Matplotlib - Split graph creation into multiple functions

In order to create figures with some same graphs, I would like to define a function per group of graph. These should be called depending on the subfigure provided in order to have these graphs at the right location. Consequently, I would liek to split this code below into separate functions as a code like the one provided after this one.
fig = plt.figure(constrained_layout=True, figsize=(10, 8))
# create top/bottom subfigs
(subfig_t, subfig_b) = fig.subfigures(2, 1, hspace=0.05, height_ratios=[1, 3])
# put ax0 in top subfig
ax0 = subfig_t.subplots()
ax0.set_title('ax0')
subfig_t.supxlabel('xlabel0')
# create left/right subfigs nested in bottom subfig
(subfig_bl, subfig_br) = subfig_b.subfigures(1, 2, wspace=0.1, width_ratios=[3, 1])
# put ax1-ax3 in gridspec of bottom-left subfig
gs = subfig_bl.add_gridspec(nrows=1, ncols=9)
ax1 = subfig_bl.add_subplot(gs[0, :1])
ax2 = subfig_bl.add_subplot(gs[0, 1:6], sharey=ax1)
ax3 = subfig_bl.add_subplot(gs[0, 6:], sharey=ax1)
ax1.set_title('ax1')
ax2.set_title('ax2')
ax3.set_title('ax3')
ax2.get_yaxis().set_visible(False)
ax3.get_yaxis().set_visible(False)
subfig_bl.supxlabel('xlabel1-3')
# put ax4 in bottom-right subfig
ax4 = subfig_br.subplots()
ax4.set_title('ax4')
subfig_br.supxlabel('xlabel4')
Below is the code-like I would like to have, to avoid to write the same code multiple times.
fig = plt.figure(constrained_layout=True, figsize=(10, 8))
# create top/bottom subfigs
(subfig_t, subfig_b) = fig.subfigures(2, 1, hspace=0.05, height_ratios=[1, 3])
(subfig_bl, subfig_br) = subfig_b.subfigures(1, 2, wspace=0.1, width_ratios=[3, 1])
def func1(subfig_t):
# put ax0 in top subfig
ax0 = subfig_t.subplots()
ax0.set_title('ax0')
subfig_t.supxlabel('xlabel0')
return subfig_t
def func2(subfig_bl):
# put ax1-ax3 in gridspec of bottom-left subfig
gs = subfig_bl.add_gridspec(nrows=1, ncols=9)
ax1 = subfig_bl.add_subplot(gs[0, :1])
ax2 = subfig_bl.add_subplot(gs[0, 1:6], sharey=ax1)
ax3 = subfig_bl.add_subplot(gs[0, 6:], sharey=ax1)
ax1.set_title('ax1')
ax2.set_title('ax2')
ax3.set_title('ax3')
ax2.get_yaxis().set_visible(False)
ax3.get_yaxis().set_visible(False)
subfig_bl.supxlabel('xlabel1-3')
return subfig_bl
def func3(subfig_br):
# put ax4 in bottom-right subfig
ax4 = subfig_br.subplots()
ax4.set_title('ax4')
subfig_br.supxlabel('xlabel4')
return subfig_bl
def func_save(fig, OutputPath):
fig.savefig(OutputPath, dpi=300, format='png', bbox_inches='tight')
subfig_t = func1(subfig_t)
subfig_bl = func2(subfig_bl)
subfig_br = func3(subfig_br)
func_save(fig, OutputPath)
The functions are not defined as functions, few of the syntax changes and the code is good to run. Python syntax is quite different from other programming languages. It is very simple to learn, and even complex to understand the unknown.
The below code will run perfectly, hope you find it useful.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=True, figsize=(10, 8))
# create top/bottom subfigs
(subfig_t, subfig_b) = fig.subfigures(2, 1, hspace=0.05, height_ratios=[1, 3])
(subfig_bl, subfig_br) = subfig_b.subfigures(1, 2, wspace=0.1, width_ratios=[3, 1])
def func1(subfig_t):
# put ax0 in top subfig
ax0 = subfig_t.subplots()
ax0.set_title('ax0')
subfig_t.supxlabel('xlabel0')
return subfig_t
def func2(subfig_bl):
# put ax1-ax3 in gridspec of bottom-left subfig
gs = subfig_bl.add_gridspec(nrows=1, ncols=9)
ax1 = subfig_bl.add_subplot(gs[0, :1])
ax2 = subfig_bl.add_subplot(gs[0, 1:6], sharey=ax1)
ax3 = subfig_bl.add_subplot(gs[0, 6:], sharey=ax1)
ax1.set_title('ax1')
ax2.set_title('ax2')
ax3.set_title('ax3')
ax2.get_yaxis().set_visible(False)
ax3.get_yaxis().set_visible(False)
subfig_bl.supxlabel('xlabel1-3')
return subfig_bl
def func3(subfig_br):
# put ax4 in bottom-right subfig
ax4 = subfig_br.subplots()
ax4.set_title('ax4')
subfig_br.supxlabel('xlabel4')
return subfig_bl
def func_save(fig, OutputPath):
fig.savefig(OutputPath, dpi=300, format='png', bbox_inches='tight')
# Enter the path for output here
OutputPath = "output.png"
subfig_t = func1(subfig_t)
subfig_bl = func2(subfig_bl)
subfig_br = func3(subfig_br)
func_save(fig, OutputPath)
Happy coding :)

Changing the Matplotlib GridSpec properties after generating the subplots

Suppose something comes up in my plot that mandates that I change the height ratio between two subplots that I've generated within my plot. I've tried changing GridSpec's height ratio to no avail.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure()
gs = GridSpec(2, 1, height_ratios=[2, 1])
ax1 = fig.add_subplot(gs[0])
ax1 = fig.axes[0]
ax2 = fig.add_subplot(gs[1])
ax2 = fig.axes[1]
ax1.plot([0, 1], [0, 1])
ax2.plot([0, 1], [1, 0])
gs.height_ratios = [2, 5]
The last line has no effect on the plot ratio.
In my actual code, it is not feasible without major reworking to set the height_ratios to 2:5 ahead of time.
How do I get this to update like I want?
The axes of relevant subplots can be manipulated and adjusted to get new height ratios.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure()
gs = GridSpec(2, 1, height_ratios=[2, 1]) #nrows, ncols
ax1 = fig.add_subplot(gs[0])
ax1 = fig.axes[0]
ax2 = fig.add_subplot(gs[1])
ax2 = fig.axes[1]
ax1.plot([0, 1], [0, 1])
ax2.plot([0, 1], [1, 0])
# new height ratio: 2:5 is required for the 2 subplots
rw, rh = 2, 5
# get dimensions of the 2 axes
box1 = ax1.get_position()
box2 = ax2.get_position()
# current dimensions
w1,h1 = box1.x1-box1.x0, box1.y1-box1.y0
w2,h2 = box2.x1-box2.x0, box2.y1-box2.y0
top1 = box1.y0+h1
#top2 = box2.y0+h2
full_h = h1+h2 #total height
# compute new heights for each axes
new_h1 = full_h*rw/(rw + rh)
new_h2 = full_h*rh/(rw + rh)
#btm1,btm2 = box1.y0, box2.y0
new_bottom1 = top1-new_h1
# finally, set new location/dimensions of the axes
ax1.set_position([box1.x0, new_bottom1, w1, new_h1])
ax2.set_position([box2.x0, box2.y0, w2, new_h2])
plt.show()
The output for ratio: (2, 5):
The output for (2, 10):

How can I fix the size of the last subplot?

I want to have 5x4 subplots, one for each group. I wrote the following code:
axeng = []
for i in range(5):
for ii in range(4):
axeng.append([i,ii])`
yy = (0.5, 4.5, 9.5, 14.5, 19.5, 24.5)
xx=np.arange(0.5,10)
f,axes = plt.subplots(5,4,figsize=(50,50), sharex=True, sharey=True)
cbar_ax = f.add_axes([.92, .3, .03, .4])
for i in range(20):
paxesrow = tuple(axeng[i])[0]
paxescol = tuple(axeng[i])[1]
# gnuplot, jet, YlGnBu, GnBu_r
g=sns.heatmap(heat[i],cmap="viridis",vmin=0.1,vmax=1,
ax=axes[paxesrow,paxescol],linewidth=.1,
cbar=True if i==3 else False,
cbar_ax=cbar_ax if i==3 else None,
square=False)
g.set_yticks(yy)
g.set_xticks(xx)
g.set_yticklabels([' ','25',' ','15',' ','5'],fontsize=33)
g.set_xticklabels([' ','2',' ','4',' ','6',' ','8',' ','10'],fontsize=33,rotation=0)
f.tight_layout(rect=[1, 1, 1, 1])
f.suptitle('Behavior of all subgroups',fontsize=70,y=.93)
cbar=axes[tuple(axeng[3])[0],tuple(axeng[3])[1]].collections[0].colorbar
cbar.ax.tick_params(labelsize=35)
plt.show()
As you can see in the image, the last subplot is scaled, but I have no idea why that would be the case.
Thanks in advance.

Text in matplotlib subplots in wrong positions

I'm trying to get some text into a matplotlib subplot, but I get it into weird locations.
The code I use is like the following:
limSX_list = [0, 0, 0]
limDX_list = [10, 10, 10]
fig, axs = plt.subplots(3, 3)
for xxx in range(3):
for yyy in range(3):
if xxx==yyy:
#axs[xxx,yyy].hist( Deltas_list[xxx], range=[limSX_list[xxx], limDX_list[xxx]], bins=100, color='red' )
axs[xxx,yyy].hist( Deltas_list[xxx], bins=100, color='red' )
else:
#axs[xxx,yyy].hist2d( Deltas_list[xxx], Deltas_list[yyy], bins=(100, 100), cmap=plt.cm.viridis, range=[[limSX_list[xxx],limDX_list[xxx]],[limSX_list[yyy],limDX_list[yyy]]], norm=LogNorm() )
axs[xxx,yyy].hist2d( Deltas_list[xxx], Deltas_list[yyy], bins=(100, 100), cmap=plt.cm.viridis, norm=LogNorm() )
for row in range(3):
for col in range(3):
axs[row, col].text(0.5, 0.5, str((row, col)),color='blue', fontsize=18, ha='center')
plt.show()
And I get this weird output:
And if I set a x/y range (i.e. I uncomment the commented lines, and comment the ones below), I get the text in a different, but still wrong, position.
If I try with an even more minimal code, like the following, instead, everything goes fine:
fig, ax = plt.subplots(rows, cols)
for row in range(3):
for col in range(3):
ax[row, col].text(0.5, 0.5, str((row, col)), color='blue', fontsize=18, ha='center')
plt.show()
Any guess why? Thanks! :)

How to set common labels with matplotlib

I have a plot obtained in this way:
f, ((ax1, ax2, ax3, ax4), (ax5, ax6, ax7, ax8), (ax9, ax10, ax11, ax12)) = plt.subplots(3, 4, sharex = 'col', sharey = 'row')
ax1.set_title('column1')
ax1.plot([x], [y])
ax5.plot([x1],[y1])
ax9.plot([x2],[y2])
.....
so, I essentially have 3 rows and 4 columns.
I would like to know how is it possible to put commond labels to the x and y axis.
I tried to write
plt_xlabel('x')
plt.ylabel('y')
or
set.xlabel('x')
set.ylabel('y')
but it doesn't work. Can you help me? Is it also possible to put text on the right end side of the plot?
You can do this by iterating over your list of axes:
f, ax_lst = plt.subplots(3, 4, sharex = 'col', sharey = 'row')
for ax_l in ax_lst:
for ax in ax_l:
ax.set_xlabel('x')
ax.set_ylabel('y')