So I am having a little alignment issue with my legend in matplotlib. Hopefully it is easily solvable with the right know-how. I have scoured the matplotlib website but I'm struggling to find the exact solution.
Essentially I have the following axis vertical spans with the following labels (note that I have used $\mathrm{}$ for these):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.axvspan(3851.1, 3951.1, color='gray', alpha=0.4, lw=1,label='$\mathrm{D}_{n}(4000)$')
ax.axvspan(4001.1, 4101.2, color='gray', alpha=0.4, lw=1)
ax.axvspan(4084.7, 4123.4, color='gray', alpha=0.8, lw=1,label='$\mathrm{H}\delta_{\mathrm{A}}$')
I also have the following legend label:
ax.legend(prop={'size':8}, loc=2)
Now the problem that I am seeing is this (for the image I have increased the prop size to 12 to show the issue but it scales down for size 8):
My issue is that the alignment is slightly off between the vspan regions and the math mode descriptive labels. Taking away the math mode solves the problem, but the labels do not contain the correct subscripts and greek lettering that I require. See here:
I was wondering if anyone knew of any alignment arguments for this niche scenario?
Related
I am currently trying to plot two regression lines for my data split by a categorical attribute (which is either freedom or happiness scores). My current qualm is that I need color to encode another separate categorical attribute in my graph (GNI/capita brackets). Having a mix of colors seemed confusing so I decided to distinguish the data points using different markers instead. However, I am having trouble changing just one of the regression lines to a dashed line as they are identical. I don't even want to think about how I am going to create a legend for all of this. If you think this is an ugly graph, I agree, but certain circumstances mandate I have four attributes encoded in a single graph. By the way, open to any suggestions at all on a better way to do this - if there is any. An example of my current graph is below and would appreciate any help!
sns.lmplot(data=combined_indicators, x='x', y='y', hue='Indicator', palette=["#000620"], markers=['x', '.'], ci=None)
plt.axvspan(0,1025, alpha=0.5, color='#de425b', zorder=-1)
plt.axvspan(1025,4035, alpha=0.5, color='#fbb862', zorder=-1)
plt.axvspan(4035,12475, alpha=0.5, color ='#afd17c', zorder=-1)
plt.axvspan(12475,100000, alpha=0.5, color='#00876c', zorder=-1)
plt.title("HFI & Happiness Regressed on GNI/capita")
plt.xlabel("GNI/Capita by Purchasing Power Parity (2017 International $)")
plt.ylabel("Standard Indicator Score (0-10)")
My current figure rears its ugly head
To my knowledge, there is no easy way to change the style of the regression line in lmplot. But you can achieve your goal if you use regplot instead of lmplot, the drawback being that you have to implement the hue-splitting "by hand"
x_col = 'total_bill'
y_col = 'tip'
hue_col = 'smoker'
df = sns.load_dataset('tips')
markers = ['x','.']
colors = ["#000620", "#000620"]
linestyles = ['-','--']
plt.figure()
for (hue,gr),m,c,ls in zip(df.groupby(hue_col),markers,colors,linestyles):
sns.regplot(data=gr, x=x_col, y=y_col, marker=m, color=c, line_kws={'ls':ls}, ci=None, label=f'{hue_col}={hue}')
ax.legend()
Just wanted to add, if anyone stumbled upon this post later, you can create a legend for this mess manually using Line2D. Looks something like this for mine:
from matplotlib.patches import Patch
from matplotlib.lines import Line2D
legend_elements = [Line2D([0], [0], color='#000620', lw=2, label='Freedom', linestyle='--'),
Line2D([0],[0], color='#000620', lw=2, label='Happiness'),
Line2D([0], [0], marker='x', color='#000620', label='Freedom',
markerfacecolor='#000620', markersize=15),
Line2D([0], [0], marker='.', color='#000620', label='Happiness',
markerfacecolor='#000620', markersize=15),
Patch(facecolor='#de425b', label='Low-Income'),
Patch(facecolor='#fbb862', label='Lower Middle-Income'),
Patch(facecolor='#afd17c', label='Upper Middle-Income'),
Patch(facecolor='#00876c', label='High-Income')]
The end result looks like this:
Graph with custom legend
There are several questions in StackOverflow regarding the position of the legend in Python's matplotlib.pyplot. For a single graph, the problem can often be solved by tweaking parameters location and bbox_to_anchor. In the following example, the legend can be placed above the graph with bbox_to_anchor = (0.5,1.3).
industry_capm_df.plot.bar
ax = plt.axes()
ax.legend(bbox_to_anchor=(0.5,1.3))
Yet, for this other graph, the same parameters (0.5,1.3) result in a legend slightly out of alignment.
industry_raw_df.plot.bar
ax = plt.axes()
ax.legend(bbox_to_anchor=(0.5,1.3))
Since I got several graphs to plot, I would like legend alignment to be automatic, without having to tweak bbox_to_anchor every time. How could I solve this?
You should add loc='lower left' to specify that the coordinates in bbox_to_anchor refer to the lower left corner of the legend:
ax.legend(loc='lower left', bbox_to_anchor=(0, 1.03), borderaxespad=0)
borderaxespad is to disable padding outside the box, so that the left edge is perfectly aligned with the axis. Then we add a bit of padding (0.03) so that the bottom edge is slightly above the top of the chart.
Reference: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.legend.html
I am trying to change location of plot legend. Below what I've got for now.
var_list=powiaty_cols[powiaty_cols.str.contains("apart_bel_40")]
for var in var_list:
fig = plt.figure(figsize=(25, 25))
ax = plt.gca()
powiaty.plot(column=var,cmap='Reds', categorical=True,
legend=True, ax=ax,edgecolor='black')
ax.legend(loc='best')
This code is plotting figure but without legend. I've received errors as follows:
No handles with labels found to put in legend.
No handles with labels found to put in legend.
No handles with labels found to put in legend.
No handles with labels found to put in legend.
But without part 'ax.legend(loc='best')' I can get my plot but legend is in upper left corner. Plotted column is filled with integer from 1 to 5. Similar issue is when I'm trying to change size of legend.
Could maybe somebody help in this?
I don't know what powiaty is, but my guess is that you need to get the Axes object back so you can continue modifying it. Try:
ax = powiaty.plot(column=var, cmap='Reds', categorical=True,
legend=True, ax=ax, edgecolor='black'
)
For me worked:
leg = ax.get_legend()
leg.set_bbox_to_anchor((0., 0.1, 0.2, 0.2))
This question is similar to this one asked 6 years ago. However, their solution is not quite solving my problem; unlike their question, I am dealing with multiple legends, some of which are getting cut off.
I'm trying to create a figure containing multiple legends that sit outside the axes of my graph. I'm following the matplotlib documentation's instructions for creating multiple legends, using add_artist to add all but the final legend to my axes. I then use the bbox_extra_artists parameter in my savefig call as described in the above question to include all my legend objects. As seen in this example output image, the wider legend still gets cut off on the right side.
The code used to generate this plot:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.gca()
for x in [0, 1, 2]:
ax.bar(x+0.5, x+1, width=1, color=['red', 'blue', 'green'][x])
handle1 = plt.Line2D((0,1), (0,0), color='purple')
lgd1 = ax.legend([handle1], ['label1 is very long'], bbox_to_anchor=(1, 1))
ax.add_artist(lgd1)
handle2 = plt.Line2D((0,1), (0,0), color='orange')
lgd2 = ax.legend([handle2], ['label2'], bbox_to_anchor=(1, 0.9))
plt.savefig('output.png', bbox_extra_artists=(lgd1,lgd2), bbox_inches='tight')
Notably, if I change the order in which the legends are added (adding the wider legend first), the problem goes away and both legends are visible as seen here:
handle2 = plt.Line2D((0,1), (0,0), color='orange')
lgd2 = ax.legend([handle2], ['label2'], bbox_to_anchor=(1, 0.9))
ax.add_artist(lgd2)
handle1 = plt.Line2D((0,1), (0,0), color='purple')
lgd1 = ax.legend([handle1], ['label1 is very long'], bbox_to_anchor=(1, 1))
plt.savefig('output.png', bbox_extra_artists=(lgd1,lgd2), bbox_inches='tight')
For my actual project (which has to handle a dynamic number of legends), I've made it figure out which legend is "longest" and always add that legend last to work around this problem. However, this feels messy and it doesn't allow for adding more legends on other sides of the figure (e.g., I cannot add an x-axis legend below the graph without it getting cut off, since only one legend can be added "last").
Is this an intractable bug in matplotlib, or is there a tidy solution I'm missing?
How to have no spaces between subplots in this example ?
(And keep a good visualization if I have more lines)
I've seen that what we see can change a lot with a value for figsize.
So do we have to guess the "correct" value for figsize ?
fig = plt.figure()
ax = fig.add_subplot(211)
ax.matshow([[1,2,3,4,5]])
ax = fig.add_subplot(212)
ax.matshow([[5,4,3,2,1]])
plt.subplots_adjust(wspace=0, hspace=0)
I've tried to use something different like gridspec but I have exactly the same issue.
Take a look at the Tight Layout Guide, you would use it like plt.tight_layout() with parameters to control the padding for your figures to make them fit nicely.