Search code examples
pythonpandasmatplotlibcdf

matplotlib: customised x-axis ticks for cdf of datetime values


I have a cdf of datetime list. After running the following code, where objDate is a list of datetime values (format: %Y-%m-%d), I get the cdf with ticks on x axis showing every second year in the range of values. How can I get customized labels for ticks along x-axis by specifying:

1. the range (min year and max year)
2. interval (lets say 6 months apart, so that tick labels are like 01/17, 07/17, 01/18, 07/18, ... )

import matplotlib.pyplot as plt
import pandas as pd

ser = pd.Series(objDate)
ser.hist(cumulative=True, density=1, bins=500, histtype='step')
plt.show()

Solution

  • As for the second question, you may use matplotlib.dates locators and formatters. Those work fine in the case of a hist.

    import matplotlib.pyplot as plt
    plt.rcParams['axes.axisbelow'] = True
    import matplotlib.dates as dates
    import numpy as np; np.random.seed(42)
    import pandas as pd
    
    objDate = dates.num2date(np.random.normal(735700, 300, 700))
    
    ser = pd.Series(objDate)
    ax = ser.hist(cumulative=True, density=1, bins=500, histtype='step', linewidth=2)
    
    ax.xaxis.set_major_locator(dates.MonthLocator([1,7]))
    ax.xaxis.set_major_formatter(dates.DateFormatter("%m/%y"))
    plt.setp(ax.get_xticklabels(), rotation=60)
    
    plt.show()
    

    enter image description here

    For the first question, this is not easy, because matplotlib always assumes the complete axis to be ticked. A solution would be to subclass the locator in use and allow it to take restrictive arguments.

    from datetime import datetime
    import matplotlib.pyplot as plt
    plt.rcParams['axes.axisbelow'] = True
    import matplotlib.dates as dates
    import numpy as np; np.random.seed(42)
    import pandas as pd
    
    objDate = dates.num2date(np.random.normal(735700, 300, 700))
    
    ser = pd.Series(objDate)
    ax = ser.hist(cumulative=True, density=1, bins=500, histtype='step', linewidth=2)
    
    
    class RestrictedLocator(dates.MonthLocator):
        def __init__(self, dmin=None, dmax=None, **kw):
            self.dmin = dmin
            self.dmax = dmax
            dates.MonthLocator.__init__(self, **kw)
    
        def __call__(self):
            try:
                dmin, dmax = self.viewlim_to_dt()
            except ValueError:
                return []
    
            self.dmin = self.dmin.replace(tzinfo=dmin.tzinfo)
            self.dmax = self.dmax.replace(tzinfo=dmin.tzinfo)
            dmin = np.max([dmin, self.dmin])
            dmax = np.min([dmax, self.dmax])
            return self.tick_values(dmin, dmax)
    
    
    loc = RestrictedLocator(dmin=datetime(2015,1,1), 
                            dmax = datetime(2017,12,31),
                            bymonth=[1,7])
    
    ax.xaxis.set_major_locator(loc)
    ax.xaxis.set_major_formatter(dates.DateFormatter("%m/%y"))
    plt.setp(ax.get_xticklabels(), rotation=60)
    
    plt.show()
    

    enter image description here