How to create broken vertical histograms in matpltolib?

I would like to create a broken vertical bar chart in matplotlib.

To better understand the result that I got, I gave an example with Balsamiq :

enter image description here

I looked at matpltolib docs and examples , but I can not find a suitable type of chart to use. The only thing that looks remotely similar to boxplot , but that is not what I need.

  • I do not need to draw a graph manually using a graphical primitive.
  • I can massage the data into the form as needed.

PS: If you know a good library that does this in another language (like javascript), I would be grateful for the pointer.

+5
source share
1 answer

It looks like you have some serial dates and a delay time.

In this case, just use barto plot and tell matplotlib that the axes are dates.

To get the time, you can use the fact that the matplotlib internal date format is a floating point, where each integer corresponds to 0:00 of that day. Therefore, to get time, we can just do times = dates % 1.

As an example (90% of this is date generation and processing. Bookmarking is just one call bar.):

import datetime as dt
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

def main():
    start, stop = dt.datetime(2012,3,1), dt.datetime(2012,4,1)

    fig, ax = plt.subplots()
    for color in ['blue', 'red', 'green']:
        starts, stops = generate_data(start, stop)
        plot_durations(starts, stops, ax, facecolor=color, alpha=0.5)
    plt.show()

def plot_durations(starts, stops, ax=None, **kwargs):
    if ax is None:
        ax = plt.gca()
    # Make the default alignment center, unless specified otherwise
    kwargs['align'] = kwargs.get('align', 'center')

    # Convert things to matplotlib internal date format...
    starts, stops = mpl.dates.date2num(starts), mpl.dates.date2num(stops)

    # Break things into start days and start times 
    start_times = starts % 1
    start_days = starts - start_times
    durations = stops - starts
    start_times += int(starts[0]) # So that we have a valid date...

    # Plot the bars
    artist = ax.bar(start_days, durations, bottom=start_times, **kwargs)

    # Tell matplotlib to treat the axes as dates...
    ax.xaxis_date()
    ax.yaxis_date()
    ax.figure.autofmt_xdate()
    return artist

def generate_data(start, stop):
    """Generate some random data..."""
    # Make a series of events 1 day apart
    starts = mpl.dates.drange(start, stop, dt.timedelta(days=1))

    # Vary the datetimes so that they occur at random times
    # Remember, 1.0 is equivalent to 1 day in this case...
    starts += np.random.random(starts.size)

    # Make some random stopping times...
    stops = starts + 0.2 * np.random.random(starts.size)

    # Convert back to datetime objects...
    return mpl.dates.num2date(starts), mpl.dates.num2date(stops)

if __name__ == '__main__':
    main()

enter image description here

On a side note, for events starting on one day and ending on the next, this will extend the y axis the next day. You can handle it in other ways if you want, but I think this is the easiest option.

+8

All Articles