1

I've drawn a plot that looks something like the following:

enter image description here

It was created using the following code:

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

# 1. Plot a figure consisting of 3 separate axes
# ==============================================

plotNames = ['Plot1','Plot2','Plot3']

figure, axisList = plt.subplots(len(plotNames), sharex=True, sharey=True)

tempDF = pd.DataFrame()
tempDF['date'] = pd.date_range('2015-01-01','2015-12-31',freq='D')
tempDF['value'] = np.random.randn(tempDF['date'].size)
tempDF['value2'] = np.random.randn(tempDF['date'].size)

for i in range(len(plotNames)):
    axisList[i].plot_date(tempDF['date'],tempDF['value'],'b-',xdate=True)

# 2. Create a new single axis in the figure. This new axis sits over
# the top of the axes drawn previously. Make all the components of
# the new single axis invisibe except for the x and y labels.

big_ax = figure.add_subplot(111)
big_ax.set_axis_bgcolor('none')
big_ax.set_xlabel('Date',fontweight='bold')
big_ax.set_ylabel('Random normal',fontweight='bold')
big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
big_ax.spines['right'].set_visible(False)
big_ax.spines['top'].set_visible(False)
big_ax.spines['left'].set_visible(False)
big_ax.spines['bottom'].set_visible(False)

# 3. Plot a separate figure
# =========================
figure2,ax2 = plt.subplots()

ax2.plot_date(tempDF['date'],tempDF['value2'],'-',xdate=True,color='green')
ax2.set_xlabel('Date',fontweight='bold')
ax2.set_ylabel('Random normal',fontweight='bold')

# Save plot
# =========
plt.savefig('tempPlot.png',dpi=300)

Basically, the rationale for plotting the whole picture is as follows:

  1. Create the first figure and plot 3 separate axes using a loop
  2. Plot a single axis in the same figure to sit on top of the graphs drawn previously. Label the x and y axes. Make all other aspects of this axis invisible.
  3. Create a second figure and plot data on a single axis.

The plot displays just as I want when using jupyter-notebook but when the plot is saved, the file contains only the second figure.

I was under the impression that plots could have multiple figures and that figures could have multiple axes. However, I suspect I have a fundamental misunderstanding of the differences between plots, subplots, figures and axes. Can someone please explain what I'm doing wrong and explain how to get the whole image to save to a single file.

user1718097
  • 4,090
  • 11
  • 48
  • 63
  • I think when you call plt.savefig it grabs the current figure and plots that. – Ted Petrou Dec 20 '16 at 15:54
  • Check this post: http://stackoverflow.com/questions/17788685/python-saving-multiple-figures-into-one-pdf-file – Ted Petrou Dec 20 '16 at 15:59
  • 2
    `figure.savefig('figure1.png')` will save the first figure, `figure2.savefig('figure2.png')` will save the second figure. – tmdavison Dec 20 '16 at 16:12
  • So, I guess I need to have just one figure that can then be saved to a single file. Is it possible to group the top 3 (blue) axes as a single area of the figure and have the bottom (green) axis as a second, distinct area? – user1718097 Dec 20 '16 at 16:39

1 Answers1

2

Matplotlib does not have "plots". In that sense,

  • plots are figures
  • subplots are axes

During runtime of a script you can have as many figures as you wish. Calling plt.save() will save the currently active figure, i.e. the figure you would get by calling plt.gcf().
You can save any other figure either by providing a figure number num:

plt.figure(num)
plt.savefig("output.png") 

or by having a refence to the figure object fig1

fig1.savefig("output.png") 

In order to save several figures into one file, one could go the way detailed here: Python saving multiple figures into one PDF file. Another option would be not to create several figures, but a single one, using subplots,

fig = plt.figure()
ax = plt.add_subplot(611)
ax2 = plt.add_subplot(612)
ax3 = plt.add_subplot(613)
ax4 = plt.add_subplot(212)

and then plot the respective graphs to those axes using

ax.plot(x,y)

or in the case of a pandas dataframe df

df.plot(x="column1", y="column2", ax=ax)

This second option can of course be generalized to arbitrary axes positions using subplots on grids. This is detailed in the matplotlib user's guide Customizing Location of Subplot Using GridSpec Furthermore, it is possible to position an axes (a subplot so to speak) at any position in the figure using fig.add_axes([left, bottom, width, height]) (where left, bottom, width, height are in figure coordinates, ranging from 0 to 1).

Community
  • 1
  • 1
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thank you. This answer has helped to improve my understanding. The comments regarding add_subplot() are useful but aren't as generalisable as I wanted. If I understand correctly, the final call to add_subplot() produces an axis that "sits over" the bottom three (of six) plots. This works if the height of the bottom plot is equal to the combined height of the top three plots. However, if the bottom plot needs to be the height of 2 of the top 3 plots then the method falls apart. Nevertheless, it is a very useful tip and I've upgraded the answer (but couldn't mark it as the accepted answer). – user1718097 Dec 21 '16 at 08:29
  • Here I took the example given in the question. Other subplot arangements are equally possible. I updated the answer. – ImportanceOfBeingErnest Dec 21 '16 at 08:54