2012-04-04 41 views
13

Estoy usando matplotlib 1.2.x y Python 2.6.5 en Ubuntu 10.0.4. Estoy tratando de crear una sola trama que consta de una trama superior y una trama inferior.matplotlib: Creando dos subparcelas (apiladas) con eje X COMPARTIDO pero con valores SEPARADOS del eje Y

El eje X es la fecha de la serie temporal. El gráfico superior contiene un gráfico de velas de los datos, y el gráfico inferior debe consistir en un gráfico de tipo barra, con su propio eje Y (también a la izquierda, igual que el gráfico superior). Estas dos parcelas NO deben OVERLAP.

Aquí hay un fragmento de lo que he hecho hasta ahora.

datafile = r'/var/tmp/trz12.csv' 
r = mlab.csv2rec(datafile, delimiter=',', names=('dt', 'op', 'hi', 'lo', 'cl', 'vol', 'oi')) 

mask = (r["dt"] >= datetime.date(startdate)) & (r["dt"] <= datetime.date(enddate)) 
selected = r[mask] 
plotdata = zip(date2num(selected['dt']), selected['op'], selected['cl'], selected['hi'], selected['lo'], selected['vol'], selected['oi']) 

# Setup charting 
mondays = WeekdayLocator(MONDAY)  # major ticks on the mondays 
alldays = DayLocator()    # minor ticks on the days 
weekFormatter = DateFormatter('%b %d') # Eg, Jan 12 
dayFormatter = DateFormatter('%d')  # Eg, 12 
monthFormatter = DateFormatter('%b %y') 

# every Nth month 
months = MonthLocator(range(1,13), bymonthday=1, interval=1) 

fig = pylab.figure() 
fig.subplots_adjust(bottom=0.1) 
ax = fig.add_subplot(111) 
ax.xaxis.set_major_locator(months)#mondays 
ax.xaxis.set_major_formatter(monthFormatter) #weekFormatter 
ax.format_xdata = mdates.DateFormatter('%Y-%m-%d') 
ax.format_ydata = price 
ax.grid(True) 

candlestick(ax, plotdata, width=0.5, colorup='g', colordown='r', alpha=0.85) 

ax.xaxis_date() 
ax.autoscale_view() 
pylab.setp(pylab.gca().get_xticklabels(), rotation=45, horizontalalignment='right') 

# Add volume data 
# Note: the code below OVERWRITES the bottom part of the first plot 
# it should be plotted UNDERNEATH the first plot - but somehow, that's not happening 
fig.subplots_adjust(hspace=0.15) 
ay = fig.add_subplot(212) 
volumes = [ x[-2] for x in plotdata] 
ay.bar(range(len(plotdata)), volumes, 0.05) 

pylab.show() 

que han logrado mostrar las dos parcelas utilizando el código anterior, sin embargo, hay dos problemas con la trama de fondo:

  1. completamente sobrescribe la parte inferior de la primera (arriba) la trama, casi como si la segunda trama se basara en el mismo "lienzo" que la primera trama, no puedo ver dónde y por qué está sucediendo eso.

  2. SOBRESCRIBE el eje X existente con su propio índice, los valores del eje X (fechas) deben COMPARTIR entre los dos trazados.

¿Qué estoy haciendo mal en mi código ?. ¿Puede alguien detectar qué está causando que la segunda trama (inferior) sobrescriba la primera trama (superior) y cómo puedo solucionarla?

Aquí está una captura de pantalla de la trama creada por el código anterior:

faulty plot

[[Editar]]

Después de modificar el código como lo sugiere hwlau, este es el nuevo trama. Es mejor que la primera en que las dos parcelas están separados, sin embargo, las siguientes cuestiones siguen:

  1. El eje X debe ser compartida por las dos parcelas (es decir, el eje X se debe mostrar sólo para el segundo [ parte inferior] parcela)

  2. valores de la Y para la segunda trama parecen ser formmated incorrectamente

partly correct plot

Creo que estos tema Sin embargo, mi matplotlib fu no es muy bueno en este momento, ya que recientemente comencé a programar con matplotlib. Cualquier ayuda será muy apreciada.

