Legend location in matplotlib subplot - matplotlib

I am trying to create subplots on (6X3) grid. I am having a problem with the location of legend. The legend is common to all subplot. The lgend is now overlapping with the y-axis label
I tried with removing the constrained_layout=True option. But this keeps a lot of white space between legend and subplots.
import numpy as np
import matplotlib.pyplot as plt
#plt.rcParams["font.family"] = "Times New Roman"
#plt.rcParams.update({'font.size': 12})
font = {'family' : 'Times New Roman',
'size' : 14}
plt.rc('font', **font)
t = np.linspace(0,10, num=200)
fig, axs = plt.subplots(6, 3, figsize=(12,16))#, constrained_layout=True)
i = 0 # i = 0 for x = 0.25; i = 3 for x = -0.25
j = 6 # j = 6 for x = 0.25; j = 9 for x = -0.25
#%%
solution = np.loadtxt(open("sequential_legs=01.csv", "rb"), delimiter=",", skiprows=0)
axs[0, 0].plot(t, solution[:,i],'r-')
axs[0, 0].plot(t, solution[:,i+1],'g-')
axs[0, 0].plot(t, solution[:,i+2],'b-')
axs[0, 0].plot(t, solution[:,j],'r--')
axs[0, 0].plot(t, solution[:,j+1],'g--')
axs[0, 0].plot(t, solution[:,j+2],'b--')
axs[0, 0].set_title('DNN-S (p = 1)', fontsize=14)
axs[0, 0].set_xlim([0, 10])
(repeated for each grid)
line_labels = ["$y_1$ (True)","$y_2$ (True)", "$y_3$ (True)", "$y_1$ (ML-Pred)","$y_2$ (ML-Pred)", "$y_3$ (ML-Pred)"]
plt.figlegend( line_labels, loc = 'lower center', borderaxespad=0.1, ncol=6, labelspacing=0., prop={'size': 13} ) #bbox_to_anchor=(0.5, 0.0), borderaxespad=0.1,
for ax in axs.flat:
ax.set(xlabel='Time', ylabel='Response')
for ax in axs.flat:
ax.label_outer()
fig.savefig('LSE_X=025.pdf', bbox_inches = 'tight')

constrained_layout does not take figure legends into account. The easiest option I currently see here is to use tight_layout to have the subplots distribute nicely in the figure, and after that, add some space for the legend manually.
import matplotlib.pyplot as plt
fig, axs = plt.subplots(6, 3, figsize=(12,8), sharex=True, sharey=True,
constrained_layout=False)
labels = [f"Label {i}" for i in range(1,5)]
for ax in axs.flat:
for i, lb in enumerate(labels):
ax.plot([1,2], [0,i+1], label=lb)
ax.set_xlabel("x label")
ax.label_outer()
fig.tight_layout()
fig.subplots_adjust(bottom=0.1) ## Need to play with this number.
fig.legend(labels=labels, loc="lower center", ncol=4)
plt.show()

Related

How to change font parameters in Z axis?

I'm trying to personalize some of my graphs so I'd like to change the font of all axis. I wrote the code below for that. The problem is that I can't change the font of the z axis. Somebody have a recommendation?
Thanks in advance.
import matplotlib.pyplot as plt
from matplotlib import cm
font = {'size' : 12}
cm=1/2.54
size_x = 10*cm
size_y = 8*cm
min_limx = 4
max_limx = 10
min_limy = 0
max_limy = 50
min_limz = 0
max_limz = 8
#%%
fig, ax = plt.subplots(figsize=(size_x, size_y), subplot_kw={"projection": "3d"})
X, Y = np.meshgrid(xnew, ynew, indexing='ij')
Z1 = m_fill
Z2 = m_nofill
surf = ax.plot_surface(X, Y, Z1, cmap=cm.viridis, linewidth=0, antialiased=False)
surf = ax.plot_surface(X, Y, Z2, color='w', linewidth=0, antialiased=False)
ax.set_xlabel('$\lambda$', **font)
ax.set_ylabel('$\chi$', **font)
ax.set_zlabel('$\it{m_{0}, кг}$', **font)
lin_x = np.arange(min_limx, max_limx, step=2)
lin_y = np.arange(min_limy, max_limy, step=15)
plt.xticks(lin_x, **font)
plt.yticks(lin_y, **font)
#plt.zticks(lin_z, **font) # AttributeError: module 'matplotlib.pyplot' has no attribute 'zticks'
ax.set_zlim(0, 8, )
#plt.gca().set_aspect('equal', adjustable='box')
ax.view_init(45, -45)
#plt.tight_layout()
plt.grid()
plt.show()
fig.savefig('p0_V_m0.png', format='png', dpi=300)
Ps: This is how the figure looks like right now.
The fontsize of the tick labels is set by the labelsize parameter:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
font = {'size': 20}
ax.tick_params('z', labelsize=font['size'])
(To set the label size for all three axes you can use ax.tick_params(labelsize=20).)

