Plot circle at the title in matplotlib python - matplotlib

I have a 2 line title and first line has a number at the end of the line.
Can we plot a circle around the number?
Here is the code to generate the figure.
from matplotlib import rcParams
from matplotlib import pyplot as plt
import numpy as np
import os
rcParams.update({'figure.autolayout': True})
some_text = 'XXX'
any_number=15
title = '%s: %d\n YYY ZZZZ WWWWW' % (some_text,any_number)
fig = plt.figure(figsize=(8, 8), dpi=100)
plt.tick_params(axis='y', which='major', labelsize=60, width=3, length=10, pad=40)
plt.tick_params(axis='y', which='minor', labelsize=60, width=3, length=10, pad=40)
ax = plt.gca()
plt.title(title, fontsize=60, pad=40, loc='center', fontweight='semibold')
plt.style.use('ggplot')
ax.set_facecolor('white')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(True)
for edge_i in ['left']:
ax.spines[edge_i].set_edgecolor("black")
ax.spines[edge_i].set_linewidth(3)
ax.spines[edge_i].set_bounds(0, 1)
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
plt.yticks(np.arange(0, 1.01, step=0.2))
data_list= np.array([1,1,1,1,1,0.9, 0.8, 0.7, 0.8,0.85])
plt.bar(x, data_list, 0.9, color='indianred',edgecolor="black", linewidth=3,zorder=1)
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, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
figure_name = 'figure_with_circle.png'
figure_file = os.path.join('/Users/burcakotlu/Desktop',figure_name)
fig.savefig(figure_file, dpi=100, bbox_inches="tight")
plt.close(fig)
Here is the current figure and the wanted circle.

One could use the following without ax.bar():
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_title('title')
circle1 = plt.Circle((2,4.15), 0.2, color='k', clip_on=False, zorder=100, fill=False)
ax.add_patch(circle1)
ax.set_xlim(0,4)
ax.set_ylim(0,4)
plt.show()

I have found a way to plot circle together with bar plots without distorting bars. Here is the code below:
from matplotlib import rcParams
from matplotlib import pyplot as plt
import numpy as np
import os
import matplotlib.patches as patches
from matplotlib.offsetbox import AnchoredText
rcParams.update({'figure.autolayout': True})
some_text = 'XXX'
any_number=15
title = '%s: %d\n YYY ZZZZ WWWWW' % (some_text,any_number)
fig = plt.figure(figsize=(12,12), dpi=100)
plt.tick_params(axis='y', which='major', labelsize=60, width=3, length=10, pad=40)
plt.tick_params(axis='y', which='minor', labelsize=60, width=3, length=10, pad=40)
ax = plt.gca()
number_of_xxx = '12'
anchored_text_number_of_xxx = AnchoredText(number_of_xxx,
frameon=False, borderpad=0, pad=0.1,
loc='upper right',
bbox_to_anchor=[0.95, 1.3],
bbox_transform=plt.gca().transAxes,
prop={'fontsize': 60,
'fontweight': 'semibold'})
ax.add_artist(anchored_text_number_of_xxx)
circle1 = patches.Circle((0.88, 1.25), radius=0.1, transform=ax.transAxes, zorder=100, fill=False, color='gold', lw=8, clip_on=False)
ax.add_patch(circle1)
ax.set_title(title, fontsize=60, pad=40, loc='center', fontweight='semibold', zorder=50)
ax.set_facecolor('white')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(True)
for edge_i in ['left']:
ax.spines[edge_i].set_edgecolor("black")
ax.spines[edge_i].set_linewidth(3)
ax.spines[edge_i].set_bounds(0, 1)
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ax.set_yticks(np.arange(0, 1.01, step=0.2))
data_list= np.array([1,1,1,1,1,0.9, 0.8, 0.7, 0.8,0.85])
ax.bar(x, data_list, 0.9, color='indianred',edgecolor="black", linewidth=3,zorder=1)
ax.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, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
figure_name = 'figure_with_circle.png'
figure_file = os.path.join('/Users/burcakotlu/Desktop',figure_name)
fig.savefig(figure_file, dpi=100, bbox_inches="tight")
plt.close(fig)

Related

create a cartesian 2D plane coordinate system using plotly

This creates the graph that I'm talking about using matplotlib:
import matplotlib.pyplot as plt
import numpy as np
xmin, xmax, ymin, ymax = -9, 9, -9, 9
fig, ax = plt.subplots(figsize=(20, 20))
ax.set(xlim=(xmin - 1, xmax + 1), ylim=(ymin - 1, ymax + 1), aspect='equal')
ax.spines['bottom'].set(position="zero", linewidth=2.5)
ax.spines['left'].set(position="zero", linewidth=2.5)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.text(10.15, 0, "x", fontdict=font, va="center")
ax.text(0, 10.15, "y", fontdict=font, ha="center")
x_ticks = np.arange(xmin, xmax)
y_ticks = np.arange(ymin, ymax)
ax.set_xticks(x_ticks[x_ticks != x_ticks])
ax.set_yticks(y_ticks[y_ticks != y_ticks])
ax.set_xticks(np.arange(xmin, xmax+1), minor=True)
ax.set_yticks(np.arange(ymin, ymax+1), minor=True)
ax.grid(which='both', color='grey', linewidth=1, linestyle='-', alpha=0.25)
plt.show()
Output: 2d cartesian plane as below
Is it possible to get a similar result with plotly?
This is how it can be done in Plotly:
import plotly.graph_objects as go
axis_range = [-9,9]
fig = go.Figure()
fig.update_xaxes(range=axis_range,title = 'y', tickmode = 'linear',
showticklabels = False, side='top',gridcolor="rgb(224,224,224)")
fig.update_yaxes(range=axis_range,title = 'x', tickmode = 'linear',
showticklabels = False, side='right', gridcolor="rgb(224,224,224)")
fig.add_vline(x=0, line_width=3)
fig.add_hline(y=0, line_width=3)
fig.update_layout(plot_bgcolor='rgb(255,255,255)', height=800, width=800)
fig.show()
The only drawback here is the label of x-axis cannot be rotated in Plotly as documented here.

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

legend outside plot when ax1 ax2 twin axes

I am applying this strategy to place legend outside plot. The main difference here is that there are ax1 and ax2 twin axes.
The x value in bbox_to_anchor is set to 0.89 in the following MWE.
As can be seen, the legend box does not display the entire string labels for each color:
MWE:
import matplotlib.pyplot as plt
import numpy as np
suptitle_label = "rrrrrrrr # ttttt yyyyyyy. uuuuuuuuuuuuuuuuuu\n$[$Xx$_{2}$Yy$_{7}]^{-}$ + $[$XxYy$_{2}$(cccc)$_{2}]^{+}$ JjYy model"
# Plotting
fig, ax1 = plt.subplots()
ax1.set_xlabel('Time')
ax1.set_ylabel('y1label')
new_time = np.linspace(1, 8, 100)
j_data = [np.linspace(1, 4, 100), np.linspace(1, 5, 100), np.linspace(1, 6, 100), np.linspace(1, 7, 100)]
sorted_new_LABELS_fmt = ['$[$XxYy$_{2}$(cc)$_{2}]^{+}$', '$[$Xx$_{2}$Yy$_{7}]^{-}$', '$[$XxYy$_{4}]^{-}$', '$[$Xx$_{2}$Yy$_{5}$(cc)$_{2}]^{+}$']
sorted_new_LABELS_colors = ['green', 'red', 'blue', 'orange']
for j,k,c in zip(j_data, sorted_new_LABELS_fmt, sorted_new_LABELS_colors):
ax1.plot(new_time, j, label='%s' % k, color='%s' %c)
All_e_chunks_n = np.linspace(-850, -860, 100)
ax2 = ax1.twinx()
ax2.set_ylabel('y2label')
ax2.plot(new_time, All_e_chunks_n, 'grey', alpha=0.6, linewidth=2.5, label='y2')
# Shrink cccrent axis
box = ax1.get_position()
ax1.set_position([box.x0, box.y0, box.width * 0.9, box.height])
# Put the legend:
fig.legend(loc='center left', bbox_to_anchor=(0.89, 0.5))
fig.suptitle(suptitle_label, fontsize=15)
fig.savefig('mwe.pdf', bbox_inches='tight')
Decreasing this x value and commenting out thebbox_inches='tight' part, yields the following:
For bbox_to_anchor=(0.85, 0.5), this is the result:
For bbox_to_anchor=(0.80, 0.5), this is the result:
For bbox_to_anchor=(0.7, 0.5), this is the result:

Align ylabel with yticks

The code below draws a plot that looks almost exactly the way I want it to be. However, I'd like the ylabel to be horizontal and left-aligned with the yticks. Currently, the ylabel is placed left relative to the yticks which looks ugly (the image below shows the upper left corner of the plot). Does someone know how to fix this?
import matplotlib.pyplot as plt
import numpy as np
xvals = range(0,10);
yvals = lambda s: [ x*x*s for x in xvals ]
# setting the x=... option does NOT help
yprops = dict(rotation=0, y=1.05, horizontalalignment='left')
plt.subplot(111,axisbg='#BBBBBB',alpha=0.1)
plt.grid(color='white', alpha=0.5, linewidth=2, linestyle='-', axis='y')
for spine_name in ['top', 'left', 'right']:
plt.gca().spines[spine_name].set_color('none')
plt.ylabel('y label', **yprops)
plt.xlabel('x label')
plt.gca().tick_params(direction='out', length=0, color='k')
plt.plot(xvals, yvals(1), 'bo-', linewidth=2)
plt.gca().set_axisbelow(True)
plt.show()
You can adjust the coordinates using ax.yaxis.set_label_coords like in this example.
With your data:
import matplotlib.pyplot as plt
import numpy as np
xvals = range(0,10);
yvals = lambda s: [ x*x*s for x in xvals ]
yprops = dict(rotation=0, x=0, y=1.05)
fig, ax = plt.subplots(1, 1, figsize=(5,3))
ax.set_ylabel('y label', **yprops )
ax.set_xlabel('x label')
ax.plot(xvals, yvals(1), 'bo-', linewidth=2)
print(ax.get_position())
ax.yaxis.set_label_coords(-0.1,1.05)
fig.savefig('cucu.png')
plt.show()
Note that if you go further away, the label will be placed outside the figure. If that is the case, you can adjust the margins before:
fig, ax = plt.subplots(1, 1, figsize=(5,3))
ax.set_ylabel('y label', **yprops )
ax.set_xlabel('x label')
ax.plot(xvals, yvals(1), 'bo-', linewidth=2)
fig.subplots_adjust(left=0.2, bottom=0.2, right=0.8, top=0.8)
ax.yaxis.set_label_coords(-0.2,1.2)
fig.savefig('cucu2.png')
plt.show()
See also this answer