# 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!