matplotlib datetime xlabel

je vois un comportement étrange dans l'auto-étiquetage x-axis pour les dates dans matplotlib. Quand j'ai lancer la commande:

from datetime import datetime as dt
plot( [ dt(2013, 1, 1), dt(2013, 5, 17)], [ 1 , 1 ], linestyle='None', marker='.')

j'obtiens le très raisonnablement marqué graphique:

enter image description here

Mais si j'augmente la date de fin de la 1 journée:

plot( [ dt(2013, 1, 1), dt(2013, 5, 18)], [ 1 , 1 ], linestyle='None', marker='.')

j'obtiens ceci:

enter image description here

j'ai reproduit ceci à plusieurs dates différentes du calendrier (en 2012), et à chaque fois le nombre magique de jours requis pour trip le bug est d'environ 140 (dans ce cas 136/137). Quelqu'un sait ce qui se passe ici? Est-ce un bug connu, et si oui, est-il une solution?

quelques notes: dans les commandes ci-dessus, J'utilise IPython en mode --pylab pour créer les tracés, mais j'ai d'abord rencontré ce problème en utilisant matplotlib directement, et il est reproductible sous forme de script (i.e. Je ne pense pas que ce soit un problème D'IPython). De plus, j'ai observé ceci à la fois dans matplotlib 1.1.0 et 1.2.X.

mise à jour:

on dirait qu'il y a une fenêtre où, si vous poussez assez loin, les étiquettes recommencent à se comporter normalement. Dans l'exemple ci-dessus, les étiquettes demeurent brouillées du 18 au 31 mai, mais le 1er juin, les étiquettes recommencent à pointer normalement. Donc,

(labels are garbled)
plot( [ dt(2013, 1, 1), dt(2013, 5, 31)], [ 1 , 1 ], linestyle='None', marker='.')

(labels are fine)
plot( [ dt(2013, 1, 1), dt(2013, 6, 1)], [ 1 , 1 ], linestyle='None', marker='.')
8
demandé sur jeremiahbuddha 2013-03-01 23:09:30

3 réponses

Il cauesed par un bogue dans AutoDateLocator. Il semble que ce bogue n'ait pas encore été signalé au suivi des problèmes.

Ça a l'air bizarre seulement parce que trop d'étiquettes et de tiques ont été tracées.

lorsque vous traquez des données avec des dates, par défaut, matplotlib utilise matplotlib.dates.AutoDateLocator comme Localisateur principal. À savoir,AutoDateLocator sert à déterminer l'intervalle et l'emplacement des tiques.

supposons que la séquence de données soit donnée par [datetime(2013, 1, 1), datetime(2013, 5, 18)].

Le temps delta est de 4 mois et 17 jours. Le mois delta est 4, et le jour delta est 4*31+17 = 141.

Selon matplotlib docs:

classe matplotlib.date.AutoDateLocator (tz=None, minticks=5, maxticks=None, interval_multiples=False)

minticks est le nombre minimum de tiques souhaité, qui est utilisé pour sélectionner le type de tic-tac (annuel, mensuel, etc.).

maxticks est le nombre maximum de tiques désirée, qui contrôle n'importe quel intervalle entre les tiques (tic-tac toutes les autres, tous les 3, etc.). Pour un contrôle à grain très fin, ceci peut être un dictionnaire cartographiant les constantes de fréquence individuelles (annuelles, mensuelles, etc.) pour leur propre nombre maximum de tiques. Cela peut être utilisé pour garder le nombre de tiques appropriées au format choisi dans la classe:AutoDateFormatter. Toute fréquence non spécifiée dans ce dictionnaire reçoit une valeur par défaut.

L'Autodétélocateur a un intervalle dictionnaire qui trace la fréquence de la tique (une constante de dateutil.rrule) et un multiple autorisé pour ce tic-tac. La valeur par défaut ressemble à ceci:

    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]
      }

