Il diagramma della barra di Pandas modifica il formato della data

Ho un semplice grafico a linee impilate che ha esattamente il formato della data che voglio impostare magicamente quando uso il seguente codice.

df_ts = df.resample("W", how='max') df_ts.plot(figsize=(12,8), stacked=True) 

inserisci la descrizione dell'immagine qui

Tuttavia, le date si trasformano misteriosamente in un formato brutto e illeggibile quando si stampano gli stessi dati di un grafico a barre.

 df_ts = df.resample("W", how='max') df_ts.plot(kind='bar', figsize=(12,8), stacked=True) 

inserisci la descrizione dell'immagine qui

I dati originali sono stati trasformati un po ‘per avere il massimo settimanale. Perché questo cambiamento radicale nell’impostazione automatica delle date sta accadendo? Come posso avere le date ben formattate come sopra?

Ecco alcuni dati fittizi

 start = pd.to_datetime("1-1-2012") idx = pd.date_range(start, periods= 365).tolist() df=pd.DataFrame({'A':np.random.random(365), 'B':np.random.random(365)}) df.index = idx df_ts = df.resample('W', how= 'max') df_ts.plot(kind='bar', stacked=True) 

Il codice di tracciamento presuppone che ogni barra di un grafico a barre meriti la propria etichetta. Potresti sovrascrivere questa ipotesi specificando il tuo formattatore:

 ax.xaxis.set_major_formatter(formatter) 

Il pandas.tseries.converter.TimeSeries_DateFormatter che i panda usano per formattare le date nella trama “buona” funziona bene con i grafici a linee quando i valori x sono date. Tuttavia, con un TimeSeries_DateFormatter.__call__ a barre i valori x (almeno quelli ricevuti da TimeSeries_DateFormatter.__call__ ) sono semplicemente numeri interi che iniziano da zero . Se si tenta di utilizzare TimeSeries_DateFormatter con un TimeSeries_DateFormatter a barre, tutte le etichette iniziano quindi da Epoch, 1970-1-1 UTC, poiché questa è la data che corrisponde a zero. Quindi il formattatore utilizzato per i grafici a linee è purtroppo inutile per i grafici a barre (almeno per quanto posso vedere).

Il modo più semplice che vedo per produrre la formattazione desiderata è generare e impostare le etichette in modo esplicito:

 import numpy as np import matplotlib.pyplot as plt import pandas as pd import matplotlib.ticker as ticker start = pd.to_datetime("5-1-2012") idx = pd.date_range(start, periods= 365) df = pd.DataFrame({'A':np.random.random(365), 'B':np.random.random(365)}) df.index = idx df_ts = df.resample('W', how= 'max') ax = df_ts.plot(kind='bar', x=df_ts.index, stacked=True) # Make most of the ticklabels empty so the labels don't get too crowded ticklabels = ['']*len(df_ts.index) # Every 4th ticklable shows the month and day ticklabels[::4] = [item.strftime('%b %d') for item in df_ts.index[::4]] # Every 12th ticklabel includes the year ticklabels[::12] = [item.strftime('%b %d\n%Y') for item in df_ts.index[::12]] ax.xaxis.set_major_formatter(ticker.FixedFormatter(ticklabels)) plt.gcf().autofmt_xdate() plt.show() 

i rendimenti inserisci la descrizione dell'immagine qui


Per coloro che cercano un semplice esempio di trama di un bar con date:

 import numpy as np import pandas as pd import matplotlib.pyplot as plt import matplotlib.ticker as mticker dates = pd.date_range('2012-1-1', '2017-1-1', freq='M') df = pd.DataFrame({'A':np.random.random(len(dates)), 'Date':dates}) fig, ax = plt.subplots() df.plot.bar(x='Date', y='A', ax=ax) ticklabels = ['']*len(df) skip = len(df)//12 ticklabels[::skip] = df['Date'].iloc[::skip].dt.strftime('%Y-%m-%d') ax.xaxis.set_major_formatter(mticker.FixedFormatter(ticklabels)) fig.autofmt_xdate() # fixes the tracker # https://matplotlib.org/users/recipes.html def fmt(x, pos=0, max_i=len(ticklabels)-1): i = int(x) i = 0 if i < 0 else max_i if i > max_i else i return dates[i] ax.fmt_xdata = fmt plt.show() 

inserisci la descrizione dell'immagine qui

Ecco un approccio probabilmente più semplice con gli mdates , sebbene richieda di eseguire il looping delle colonne, chiamando il grafico della barra da matplotlib. Ecco un esempio in cui grafico solo una colonna e utilizzo di mate per tick ed etichette personalizzati (funzione di looping EDIT Added per tracciare tutte le colonne impilate):

 import datetime import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib.dates as mdates def format_x_date_month_day(ax): # Standard date x-axis formatting block, labels each month and ticks each day days = mdates.DayLocator() months = mdates.MonthLocator() # every month dayFmt = mdates.DateFormatter('%D') monthFmt = mdates.DateFormatter('%Y-%m') ax.figure.autofmt_xdate() ax.xaxis.set_major_locator(months) ax.xaxis.set_major_formatter(monthFmt) ax.xaxis.set_minor_locator(days) def df_stacked_bar_formattable(df, ax, **kwargs): P = [] lastBar = None for col in df.columns: X = df.index Y = df[col] if lastBar is not None: P.append(ax.bar(X, Y, bottom=lastBar, **kwargs)) else: P.append(ax.bar(X, Y, **kwargs)) lastBar = Y plt.legend([p[0] for p in P], df.columns) span_days = 90 start = pd.to_datetime("1-1-2012") idx = pd.date_range(start, periods=span_days).tolist() df=pd.DataFrame(index=idx, data={'A':np.random.random(span_days), 'B':np.random.random(span_days)}) plt.close('all') fig, ax = plt.subplots(1) df_stacked_bar_formattable(df, ax) format_x_date_month_day(ax) plt.show() 

( Facendo riferimento a matplotlib.org per esempio di looping per creare un grafico a barre in pila.) Questo ci dà

inserisci la descrizione dell'immagine qui

Un altro approccio che dovrebbe funzionare ed essere molto più semplice è usare df.plot.bar(ax=ax, stacked=True) , tuttavia non ammette la formattazione dell’asse della data con mdates ed è l’argomento della mia domanda .