97

I have written code that opens 16 figures at once. Currently, they all open as separate graphs. I'd like them to open all on the same page. Not the same graph. I want 16 separate graphs on a single page/window. Furthermore, for some reason, the format of the numbins and defaultreallimits doesn't hold past figure 1. Do I need to use the subplot command? I don't understand why I would have to but can't figure out what else I would do?

import csv
import scipy.stats
import numpy
import matplotlib.pyplot as plt

for i in range(16):
    plt.figure(i)
    filename= easygui.fileopenbox(msg='Pdf distance 90m contour', title='select file', filetypes=['*.csv'], default='X:\\herring_schools\\')
    alt_file=open(filename)    
    a=[]
    for row in csv.DictReader(alt_file):
        a.append(row['Dist_90m(nmi)'])
    y= numpy.array(a, float)    
    relpdf=scipy.stats.relfreq(y, numbins=7, defaultreallimits=(-10,60))
    bins = numpy.arange(-10,60,10)
    print numpy.sum(relpdf[0])
    print bins
    patches=plt.bar(bins,relpdf[0], width=10, facecolor='black')
    titlename= easygui.enterbox(msg='write graph title', title='', default='', strip=True, image=None, root=None)
    plt.title(titlename)
    plt.ylabel('Probability Density Function')
    plt.xlabel('Distance from 90m Contour Line(nm)')
    plt.ylim([0,1])

plt.show()
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73
FS.
  • 1,201
  • 1
  • 12
  • 11

6 Answers6

203

The answer from las3rjock, which somehow is the answer accepted by the OP, is incorrect--the code doesn't run, nor is it valid matplotlib syntax; that answer provides no runnable code and lacks any information or suggestion that the OP might find useful in writing their own code to solve the problem in the OP.

Given that it's the accepted answer and has already received several up-votes, I suppose a little deconstruction is in order.

First, calling subplot does not give you multiple plots; subplot is called to create a single plot, as well as to create multiple plots. In addition, "changing plt.figure(i)" is not correct.

