Clicquot the Dog
Clicquot the Dog

Reputation: 560

How to do a range bar graph in matplotlib?

I'm trying to make a plot using matplotlib that resembles the following:

Desired sort of image

However, I'm not quite sure which type of graph to use. My data has the following form, where start x position is a positive value greater or equal to 0:

<item 1><start x position><end x position>
<item 2><start x position><end x position>

Looking at the docs, I see that there is barh and errorbar, but I'm not sure if its possible to use barh with a start offset. What would be the best method to use, given my type of data? I'm not that familiar with the library, so I was hoping to get some insight.

Upvotes: 3

Views: 2334

Answers (1)

gboffi
gboffi

Reputation: 25053

Appetizer

exactly what you asked for

Commented Code

As far as I know, the most direct way to do what you want requires that you directly draw your rectangles on the matplotlib canvas using the patches module of matplotlib

A simple implementation follows

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

def plot_rect(data, delta=0.4):
    """data is a dictionary, {"Label":(low,hi), ... }
    return a drawing that you can manipulate, show, save etc"""

    yspan = len(data)
    yplaces = [.5+i for i in range(yspan)]
    ylabels = sorted(data.keys())

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.set_yticks(yplaces)
    ax.set_yticklabels(ylabels)
    ax.set_ylim((0,yspan))

    # later we'll need the min and max in the union of intervals
    low, hi =  data[ylabels[0]]
    for pos, label in zip(yplaces,ylabels):
        start, end = data[label]
        ax.add_patch(patches.Rectangle((start,pos-delta/2.0),end-start,delta))
        if start<low : low=start
        if end>hi : hi=end

    # little small trick, draw an invisible line so that the x axis
    # limits are automatically adjusted...
    ax.plot((low,hi),(0,0))

    # now get the limits as automatically computed
    xmin, xmax = ax.get_xlim()
    # and use them to draw the hlines in your example
    ax.hlines(range(1,yspan),xmin,xmax)
    # the vlines are simply the x grid lines
    ax.grid(axis='x')
    # eventually return what we have done
    return ax

# this is the main script, note that we have imported pyplot as plt

# the data, inspired by your example, 
data = {'A':(1901,1921),
        'B':(1917,1935),
        'C':(1929,1948),
        'D':(1943,1963),
        'E':(1957,1983),
        'F':(1975,1991),
        'G':(1989,2007)}

# call the function and give its result a name
ax = plot_rect(data)
# so that we can further manipulate it using the `axes` methods, e.g.
ax.set_xlabel('Whatever')
# finally save or show what we have
plt.show()

The result of our sufferings has been shown in the first paragraph of this post...

Addendum

Let's say that you feel that blue is a very dull color...

The patches you've placed in your drawing are accessible as a property (aptly named patches...) of the drawing and modifiable too, e.g.,

ax = plot_rect(data)
ax.set_xlabel('Whatever')
for rect in ax.patches:
    rect.set(facecolor=(0.9,0.9,0.2,1.0), # a tuple, RGBA
          edgecolor=(0.6,0.2,0.3,1.0),
          linewidth=3.0)
plt.show()

NO. MORE. DULLNESS.

In my VH opinion, a custom plotting function should do the least indispensable to characterize the plot, as this kind of post-production is usually very easy in matplotlib.

Upvotes: 5

Related Questions