L'intervalle est utilisé pour spécifier des multiples qui sont appropriés pour la fréquence de tic-tac. Par exemple, tous les 7 jours est raisonnable pour les tiques quotidiennes, mais pour les minutes/secondes, 15 ou 30 ont un sens. Vous pouvez personnaliser ce dictionnaire en faisant:

depuis le mois delta est de 4, moins de 5, et le jour delta est de 141, pas moins de 5. Le type de coutil quotidiennes.

Après avoir résolu le type de Tic-Tac, AutoDateLocator utilisera le dictionnaire des intervalles et le dictionnaire des maxticks pour déterminer l'intervalle entre les tiques.

Quand maxticksNone,AutoDateLocator utilise son dictionnaire de maxticks par défaut. La documentation nous montre le dictionnaire d'intervalle par défaut, et ne nous dit pas à quoi ressemble le dictionnaire de maxticks par défaut.

Nous pouvons trouver dans le dates.py.

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

L'algorithme permettant de déterminer la tique intervalle

# 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

le type de Tic-Tac est DAILY maintenant. Donc freqDAILY et num est-141, le jour du delta. Le code ci-dessus sera équivalent à

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

141 est trop grand. Tous les intervalles quotidiens donneront trop de tiques. else clause sera exécutée et l'intervalle de tick sera réglé à 1.

Il signifie que plus de 140 étiquettes et tiques seraient tracées. On peut s'attendre à un vilain axe X.

Si la séquence de données est donnée par [datetime(2013, 1, 1), datetime(2013, 5, 17)], juste un jour plus courte. le delta du jour est à 140. Puis AutoDateLocator choisira 14 comme intervalle entre les tiques et seulement 10 étiquettes seront tracées. Ainsi votre premier graphique semble bien.

en fait je ne comprends pas pourquoi matplotlib choisit de définir l'intervalle à 1 si maxticks la contrainte ne peut pas être satisfaite. Il en résultera un nombre beaucoup plus important de tiques si l'intervalle est de 1. Je préfère utiliser l'intervalle le plus long.

CONCLUSION:

Compte tenu de toute séquence de date dont l'intervalle est supérieur ou égal à 4 mois et 18 jours, et inférieur à 5 mois, AutoDateLocator choisira 1 comme intervalle de temps. Vous verrez un comportement laid dans l'axe x ou l'axe y lorsque vous tracerez cette séquence de date avec le Localisateur principal par défaut, à savoir,AutoDateLocator.

SOLUTION:

Le plus simple solution augmente les maxticks quotidiens à 12. Par exemple:

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()
7
répondu nymk 2013-03-06 14:53:41

cela ressemble à un bug; je l'apporterais à la liste de diffusion matplotlib et verrais ce que les gens peuvent en dire.

une solution que je peux fournir est la suivante:

from datetime import datetime as dt
from matplotlib import pylab as pl

fig = pl.figure()
axes = fig.add_subplot(111)
axes.plot( [ dt(2013, 1, 1), dt(2013, 5, 18)], [ 1 , 1 ], linestyle='None', marker='.')
ticks = axes.get_xticks()
n = len(ticks)//6
axes.set_xticks(ticks[::n])
fig.savefig('dateticks.png')

mes excuses pour l'approche OO (ce qui n'est pas ce que vous avez fait), mais cela rend beaucoup plus facile de lier les tiques à l'intrigue. Le nombre 6 est juste le nombre d'étiquettes que je veux, le long de l'axe des x, puis-je réduire le nombre de tiques qui matplotlib est venu avec, par la calculé n.

2
répondu 2013-03-05 10:00:39

vous devez réinitialiser Localisateur.MAXTICKS à un plus grand nombre pour éviter l'erreur: dépasse Localisateur.MAXTICKS * 2 (2000)

par exemple:

    alldays = DayLocator()              # minor ticks on the days
    alldays.MAXTICKS = 2000
0
répondu Eric Wang 2014-10-15 12:01:13