matplotlib datetime xlabel issue

后端 未结 3 1932
离开以前
离开以前 2021-02-20 15:19

I\'m seeing some strange behavior in the x-axis auto-labeling for dates in matplotlib. When I issue the command:

from datetime import datetime as dt
plot( [ dt(2         


        
3条回答
  •  既然无缘
    2021-02-20 15:49

    It cauesed by a bug in AutoDateLocator. It seems this bug has not been reported to the issue tracker yet.
    It looks weird only because too many labels and ticks have been plotted.

    When plotting with data with dates, by default, matplotlib uses matplotlib.dates.AutoDateLocator as the major locator. Namely, AutoDateLocator is used to determine the tick interval and tick locations.

    Suppose, the data sequence is given by [datetime(2013, 1, 1), datetime(2013, 5, 18)].
    The time delta is 4 months and 17 days. The month delta is 4, and the day delta is 4*31+17=141.

    According to matplotlib docs:

    class matplotlib.dates.AutoDateLocator(tz=None, minticks=5, maxticks=None, interval_multiples=False)

    minticks is the minimum number of ticks desired, which is used to select the type of ticking (yearly, monthly, etc.).

    maxticks is the maximum number of ticks desired, which controls any interval between ticks (ticking every other, every 3, etc.). For really fine-grained control, this can be a dictionary mapping individual rrule frequency constants (YEARLY, MONTHLY, etc.) to their own maximum number of ticks. This can be used to keep the number of ticks appropriate to the format chosen in class:AutoDateFormatter. Any frequency not specified in this dictionary is given a default value.

    The AutoDateLocator has an interval dictionary that maps the frequency of the tick (a constant from dateutil.rrule) and a multiple allowed for that ticking. The default looks like this:

        self.intervald = {
          YEARLY  : [1, 2, 4, 5, 10],
          MONTHLY : [1, 2, 3, 4, 6],
          DAILY   : [1, 2, 3, 7, 14],
          HOURLY  : [1, 2, 3, 4, 6, 12],
          MINUTELY: [1, 5, 10, 15, 30],
          SECONDLY: [1, 5, 10, 15, 30]
          }

    The interval is used to specify multiples that are appropriate for the frequency of ticking. For instance, every 7 days is sensible for daily ticks, but for minutes/seconds, 15 or 30 make sense. You can customize this dictionary by doing:

    Since the month delta is 4, less than 5, and the day delta is 141, not less than 5. The type of ticking will be daily.
    After resolving the type of ticking, AutoDateLocator will use the interval dictionary and maxticks dictionary to determine the tick interval.

    When maxticks is None, AutoDateLocator uses its default maxticks dictionary. The documentation shows us the default interval dictionary, and does not tell us what the default maxticks dictionary looks like.
    We can find it in the dates.py.

    self.maxticks = {YEARLY : 16, MONTHLY : 12, DAILY : 11, HOURLY : 16,
                MINUTELY : 11, SECONDLY : 11}
    

    The algorithm to determine the tick interval is

    # Find the first available interval that doesn't give too many ticks
    for interval in self.intervald[freq]:
        if num <= interval * (self.maxticks[freq] - 1):
            break
    else:
        # We went through the whole loop without breaking, default to 1
        interval = 1
    

    The type of ticking is DAILY now. So freq is DAILY and num is 141, the day delta. The above code will be equivalent to

    for interval in [1, 2, 3, 7, 14]:
        if 141 <= interval * (11 - 1):
            break
    else:
        interval = 1
    

    141 is too large. All daily intervals will give too many ticks. else clause will be executed and the tick interval will be set to 1.
    It means 140+ labels and ticks would beplotted. We can expect an ugly x-axis.

    If the data sequence is given by [datetime(2013, 1, 1), datetime(2013, 5, 17)], just one day shorter. the day delta is 140. Then AutoDateLocator will choose 14 as the tick interval and only 10 labels will be plotted. Thus your first graph looks fine.

    Actually I don't understand why matplotlib choose to set the interval to 1 if maxticks constraint cannot be satisfied. It will only result in a much larger number of ticks if the interval is 1. I prefer to use the longest interval.

    CONCLUSION:
    Given any date sequence whose range is greater than or equal to 4 months and 18 days, and less than 5 months, AutoDateLocator will choose 1 as the tick interval. You will see some ugly behavior in the x-axis or y-axis when plotting such date sequence with the default major locator, namely, AutoDateLocator.

    SOLUTION:
    The simplest solution is increasing the daily maxticks to 12. For example:

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.dates import DAILY
    from datetime import datetime
    
    ax = plt.subplot(111)
    plt.plot_date([datetime(2013, 1, 1), datetime(2013, 5, 31)],
                  [datetime(2013, 1, 1), datetime(2013, 5, 10)])
    
    loc = ax.xaxis.get_major_locator()
    loc.maxticks[DAILY] = 12
    
    plt.show()
    

提交回复
热议问题