How can I increase Horizontal Space (hspace) between two specific matplotlib subplots?

f = plt.figure(figsize=(12,10))
ax1 = f.add_subplot(411)
ax2 = f.add_subplot(422)
ax3 = f.add_subplot(423)
ax4 = f.add_subplot(424)
ax5 = f.add_subplot(425)
ax6 = f.add_subplot(426)
ax7 = f.add_subplot(427)
ax8 = f.add_subplot(428)
I want to increase space between two rows: ax1 and ax2-ax3. Other spaces should remain the same. Using "f.subplots_adjust(hspace = 0.2, wspace= 0.25)" adjusts the spacing for all subplots. What can I do to increase hspace for the top-most subplot only?
import matplotlib.pyplot as plt
fig, axs = plt.subplot_mosaic([['top', 'top'],['left1', 'right1'], ['left2', 'right2']],
constrained_layout=True)
axs['top'].set_xlabel('Xlabel\n\n')
plt.show()
This will make all the y-axes the same size. If that is not important to you, then #r-beginners answer is helpful. Note that you need-not use subplot mosaic, though it is a useful new feature.
If you are not worried about the axes sizes matching, then a slightly better way than proposed above is to use the new subfigure functionality:
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2, 1, height_ratios=[1, 2], hspace=0.15)
# top
axtop = subfigs[0].subplots()
# 2x2 grid
axs = subfigs[1].subplots(2, 2)
plt.show()
Based on the gridspec sample in the official reference, I customized it using this example answer.The point is to use gridspec for the separate graphs you want to configure.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
def format_axes(fig):
for i, ax in enumerate(fig.axes):
ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
ax.tick_params(labelbottom=False, labelleft=False)
fig = plt.figure()
gs_top = GridSpec(3, 3, top=0.95)
gs_base = GridSpec(3, 3)
ax1 = fig.add_subplot(gs_top[0, :])
# identical to ax1 = plt.subplot(gs.new_subplotspec((0, 0), colspan=3))
ax2 = fig.add_subplot(gs_base[1, :-1])
ax3 = fig.add_subplot(gs_base[1:, -1])
ax4 = fig.add_subplot(gs_base[-1, 0])
ax5 = fig.add_subplot(gs_base[-1, -2])
# fig.suptitle("GridSpec")
format_axes(fig)
plt.show()

Is there a way to label each wedge of pie chart in this grid?

