How to set axis limits with text objects ?

Hi,

I’m trying to draw a bar chart according to http://matplotlib.sourceforge.net/examples/api/barchart_demo.html, and I’m having to difficulties :

  • when the first bar has a zero height, it is no taken into account during the axis limits calculation and is not visible

  • texts objects can exceed chart area and collide with the chart title.

Is there a way to calculate the axis limit to use to include all texts objects into the chart area ?

Here is the code I used :

#!/usr/bin/env python

import numpy as np
import matplotlib.pyplot as plt

menMeans = [    0.        ,   998.        ,  1210.        ,  1449.       ,
        1496.        ,  1493.        ,  1593.40002441,  1517.5       ]

ind = np.arange(len(menMeans))  # the x locations for the groups
width = 0.8            # the width of the bars

fig = plt.figure()
ax = fig.add_subplot(111)
rects1 = ax.bar(ind, menMeans, width, color='r')

# add some
ax.set_ylabel('Scores')
ax.set_title('A very very long title for testing.')

def autolabel(rects):
    # attach some text labels
    for rect in rects:
        height = rect.get_height()
        ax.text(rect.get_x()+rect.get_width()/2., 1.05*height, '%d'%int(height),
                ha='center', va='bottom')

autolabel(rects1)

plt.show()

Best regards,

···


LB

LB,

I can confirm the problem with zero height bars not being included in the determination of the axes limits.

As for the text getting too high, this is something that many users have noticed in a variety of different scenarios. Part of the problem is that it is perfectly valid to have text outside the axes area, or straddling it, and that we have yet to come up with a decent way to handle this properly. Imagine, for example, contour labels near an axes edge. I certainly would not like to have the text labels to force the axes limits to change just to fit the text (I would rather it be clipped). Then again, your use-case is an example where auto resizing makes sense.

The other issue is that it is impossible to know (in a general sense) a priori the extent of a given text object before rendering (although I think there might be some code in text.py to address this, but it might be assuming that the object has been rendered already). The particular sticking point is LaTeX strings.

Therefore, rather than design a layout engine for all of matplotlib, it has become standard practice to make it easy to adjust the plot parameters programatically to deal with situations like this. For example, in your use-case, you know what the maximum bar height will be, and you know roughly how high the text will be from previous runs. You could then set the y limits to pad a certain amount on top of the maximum bar height to make sure you have enough clearance.

I hope that helps, and I will take a peek at the zero-height bar issue as well.

Ben Root

···

On Sun, Nov 7, 2010 at 9:40 AM, LB <bravo.loic@…287…> wrote:

Hi,

I’m trying to draw a bar chart according to http://matplotlib.sourceforge.net/examples/api/barchart_demo.html, and I’m having to difficulties :

  • when the first bar has a zero height, it is no taken into account during the axis limits calculation and is not visible

  • texts objects can exceed chart area and collide with the chart title.

Is there a way to calculate the axis limit to use to include all texts objects into the chart area ?

Here is the code I used :

#!/usr/bin/env python
> 
> import numpy as np
> import matplotlib.pyplot as plt
> 
> menMeans = [    0.        ,   998.        ,  1210.        ,  1449.       ,
>         1496.        ,  1493.        ,  1593.40002441,  1517.5       ]
> 
> 
> 
> 
> ind = np.arange(len(menMeans))  # the x locations for the groups
> width = 0.8            # the width of the bars
> 
> fig = plt.figure()
> ax = fig.add_subplot(111)
> rects1 = ax.bar(ind, menMeans, width, color='r')
> 
> 
> 
> 
> # add some
> ax.set_ylabel('Scores')
> ax.set_title('A very very long title for testing.')
> 
> def autolabel(rects):
>     # attach some text labels
>     for rect in rects:
>         height = rect.get_height()
> 
> 
> 
>         ax.text(rect.get_x()+rect.get_width()/2., 1.05*height, '%d'%int(height),
>                 ha='center', va='bottom')
> 
> autolabel(rects1)
> 
> plt.show()

Best regards,


LB