34

I want to have a figure consisting of, let's say, four subplots. Two of them are usual line-plots, two of them imshow-images.

I can format the imshow-images to proper plots itself, because every single one of them needs its own colorbar, a modified axis and the other axis removed. This, however, seems to be absolutely useless for the subplotting. Can anyone help me with that?

I use this for displaying the data of the "regular" plots above as a colormap (by scaling the input-array i to [ i, i, i, i, i, i ] for 2D and calling imshow() with it).

The following code first displays what I need as a subplot and the second one shows all I can do, which is not sufficient.

#!/usr/bin/env python

import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm

s = { 't':1, 'x':[1,2,3,4,5,6,7,8], 'D':[0.3,0.5,0.2,0.3,0.5,0.5,0.3,0.4] }
width = 40

# how I do it in just one plot
tot = []
for i in range(width):
    tot.append(s['D'])

plt.imshow(tot, norm=LogNorm(vmin=0.001, vmax=1))
plt.colorbar()
plt.axes().axes.get_xaxis().set_visible(False)
plt.yticks([0, 2, 4, 6], [s['x'][0], s['x'][2], s['x'][4], s['x'][6]])

plt.show()


f = plt.figure(figsize=(20,20))

plt.subplot(211)
plt.plot(s['x'], s['D'])
plt.ylim([0, 1])

#colorplot
sp = f.add_subplot(212)

#reshape (just necessary to see something)
tot = []
for i in range(width):
    tot.append(s['D'])

sp.imshow(tot, norm=LogNorm(vmin=0.001, vmax=1))

    #what I can't do now but needs to be done:
    #sp.colorbar()
#sp.axes().axes.get_xaxis().set_visible(False)
#sp.yticks([0, 200, 400, 600, 800, 1000], [s['x'][0], s['x'][200], s['x'][400], s['x'][600], s['x'][800], s['x'][1000]])

plt.show()
sodd
  • 12,482
  • 3
  • 54
  • 62
michael
  • 349
  • 1
  • 3
  • 4
  • __Your examples don't run!__ Can you add some sample data for s and tot so that we can see what you are looking at? For completeness it would also be nice if each example ended with the show command. – Steve Barnes Aug 16 '13 at 06:06
  • Sorry, I've appended runnable code. – michael Aug 16 '13 at 15:42
  • you don't need all of those `cla` commands. Also, when posting examples it is easiest to plot random data (unless the problem depends on the exact values of the data). – tacaswell Aug 16 '13 at 16:32
  • Please reduce this code to the _minimum_ needed to display your problem. – tacaswell Aug 16 '13 at 16:38
  • Done. [some letters to fullfill the min. char-requirement] – michael Aug 16 '13 at 17:04

1 Answers1

55

You can make use of matplotlibs object oriented interface rather than the state-machine interace in order to get better control over each axes. Also, to get control over the height/width of the colorbar you can make use of the AxesGrid toolkit of matplotlib.

For example:

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.colors import LogNorm
from matplotlib.ticker import MultipleLocator

s = {'t': 1,
     'x': [1, 2, 3, 4, 5, 6, 7, 8],
     'T': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8],
     'D': [0.3, 0.5, 0.2, 0.3, 0.5, 0.5, 0.3, 0.4]}

width = 40

tot = np.repeat(s['D'],width).reshape(len(s['D']), width)
tot2 = np.repeat(s['T'],width).reshape(len(s['D']), width)

fig, (ax1, ax2, ax3, ax4) = plt.subplots(1,4)

fig.suptitle('Title of figure', fontsize=20)

# Line plots
ax1.set_title('Title of ax1')
ax1.plot(s['x'], s['T'])
ax1.set_ylim(0,1)

ax2.set_title('Title of ax2')
ax2.plot(s['x'], s['D'])
# Set locations of ticks on y-axis (at every multiple of 0.25)
ax2.yaxis.set_major_locator(MultipleLocator(0.25))
# Set locations of ticks on x-axis (at every multiple of 2)
ax2.xaxis.set_major_locator(MultipleLocator(2))
ax2.set_ylim(0,1)

ax3.set_title('Title of ax3')
# Display image, `aspect='auto'` makes it fill the whole `axes` (ax3)
im3 = ax3.imshow(tot, norm=LogNorm(vmin=0.001, vmax=1), aspect='auto')
# Create divider for existing axes instance
divider3 = make_axes_locatable(ax3)
# Append axes to the right of ax3, with 20% width of ax3
cax3 = divider3.append_axes("right", size="20%", pad=0.05)
# Create colorbar in the appended axes
# Tick locations can be set with the kwarg `ticks`
# and the format of the ticklabels with kwarg `format`
cbar3 = plt.colorbar(im3, cax=cax3, ticks=MultipleLocator(0.2), format="%.2f")
# Remove xticks from ax3
ax3.xaxis.set_visible(False)
# Manually set ticklocations
ax3.set_yticks([0.0, 2.5, 3.14, 4.0, 5.2, 7.0])

ax4.set_title('Title of ax4')
im4 = ax4.imshow(tot2, norm=LogNorm(vmin=0.001, vmax=1), aspect='auto')
divider4 = make_axes_locatable(ax4)
cax4 = divider4.append_axes("right", size="20%", pad=0.05)
cbar4 = plt.colorbar(im4, cax=cax4)
ax4.xaxis.set_visible(False)
# Manually set ticklabels (not ticklocations, they remain unchanged)
ax4.set_yticklabels([0, 50, 30, 'foo', 'bar', 'baz'])

plt.tight_layout()
# Make space for title
plt.subplots_adjust(top=0.85)
plt.show()

enter image description here


You can change the locations and labels of the ticks on either axis with the set_ticks and set_ticklabels methods as in the example above.


As for what the make_axes_locatable function does, from the matplotlib site about the AxesGrid toolkit:

The axes_divider module provides a helper function make_axes_locatable, which can be useful. It takes a existing axes instance and create a divider for it.

ax = subplot(1,1,1)
divider = make_axes_locatable(ax)

make_axes_locatable returns an instance of the AxesLocator class, derived from the Locator. It provides append_axes method that creates a new axes on the given side of (“top”, “right”, “bottom” and “left”) of the original axes.

sodd
  • 12,482
  • 3
  • 54
  • 62
  • 1
    http://stackoverflow.com/questions/14254379/how-can-i-attach-a-pyplot-function-to-a-figure-instance/14261698#14261698 <- plugging my answer re state machine vs OO – tacaswell Aug 16 '13 at 17:13
  • What does make_axis_locatable exactely do? – michael Aug 16 '13 at 21:18
  • Besides that, I need to adjust the yticks of the image itself in a way that at the moment the yticks are the index of the field, but they are representing a certain range in form of bins, so I'd like to write this at the axis instead of the index. Is there a way to replace the yticks? – michael Aug 16 '13 at 21:24
  • @michael If I understand your last comment correctly, you are looking for the `set_ticklabels` method. I have used this in the exmaple (rightmost axes). You can give a list of the desired labels to this method. If the list is shorter than the number of ticks (as in my example) the remaining ticklabels are empty strings, i.e. they do not show. – sodd Aug 17 '13 at 14:47
  • Perfect, looks like I want it to have now. Thanks (and sorry for the answering-delay). – michael Aug 27 '13 at 22:20