Plotagem da série temporal Pandas definindo marcas e rótulos principais e secundários do eixo x

87

Eu quero ser capaz de definir os xticks principais e secundários e seus rótulos para um gráfico de série temporal traçado a partir de um objeto de série temporal Pandas.

A página "novidades" do Pandas 0.9 diz:

"você pode usar to_pydatetime ou registrar um conversor para o tipo de carimbo de data / hora"

mas não consigo descobrir como fazer isso para poder usar os comandos matplotlib ax.xaxis.set_major_locatore ax.xaxis.set_major_formatter(e secundários).

Se eu usá-los sem converter os tempos de pandas, os marcadores e os rótulos do eixo x acabam errados.

Usando o parâmetro 'xticks', posso passar as marcas principais para pandas.plot e, em seguida, definir os rótulos das marcas principais. Não consigo descobrir como fazer os pequenos tiques usando essa abordagem. (Eu posso definir os rótulos nas marcações secundárias padrão definidas por pandas.plot)

Aqui está meu código de teste:

import pandas
print 'pandas.__version__ is ', pandas.__version__
print 'matplotlib.__version__ is ', matplotlib.__version__    

dStart = datetime.datetime(2011,5,1) # 1 May
dEnd = datetime.datetime(2011,7,1) # 1 July    

dateIndex = pandas.date_range(start=dStart, end=dEnd, freq='D')
print "1 May to 1 July 2011", dateIndex      

testSeries = pandas.Series(data=np.random.randn(len(dateIndex)),
                           index=dateIndex)    

ax = plt.figure(figsize=(7,4), dpi=300).add_subplot(111)
testSeries.plot(ax=ax, style='v-', label='first line')    

# using MatPlotLib date time locators and formatters doesn't work with new
# pandas datetime index
ax.xaxis.set_minor_locator(matplotlib.dates.WeekdayLocator(byweekday=(1),
                                                           interval=1))
ax.xaxis.set_minor_formatter(matplotlib.dates.DateFormatter('%d\n%a'))
ax.xaxis.grid(True, which="minor")
ax.xaxis.grid(False, which="major")
ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('\n\n\n%b%Y'))
plt.show()    

# set the major xticks and labels through pandas
ax2 = plt.figure(figsize=(7,4), dpi=300).add_subplot(111)
xticks = pandas.date_range(start=dStart, end=dEnd, freq='W-Tue')
print "xticks: ", xticks
testSeries.plot(ax=ax2, style='-v', label='second line',
                xticks=xticks.to_pydatetime())
ax2.set_xticklabels([x.strftime('%a\n%d\n%h\n%Y') for x in xticks]);
# set the text of the first few minor ticks created by pandas.plot
#    ax2.set_xticklabels(['a','b','c','d','e'], minor=True)
# remove the minor xtick labels set by pandas.plot 
ax2.set_xticklabels([], minor=True)
# turn the minor ticks created by pandas.plot off 
# plt.minorticks_off()
plt.show()
print testSeries['6/4/2011':'6/7/2011']

e sua saída:

pandas.__version__ is  0.9.1.dev-3de54ae
matplotlib.__version__ is  1.1.1
1 May to 1 July 2011 <class 'pandas.tseries.index.DatetimeIndex'>
[2011-05-01 00:00:00, ..., 2011-07-01 00:00:00]
Length: 62, Freq: D, Timezone: None

Gráfico com datas estranhas no xaxis

xticks:  <class 'pandas.tseries.index.DatetimeIndex'>
[2011-05-03 00:00:00, ..., 2011-06-28 00:00:00]
Length: 9, Freq: W-TUE, Timezone: None

Gráfico com datas corretas

2011-06-04   -0.199393
2011-06-05   -0.043118
2011-06-06    0.477771
2011-06-07   -0.033207
Freq: D

Atualização: consegui chegar mais perto do layout que queria usando um loop para construir os principais rótulos xtick:

# only show month for first label in month
month = dStart.month - 1
xticklabels = []
for x in xticks:
    if  month != x.month :
        xticklabels.append(x.strftime('%d\n%a\n%h'))
        month = x.month
    else:
        xticklabels.append(x.strftime('%d\n%a'))

No entanto, é um pouco como fazer o eixo x usando ax.annotate: possível, mas não ideal.

brenda
fonte
1
Eu sei que isso realmente não responde à pergunta, mas como uma abordagem geral, quando realmente me importo com a aparência de um enredo, geralmente tento obter uma versão vetorial dele e torná-lo bonito no Illustrator ou Inkscape. Descobri que muitas outras pessoas que conheço parecem fazer o mesmo.
John McDonnell
2
Você pode simplesmente ignorar totalmente os argumentos para a plotfunção pandas e definir todos os tiques após a plotagem, usando métodos matplotlib do axobjeto retornado (por exemplo, ax.set_xticks)?
BrenBarn
@BrenBarn Não consegui descobrir como obter a data como uma data python em vez de uma data e hora pandas para os métodos matplotlib. A resposta do bmu corrige isso convertendo as datas antes de plotar.
brenda

Respostas:

89

Ambos pandase matplotlib.datesuse matplotlib.unitspara localizar os carrapatos.

Mas, embora matplotlib.datestenha maneiras convenientes de definir os carrapatos manualmente, o pandas parece estar focado na formatação automática até agora (você pode dar uma olhada no código para conversão de data e formatação no pandas).

Portanto, no momento, parece mais razoável de usar matplotlib.dates(como mencionado por @BrenBarn em seu comentário).

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 
import matplotlib.dates as dates

idx = pd.date_range('2011-05-01', '2011-07-01')
s = pd.Series(np.random.randn(len(idx)), index=idx)

fig, ax = plt.subplots()
ax.plot_date(idx.to_pydatetime(), s, 'v-')
ax.xaxis.set_minor_locator(dates.WeekdayLocator(byweekday=(1),
                                                interval=1))
ax.xaxis.set_minor_formatter(dates.DateFormatter('%d\n%a'))
ax.xaxis.grid(True, which="minor")
ax.yaxis.grid()
ax.xaxis.set_major_locator(dates.MonthLocator())
ax.xaxis.set_major_formatter(dates.DateFormatter('\n\n\n%b\n%Y'))
plt.tight_layout()
plt.show()

pandas_like_date_fomatting

(meu local é alemão, então terça-feira [ter] se torna Dienstag [Di])

bmu
fonte