Not getting legend in figure/plot - matplotlib

I am sharing Y-axis in two subplots, with the following codes but both shared plots are missing legends in them.
projectDir = r'/media/DATA/banikr_D_drive/model/2021-04-28-01-18-15_5_fold_114sub'
logPath = os.path.join(projectDir, '2021-04-28-01-18-15_fold_1_Mixed_loss_dice.bin')
with open(logPath, 'rb') as pfile:
h = pickle.load(pfile)
print(h.keys())
fig, ax = plt.subplots(2, figsize=(20, 20), dpi=100)
ax[0].plot(h['dice_sub_train'], color='tab:cyan', linewidth=2.0, label="Train")
ax[0].plot(smooth_curve(h['dice_sub_train']), color='tab:purple')
ax[0].set_xlabel('Epoch/iterations', fontsize=20)
ax[0].set_ylabel('Dice Score', fontsize=20)
ax[0].legend(loc='lower right', fontsize=20)#, frameon=False)
ax1 = ax[0].twiny()
ax1.plot(h['dice_sub_valid'], color='tab:orange', linewidth=2.0, alpha=0.9, label="Validation" )
ax1.plot(smooth_curve(h['dice_sub_valid']), color='tab:red')
# , bbox_to_anchor = (0.816, 0.85)
ax[1].plot(h['loss_sub_train'], color='tab:cyan', linewidth=2.0, label="Train")
ax[1].plot(smooth_curve(h['loss_sub_train']), color='tab:purple')
ax2 = ax[1].twiny()
ax2.plot(h['loss_sub_valid'], color='tab:orange', linewidth=2.0, label="Validation", alpha=0.6)
ax2.plot(smooth_curve(h['loss_sub_valid']), color='tab:red')
ax[1].set_xlabel('Epoch/iterations', fontsize=20)
ax[1].set_ylabel('loss(a.u.)', fontsize=20)
ax[1].legend(loc='upper right', fontsize=20)
# ,bbox_to_anchor = (0.8, 0.9)
plt.suptitle('Subject wise dice score and loss', fontsize=30)
plt.setp(ax[0].get_xticklabels(), fontsize=20, fontweight="normal", horizontalalignment="center") #fontweight="bold"
plt.setp(ax[0].get_yticklabels(), fontsize=20, fontweight='normal', horizontalalignment="right")
plt.setp(ax[1].get_xticklabels(), fontsize=20, fontweight="normal", horizontalalignment="center")
plt.setp(ax[1].get_yticklabels(), fontsize=20, fontweight="normal", horizontalalignment="right")
plt.show()
Any idea how to solve the issue?
[1]: https://i.stack.imgur.com/kg7PY.png

ax1 has a twin y-axis with ax[0], but they are two separate axes. That's why ax[0].legend() does not know about the Validation line of ax1.
To have Train and Validation on the same legend, plot empty lines on the main axes ax[0] and ax[1] with the desired color and label. This will generate dummy Validation entries on the main legend:
...
ax[0].plot([], [], color='tab:orange', label="Validation")
ax[0].legend(loc='lower right', fontsize=20)
...
ax[1].plot([], [], color='tab:orange', label="Validation")
ax[1].legend(loc='upper right', fontsize=20)
...

Related

How to access second plot axis parameters?

