25

I have to plot some data and some vertical lines to delimit interesting intervals and then I would like to add some labels to that using text. I can not entirely avoid the labels overlapping with the data or the vertical lines, so I decided to put a bbox around the text to keep it readable. My problem is that I am not able to align it centrally within this box and this is clearly visible and quite annoying in my opinion.

I'm doing something like this:

import numpy
import matplotlib
import matplotlib.pyplot as plt

fig=plt.figure()
plot=fig.add_subplot(111)
x=numpy.linspace(1,10,50)
y=numpy.random.random(50)
plot.plot(x,y)
plot.text(4.5,.5,'TEST TEST',\
          bbox={'facecolor':'white','alpha':1,'edgecolor':'none','pad':1})
plot.axvline(5,color='k',linestyle='solid')
plt.show()

Which creates the following plot: enter image description here

It is quite apparent, that the text is not centered in its bbox. How can I change this? I've spent quite some time on Google but I could not find anything.

EDIT:

Thanks for the suggestions so far.

This suggests that what I see is actually desired behavior. Apparently the bbox in new versions of matplotlib is chosen taking into account the possible maximum descent of the text it contains (the descent of 'g').

When a 'g' appears in the text, this does indeed look good: enter image description here Unfortunately in my case there is no 'g' or anything with a similar descent. Does anyone have any further ideas?

user35915
  • 1,222
  • 3
  • 17
  • 23
  • Looking at e.g. [this](http://stackoverflow.com/questions/17086847/box-around-text-in-matplotlib) answer, it used to work correctly at one point in history. But I get the same results as you have, independent of which backend I use – Bart Dec 04 '15 at 13:30
  • Yes, that is where I took the suggestion from, should maybe have mentioned that... – user35915 Dec 05 '15 at 10:41

2 Answers2

33

Use the text properties ha and va:

plot.text(5.5,.5,'TEST TEST TEST TEST',
          bbox={'facecolor':'white','alpha':1,'edgecolor':'none','pad':1},
          ha='center', va='center') 

To check, draw lines in the center of your plot:

plot.axvline(5.5,color='k',linestyle='solid')
plot.axhline(0.5,color='k',linestyle='solid')
iMo51
  • 419
  • 3
  • 11
  • 2
    perhaps this can give hints: http://stackoverflow.com/questions/32167608/alignment-of-text-and-the-icon-in-the-legend-box-matplotlib – iMo51 Dec 04 '15 at 13:34
  • 6
    From [link](http://matplotlib.org/users/text_props.html): "horizontalalignment controls whether the x positional argument for the text indicates the left, center or right side of the text bounding box". I.e. ha and va only influence the position of the `bbox` and not how the text is aligned inside it. – user35915 Dec 05 '15 at 10:42
  • I will look at the answer you linked. – user35915 Dec 05 '15 at 10:47
5

It seems that there are now options to properly position the text in the coordinate system (in particular the new va = 'baseline'). However, as pointed out by user35915, this does not change the alignment of the box relative to the text. The misalignment is particularly obvious in single digit numbers, in particular number '1' (see also this bug). Until this is fixed, my workaround is to place the rectangle by hand, not via the bbox parameter:

import matplotlib.pyplot as plt
import matplotlib.patches as patches

# define the rectangle size and the offset correction
rect_w = 0.2
rect_h = 0.2
rect_x_offset = 0.004
rect_y_offset =0.006

# text coordinates and content
x_text = 0.5
y_text = 0.5
text = '1'

# create the canvas
fig,ax = plt.subplots(figsize=(1,1),dpi=120)
ax.set_xlim((0,1))
ax.set_ylim((0,1))

# place the text
ax.text(x_text, y_text, text, ha="center", va="center", zorder=10)
# compare: vertical alignment with bbox-command: box is too low.
ax.text(x_text+0.3, y_text, text, ha="center", va="center",
        bbox=dict(facecolor='wheat',boxstyle='square',edgecolor='black',pad=0.1), zorder=10)
# compare: horizontal alignment with bbox-command: box is too much to the left.
ax.text(x_text, y_text+0.3, text, ha="center", va="center",
        bbox=dict(facecolor='wheat',boxstyle='square',edgecolor='black',pad=0.2), zorder=10)
# create the rectangle (below the text, hence the smaller zorder)
rect = patches.Rectangle((x_text-rect_w/2+rect_x_offset, y_text-rect_h/2+rect_y_offset),
                         rect_w,rect_h,linewidth=1,edgecolor='black',facecolor='white',zorder=9)
# add rectangle to plot
ax.add_patch(rect)

# show figure
fig.show()

enter image description here

Felix
  • 659
  • 2
  • 10
  • 24