How to stop multiple subplots overlapping in mathplotlib? - python

I want to display two pie-charts, well donut charts, side by side. But using the code below all I'm getting is overlapping graphs. I've tried using various values for subplot adjust but the legends always end up overlapping. Chopped out all the non relevant code in the function
#Function to draw graphs for sports data
#Create figure with two subplots
fig,ax=plt.subplots(1,2,subplot_kw=dict(aspect="equal"))
j=0
#Loop through all columns we want to graph
for type in types:
#Create a pie chart
wedges, texts, autotexts = ax[j].pie(to_plot,
explode=explode,
labels=labels,
colors=colors,
autopct=lambda pct: func(pct, data),
pctdistance=0.8,
counterclock=False,
startangle=90,
wedgeprops={'width': 0.75},
radius=1.75
)
#Set label colors
for text in texts:
text.set_color('grey')
#Create legend
ax[j].legend(wedges, leg_labels,
title=title,
title_fontsize="x-large",
loc="center left",
bbox_to_anchor=(1.5, 0, 0.5, 1),
prop={'size': 12})
j += 1
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.5, hspace=None)
plt.show()
return

The bbox setting that determines the position of the legend is set to the right of each pie chart, so they overlap. Therefore, we can avoid overlapping the legends by setting the respective positions for the graphs.
import matplotlib.pyplot as plt
# Some data
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15, 30, 45, 10]
titles = ['20 members\nfollow Basketball','23 members\nfollow Basketball']
legend_pos = ['center left','center right']
bboxes = [(-1.0, 0, 0.5, 1),(1.5, 0, 0.5, 1)]
# Make figure and axes
fig, axs = plt.subplots(1, 2, subplot_kw=dict(aspect="equal"))
for i in range(2):
wedges, texts,_ = axs[i].pie(fracs,
labels=labels,
autopct='%.0f%%',
shadow=True,
explode=(0, 0.1, 0, 0),
wedgeprops=dict(width=0.6))
axs[i].legend(wedges,
labels,
title=titles[i],
title_fontsize="x-large",
loc=legend_pos[i],
bbox_to_anchor=bboxes[i],
prop={'size': 12})
plt.show()

Related

How do I replace labels in a legend on a pie chart?

I have a pie chart showing the percentage of product sales from total revenue. The product names are too big, so they are abbreviated around the diagram, but in the legend the names should be full. Basically I just need to replace the labels in the legend. However, when creating a pie chart, I specify a list of labels from where the program should take them and the subsequent change of labels in the legend does not change anything.
data_names - Full names
data_names 2 - Abbreviated names
The labels=data_names command in plt.legend does not work.
ax.pie(data_values, labels=data_names2, colors=colors, radius=5, center=(0, 0),
wedgeprops={"linewidth": 0.4, "edgecolor": "white"}, frame=True, rotatelabels=True)
plt.legend(
loc='best', bbox_to_anchor=(1, 0.92),labels=data_names)
I found this link that might be helpful.
Lengend Labels
Using their example, I created a simple pie chart.
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = [7.50, 3.50]
plt.rcParams["figure.autolayout"] = True
labels = ['Walking', 'Talking', 'Sleeping', 'Working']
abbrevs = ['Walk', 'Talk', 'Sleep', 'Work'] # Abbreviations for the legend
sizes = [23, 45, 12, 20]
colors = ['red', 'blue', 'green', 'yellow']
patches, texts = plt.pie(sizes, colors=colors, shadow=True, labels = labels, startangle=90)
plt.legend(patches, abbrevs, loc="best")
plt.axis('equal')
plt.show()
And that produced the pie chart with a legend with custom labels.
Hope that helps.
Regards.

How do I make a SINGLE legend using subplots? _get_legend_handles_labels is not working

I want to make a single legend with corresponding colors for the models that are in the individual plots on the whole subplot.
My current code is as follows:
from matplotlib.legend import _get_legend_handles_labels
colors = ['red', 'blue','darkblue', 'purple', 'orange', 'brown', 'pink', 'darkgreen', 'gray']
models = ['Logistic Regression', 'SVM', 'Decision Tree', 'Random Forest', 'XGBoost', 'ADABoost', 'Gaussian NB', 'KNN', 'MLP']
# English
fig, axs = plt.subplots(4,2, figsize=(25,15))
fig.suptitle('Performance measures for English and Arabic data', fontsize = 25)
axs[0,0].bar(models, en_f1_scores, color=colors)
axs[0,0].set_title("English F1 score", fontsize = 20)
axs[1,0].bar(models, en_acc_scores, color=colors)
axs[1,0].set_title("English accuracy score", fontsize = 20)
axs[2,0].bar(models, en_recall_scores, color=colors)
axs[2,0].set_title("English recall score", fontsize = 20)
axs[3,0].bar(models, en_precision_scores, color=colors)
axs[3,0].set_title("English precision score", fontsize = 20)
# Arabic
axs[0,1].bar(models, ar_f1_scores, color=colors)
axs[0, 1].set_title("Arabic F1 score", fontsize = 20)
axs[1,1].bar(models, ar_accuracy_scores, color=colors)
axs[1,1].set_title("Arabic accuracy score", fontsize = 20)
axs[2,1].bar(models, ar_recall_scores, color=colors)
axs[2,1].set_title("Arabic recall score", fontsize = 20)
axs[3,1].bar(models, ar_precision_scores, color=colors)
axs[3,1].set_title("Arabic precision score", fontsize = 20)
fig.tight_layout(pad=3.0)
And the current output looks like this:
Adding this code:
lines, labels = fig.axes[-1].get_legend_handles_labels()
fig.legend(lines, labels, loc = 'upper center')
Does not do anything, all it shows me is:
<matplotlib.legend.Legend at 0x266574402b0>
Moreover, both lines and labels are empty arrays; []
What can I do to add a legend at the top of the subplot figure? (if it is a horizontal legend, even better!)
Thank you!
First, I would suggest to save all information into lists, so the plot can be made via a large loop. That way, if some detail changes, it only needs to be changed at one spot.
To create a legend, graphical elements that have a "label" will be added automatically. Normally, a complete bar plot only gets one label. By diving into the generated bars, individual labels can be assigned.
The code first creates a dummy legend, so fig.tight_layout() can adapt all the spacings and leave some place for the legend. After calling fig.tight_layout(), the real legend is created. (With the real legend, fig.tight_layout() would try to assign it completely to one subplot, and create a wide gap between the two columns of subplots).
import matplotlib.pyplot as plt
import numpy as np
colors = ['red', 'blue', 'darkblue', 'purple', 'orange', 'brown', 'pink', 'darkgreen', 'gray']
models = ['Logistic Regression', 'SVM', 'Decision Tree', 'Random Forest', 'XGBoost', 'ADABoost', 'Gaussian NB', 'KNN', 'MLP']
titles = ["F1 score", "accuracy score", "recall score", "precision score"]
N = len(models)
en_f1_scores = np.random.rand(N)
en_acc_scores = np.random.rand(N)
en_recall_scores = np.random.rand(N)
en_precision_scores = np.random.rand(N)
en_scores = [en_f1_scores, en_acc_scores, en_recall_scores, en_precision_scores]
ar_f1_scores = np.random.rand(N)
ar_acc_scores = np.random.rand(N)
ar_recall_scores = np.random.rand(N)
ar_precision_scores = np.random.rand(N)
ar_scores = [ar_f1_scores, ar_acc_scores, ar_recall_scores, ar_precision_scores]
fig, axs = plt.subplots(4, 2, figsize=(25, 15), sharex=True, sharey='row')
fig.suptitle('Performance measures for English and Arabic data', fontsize=25)
for axs_row, en_score, ar_score, title in zip(axs, en_scores, ar_scores, titles):
for language, score, ax in zip(['English', 'Arabic'], [en_score, ar_score], axs_row):
ax.bar(models, score, color=colors)
ax.set_title(language + ' ' + title, fontsize=20)
ax.set_xticks([]) # remove the x tick and their labels
ax.grid(axis='y', ls=':', color='black') # add some gridlines
ax.set_axisbelow(True) # gridlines behind the bars
for spine in ['top', 'right', 'left']: # remove part of the surrounding box, as it gets busy with the grid lines
ax.spines[spine].set_visible(False)
ax.margins(x=0.01) # less white space left and right
# the legend is created for each graphical element that has a "label"
for bar, model in zip(axs[0, 0].containers[0], models):
bar.set_label(model)
# first create a dummy legend, so fig.tight_layout() makes enough space
axs[0, 0].legend(handles=axs[0, 0].containers[0][:1],
bbox_to_anchor=(0, 1.12), loc='lower left')
fig.tight_layout(pad=3.0)
# now create the real legend; if fig.tight_layout() were called on this,
# it would create a large empty space between the columns of subplots
# as it wants the legend to belong to only one of the subplots
axs[0, 0].legend(handles=axs[0, 0].containers[0], ncol=len(models),
bbox_to_anchor=(1.03, 1.12), loc='lower center', fontsize=18)
plt.show()

python: pie chart font size

