12115Shsul@eecs.umich.edu# Copyright (c) 2005-2006 The Regents of The University of Michigan
21881Sbinkertn@umich.edu# All rights reserved.
31881Sbinkertn@umich.edu#
41881Sbinkertn@umich.edu# Redistribution and use in source and binary forms, with or without
51881Sbinkertn@umich.edu# modification, are permitted provided that the following conditions are
61881Sbinkertn@umich.edu# met: redistributions of source code must retain the above copyright
71881Sbinkertn@umich.edu# notice, this list of conditions and the following disclaimer;
81881Sbinkertn@umich.edu# redistributions in binary form must reproduce the above copyright
91881Sbinkertn@umich.edu# notice, this list of conditions and the following disclaimer in the
101881Sbinkertn@umich.edu# documentation and/or other materials provided with the distribution;
111881Sbinkertn@umich.edu# neither the name of the copyright holders nor the names of its
121881Sbinkertn@umich.edu# contributors may be used to endorse or promote products derived from
131881Sbinkertn@umich.edu# this software without specific prior written permission.
141881Sbinkertn@umich.edu#
151881Sbinkertn@umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
161881Sbinkertn@umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
171881Sbinkertn@umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
181881Sbinkertn@umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
191881Sbinkertn@umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
201881Sbinkertn@umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
211881Sbinkertn@umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
221881Sbinkertn@umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
231881Sbinkertn@umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
241881Sbinkertn@umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
251881Sbinkertn@umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
261881Sbinkertn@umich.edu#
271881Sbinkertn@umich.edu# Authors: Nathan Binkert
281881Sbinkertn@umich.edu#          Lisa Hsu
291881Sbinkertn@umich.edu
301881Sbinkertn@umich.eduimport matplotlib, pylab
312006Sbinkertn@umich.edufrom matplotlib.font_manager import FontProperties
321881Sbinkertn@umich.edufrom matplotlib.numerix import array, arange, reshape, shape, transpose, zeros
331881Sbinkertn@umich.edufrom matplotlib.numerix import Float
342119Shsul@eecs.umich.edufrom matplotlib.ticker import NullLocator
351881Sbinkertn@umich.edu
361881Sbinkertn@umich.edumatplotlib.interactive(False)
371881Sbinkertn@umich.edu
382006Sbinkertn@umich.edufrom chart import ChartOptions
391881Sbinkertn@umich.edu
402006Sbinkertn@umich.educlass BarChart(ChartOptions):
412006Sbinkertn@umich.edu    def __init__(self, default=None, **kwargs):
422006Sbinkertn@umich.edu        super(BarChart, self).__init__(default, **kwargs)
431881Sbinkertn@umich.edu        self.inputdata = None
441881Sbinkertn@umich.edu        self.chartdata = None
452180Sbinkertn@umich.edu        self.inputerr = None
462180Sbinkertn@umich.edu        self.charterr = None
471881Sbinkertn@umich.edu
481881Sbinkertn@umich.edu    def gen_colors(self, count):
491881Sbinkertn@umich.edu        cmap = matplotlib.cm.get_cmap(self.colormap)
501881Sbinkertn@umich.edu        if count == 1:
511881Sbinkertn@umich.edu            return cmap([ 0.5 ])
522179Sbinkertn@umich.edu
532179Sbinkertn@umich.edu        if count < 5:
542179Sbinkertn@umich.edu            return cmap(arange(5) / float(4))[:count]
552179Sbinkertn@umich.edu
562179Sbinkertn@umich.edu        return cmap(arange(count) / float(count - 1))
571881Sbinkertn@umich.edu
581881Sbinkertn@umich.edu    # The input data format does not match the data format that the
591881Sbinkertn@umich.edu    # graph function takes because it is intuitive.  The conversion
601881Sbinkertn@umich.edu    # from input data format to chart data format depends on the
611881Sbinkertn@umich.edu    # dimensionality of the input data.  Check here for the
621881Sbinkertn@umich.edu    # dimensionality and correctness of the input data
631881Sbinkertn@umich.edu    def set_data(self, data):
641881Sbinkertn@umich.edu        if data is None:
651881Sbinkertn@umich.edu            self.inputdata = None
661881Sbinkertn@umich.edu            self.chartdata = None
671881Sbinkertn@umich.edu            return
681881Sbinkertn@umich.edu
691881Sbinkertn@umich.edu        data = array(data)
701881Sbinkertn@umich.edu        dim = len(shape(data))
711881Sbinkertn@umich.edu        if dim not in (1, 2, 3):
721881Sbinkertn@umich.edu            raise AttributeError, "Input data must be a 1, 2, or 3d matrix"
731881Sbinkertn@umich.edu        self.inputdata = data
741881Sbinkertn@umich.edu
751881Sbinkertn@umich.edu        # If the input data is a 1d matrix, then it describes a
761881Sbinkertn@umich.edu        # standard bar chart.
771881Sbinkertn@umich.edu        if dim == 1:
781881Sbinkertn@umich.edu            self.chartdata = array([[data]])
791881Sbinkertn@umich.edu
801881Sbinkertn@umich.edu        # If the input data is a 2d matrix, then it describes a bar
811881Sbinkertn@umich.edu        # chart with groups. The matrix being an array of groups of
821881Sbinkertn@umich.edu        # bars.
831881Sbinkertn@umich.edu        if dim == 2:
841881Sbinkertn@umich.edu            self.chartdata = transpose([data], axes=(2,0,1))
851881Sbinkertn@umich.edu
861881Sbinkertn@umich.edu        # If the input data is a 3d matrix, then it describes an array
871881Sbinkertn@umich.edu        # of groups of bars with each bar being an array of stacked
881881Sbinkertn@umich.edu        # values.
891881Sbinkertn@umich.edu        if dim == 3:
901881Sbinkertn@umich.edu            self.chartdata = transpose(data, axes=(1,2,0))
911881Sbinkertn@umich.edu
921881Sbinkertn@umich.edu    def get_data(self):
931881Sbinkertn@umich.edu        return self.inputdata
941881Sbinkertn@umich.edu
951881Sbinkertn@umich.edu    data = property(get_data, set_data)
961881Sbinkertn@umich.edu
972180Sbinkertn@umich.edu    def set_err(self, err):
982180Sbinkertn@umich.edu        if err is None:
992180Sbinkertn@umich.edu            self.inputerr = None
1002180Sbinkertn@umich.edu            self.charterr = None
1012180Sbinkertn@umich.edu            return
1022180Sbinkertn@umich.edu
1032180Sbinkertn@umich.edu        err = array(err)
1042180Sbinkertn@umich.edu        dim = len(shape(err))
1052180Sbinkertn@umich.edu        if dim not in (1, 2, 3):
1062180Sbinkertn@umich.edu            raise AttributeError, "Input err must be a 1, 2, or 3d matrix"
1072180Sbinkertn@umich.edu        self.inputerr = err
1082180Sbinkertn@umich.edu
1092180Sbinkertn@umich.edu        if dim == 1:
1102180Sbinkertn@umich.edu            self.charterr = array([[err]])
1112180Sbinkertn@umich.edu
1122180Sbinkertn@umich.edu        if dim == 2:
1132180Sbinkertn@umich.edu            self.charterr = transpose([err], axes=(2,0,1))
1142180Sbinkertn@umich.edu
1152180Sbinkertn@umich.edu        if dim == 3:
1162180Sbinkertn@umich.edu            self.charterr = transpose(err, axes=(1,2,0))
1172180Sbinkertn@umich.edu
1182180Sbinkertn@umich.edu    def get_err(self):
1192180Sbinkertn@umich.edu        return self.inputerr
1202180Sbinkertn@umich.edu
1212180Sbinkertn@umich.edu    err = property(get_err, set_err)
1222180Sbinkertn@umich.edu
1231881Sbinkertn@umich.edu    # Graph the chart data.
1241881Sbinkertn@umich.edu    # Input is a 3d matrix that describes a plot that has multiple
1251881Sbinkertn@umich.edu    # groups, multiple bars in each group, and multiple values stacked
1261881Sbinkertn@umich.edu    # in each bar.  The underlying bar() function expects a sequence of
1271881Sbinkertn@umich.edu    # bars in the same stack location and same group location, so the
1281881Sbinkertn@umich.edu    # organization of the matrix is that the inner most sequence
1291881Sbinkertn@umich.edu    # represents one of these bar groups, then those are grouped
1301881Sbinkertn@umich.edu    # together to make one full stack of bars in each group, and then
1311881Sbinkertn@umich.edu    # the outer most layer describes the groups.  Here is an example
1321881Sbinkertn@umich.edu    # data set and how it gets plotted as a result.
1331881Sbinkertn@umich.edu    #
1341881Sbinkertn@umich.edu    # e.g. data = [[[10,11,12], [13,14,15],  [16,17,18], [19,20,21]],
1351881Sbinkertn@umich.edu    #              [[22,23,24], [25,26,27],  [28,29,30], [31,32,33]]]
1361881Sbinkertn@umich.edu    #
1371881Sbinkertn@umich.edu    # will plot like this:
1381881Sbinkertn@umich.edu    #
1391881Sbinkertn@umich.edu    #    19 31    20 32    21 33
1401881Sbinkertn@umich.edu    #    16 28    17 29    18 30
1411881Sbinkertn@umich.edu    #    13 25    14 26    15 27
1421881Sbinkertn@umich.edu    #    10 22    11 23    12 24
1431881Sbinkertn@umich.edu    #
1441881Sbinkertn@umich.edu    # Because this arrangement is rather conterintuitive, the rearrange
1451881Sbinkertn@umich.edu    # function takes various matricies and arranges them to fit this
1461881Sbinkertn@umich.edu    # profile.
1471881Sbinkertn@umich.edu    #
1481881Sbinkertn@umich.edu    # This code deals with one of the dimensions in the matrix being
1491881Sbinkertn@umich.edu    # one wide.
1501881Sbinkertn@umich.edu    #
1511881Sbinkertn@umich.edu    def graph(self):
1521881Sbinkertn@umich.edu        if self.chartdata is None:
1531881Sbinkertn@umich.edu            raise AttributeError, "Data not set for bar chart!"
1541881Sbinkertn@umich.edu
1551881Sbinkertn@umich.edu        dim = len(shape(self.inputdata))
1561881Sbinkertn@umich.edu        cshape = shape(self.chartdata)
1572180Sbinkertn@umich.edu        if self.charterr is not None and shape(self.charterr) != cshape:
1582180Sbinkertn@umich.edu            raise AttributeError, 'Dimensions of error and data do not match'
1592180Sbinkertn@umich.edu
1601881Sbinkertn@umich.edu        if dim == 1:
1611881Sbinkertn@umich.edu            colors = self.gen_colors(cshape[2])
1621881Sbinkertn@umich.edu            colors = [ [ colors ] * cshape[1] ] * cshape[0]
1631881Sbinkertn@umich.edu
1641881Sbinkertn@umich.edu        if dim == 2:
1651881Sbinkertn@umich.edu            colors = self.gen_colors(cshape[0])
1661881Sbinkertn@umich.edu            colors = [ [ [ c ] * cshape[2] ] * cshape[1] for c in colors ]
1671881Sbinkertn@umich.edu
1681881Sbinkertn@umich.edu        if dim == 3:
1691881Sbinkertn@umich.edu            colors = self.gen_colors(cshape[1])
1701881Sbinkertn@umich.edu            colors = [ [ [ c ] * cshape[2] for c in colors ] ] * cshape[0]
1711881Sbinkertn@umich.edu
1721881Sbinkertn@umich.edu        colors = array(colors)
1731881Sbinkertn@umich.edu
1742115Shsul@eecs.umich.edu        self.figure = pylab.figure(figsize=self.chart_size)
1752115Shsul@eecs.umich.edu
1762115Shsul@eecs.umich.edu        outer_axes = None
1772115Shsul@eecs.umich.edu        inner_axes = None
1782119Shsul@eecs.umich.edu        if self.xsubticks is not None:
1792119Shsul@eecs.umich.edu            color = self.figure.get_facecolor()
1802182Sbinkertn@umich.edu            self.metaaxes = self.figure.add_axes(self.figure_size,
1812182Sbinkertn@umich.edu                                                 axisbg=color, frameon=False)
1822119Shsul@eecs.umich.edu            for tick in self.metaaxes.xaxis.majorTicks:
1832119Shsul@eecs.umich.edu                tick.tick1On = False
1842119Shsul@eecs.umich.edu                tick.tick2On = False
1852115Shsul@eecs.umich.edu            self.metaaxes.set_yticklabels([])
1862115Shsul@eecs.umich.edu            self.metaaxes.set_yticks([])
1872115Shsul@eecs.umich.edu            size = [0] * 4
1882115Shsul@eecs.umich.edu            size[0] = self.figure_size[0]
1892160Shsul@eecs.umich.edu            size[1] = self.figure_size[1] + .12
1902115Shsul@eecs.umich.edu            size[2] = self.figure_size[2]
1912160Shsul@eecs.umich.edu            size[3] = self.figure_size[3] - .12
1922115Shsul@eecs.umich.edu            self.axes = self.figure.add_axes(size)
1932115Shsul@eecs.umich.edu            outer_axes = self.metaaxes
1942115Shsul@eecs.umich.edu            inner_axes = self.axes
1952115Shsul@eecs.umich.edu        else:
1962115Shsul@eecs.umich.edu            self.axes = self.figure.add_axes(self.figure_size)
1972115Shsul@eecs.umich.edu            outer_axes = self.axes
1982115Shsul@eecs.umich.edu            inner_axes = self.axes
1992115Shsul@eecs.umich.edu
2001881Sbinkertn@umich.edu        bars_in_group = len(self.chartdata)
2012160Shsul@eecs.umich.edu
2022160Shsul@eecs.umich.edu        width = 1.0 / ( bars_in_group + 1)
2032160Shsul@eecs.umich.edu        center = width / 2
2041881Sbinkertn@umich.edu
2051881Sbinkertn@umich.edu        bars = []
2061881Sbinkertn@umich.edu        for i,stackdata in enumerate(self.chartdata):
2072006Sbinkertn@umich.edu            bottom = array([0.0] * len(stackdata[0]), Float)
2081881Sbinkertn@umich.edu            stack = []
2091881Sbinkertn@umich.edu            for j,bardata in enumerate(stackdata):
2101881Sbinkertn@umich.edu                bardata = array(bardata)
2111881Sbinkertn@umich.edu                ind = arange(len(bardata)) + i * width + center
2122180Sbinkertn@umich.edu                yerr = None
2132180Sbinkertn@umich.edu                if self.charterr is not None:
2142180Sbinkertn@umich.edu                    yerr = self.charterr[i][j]
2151881Sbinkertn@umich.edu                bar = self.axes.bar(ind, bardata, width, bottom=bottom,
2162180Sbinkertn@umich.edu                                    color=colors[i][j], yerr=yerr)
2172160Shsul@eecs.umich.edu                if self.xsubticks is not None:
2182115Shsul@eecs.umich.edu                    self.metaaxes.bar(ind, [0] * len(bardata), width)
2191881Sbinkertn@umich.edu                stack.append(bar)
2201881Sbinkertn@umich.edu                bottom += bardata
2211881Sbinkertn@umich.edu            bars.append(stack)
2221881Sbinkertn@umich.edu
2231881Sbinkertn@umich.edu        if self.xlabel is not None:
2242115Shsul@eecs.umich.edu            outer_axes.set_xlabel(self.xlabel)
2251881Sbinkertn@umich.edu
2261881Sbinkertn@umich.edu        if self.ylabel is not None:
2272115Shsul@eecs.umich.edu            inner_axes.set_ylabel(self.ylabel)
2281881Sbinkertn@umich.edu
2291881Sbinkertn@umich.edu        if self.yticks is not None:
2301881Sbinkertn@umich.edu            ymin, ymax = self.axes.get_ylim()
2311881Sbinkertn@umich.edu            nticks = float(len(self.yticks))
2321881Sbinkertn@umich.edu            ticks = arange(nticks) / (nticks - 1) * (ymax - ymin)  + ymin
2332115Shsul@eecs.umich.edu            inner_axes.set_yticks(ticks)
2342115Shsul@eecs.umich.edu            inner_axes.set_yticklabels(self.yticks)
2352006Sbinkertn@umich.edu        elif self.ylim is not None:
2362283Sbinkertn@umich.edu            inner_axes.set_ylim(self.ylim)
2371881Sbinkertn@umich.edu
2381881Sbinkertn@umich.edu        if self.xticks is not None:
2392115Shsul@eecs.umich.edu            outer_axes.set_xticks(arange(cshape[2]) + .5)
2402115Shsul@eecs.umich.edu            outer_axes.set_xticklabels(self.xticks)
2412119Shsul@eecs.umich.edu
2422115Shsul@eecs.umich.edu        if self.xsubticks is not None:
2432182Sbinkertn@umich.edu            numticks = (cshape[0] + 1) * cshape[2]
2442182Sbinkertn@umich.edu            inner_axes.set_xticks(arange(numticks) * width + 2 * center)
2452283Sbinkertn@umich.edu            xsubticks = list(self.xsubticks) + [ '' ]
2462283Sbinkertn@umich.edu            inner_axes.set_xticklabels(xsubticks * cshape[2], fontsize=7,
2472283Sbinkertn@umich.edu                                       rotation=30)
2482119Shsul@eecs.umich.edu
2491881Sbinkertn@umich.edu        if self.legend is not None:
2501881Sbinkertn@umich.edu            if dim == 1:
2511881Sbinkertn@umich.edu                lbars = bars[0][0]
2521881Sbinkertn@umich.edu            if dim == 2:
2531881Sbinkertn@umich.edu                lbars = [ bars[i][0][0] for i in xrange(len(bars))]
2541881Sbinkertn@umich.edu            if dim == 3:
2551881Sbinkertn@umich.edu                number = len(bars[0])
2561881Sbinkertn@umich.edu                lbars = [ bars[0][number - j - 1][0] for j in xrange(number)]
2571881Sbinkertn@umich.edu
2582180Sbinkertn@umich.edu            if self.fig_legend:
2592180Sbinkertn@umich.edu                self.figure.legend(lbars, self.legend, self.legend_loc,
2602180Sbinkertn@umich.edu                                   prop=FontProperties(size=self.legend_size))
2612180Sbinkertn@umich.edu            else:
2622180Sbinkertn@umich.edu                self.axes.legend(lbars, self.legend, self.legend_loc,
2632180Sbinkertn@umich.edu                                 prop=FontProperties(size=self.legend_size))
2641881Sbinkertn@umich.edu
2651881Sbinkertn@umich.edu        if self.title is not None:
2661881Sbinkertn@umich.edu            self.axes.set_title(self.title)
2671881Sbinkertn@umich.edu
2681881Sbinkertn@umich.edu    def savefig(self, name):
2691881Sbinkertn@umich.edu        self.figure.savefig(name)
2701881Sbinkertn@umich.edu
2712006Sbinkertn@umich.edu    def savecsv(self, name):
2722006Sbinkertn@umich.edu        f = file(name, 'w')
2732006Sbinkertn@umich.edu        data = array(self.inputdata)
2742006Sbinkertn@umich.edu        dim = len(data.shape)
2752006Sbinkertn@umich.edu
2762006Sbinkertn@umich.edu        if dim == 1:
2772006Sbinkertn@umich.edu            #if self.xlabel:
2782006Sbinkertn@umich.edu            #    f.write(', '.join(list(self.xlabel)) + '\n')
2792006Sbinkertn@umich.edu            f.write(', '.join([ '%f' % val for val in data]) + '\n')
2802006Sbinkertn@umich.edu        if dim == 2:
2812006Sbinkertn@umich.edu            #if self.xlabel:
2822006Sbinkertn@umich.edu            #    f.write(', '.join([''] + list(self.xlabel)) + '\n')
2832006Sbinkertn@umich.edu            for i,row in enumerate(data):
2842006Sbinkertn@umich.edu                ylabel = []
2852006Sbinkertn@umich.edu                #if self.ylabel:
2862006Sbinkertn@umich.edu                #    ylabel = [ self.ylabel[i] ]
2872182Sbinkertn@umich.edu                f.write(', '.join(ylabel + [ '%f' % v for v in row]) + '\n')
2882006Sbinkertn@umich.edu        if dim == 3:
2892006Sbinkertn@umich.edu            f.write("don't do 3D csv files\n")
2902006Sbinkertn@umich.edu            pass
2912006Sbinkertn@umich.edu
2922006Sbinkertn@umich.edu        f.close()
2932006Sbinkertn@umich.edu
2941881Sbinkertn@umich.eduif __name__ == '__main__':
2952006Sbinkertn@umich.edu    from random import randrange
2961881Sbinkertn@umich.edu    import random, sys
2971881Sbinkertn@umich.edu
2981881Sbinkertn@umich.edu    dim = 3
2991881Sbinkertn@umich.edu    number = 5
3001881Sbinkertn@umich.edu
3011881Sbinkertn@umich.edu    args = sys.argv[1:]
3021881Sbinkertn@umich.edu    if len(args) > 3:
3031881Sbinkertn@umich.edu        sys.exit("invalid number of arguments")
3041881Sbinkertn@umich.edu    elif len(args) > 0:
3051881Sbinkertn@umich.edu        myshape = [ int(x) for x in args ]
3061881Sbinkertn@umich.edu    else:
3071881Sbinkertn@umich.edu        myshape = [ 3, 4, 8 ]
3081881Sbinkertn@umich.edu
3091881Sbinkertn@umich.edu    # generate a data matrix of the given shape
3101881Sbinkertn@umich.edu    size = reduce(lambda x,y: x*y, myshape)
3111881Sbinkertn@umich.edu    #data = [ random.randrange(size - i) + 10 for i in xrange(size) ]
3121881Sbinkertn@umich.edu    data = [ float(i)/100.0 for i in xrange(size) ]
3131881Sbinkertn@umich.edu    data = reshape(data, myshape)
3141881Sbinkertn@umich.edu
3151881Sbinkertn@umich.edu    # setup some test bar charts
3161881Sbinkertn@umich.edu    if True:
3171881Sbinkertn@umich.edu        chart1 = BarChart()
3181881Sbinkertn@umich.edu        chart1.data = data
3191881Sbinkertn@umich.edu
3201881Sbinkertn@umich.edu        chart1.xlabel = 'Benchmark'
3211881Sbinkertn@umich.edu        chart1.ylabel = 'Bandwidth (GBps)'
3221881Sbinkertn@umich.edu        chart1.legend = [ 'x%d' % x for x in xrange(myshape[-1]) ]
3231881Sbinkertn@umich.edu        chart1.xticks = [ 'xtick%d' % x for x in xrange(myshape[0]) ]
3241881Sbinkertn@umich.edu        chart1.title = 'this is the title'
3252119Shsul@eecs.umich.edu        if len(myshape) > 2:
3262117Shsul@eecs.umich.edu            chart1.xsubticks = [ '%d' % x for x in xrange(myshape[1]) ]
3271881Sbinkertn@umich.edu        chart1.graph()
3282006Sbinkertn@umich.edu        chart1.savefig('/tmp/test1.png')
3292006Sbinkertn@umich.edu        chart1.savefig('/tmp/test1.ps')
3302006Sbinkertn@umich.edu        chart1.savefig('/tmp/test1.eps')
3312006Sbinkertn@umich.edu        chart1.savecsv('/tmp/test1.csv')
3321881Sbinkertn@umich.edu
3331881Sbinkertn@umich.edu    if False:
3341881Sbinkertn@umich.edu        chart2 = BarChart()
3351881Sbinkertn@umich.edu        chart2.data = data
3361881Sbinkertn@umich.edu        chart2.colormap = 'gray'
3371881Sbinkertn@umich.edu        chart2.graph()
3382006Sbinkertn@umich.edu        chart2.savefig('/tmp/test2.png')
3392006Sbinkertn@umich.edu        chart2.savefig('/tmp/test2.ps')
3401881Sbinkertn@umich.edu
3412160Shsul@eecs.umich.edu#    pylab.show()
342