I am trying to access the ax parameters of the second subplot figure, ax1. I am trying to put a title and remove the overlapping ticks but can't manage to get to them.
Here is the code and figure I have made :
fig, (ax0, ax1)= plt.subplots(nrows=1,
ncols=2,
sharey=True,
tight_layout = True,
gridspec_kw={'width_ratios': [3, 24],'wspace': 0})
ax1=librosa.display.specshow(data=df.iloc[i,2],
sr=fe,
x_axis='time',
y_axis='mel',
htk=False,
x_coords=np.linspace(0,duree[i],df.iloc[i,2].shape[1]),
hop_length=1000,
cmap=plt.get_cmap("magma"),
fmin=0, fmax=fe//2,
vmin=40, vmax=140)
ax0.plot(df.loc[Names[i], "DSP"], df.loc[Names[i], "f_dsp"],
linewidth=1, label=Names[i]) # ,color='grey')
ax0.set_title('Subplot 1')
ax0.set_xlim([20, 100])
ax0.set_ylim([0, fe//2])
ax0.set_ylabel('Fréquence [Hz]')
ax0.set_xlabel('Amplitude [dB ref 1µPa²]')
ax0.grid(visible=True)
ax0.grid(b=True, which='minor', color='k', linestyle=':', lw=0.5)
yticks=np.array([10,100,1000,10000,100000],dtype=int)
yticks_hz = np.unique(np.concatenate((np.array([fe//2],dtype=int),
np.array([250*2**n for n in range(0,int(np.log2(fe//(2*250))))]))))
ax0.set_yticks(yticks, minor=False)
ax0.set_yticks(yticks_hz, minor=True)
ax0.set_yticklabels(yticks,
minor=False,
fontdict={'fontweight':'demibold','fontsize':8})
ax0.set_yticklabels(yticks_hz,
minor=True,
fontdict={'fontsize':8})
# =============================================================================
# Doesnt work :(
# =============================================================================
ax1.set_title("Subplot 2")
ax1.get_yaxislabels().set_visible(False)
ax1.get_xaxislabels()[0].set_visible(False)

How to plot subplots horizontally and vertically aligned with respect to each other in matplotlib?

I have 3 subplots in matplotlib (2 rows and 3 columns).
I intend to have the first subplot in the 1st row covering the all 3 columns;
second subplot in the 2nd row left most aligned
third subplot in the 2nd row right most aligned.
Both second and third subplots horizontally aligned to the top with respect to each other.
However with the code below, I could not horizontally align them.
I also used gridspec.GridSpec with some width_ratios.
import numpy as np
import os
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib import gridspec
def plot_figure():
xticklabels_list = ['a','b','c','d','e','f'] * 6
rows=['row1']
plot1 = plt.figure(figsize=(5 + 1.5 * len(xticklabels_list), 5 + 1.5 * len(rows)))
gs = gridspec.GridSpec(2, 3, width_ratios=[1, 1, 1], height_ratios=[1, 1])
top_axis = plt.subplot(gs[0, :])
bottom_left_axis = plt.subplot(gs[-1, 0])
bottom_right_axis = plt.subplot(gs[-1, -1])
plot_at_bottom_left_axis(bottom_left_axis)
top_axis.set_xlim([0, 36])
top_axis.set_xticklabels([])
top_axis.tick_params(axis='x', which='minor', length=0, labelsize=35)
top_axis.set_xticks(np.arange(0, 36, 1))
top_axis.set_xticks(np.arange(0, 36, 1) + 0.5, minor=True)
top_axis.set_xticklabels(xticklabels_list, minor=True)
top_axis.xaxis.set_label_position('top')
top_axis.xaxis.set_ticks_position('top')
plt.tick_params(
axis='x', # changes apply to the x-axis
which='major', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False) # labels along the bottom edge are off
# CODE GOES HERE TO CENTER Y-AXIS LABELS...
top_axis.set_ylim([0, len(rows)])
top_axis.set_yticklabels([])
top_axis.tick_params(axis='y', which='minor', length=0, labelsize=40)
top_axis.set_yticks(np.arange(0, len(rows), 1))
top_axis.set_yticks(np.arange(0, len(rows), 1) + 0.5, minor=True)
top_axis.set_yticklabels(rows, minor=True) # fontsize
plt.tick_params(
axis='y', # changes apply to the x-axis
which='major', # both major and minor ticks are affected
left=False) # labels along the bottom edge are off
# Gridlines based on major ticks
top_axis.grid(which='major', color='black', zorder=3)
# Put the legend
legend_elements = [ Line2D([0], [0], marker='o', color='white', label='legend1', markerfacecolor='red',markersize=40),
Line2D([0], [0], marker='o', color='white', label='legend2', markerfacecolor='green',markersize=40)]
bottom_right_axis.set_axis_off()
bottom_right_axis.legend(handles=legend_elements, ncol=len(legend_elements), bbox_to_anchor=(1,1), loc='upper right',fontsize=40)
figFile = os.path.join('/Users/burcakotlu/Desktop', 'test.png')
plot1.savefig(figFile, dpi=100, bbox_inches="tight")
plt.cla()
plt.close(plot1)
def plot_at_bottom_left_axis(ax):
box = ax.get_position()
ax.set_position([0, 1, box.width * 1, box.height * 1], which='active')
diameter_labels = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
diameter_ticklabels = ['label1', '', '', '', 'label2', '', '', '', '', 'label3']
row_labels = ['circle']
ax.grid(which="major", color="w", linestyle='-', linewidth=3)
plt.setp(ax.spines.values(), color='white')
ax.set_aspect(1.0)
ax.set_facecolor('lightcyan')
# CODE GOES HERE TO CENTER X-AXIS LABELS...
ax.set_xlim([0, len(diameter_labels)])
ax.set_xticklabels([])
ax.tick_params(axis='x', which='both', length=0, labelsize=30)
ax.set_xticks(np.arange(0, len(diameter_labels), 1))
ax.set_xticks(np.arange(0, len(diameter_labels), 1) + 0.5, minor=True)
ax.set_xticklabels(diameter_ticklabels, minor=True)
ax.xaxis.set_ticks_position('bottom')
plt.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False) # labels along the bottom edge are off
ax.set_xlabel('labels to be shown', fontsize=40, labelpad=10)
ax.set_ylim([0, len(row_labels)])
ax.set_yticklabels([])
ax.tick_params(axis='y', which='minor', length=0, labelsize=12)
ax.set_yticks(np.arange(0, len(row_labels), 1))
ax.set_yticks(np.arange(0, len(row_labels), 1) + 0.5, minor=True)
plt.tick_params(
axis='y', # changes apply to the x-axis
which='major', # both major and minor ticks are affected
left=False) # labels along the bottom edge are off
plot_figure()
Here is the resulting figure for the code above.

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()

interactive legend with twinx - matplotlib

i want to create a plot with two y-axis and interactive legend. I made a minimal "working" example based on: https://matplotlib.org/3.1.1/gallery/event_handling/legend_picking.html
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0.0, 5, 0.01)
y1 = 2*np.sin(2*np.pi*t)
y2 = 4*np.sin(2*np.pi*2*t)+1
fig, ax = plt.subplots()
ax.set_title('Click on legend line to toggle line on/off')
line1, = ax.plot(t, y1, lw=2, label='1 HZ')
ax2 = ax.twinx()
line2, = ax2.plot(t, y2, lw=2, label='2 HZ')
lines_twinx=[line1,line2]
lbl = [l.get_label() for l in lines_twinx]
leg=ax.legend(lines_twinx, lbl, loc="upper left",fontsize='xx-small', shadow=True)
leg.get_frame().set_alpha(0.4)
lined = dict()
for legline, origline in zip(leg.get_lines(), lines_twinx):
legline.set_picker(5) # 5 pts tolerance
lined[legline] = origline
def onpick(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined[legline]
vis = not origline.get_visible()
origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
plot-figure
Somehow the legend is not clickable. Anybody knows what to do?
Using fig.legend() instead of ax.legend() helped me
leg=fig.legend(lines_twinx, lbl, loc="upper left",fontsize='xx-small', shadow=True)
view here

How to increase padding (whitespace) for subplots when saving as png?

I have created a 4x2 subplot and I want to save the individual plots as separate png files. I am able to do this with the following code, however the tightbbox format is too tight and it makes it tough to read the title and x,y labels. Is there a way to increase the padding (whitespace) around each individual plot when using this tightbbox layout?
nrow = combined.shape[0]
ncol = combined.shape[1]
shape_row = int((nrow + 1)/2 if (nrow % 2) != 0 else nrow/2)
fig, axes = plt.subplots(shape_row, 2, figsize=(20,45))
fig.subplots_adjust(hspace=0.5, wspace=0.4)
plt.rcParams['savefig.facecolor'] = 'white'
axes = axes.ravel()
for i in range(nrow):
axes[i].bar(range(2), combined.iloc[i,:2].values, color='blue', label=label_1)
axes[i].plot(range(2,ncol-1), combined.iloc[i,2:-1], 'o-', color='orange', markersize=10, label=label_2)
axes[i].plot([-1, 7], [combined.iloc[i,-1]]*2, linestyle='--', color='red', label=label_3)
axes[i].plot([ncol-1,ncol-1],[0, combined.iloc[i,-1]], '--', color='red')
axes[i].set_title(combined.index[i], fontsize=26, fontweight='bold', pad=15)
axes[i].set_xlabel('')
axes[i].set_ylabel("(in local currency '000s)", fontsize=18, labelpad=10)
axes[i].set_xticks(range(ncol))
axes[i].set_xticklabels(combined.columns, rotation=45)
axes[i].tick_params(labelsize=18, pad=10)
axes[i].set_xlim([-.7, nrow-.97])
axes[i].margins(x=0, y=0.2)
blue_bar= mpatches.Patch(color='blue', label='aaa')
orange_line=mlines.Line2D(range(2,ncol-1), combined.iloc[i,2:-1], color='orange', linestyle='-', marker = 'o', markersize=10, label='bbb')
red_line=mlines.Line2D([-1, 7], [combined.iloc[i,-1]]*2, color='red', linestyle='--', label='ccc')
lgd = axes[i].legend(handles=[blue_bar, orange_line, red_line],
loc='upper right', bbox_to_anchor=(1,1), fontsize=18, shadow=True, borderpad=1)
bbox = axes[i].get_tightbbox(fig.canvas.get_renderer())
fig.savefig("subplot{}.png".format(i),
bbox_inches=bbox.transformed(fig.dpi_scale_trans.inverted()))