I'm plotting a pie chart using the following code:
fig1, ax1 = plt.subplots(figsize=(8,12))
ax1.pie(data,
labels=label,
radius=1,
startangle=90,
colors=cols,
counterclock=False,
shadow=False,
wedgeprops={'edgecolor': 'white', 'linewidth': 1},
textprops={'fontsize': 8},
pctdistance=0.85,
autopct='%1.1f%%')
plt.title('Title', fontsize=16)
plt.tight_layout()
When I change the font size in textprops both the font size of the labels and the percentages change.
What I would like to do is use different font sizes for the labels and the percentages.
I applied your code to the example in the official reference and added the code to change the font.
import matplotlib.pyplot as plt
# Pie chart, where the slices will be ordered and plotted counter-clockwise:
l = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')
cols = ['C0','C1','C2','C3']
fig1, ax1 = plt.subplots(figsize=(8,12))
wdges, labels, autopct = ax1.pie(sizes,
labels=l,
radius=1,
startangle=90,
colors=cols,
counterclock=False,
shadow=False,
wedgeprops={'edgecolor': 'white', 'linewidth': 1},
textprops={'fontsize': 8},
pctdistance=0.85,
autopct='%1.1f%%')
plt.setp(labels, fontsize=15) #update
plt.title('Title', fontsize=16)
plt.tight_layout()

Matplotlib/Seaborn: ValueError when annotating 2nd subplot (annotating 1st subplot was successful)

I created this figure with two subplots and wanted to annotate both subplots separately (see figure and code below)
# Declare fig ax
fig, ax = plt.subplots(2,1,figsize=[12,10], sharey=True)
# Line plot for ax[0]
sns.lineplot(x=['2020-01-01', '2020-01-31'], y=[32.7477, 49.6184], ax=ax[0], marker='o', markersize=10)
# Add title for ax[0]
ax[0].set(title='Figure 1', xlabel=None, ylabel="Index")
ax[0].tick_params(axis = 'x', rotation = 45)
# Annotation for ax[0]
ax[0].text(0, 55, 52.53)
ax[0].text(0.4, 45, "Some annotation here", horizontalalignment='center', size='medium', color='black')
ax[0].text(1, 52, 49.62, horizontalalignment='center', size='medium', color='black')
# Line plot for ax[1]
sns.lineplot(x="date", y="ari", data=df_ari_jan, ax=ax[1], marker='o', markersize=10)
# Add title for ax[1]
ax[1].set(title='Figure 2', xlabel=None, ylabel="Index")
ax[1].set_xticks(df_ari_jan["date"].values)
ax[1].tick_params(axis = 'x', rotation = 45)
# Annotation for ax[1]
ax[1].text(-1, 0, 52.53)
fig.tight_layout()
Annotating the first subplot was fine, but I kept getting this ValueError: Image size of 15006958x630 pixels is too large. It must be less than 2^16 in each direction. when trying to annotate the 2nd one.
fig.tight_layout() wouldn't work either UserWarning: Tight layout not applied. The left and right margins cannot be made large enough to accommodate all axes decorations.
Any idea as to why?

How to add plot labels of different axes to the same legend in Python?

I am trying to plot two curves on two y-axes as shown in figure. The red plot (pressure) the primary axis and green (needle lift) the secondary axis. And I am trying to add the plot labels to the same legend. But I cannot add them to the same legend. It overlaps as shown in figure, Raw placed above Needle lift.
The code I used:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker as mtick
data = np.genfromtxt("secondary_axis.dat", skiprows = 2, delimiter = ',')
time = data[:, 0]
pressure = data[:, 1] * 0.006894759086775369
pressure_charge = data[0, 0]
needle_lift = data[:, 2]
figure = plt.figure(figsize=(5.15, 5.15))
figure.clf()
plot = plt.subplot(111)
plot.plot(time, pressure, label = r'\textit{Raw}')
plot.set_xlabel(r'\textit{X}', labelpad=6)
plot.set_ylabel(r'\textit{Y}', labelpad=6)
primary_ticks = len(plot.yaxis.get_major_ticks())
ax2 = plot.twinx()
ax2.plot(time, needle_lift, label = r'\textit{Needle lift}', color='#4DAF4A')
plot.set_zorder(ax2.get_zorder()+2)
plot.patch.set_visible(False)
ax2.grid(False)
ax2.set_ylabel(r'\textit{Z}', labelpad=6)
ax2.yaxis.set_major_locator(mtick.LinearLocator(primary_ticks))
plot.legend(loc = 'center left', bbox_to_anchor = (1.2, 0.5))
ax2.legend(loc = 'center left', bbox_to_anchor = (1.2, 0.5))
plt.show()
The data is available here
How to add plot labels of different axes to the same legend? I want them to be ordered as you when multiple lines are plot on the primary axis as given below:
The problem is that you create two legends. You get nicer results with only one. For that you need to store the line artists:
l1, = plot.plot(time, pressure, label=r'\textit{Raw}')
# ...
l2, = ax2.plot(time, needle_lift, label=r'\textit{Needle lift}', color='#4DAF4A')
And then you can use them to create the legend, by supplying the artists and the desired labels (you could also provide the strings here directly):
plt.legend((l1, l2), (l1.get_label(), l2.get_label()), loc='center left',
bbox_to_anchor=(1.2, 0.5))
Result:

Categories

Resources