Using matplotlib to plot a distribution of time occurrences. I would like the x axis to have hours (12:00 PM) rather than integers (12)

Here's my plot, which is generated using the following code:

bins = np.linspace(0,24,25)
plt.hist(hours,bins, edgecolor='black', linewidth = 1.2, color = 'red')

I would like the x axis to show 24 entries, from 12:00AM to 11:00 PM ideally rotated left 90 degrees.

I see two paths: convert the actual data to time values so the histogram reads in time values or simply add a custom x axis with 12:00AM, 1:00 AM, etc. What's the easiest / cleanest approach here? I'm not familiar with how to do either. For reference, "hours" is a int64 array.

2 answers

  • answered 2017-11-14 23:42 ashgetstazered

    Here's a working example:

    import numpy as np  
    import matplotlib.pyplot as plt
    
    bins = np.arange(0,25)
    hours = np.random.rand(50)*25
    
    fig, ax = plt.subplots()
    
    labels = []
    for i in bins:
        if i<12:
            labels.append("{}:00AM".format(i))
        elif i == 12:
            labels.append("12:00PM")
        else:
            labels.append("{}:00PM".format(i-12))
    
    ax.hist(hours, bins)
    ax.set_xticks(bins + 0.5) # 0.5 is half of the "1" auto width
    ax.set_xticklabels(labels, rotation='vertical')
    fig.subplots_adjust(bottom = 0.2) # makes space for the vertical 
                                      #labels.
    plt.show()
    

    which gives: enter image description here I've changed the linspace to arange as it returns integers

  • answered 2017-11-15 11:19 ImportanceOfBeingErnest

    To get a nice time format on the xaxis, the idea could be to calculate the histogram in terms of numbers which can be interpreted as datetimes.
    In case you only have times, you would not mind too much about the actual date. So dividing the data by 24 gives fraction of a day. Since matplotlib interpretes numbers as days since 0001-01-01 UTC, plus 1, one then needs to add some whole number >=2 not to run into trouble with negative dates.

    Then usual matplotlib.dates locators and formatters can be used to get nice ticklabels. "%I:%M %p" would give the time representation in hours by 12 with am/pm appendix.

    import numpy as np; np.random.seed(3)
    import matplotlib.pyplot as plt
    import matplotlib.dates
    
    data = np.random.normal(12,7, size=200)
    data = data[(data >=0) & (data <24)]
    
    f = lambda x: 2+x/24.
    bins=np.arange(25)
    plt.hist(f(data), bins=f(bins))
    
    plt.gca().xaxis.set_major_locator(matplotlib.dates.HourLocator())
    plt.gca().xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%I:%M %p"))
    plt.setp(plt.gca().get_xticklabels(),rotation=90)
    plt.tight_layout()
    plt.show()
    

    enter image description here

    (This would hence be the histogram of datetimes of the 2nd of january 0001.)