plt.figure() (in which plt or PLT is usually matplotlib's pyplot library imported and rebound as a global variable, plt or sometimes PLT, like so:

from matplotlib import pyplot as PLT

fig = PLT.figure()

the line just above creates a matplotlib figure instance; this object's add_subplot method is then called for every plotting window (informally think of an x & y axis comprising a single subplot). You create (whether just one or for several on a page), like so

fig.add_subplot(111)

this syntax is equivalent to

fig.add_subplot(1,1,1)

choose the one that makes sense to you.

Below I've listed the code to plot two plots on a page, one above the other. The formatting is done via the argument passed to add_subplot. Notice the argument is (211) for the first plot and (212) for the second.

from matplotlib import pyplot as PLT

fig = PLT.figure()

ax1 = fig.add_subplot(211)
ax1.plot([(1, 2), (3, 4)], [(4, 3), (2, 3)])

ax2 = fig.add_subplot(212)
ax2.plot([(7, 2), (5, 3)], [(1, 6), (9, 5)])

PLT.show()

Each of these two arguments is a complete specification for correctly placing the respective plot windows on the page.

211 (which again, could also be written in 3-tuple form as (2,1,1) means two rows and one column of plot windows; the third digit specifies the ordering of that particular subplot window relative to the other subplot windows--in this case, this is the first plot (which places it on row 1) hence plot number 1, row 1 col 1.

The argument passed to the second call to add_subplot, differs from the first only by the trailing digit (a 2 instead of a 1, because this plot is the second plot (row 2, col 1).

An example with more plots: if instead you wanted four plots on a page, in a 2x2 matrix configuration, you would call the add_subplot method four times, passing in these four arguments (221), (222), (223), and (224), to create four plots on a page at 10, 2, 8, and 4 o'clock, respectively and in this order.

Notice that each of the four arguments contains two leadings 2's--that encodes the 2 x 2 configuration, ie, two rows and two columns.

The third (right-most) digit in each of the four arguments encodes the ordering of that particular plot window in the 2 x 2 matrix--ie, row 1 col 1 (1), row 1 col 2 (2), row 2 col 1 (3), row 2 col 2 (4).

doug
  • 69,080
  • 24
  • 165
  • 199
  • i appreciate this question because it's clear and exaustive. but i need to plot 25 subplot in a 5x5 matrix layout. i get this error: __ValueError: Argument to subplot must be a 3 digits long__ – nkint Jul 14 '11 at 15:08
  • @nkint did you find out how to do it? – pms Oct 04 '12 at 09:21
  • 1
    add_subplot can take a single integer argument for small numbers of subplots, but once you need more than 9 subplots you should use the version from the other answers which takes 3 separate integer arguments. – user1244215 Aug 26 '13 at 03:49
  • 1
    can I use `fig.add_axes(rect)` instead of `add_subplot`? – Alcott Oct 05 '13 at 14:45
  • 19
    `PLT` ? Never seen that being used before – Darshan Chaudhary Oct 07 '15 at 10:06
  • (upped) thank you doug for explaining in simple English the meaning behind the slightly enigmatic syntax `add_subplot(xyz)` ... I have recently fallen under the spell of matplotlib and your post has really helped. – whytheq Sep 04 '16 at 07:43
  • 4
    Your first paragraph here is patently untrue, as you can verify by running `for i in range(16): plt.subplot(4, 4, i+1)` and observe no syntax error. The answer is accepted because it probably did what the OP wanted. – chthonicdaemon Jan 17 '17 at 11:07
  • 4
    This is a nice solution. But if I want to use one variable to loop, how to do? – Pengju Zhao Jul 21 '17 at 04:54
  • @DarshanChaudhary `PLT` here is used as a local alias for `pyplot` (see import at first line) – stelios Jul 12 '18 at 21:31
84

Since this question is from 4 years ago new things have been implemented and among them there is a new function plt.subplots which is very convenient:

fig, axes = plot.subplots(nrows=2, ncols=3, sharex=True, sharey=True)

where axes is a numpy.ndarray of AxesSubplot objects, making it very convenient to go through the different subplots just using array indices [i,j].

gibbone
  • 2,300
  • 20
  • 20
Saullo G. P. Castro
  • 56,802
  • 26
  • 179
  • 234
  • Does this work also for cases where I need to use matplotlib API only (due to display not being available and other funny things)? – eudoxos Jul 15 '15 at 15:58
  • 2
    @eudoxos yes, it should, you can create the subplots like this in order to print multiple graphics in one page, for example, without needing to show what is being done – Saullo G. P. Castro Jul 15 '15 at 16:32
17

To answer your main question, you want to use the subplot command. I think changing plt.figure(i) to plt.subplot(4,4,i+1) should work.

las3rjock
  • 8,612
  • 1
  • 31
  • 33
12

This works also:

for i in range(19):
    plt.subplot(5,4,i+1) 

It plots 19 total graphs on one page. The format is 5 down and 4 across..

FS.
  • 1,201
  • 1
  • 12
  • 11
4

@doug & FS.'s answer are very good solutions. I want to share the solution for iteration on pandas.dataframe.

import pandas as pd
df=pd.DataFrame([[1, 2], [3, 4], [4, 3], [2, 3]])
fig = plt.figure(figsize=(14,8))
for i in df.columns:
    ax=plt.subplot(2,1,i+1) 
    df[[i]].plot(ax=ax)
    print(i)
plt.show()
drevicko
  • 14,382
  • 15
  • 75
  • 97
Pengju Zhao
  • 1,439
  • 3
  • 14
  • 17
  • If you data frame has named columns, you'll need to use something like `for i,col in enumerate(df.columns):` – drevicko Sep 22 '17 at 10:45
3

You can use the method add_subplot. For example, to create 6 subplots with 2 rows and 3 columns you can use:

funcs = [np.cos, np.sin, np.tan, np.arctan]
x = np.linspace(0, 10, 100)

fig = plt.figure(figsize=(10, 5))

# iterate over the function list and add a subplot for each function
for num, func in enumerate(funcs, start=1):  
    ax = fig.add_subplot(2, 2, num) # plot with 2 rows and 2 columns
    ax.plot(x, func(x))
    ax.set_title(func.__name__)

# add spacing between subplots
fig.tight_layout()

Result:

enter image description here

Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73