I want to have multiple pie charts in a grid.
Each pie chart will have a different number of wedges, values, and labels.
The code below shows multiple labels in one pie chart.
Is there a way to label each wedge of pie-charts in this grid?
import matplotlib.pyplot as plt
import numpy as np
def heatmap_with_circles(data_array,row_labels,column_labels,ax=None, cmap=None, norm=None, cbar_kw={}, cbarlabel="", **kwargs):
for row_index, row in enumerate(row_labels,0):
for column_index, column in enumerate(column_labels,0):
print('row_index: %d column_index: %d' %(row_index,column_index))
if row_index==0 and column_index==0:
colors=['indianred','orange','gray']
values=[10,20,30]
else:
values=[45,20,38]
colors=['pink','violet','green']
wedges, text = plt.pie(values,labels=['0', '2', '3'],labeldistance = 0.25,colors=colors)
print('len(wedges):%d wedges: %s, text: %s' %(len(wedges), wedges, text))
radius = 0.45
[w.set_center((column_index,row_index)) for w in wedges]
[w.set_radius(radius) for w in wedges]
# We want to show all ticks...
ax.set_xticks(np.arange(data_array.shape[1]))
ax.set_yticks(np.arange(data_array.shape[0]))
fontsize=10
ax.set_xticklabels(column_labels, fontsize=fontsize)
ax.set_yticklabels(row_labels, fontsize=fontsize)
#X axis labels at top
ax.tick_params(top=True, bottom=False,labeltop=True, labelbottom=False,pad=5)
plt.setp(ax.get_xticklabels(), rotation=55, ha="left", rotation_mode="anchor")
# We want to show all ticks...
ax.set_xticks(np.arange(data_array.shape[1]+1)-.5, minor=True)
ax.set_yticks(np.arange(data_array.shape[0]+1)-.5, minor=True)
ax.grid(which="minor", color="black", linestyle='-', linewidth=2)
ax.tick_params(which="minor", bottom=False, left=False)
data_array=np.random.rand(3,4)
row_labels=['Row1', 'Row2', 'Row3']
column_labels=['Column1', 'Column2', 'Column3','Column4']
fig, ax = plt.subplots(figsize=(1.9*len(column_labels),1.2*len(row_labels)))
ax.set_aspect(1.0)
ax.set_facecolor('white')
heatmap_with_circles(data_array,row_labels,column_labels, ax=ax)
plt.tight_layout()
plt.show()
After updating heatmap_with_circles
def heatmap_with_circles(data_array,row_labels,column_labels,ax=None, cmap=None, norm=None, cbar_kw={}, cbarlabel="", **kwargs):
labels = ['x', 'y', 'z']
for row_index, row in enumerate(row_labels,0):
for column_index, column in enumerate(column_labels,0):
print('row_index: %d column_index: %d' %(row_index,column_index))
if row_index==0 and column_index==0:
colors=['indianred','orange','gray']
values=[10,20,30]
else:
values=[45,20,38]
colors=['pink','violet','green']
# wedges, texts = plt.pie(values,labels=['0', '2', '3'],labeldistance = 0.45,colors=colors)
wedges, texts = plt.pie(values,labeldistance = 0.25,colors=colors)
print('text:%s len(wedges):%d wedges: %s' %(texts, len(wedges), wedges))
radius = 0.45
[w.set_center((column_index,row_index)) for w in wedges]
[w.set_radius(radius) for w in wedges]
[text.set_position((text.get_position()[0]+column_index,text.get_position()[1]+row_index)) for text in texts]
[text.set_text(labels[text_index]) for text_index, text in enumerate(texts,0)]
I got the following image :)
You could loop through the texts of each pie, get its xy position, add column_index and row_index, and set that as new position.
Some small changes to the existing code:
ax.grid(which="minor", ..., clip_on=False) to make sure the thick lines are shown completely, also near the border
ax.set_xlim(xmin=-0.5) to set the limits
import matplotlib.pyplot as plt
import numpy as np
def heatmap_with_circles(data_array, row_labels, column_labels, ax=None):
ax = ax or plt.gca()
for row_index, row in enumerate(row_labels, 0):
for column_index, column in enumerate(column_labels, 0):
colors = np.random.choice(['indianred', 'orange', 'gray', 'pink', 'violet', 'green'], 3, replace=False)
values = np.random.randint(10, 41, 3)
wedges, text = plt.pie(values, labels=['1', '2', '3'], labeldistance=0.25, colors=colors)
radius = 0.45
for w in wedges:
w.set_center((column_index, row_index))
w.set_radius(radius)
w.set_edgecolor('white')
# w.set_linewidth(1)
for t in text:
x, y = t.get_position()
t.set_position((x + column_index, y + row_index))
# We want to show all ticks...
ax.set_xticks(np.arange(data_array.shape[1]))
ax.set_yticks(np.arange(data_array.shape[0]))
fontsize = 10
ax.set_xticklabels(column_labels, fontsize=fontsize)
ax.set_yticklabels(row_labels, fontsize=fontsize)
# X axis labels at top
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False, pad=5)
plt.setp(ax.get_xticklabels(), rotation=55, ha="left", rotation_mode="anchor")
# We want to show all minor ticks...
ax.set_xticks(np.arange(data_array.shape[1] + 1) - .5, minor=True)
ax.set_yticks(np.arange(data_array.shape[0] + 1) - .5, minor=True)
ax.set_xlim(xmin=-.5)
ax.set_ylim(ymin=-.5)
ax.grid(which="minor", color="black", linestyle='-', linewidth=2, clip_on=False)
ax.tick_params(axis="both", which="both", length=0) # hide tick marks
data_array = np.random.rand(3, 4)
row_labels = ['Row1', 'Row2', 'Row3']
column_labels = ['Column1', 'Column2', 'Column3', 'Column4']
fig, ax = plt.subplots(figsize=(1.9 * len(column_labels), 1.2 * len(row_labels)))
ax.set_aspect(1.0)
ax.set_facecolor('white')
heatmap_with_circles(data_array, row_labels, column_labels, ax=ax)
plt.tight_layout()
plt.show()

