Related
I want to draw a line&bar chart by Matplotlib, it seems the legend of chart will overlap on upper left part.
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.figure(figsize=(7,5), dpi=100)
x = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','>30']
y = [0.0964,0.1606,0.1651,0.1395,0.1105,0.0829,0.0603,0.0436,0.0328,0.0245,0.0183,0.0135,0.0105,0.0082,0.0061,0.0052,0.0041,0.0030,0.0024,0.0022,0.0013,0.0012,0.0010,0.0009,0.0009,0.0007,0.0006,0.0004,0.0005,0.0002,0.0023]
z = [11541,19234,19776,16704,13237,9931,7215,5218,3931,2936,2194,1619,1262,984,735,625,496,357,282,269,161,146,124,108,104,78,69,50,62,29,274]
plt.bar(x=x, height=z, label='Vehicles Num', color='Blue', alpha=0.7, width=0.5)
# plt.legend(loc="upper left")
plt.legend(loc="upper right")
plt.title("Frequency Distribution of Hood Ajar/Closed Events",size=18)
plt.xlabel("Frequency",size=15)
plt.ylabel("Number of Vehicles",size=15)
plt.yticks(size = 11)
plt.xticks(size = 11)
ax2 = plt.twinx()
ax2.set_ylabel("Percentage",size=15)
ax2.plot(y)
ax2.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1, decimals=1))
plt.plot(x, y, "r", marker='.', c='r', ms=8, linewidth='1', label="Percentage")
plt.legend(loc="upper right")
# for a, b in zip(x, y):
# plt.text(a, b, str(b*100)+'%', ha='center', va='bottom', fontsize=13)
#plt.savefig('cx483_2.jpg')
plt.show()
What can I do to make my legend more harmonious? Thanks a lot.
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)
...
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()
I have profiles that plot outside the axes limits. That is a given. It cannot be extended as it is shared with more axes below and above that have raster data with a strict extent.
I would like to provide a scale in the form of an axis spine to the first profile (see attached code and figure).
Is there a way to place ticks and ticklabels outside the axis limit?
fig, ax = plt.subplots()
y = np.linspace(0, 10, 100)
x = 10 * np.sin(y)
x_offsets = np.linspace(0, 100, 20)
for offset in x_offsets:
if offset == 0:
color = 'tab:blue'
ax.axvline(0, color=color, ls='dotted', lw=0.5)
else:
color = 'k'
ax.plot(x + offset, y, color, clip_on=False)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
major_ticks = np.linspace(x.min(), x.max(), 5)
minor_ticks = np.linspace(x.min(), x.max(), 9)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, True)
ax.spines['top'].set_bounds(major_ticks[0], major_ticks[-1])
ax.spines['top'].set_color('tab:blue')
ax.xaxis.tick_top()
ax.tick_params('x', which='both', color='tab:blue', labelcolor='tab:blue')
ax.set_xlabel('x label', position=(0, -0.1), color='tab:blue')
ax.xaxis.set_label_position('top')
# ax.tick_params('x', which='both', bottom=False, top=False, labelbottom=False)
ax.tick_params('y', which='both', left=False, right=False, labelleft=False)
ax.axis((0, 100, 0, 11))
Ok, so there is a very easy solution to this, however, unfortunately, I cannot really explain why it works. All you need to do is to put the repositioning of the axes at the beginning and not at the end:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.axis((0, 100, 0, 11)) # just move this line here
y = np.linspace(0, 10, 100)
x = 10 * np.sin(y)
x_offsets = np.linspace(0, 100, 20)
for offset in x_offsets:
if offset == 0:
color = 'tab:blue'
ax.axvline(0, color=color, ls='dotted', lw=0.5)
else:
color = 'k'
ax.plot(x + offset, y, color, clip_on=False)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
xticks = ax.get_xticklines()
for tick in xticks:
tick.set_clip_on(False)
major_ticks = np.linspace(x.min(), x.max(), 5)
minor_ticks = np.linspace(x.min(), x.max(), 9)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, True)
ax.spines['top'].set_bounds(major_ticks[0], major_ticks[-1])
ax.spines['top'].set_color('tab:blue')
ax.xaxis.tick_top()
ax.tick_params('x', which='both', color='tab:blue', labelcolor='tab:blue')
ax.set_xlabel('x label', position=(0.12, 0), color='tab:blue')
ax.xaxis.set_label_position('top')
# ax.tick_params('x', which='both', bottom=False, top=False, labelbottom=False)
ax.tick_params('y', which='both', left=False, right=False, labelleft=False)
xticks = ax.get_xticks()
axtrans = (ax.transData + ax.transAxes.inverted()).transform
figtrans = (ax.transData + fig.transFigure.inverted()).transform
for xtick in xticks:
print(axtrans((0, xtick)), figtrans((0, xtick)))
fig.show()
What is curious is that, if we believe the transformation data printed at the end, some of the ticks(-labels) are not only located outside of the axis, but even outside of the figure, although we can clearly see that they are still inside the figure. I am not sure what to make of this, especially since the same ticks(-labels) are also outside (although at different values), when the repositioning of the axes is done at the end. It would be interesting to have someone more knowledgeble to explain what is going on.
Here is another answer, which I hope should satisfy your requirement. Collect all the relevant ticks and labels and add them to the axes (again?):
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
y = np.linspace(0, 10, 100)
x = 10 * np.sin(y)
x_offsets = np.linspace(0, 100, 20)
for offset in x_offsets:
if offset == 0:
color = 'tab:blue'
ax.axvline(0, color=color, ls='dotted', lw=0.5)
else:
color = 'k'
ax.plot(x + offset, y, color, clip_on=False)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
major_ticks = np.linspace(x.min(), x.max(), 5)
minor_ticks = np.linspace(x.min(), x.max(), 9)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, True)
ax.spines['top'].set_bounds(major_ticks[0], major_ticks[-1])
ax.spines['top'].set_color('tab:blue')
ax.xaxis.tick_top()
ax.tick_params('x', which='both', color='tab:blue', labelcolor='tab:blue')
ax.set_xlabel('x label', position=(0, -0.1), color='tab:blue')
ax.xaxis.set_label_position('top')
ax.tick_params('y', which='both', left=False, right=False, labelleft=False)
ax.axis((0, 100, 0, 11))
ticks = ax.get_xticklines()
mticks = ax.get_xaxis().get_minor_ticks()
labels = ax.get_xticklabels()
for artist in [*ticks, *mticks, *labels]:
if artist.get_visible():
print(artist.axes)
ax.add_artist(artist)
artist.set_clip_on(False)
fig.show()
I find it very curious that:
there are more major xticks than there should be, and that the exessive ones aren't visible
the ticks and labels outside the axes that are obviously not visible, since they are not drawn, are alledgedly visible according to the artists.
except for the minor ticks, none of the artists are assigned to the axes, although half of them can clearly be seen to be part of the axes
even though all the minor ticks are supposed to be visible and belong to the axes, you still need to add them to the axes again or they won't show up
Thus, I cannot think of a way of how to only add the artists that are truely not visible but are actually supposed to be, other than to look at their x-axis position.
The solution was to use a blended transform to add an individual axes for the left most (or any) profile:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import blended_transform_factory
# make some sample data
dx = dy = 1
y = np.arange(80, 0 - dy, -dy)
x = np.arange(0, 100 + dx, dx)
x_offsets = np.linspace(0, 100, 11)
xx, yy = np.meshgrid(0.05 * (x + 10), 0.1 * (y - 40))
data1 = np.exp(-xx**2 - yy**2) - np.exp(-(xx - 1)**2 - (yy - 1)**2)
xx, yy = np.meshgrid(0.05 * (x - 90), 0.1 * (y - 40))
data2 = np.exp(-xx**2 - yy**2) - np.exp(-(xx - 1)**2 - (yy - 1)**2)
data = data1 + data2
data += np.random.rand(data.shape[0], data.shape[1]) * 0.5 * data
extent = (x[0] - 0.5 * dx, x[-1] + 0.5 * dx, y[-1] - 0.5 * dy, y[0] + 0.5 * dy)
# set up the plot
fig, ax = plt.subplots(
2, 2, sharey=True, figsize=(8, 4),
gridspec_kw=dict(width_ratios=(0.2, 1), wspace=0.1)
)
axTL = ax[0, 0]
axTR = ax[0, 1]
axBL = ax[1, 0]
axBR = ax[1, 1]
trans = blended_transform_factory(axTR.transData, axTR.transAxes)
data_abs_max = np.abs(data).max()
im = axBR.imshow(data, 'RdBu_r', vmin=-data_abs_max, vmax=data_abs_max,
extent=extent, aspect='auto', interpolation='bilinear')
axBR.axis(extent)
axBL.plot(data.sum(axis=1), y, 'k')
scale = 8
for offset in x_offsets:
profile = data[:, int(offset / dx)]
profile = scale * profile
xmin, xmax = profile.min(), profile.max()
if offset == 0:
bounds = (offset + xmin, 0, xmax - xmin, 1)
inset_ax = axTR.inset_axes(bounds, transform=trans)
inset_ax.set_ylim(axTR.get_ylim())
inset_ax.set_xlim(xmin, xmax)
color = 'tab:blue'
inset_ax.axvline(0, color=color, ls='dotted', lw=0.5)
inset_ax.plot(profile, y, color, clip_on=False, zorder=1)
inset_ax.set_facecolor('none')
inset_ax.spines['left'].set_visible(False)
inset_ax.spines['bottom'].set_visible(False)
inset_ax.spines['right'].set_visible(False)
inset_ax.spines['top'].set_color('tab:blue')
inset_ax.tick_params(
'both', which='both',
top=True, left=False, right=False, bottom=False,
labeltop=True, labelleft=False,
color='tab:blue', labelcolor='tab:blue'
)
inset_ax.set_xlabel('x label', color='tab:blue')
inset_ax.xaxis.set_label_position('top')
inset_ax.xaxis.tick_top()
else:
color = 'k'
axTR.plot(profile + offset, y, color, clip_on=False, zorder=0)
# remove unwanted spines and ticks
axTR.axis('off')
axTL.spines['top'].set_visible(False)
axTL.spines['right'].set_visible(False)
axTL.spines['bottom'].set_visible(False)
axTL.tick_params('both', which='both', top=False, right=False, bottom=False,
labelbottom=False)
axBR.tick_params('both', which='both', labelleft=False)
axTR.axis(extent)
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()))