Publication-quality plot production with SimPy and matplotlib.

This document deals with producing production-quality plots from SimPy simulation output using the matplotlib library. Matplotlib is known to work on linux, unix, MS Windows and OS X platforms. This library is not part of the SimPy distribution and has to be downloaded and installed separately.

Simulation programs normally produce large quantities of output which needs to be visualized, e.g. by plotting. These plots can help with aggregating data, e.g. for detecting trends over time, frequency distributions or determining the warm-up period of a simulation model experiment.

 SimPy's SimPlot plotting package is a easy to use, out-of-the-box capability which can produce a full range of plot graphs on the screen and in PostScript format. After installing SimPy, it can be used without installing any other software. It is tightly integrated with SimPy, e.g. its Monitor data collection class.

The capabilities of matplotlib exceed those of SimPy's SimPlot. This is how matplotlib is described on its home page:

"matplotlib is a python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms. matplotlib can be used in python scripts, the python and ipython shell (ala matlab or mathematica), web application servers, and six graphical user interface toolkits."

The matplotlib screenshots (with Python code) at http://matplotlib.sourceforge.net/screenshots.html show the great range of quality displays the library can produce with little coding. For the investment in time in downloading, installing and learning matplotlib, the SimPy user is rewarded with a powerful plotting capability.

Downloading matplotlib

You can download matplotlib from http://matplotlib.sourceforge.net/. Extensive installation instructions are provided at http://matplotlib.sourceforge.net/installing.html.

Using matplotlib with SimPy

The matplotlib library can generate all the plot types of SimPlot, plus a lot more. This document is not going to show them all. Instead, it just presents an example with two plot outputs to demonstrate how easy the use of matplotlib is from SimPy.

An example from the Bank Tutorial

As an example of how to use matplotlib with SimPy, here is a modified version of bank12.py from the Bank Tutorial:

001 #! /usr/local/bin/python
002 """ Based on bank12.py in Bank Tutorial.
003 This program uses matplotlib. It produces two plots:
004 - Queue length over time
005 - Histogram of queue length
006 """
007 from SimPy.Simulation import *
008 import pylab as pyl
009 from random import Random
010 ## Model components
011 class Source(Process):
012     """ Source generates customers randomly"""
013     def __init__(self,seed=333):
014         Process.__init__(self)
015         self.SEED = seed
016     def generate(self,number,interval):
017         rv = Random(self.SEED)
018         for i in range(number):
019             c = Customer(name = "Customer%02d"%(i,))
020             activate(c,c.visit(timeInBank=12.0))
021             t = rv.expovariate(1.0/interval)
022             yield hold,self,t
023 class Customer(Process):
024     """ Customer arrives, is served and leaves """
025     def __init__(self,name):
026         Process.__init__(self)
027         self.name = name
028
029     def visit(self,timeInBank=0):
030         arrive=now()
031         yield request,self,counter
032         wait=now()-arrive
033         wate.append(wait)
034         tme.append(now())
035         waitMonitor.tally(wait)
036         tib = counterRV.expovariate(1.0/timeInBank)
037         yield hold,self,tib
038         yield release,self,counter
039 class Observer(Process):
040     def __init__(self):
041         Process.__init__(self)
042     def observe(self):
043         while True:
044             yield hold,self,5
045             q.append(len(counter.waitQ))
046             t.append(now())
047 ## Model
048 def model(counterseed=3939393):
049     global counter,counterRV,waitMonitor
050     counter = Resource(name="Clerk",capacity = 1)
051     counterRV = Random(counterseed)
052     waitMonitor = Monitor()
053     initialize()
054     sourceseed=1133
055     source = Source(seed = sourceseed)
056     activate(source,source.generate(100,10.0))
057     ob=Observer()
058     activate(ob,ob.observe())
059     simulate(until=2000.0)
060     return waitMonitor.mean()
061 q=[]
062 t=[]
063 wate=[]
064 tme=[]
065 ## Experiment data
066 sourceSeed=333
067 ## Experiment
068 model()
069 ## Output
070 pyl.figure(figsize=(5.5,4))
071 pyl.plot(t,q)                       
072 pyl.title("Bank12: queue length over time",
073           fontsize=12,fontweight="bold")
074 pyl.xlabel("time",fontsize=9,fontweight="bold")
075 pyl.ylabel("queue length before counter",fontsize=9,fontweight="bold")
076 pyl.grid(True)
077 pyl.savefig(r".\bank12.png")
078 
080 pyl.clf()
081 n, bins, patches = pyl.hist(q, 10, normed=True)
082 pyl.title("Bank12: Frequency of counter queue length",
083           fontsize=12,fontweight="bold")
084 pyl.xlabel("queuelength",fontsize=9,fontweight="bold")
085 pyl.ylabel("frequency",fontsize=9,fontweight="bold")
086 pyl.grid(True)
087 pyl.xlim(0,30)
088 pyl.savefig(r".\bank12histo.png")

 

Here is the explanation of what the various matplotlib-related code lines do:

Line

Explanation
008 Imports the pylab module from matplotlib. Note that the pylab import is not of the form "from pylab import *", as there are clashes between the names in the matplotlib and SimPy namespaces.
070 Sets the size of the figures following to a width of 5.5 and a height of 4 inches
071 Plot the series of queue-length values (q) over their observation times series (t). Series q and t must have the same length.
072 Sets the figure title, its font size, and its font weight.
074 Sets the x-axis label, its font size, and its font weight.
075 Sets the y-axis label, its font size, and its font weight.
076 Gives the graph a grid
077 Saves the plot under the given name
080 Clears the current figure (e.g., resets the axes values from the previous plot)
081 Makes a histogram plot of the queue-length series (q) with 10 bins. The normed parameter makes the frequency counts relative to 1
082 Sets the title etc.
084 Sets the x-axis label etc.
085 Sets the y-axis label etc.
086 Gives the graph a grid
087 Limits the x-axis to the range[0..30]
088 Saves the plot under the given name

Running the program above results in two PNG files. The first (. . . /bank12.png) shows the queue length over time:

The second output file (. . ./bank12histo.png) is a histogram of the frequency of the length of the queue:


Conclusion

The small example above shows already the power, flexibility and quality of graphics provided by matplotlib. Almost anything (fonts, graph sizes, line types, number of series in one plot, number of subplots in a plot, . . . ) is under user control by setting parameters or calling functions. Admittedly, it initially takes a lot of reading in the extensive documentation and some experimentation, but the results are definitely worth the effort!