Skip to content
Advertisement

Avoid annotations and tick y_tick labels overlapping

I went through several questions but none seem to address the problem of annotations overlapping with y tick labels. I found a nice code that prevents annotations overlapping among themselves, but nothing with tick labels.

My problem is quite simple actually. I use the following lines to create the graph I paste just below them. I use annotate to show what the last value for both lines is. I set the position of the annotation based on the ratio of the last value to the total range of the y axis. It works pretty well except when the annotation overlaps the tick label. It is not that big a deal but it does not look nice when including the graph in a report.

Here is the code — I ommit the lines that manipulate the data:

x = MERVAL.index[(MERVAL.index >= '2014-01-01')]
y1 = MERVAL['MERVAL'][(MERVAL.index >= '2014-01-01')]
y2 = MERVAL['MERVAL_USD'][(MERVAL.index >= '2014-01-01')]
last_date = MERVAL.tail(1).index
right_limit = last_date + datetime.timedelta(days=30)
months = mdates.MonthLocator(1)
monthsFmt = mdates.DateFormatter('%m/%Y')
datemin = datetime.datetime.strptime('01/01/2014', '%m/%d/%Y')
f, ax = plt.subplots()
ax.plot(x,y1, color='b', linewidth=1, label='MERVAL')
ax2 = ax.twinx()
ax2.plot(x,y2, color='r', linewidth=1, label='MERVAL in USD')
ax.set_title('MERVAL',fontsize=20,color='green')
ax.xaxis.set_major_locator(months)
ax.xaxis.set_major_formatter(monthsFmt)
ax.set_xlim(left=datemin, right=right_limit)
ax2.set_xlim(left=datemin, right=right_limit)
ax.grid(axis='x', linestyle=':')
ax.legend(loc=(0.01,0.9))
ax2.legend(loc=(0.01,0.8))
bottom, top = ax.get_ylim()
bottom1, top1 = ax2.get_ylim()
MERVAL_last_price = MERVAL.iloc[-1,0]
MERVAL_USD_last_price = MERVAL.iloc[-1,1]
ax.annotate(str(MERVAL.iloc[-1,0].round(2)), xy=(0,(MERVAL.iloc[-1,0])), xytext=(-0.13 ,((MERVAL_last_price - bottom) / (top - bottom))), xycoords='axes fraction', color='b', annotation_clip=False)
ax2.annotate(str(MERVAL.iloc[-1,1].round(2)), xy=(1,(MERVAL.iloc[-1,1])), xytext=(1.01,((MERVAL_USD_last_price - bottom1) / (top1 - bottom1))), xycoords='axes fraction',color='r', annotation_clip=False)
plt.show()

Here is the graph. Highlighted in yellow what I would like to fix: enter image description here

As noted in the comment below, I would like the red label to either be above (preferably since it’s a higher number) or below the tick label). I know how to take it far to the right or to the left. I also know how to move it manually up or down. Is there a way to have Matplotlib check if it overlaps with the tick label and automatically move it up or down?

Advertisement

Answer

I thought it would be a good idea to complete the post with the solution I found thanks to the above comments. I went for the third option in Jody Klymak’s comment.

I added a few lines to find what the y_ticks are, remove any ticks within a specific range around the last value, and finally set new y_ticks.

Updated code:

x = MERVAL.index[(MERVAL.index >= '2014-01-01')]
y1 = MERVAL['MERVAL'][(MERVAL.index >= '2014-01-01')]
y2 = MERVAL['MERVAL_USD'][(MERVAL.index >= '2014-01-01')]
last_date = MERVAL.tail(1).index
right_limit = last_date + datetime.timedelta(days=30)
months = mdates.MonthLocator(1)
monthsFmt = mdates.DateFormatter('%m/%Y')
datemin = datetime.datetime.strptime('01/01/2014', '%m/%d/%Y')
f, ax = plt.subplots()
ax.plot(x,y1, color='b', linewidth=1, label='MERVAL')
ax2 = ax.twinx()
ax2.plot(x,y2, color='r', linewidth=1, label='MERVAL in USD')
ax.set_title('MERVAL',fontsize=20,color='green')
ax.xaxis.set_major_locator(months)
ax.xaxis.set_major_formatter(monthsFmt)
ax.set_xlim(left=datemin, right=right_limit)
ax2.set_xlim(left=datemin, right=right_limit)
ax.grid(axis='x', linestyle=':')
ax.legend(loc=(0.01,0.9))
ax2.legend(loc=(0.01,0.8))
bottom, top = ax.get_ylim()
bottom1, top1 = ax2.get_ylim()
MERVAL_last_price = MERVAL.iloc[-1,0]
MERVAL_USD_last_price = MERVAL.iloc[-1,1]
ax.annotate(str(MERVAL.iloc[-1,0].round(2)), xy=(0,(MERVAL.iloc[-1,0])), xytext=(-0.13 ,((MERVAL_last_price - bottom) / (top - bottom))), xycoords='axes fraction', color='b', annotation_clip=False)
ax2.annotate(str(MERVAL.iloc[-1,1].round(2)), xy=(1,(MERVAL.iloc[-1,1])), xytext=(1.01,((MERVAL_USD_last_price - bottom1) / (top1 - bottom1))), xycoords='axes fraction',color='r', annotation_clip=False)
loc = ax2.get_yticks()
space = loc[1] - loc[0]
print(space)
new_loc = list()
for x in loc:
    if x <= MERVAL.iloc[-1,1] + space / 2 and x >= MERVAL.iloc[-1,1] - space / 2:
        new_loc.append('')
    else:
        new_loc.append(x)
ax2.set_yticklabels(new_loc)
plt.show()

Updated chart:

enter image description here

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement