Search code examples
pythondatematplotlibpython-dateutil

DateLocator in matplotlib to show the first days of both the week and the month


I would like to create a DateLocator in matplotlib that selects all Mondays and the first days of the month. As matplotlib uses the dateutil library I read the docs of how to use RRuleLocator with rrule objects. With the rruleset object from dateutil I can achieve the required functionality:

>>> rrset = rruleset()
>>> rrset.rrule(rrule(DAILY, byweekday=MO, count=5))
>>> rrset.rrule(rrule(DAILY, bymonthday=1, count=5))
>>> list(rrset)
[datetime.datetime(2020, 11, 30, 16, 10, 2),
 datetime.datetime(2020, 12, 1, 16, 10, 2),
 datetime.datetime(2020, 12, 7, 16, 10, 2),
 datetime.datetime(2020, 12, 14, 16, 10, 2),
 datetime.datetime(2020, 12, 21, 16, 10, 2),
 datetime.datetime(2020, 12, 28, 16, 10, 2),
 datetime.datetime(2021, 1, 1, 16, 10, 2),
 datetime.datetime(2021, 2, 1, 16, 10, 2),
 datetime.datetime(2021, 3, 1, 16, 10, 2),
 datetime.datetime(2021, 4, 1, 16, 10, 2)]

But unfortunately I did not manage to find out how to use rruleset with matplotlib. RRuleLocator expects a rrulewrapper object (defined in matplotlib) that hides away the rrule instance and I can not use it with rruleset. Any other way to do this?


Solution

  • If I understood you correctly, calling .set_xticks(list(rrset)) might be enough. For example:

    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
    import dateutil
    from dateutil.rrule import *
    import datetime
    import numpy as np
    
    rrset = rruleset()
    rrset.rrule(rrule(DAILY, byweekday=MO, count=5))
    rrset.rrule(rrule(DAILY, bymonthday=1, count=5))
    print(list(rrset))
    
    ## generate dates 90 days into the future
    base = datetime.datetime.today()
    dates = [base + datetime.timedelta(days=3*x) for x in range(30)]
    
    fig = plt.figure(figsize=(10,5))
    ax = plt.subplot(111)
    ax.set_autoscale_on(True)
    
    ## simply plot dates over dates
    ax.plot(dates,dates,marker='s')
    
    ax.set_xticks(list(rrset))
    
    formatter = mdates.DateFormatter('%m/%d/%y')
    ax.xaxis.set_major_formatter(formatter)
    
    ax.xaxis.set_tick_params(rotation=30, labelsize=10)
    
    ax.autoscale_view()
    ax.grid()
    plt.show()
    

    yields (today on 11/26/20 where 11/30/2020 is the next Monday, hence the tick label overlapping with the first of the month):

    plotting with a custom date formatter