Respuesta

10

Parece que hay un par de problemas con su código:

  1. Si estaba utilizando figure.add_subplots con el firma completa de subplot(nrows, ncols, plotNum) que puede tener sido más evidente que su primera parcela pidiendo 1 fila y 1 columna y el segundo gráfico solicitó 2 filas y 1 columna. Por lo tanto, tu primera trama está llenando toda la figura. En lugar de fig.add_subplot(111) seguido de fig.add_subplot(212) use fig.add_subplot(211) seguido de fig.add_subplot(212).

  2. compartir un eje se debe hacer en el comando add_subplot usando sharex=first_axis_instance

He reunido un ejemplo que debe ser capaz de ejecutar:

import matplotlib.pyplot as plt 
import matplotlib.ticker as mticker 
import matplotlib.dates as mdates 


import datetime as dt 


n_pts = 10 
dates = [dt.datetime.now() + dt.timedelta(days=i) for i in range(n_pts)] 

ax1 = plt.subplot(2, 1, 1) 
ax1.plot(dates, range(10)) 

ax2 = plt.subplot(2, 1, 2, sharex=ax1) 
ax2.bar(dates, range(10, 20)) 

# Now format the x axis. This *MUST* be done after all sharex commands are run. 

# put no more than 10 ticks on the date axis. 
ax1.xaxis.set_major_locator(mticker.MaxNLocator(10)) 
# format the date in our own way. 
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d')) 

# rotate the labels on both date axes 
for label in ax1.xaxis.get_ticklabels(): 
    label.set_rotation(30) 
for label in ax2.xaxis.get_ticklabels(): 
    label.set_rotation(30) 

# tweak the subplot spacing to fit the rotated labels correctly 
plt.subplots_adjust(hspace=0.35, bottom=0.125) 

plt.show() 

Espero que ayude.

+0

Las propiedades 'sharex' y' sharey' en subtramas son realmente útiles, gracias por publicarlas. (Puede ser mucho mejor que establecer límites comunes leyendo de uno y configurando el otro: al inspeccionar los resultados mediante el acercamiento interactivo, ambas/subtramas se acercarán si se comparten). – Bonlenfum

+0

'sharex = first_axis_instance' esto es un' ejes 'instancia, ¿correcto? –

6

debería cambiar esta línea:

ax = fig.add_subplot(111) 

a

ax = fig.add_subplot(211) 

El comando original significa que hay una fila y una columna para que ocupe toda la gráfica. Entonces su segundo gráfico fig.add_subplot (212) cubre la parte inferior del primer gráfico.

Editar

Si no quieres la brecha entre dos parcelas, uso subplots_adjust() para cambiar el tamaño del margen subtramas.

+0

Gracias por detectar eso. Estoy casi avergonzado de haber perdido algo tan obvio. Tal vez he estado mirando el código durante demasiado tiempo;). La trama ahora está mucho más cerca de lo que quiero. Sin embargo, hay un par de problemas pendientes. Por favor, eche un vistazo a mi pregunta editada cuando tenga oportunidad. –

2

El ejemplo de @Pelson, simplificado.

import matplotlib.pyplot as plt 
import datetime as dt 

#Two subplots that share one x axis 
fig,ax=plt.subplots(2,sharex=True) 

#plot data 
n_pts = 10 
dates = [dt.datetime.now() + dt.timedelta(days=i) for i in range(n_pts)] 
ax[0].bar(dates, range(10, 20)) 
ax[1].plot(dates, range(10)) 

#rotate and format the dates on the x axis 
fig.autofmt_xdate() 

Las tramas secundarias que comparten un eje X se crean en una línea, lo cual es conveniente cuando se desea más de dos subtramas:

fig, ax = plt.subplots(number_of_subplots, sharex=True) 

Para dar formato a la fecha correcta en el eje x, se puede basta con utilizar fig.autofmt_xdate()

shared x xaxis

Para informaciones adicionales, consulte shared axis demo y date demo de los ejemplos de pylab. Este ejemplo se ejecutó en Python3, matplotlib 1.5.1

Cuestiones relacionadas