2015-05-08 19 views
25

Ho un semplice grafico a linee impilate che ha esattamente il formato della data che desidero impostare magicamente quando si utilizza il seguente codice.Formato della trama delle modifiche della barra di Pandas

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

enter image description here

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

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

enter image description here

I dati originali è stata trasformata 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) 

risposta

29

Il codice di tracciamento si assume che ogni bar, in un grafico a barre merita la sua propria etichetta. Si potrebbe ignorare questa ipotesi specificando il proprio formattatore:

ax.xaxis.set_major_formatter(formatter) 

Il pandas.tseries.converter.TimeSeries_DateFormatter che i panda usa in formato le date nel complotto "buono" funziona bene con grafici lineari quando i valori x sono date . Tuttavia, con un grafico del bar i valori x (almeno quelli ricevuti da TimeSeries_DateFormatter.__call__) sono semplicemente numeri interi a partire da a zero. Se si tenta di utilizzare TimeSeries_DateFormatter con un grafico 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 è sfortunatamente inutile per i grafici della barra (almeno per quanto posso vedere).

Il modo più semplice che vedo per produrre la formattazione desiderata è quello di 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() 

rendimenti enter image description here

+0

@unutbu perché le date iniziano a gennaio 1970?Ho un problema molto simile (formattazione xtick del grafico a barre di panda) e quando provo a usare il tuo codice, le date iniziano a gennaio 1970 indipendentemente dalle date effettive. La mia domanda è su: http://stackoverflow.com/questions/33642388/pandas-bar-plot-with-multiindex-dataframe – marillion

+0

@marillion: Grazie per aver segnalato questo errore. Dopo aver studiato ulteriormente il codice Pandas/matplotlib sottostante, ritengo che il modo più semplice per personalizzare il tick plot di barare sia farlo in modo esplicito usando 'set_major_formatter' con un' FixedFormatter'. – unutbu

+1

utilizzare meglio il passo in base alla lunghezza 'show = 6; step = int (len (df_ts.index)/show) 'then ogni passo ticklable mostra mese, giorno e anno ' ticklabels [:: step] = [item.strftime ('% b% d \ n% Y') per l'elemento in '' df_ts.index [:: fase]] ' – jrovegno

0

Ecco un approccio forse più facile utilizzando mdates, anche se richiede di ciclo sulle tue colonne, chiamando il grafico a barre da matplotlib. Ecco un esempio in cui ho tracciare solo una colonna e sull'uso mdates per le zecche e le etichette personalizzate (EDIT Aggiunto funzione di loop per tracciare tutte le colonne in pila):

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() 

(Riferimenti a matplotlib.org per esempio di loop per creare una barra impilato trama.) Questo ci dà

enter image description here

Un altro approccio che dovrebbe lavoro ed essere molto più semplice è quello di utilizzare df.plot.bar(ax=ax, stacked=True), ma non ammette la data ascia sta formattando con mdates ed è oggetto di my question.