web-dev-qa-db-fra.com

Seaborn tsplot n'affiche pas bien les dates/heures sur l'axe des x

Ci-dessous, j'ai le script suivant qui crée un graphique de série temporelle simple:

%matplotlib inline
import datetime
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

df = []
start_date = datetime.datetime(2015, 7, 1)
for i in range(10):
    for j in [1,2]:
        unit = 'Ones' if j == 1 else 'Twos'
        date = start_date + datetime.timedelta(days=i)

        df.append({
                'Date': date.strftime('%Y%m%d'),
                'Value': i * j,
                'Unit': unit
            })

df = pd.DataFrame(df)

sns.tsplot(df, time='Date', value='Value', unit='Unit', ax=ax)
fig.autofmt_xdate()

Et le résultat est le suivant:

enter image description here

Comme vous pouvez le constater, l’axe des x comporte des nombres étranges pour les dates-heures, et non les représentations "Nice" habituelles fournies avec matplotlib et d’autres utilitaires de traçage. J'ai essayé beaucoup de choses, reformater les données mais elles ne sortent jamais proprement. Quelqu'un sait-il un moyen de contourner?

12
sedavidw

Matplotlib représente les dates sous forme de nombres à virgule flottante (en jours). Ainsi, à moins que vous (ou les pandas ou les natifs de la mer), leur disiez que vos valeurs représentent des dates, les ticks ne seront pas formatés en tant que dates. Je ne suis pas un expert né de la mer, mais il semble que cela convertisse les objets datetime en dates matplotlib, mais n'affecte pas les localisateurs et les formateurs appropriés aux axes. C'est pourquoi vous obtenez ces nombres étranges, qui ne sont en fait que quelques jours depuis le 0001.01.01. Vous devrez donc vous occuper des tiques manuellement (ce qui, dans la plupart des cas, est meilleur de toute façon car cela vous donne plus de contrôle).

Vous devrez donc affecter un localisateur de date qui décidera où placer les ticks et un formateur de date qui formatera ensuite les chaînes pour les libellés de ticks.

import datetime
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# build up the data
df = []
start_date = datetime.datetime(2015, 7, 1)
for i in range(10):
    for j in [1,2]:
        unit = 'Ones' if j == 1 else 'Twos'
        date = start_date + datetime.timedelta(days=i)

        # I believe it makes more sense to directly convert the datetime to a
        # "matplotlib"-date (float), instead of creating strings and then let
        # pandas parse the string again
        df.append({
                'Date': mdates.date2num(date),
                'Value': i * j,
                'Unit': unit
            })
df = pd.DataFrame(df)

# build the figure
fig, ax = plt.subplots()
sns.tsplot(df, time='Date', value='Value', unit='Unit', ax=ax)

# assign locator and formatter for the xaxis ticks.
ax.xaxis.set_major_locator(mdates.AutoDateLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y.%m.%d'))

# put the labels at 45deg since they tend to be too long
fig.autofmt_xdate()
plt.show()

Résultat:

enter image description here

13
hitzg

Pour moi, la réponse de @ hitzg se traduit par "OverflowError: le nombre entier signé est supérieur au maximum" dans les profondeurs de DateFormatter.

En regardant mon cadre de données, mes index sont datetime64, pas datetime. Pandas les convertit bien cependant. Ce qui suit fonctionne très bien pour moi:

import matplotlib as mpl

def myFormatter(x, pos):
    return pd.to_datetime(x)

[ . . . ]

ax.xaxis.set_major_formatter(mpl.ticker.FuncFormatter(myFormatter))
11
T Smith

Voici une solution potentiellement peu élégante, mais c’est la seule que j’ai ... Espérons que cela aide!

    g = sns.pointplot(x, y, data=df, ci=False);

    unique_dates = sorted(list(df['Date'].drop_duplicates()))
    date_ticks = range(0, len(unique_dates), 5)

    g.set_xticks(date_ticks);
    g.set_xticklabels([unique_dates[i].strftime('%d %b') for i in date_ticks], rotation='vertical');
    g.set_xlabel('Date');

Faites-moi savoir si vous voyez des problèmes!

0
ltjds
def myFormatter(x, pos):
       return pd.to_datetime(x).strftime('%Y%m%d')
ax.xaxis.set_major_formatter(mpl.ticker.FuncFormatter(myFormatter))
0
peter254