Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

matplotlib: Creating two (stacked) subplots with SHARED X axis but SEPARATE Y axis values

I am using matplotlib 1.2.x and Python 2.6.5 on Ubuntu 10.0.4. I am trying to create a SINGLE plot that consists of a top plot and a bottom plot.

The X axis is the date of the time series. The top plot contains a candlestick plot of the data, and the bottom plot should consist of a bar type plot - with its own Y axis (also on the left - same as the top plot). These two plots should NOT OVERLAP.

Here is a snippet of what I have done so far.

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

I have managed to display the two plots using the code above, however, there are two problems with the bottom plot:

  1. It COMPLETELY OVERWRITES the bottom part of the first (top) plot - almost as though the second plot was drawing on the same 'canvas' as the first plot - I can't see where/why that is happening.

  2. It OVERWRITES the existing X axis with its own indice, the X axis values (dates) should be SHARED between the two plots.

What am I doing wrong in my code?. Can someone spot what is causing the 2nd (bottom) plot to overwrite the first (top) plot - and how can I fix this?

Here is a screenshot of the plot created by the code above:

faulty plot

[[Edit]]

After modifying the code as suggested by hwlau, this is the new plot. It is better than the first in that the two plots are separate, however the following issues remain:

  1. The X axis should be SHARED by the two plots (i.e. the X axis should be shown only for the 2nd [bottom] plot)

  2. The Y values for the 2nd plot seem to be formmated incorrectly

partly correct plot

I think these issues should be quite easy to resolve however, my matplotlib fu is not great at the moment, as I have only recently started programming with matplotlib. any help will be much appreciated.

like image 546
Homunculus Reticulli Avatar asked Apr 04 '12 07:04

Homunculus Reticulli


People also ask

How do I split a subplot in Matplotlib?

Using subplots_adjust() method to set the spacing between subplots. We can use the plt. subplots_adjust() method to change the space between Matplotlib subplots. The parameters wspace and hspace specify the space reserved between Matplotlib subplots.

How do I plot multiple Y values in Matplotlib?

MatPlotLib with Python To make a scatter plot with multiple Y values for each X, we can create x and y data points using numpy, zip and iterate them together to create the scatter plot.


2 Answers

There seem to be a couple of problems with your code:

  1. If you were using figure.add_subplots with the full signature of subplot(nrows, ncols, plotNum) it may have been more apparent that your first plot asking for 1 row and 1 column and the second plot was asking for 2 rows and 1 column. Hence your first plot is filling the whole figure. Rather than fig.add_subplot(111) followed by fig.add_subplot(212) use fig.add_subplot(211) followed by fig.add_subplot(212).

  2. Sharing an axis should be done in the add_subplot command using sharex=first_axis_instance

I have put together an example which you should be able to run:

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

Hope that helps.

like image 125
pelson Avatar answered Oct 14 '22 06:10

pelson


You should change this line:

ax = fig.add_subplot(111)

to

ax = fig.add_subplot(211)

The original command means that there is one row and one column so it occupies the whole graph. So your second graph fig.add_subplot(212) cover the lower part of the first graph.

Edit

If you dont want the gap between two plots, use subplots_adjust() to change the size of the subplots margin.

like image 31
unsym Avatar answered Oct 14 '22 05:10

unsym