jupyter notebook matplotlib show plot and then plot on the origin figure

I want to plot a white plot with two axes, show it to the user, then add a line to the white plot with two axes, show it to the user, then add some dot to the line, then show it to the user. How can I do this without copying the code again and again?
What I'm doing now is in the first code chunk
import math
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
plt.show()
then in the second code chunk
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
x = np.linspace(0, 1.0, 1000)
ax.plot(x, 1.0-x,zorder = 0)
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
plt.show()
then in the third code chunk
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
x = np.linspace(0, 1.0, 1000)
ax.plot(x, 1.0-x,zorder = 0)
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
p0 = 0.5
p1 = 0.5
color = "blue"
textd =0.05
ax.scatter([p0],[p1], color = color,zorder=1)
ax.text(p0+textd, p1+textd, 'tiger',color = color,zorder =2)
plt.show()
What I'm looking for is things like in the first code chunk
import math
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
plt.show()
then in the second code chunk
add line directly without duplicating the code for making axes
plt.show()
then in the third code chunk
add point directly without duplicating the code for making axes and lines
plt.show()
Update: I actually figured out the answer.
def plot(step):
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
if step>=1:
x = np.linspace(0, 1.0, 1000)
ax.plot(x, 1.0-x,zorder = 0)
if step>=2:
p0 = 0.5
p1 = 0.5
color = "blue"
textd =0.05
ax.scatter([p0],[p1], color = color,zorder=1)
ax.text(p0+textd, p1+textd, 'tiger',color = color,zorder =2)
plot.show()
should be able to solve the problem.

Custom background sections for matplotlib figure

I would like to define colors sections (blue: [0-15000], green: [15000-23000], red[23000,]) that should be used for y-values. Is it somehow possible in matplotlib?
You can color regions on a matplotlib plot using collections.BrokenBarHCollection:
import matplotlib.pyplot as plt
import matplotlib.collections as collections
fig = plt.figure()
ax = fig.add_subplot(111)
# Plot your own data here
x = range(0, 30000)
y = range(0, 30000)
ax.plot(x, y)
xrange = [(0, 30000)]
yrange1 = (0, 15000)
yrange2 = (15000, 23000)
yrange3 = (23000, 30000)
c1 = collections.BrokenBarHCollection(xrange, yrange1, facecolor='blue', alpha=0.5)
c2 = collections.BrokenBarHCollection(xrange, yrange2, facecolor='green', alpha=0.5)
c3 = collections.BrokenBarHCollection(xrange, yrange3, facecolor='red', alpha=0.5)
ax.add_collection(c1)
ax.add_collection(c2)
ax.add_collection(c3)
plt.show()