barchart.py revision 2117:cd2f6cff36df
19259SAli.Saidi@ARM.com# Copyright (c) 2005-2006 The Regents of The University of Michigan 29259SAli.Saidi@ARM.com# All rights reserved. 39259SAli.Saidi@ARM.com# 49259SAli.Saidi@ARM.com# Redistribution and use in source and binary forms, with or without 59259SAli.Saidi@ARM.com# modification, are permitted provided that the following conditions are 69259SAli.Saidi@ARM.com# met: redistributions of source code must retain the above copyright 79259SAli.Saidi@ARM.com# notice, this list of conditions and the following disclaimer; 89259SAli.Saidi@ARM.com# redistributions in binary form must reproduce the above copyright 99259SAli.Saidi@ARM.com# notice, this list of conditions and the following disclaimer in the 109259SAli.Saidi@ARM.com# documentation and/or other materials provided with the distribution; 119259SAli.Saidi@ARM.com# neither the name of the copyright holders nor the names of its 129259SAli.Saidi@ARM.com# contributors may be used to endorse or promote products derived from 139259SAli.Saidi@ARM.com# this software without specific prior written permission. 149259SAli.Saidi@ARM.com# 159259SAli.Saidi@ARM.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 169259SAli.Saidi@ARM.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 179259SAli.Saidi@ARM.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 189259SAli.Saidi@ARM.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 199259SAli.Saidi@ARM.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 209259SAli.Saidi@ARM.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 219259SAli.Saidi@ARM.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 229259SAli.Saidi@ARM.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 239259SAli.Saidi@ARM.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 249259SAli.Saidi@ARM.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 259259SAli.Saidi@ARM.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 269259SAli.Saidi@ARM.com# 279259SAli.Saidi@ARM.com# Authors: Nathan Binkert 289259SAli.Saidi@ARM.com# Lisa Hsu 299259SAli.Saidi@ARM.com 309259SAli.Saidi@ARM.comimport matplotlib, pylab 319259SAli.Saidi@ARM.comfrom matplotlib.font_manager import FontProperties 329259SAli.Saidi@ARM.comfrom matplotlib.numerix import array, arange, reshape, shape, transpose, zeros 339259SAli.Saidi@ARM.comfrom matplotlib.numerix import Float 349259SAli.Saidi@ARM.com 359259SAli.Saidi@ARM.commatplotlib.interactive(False) 369259SAli.Saidi@ARM.com 379259SAli.Saidi@ARM.comfrom chart import ChartOptions 389259SAli.Saidi@ARM.com 399259SAli.Saidi@ARM.comclass BarChart(ChartOptions): 409259SAli.Saidi@ARM.com def __init__(self, default=None, **kwargs): 419259SAli.Saidi@ARM.com super(BarChart, self).__init__(default, **kwargs) 429259SAli.Saidi@ARM.com self.inputdata = None 439259SAli.Saidi@ARM.com self.chartdata = None 449259SAli.Saidi@ARM.com 459259SAli.Saidi@ARM.com def gen_colors(self, count): 469259SAli.Saidi@ARM.com cmap = matplotlib.cm.get_cmap(self.colormap) 479259SAli.Saidi@ARM.com if count == 1: 489259SAli.Saidi@ARM.com return cmap([ 0.5 ]) 499259SAli.Saidi@ARM.com else: 509259SAli.Saidi@ARM.com return cmap(arange(count) / float(count - 1)) 519259SAli.Saidi@ARM.com 529259SAli.Saidi@ARM.com # The input data format does not match the data format that the 539259SAli.Saidi@ARM.com # graph function takes because it is intuitive. The conversion 549259SAli.Saidi@ARM.com # from input data format to chart data format depends on the 559259SAli.Saidi@ARM.com # dimensionality of the input data. Check here for the 569259SAli.Saidi@ARM.com # dimensionality and correctness of the input data 579259SAli.Saidi@ARM.com def set_data(self, data): 589259SAli.Saidi@ARM.com if data is None: 599259SAli.Saidi@ARM.com self.inputdata = None 609259SAli.Saidi@ARM.com self.chartdata = None 619259SAli.Saidi@ARM.com return 629294Sandreas.hansson@arm.com 639294Sandreas.hansson@arm.com data = array(data) 649259SAli.Saidi@ARM.com dim = len(shape(data)) 659259SAli.Saidi@ARM.com if dim not in (1, 2, 3): 669259SAli.Saidi@ARM.com raise AttributeError, "Input data must be a 1, 2, or 3d matrix" 679259SAli.Saidi@ARM.com self.inputdata = data 689259SAli.Saidi@ARM.com 699259SAli.Saidi@ARM.com # If the input data is a 1d matrix, then it describes a 709259SAli.Saidi@ARM.com # standard bar chart. 719259SAli.Saidi@ARM.com if dim == 1: 729294Sandreas.hansson@arm.com self.chartdata = array([[data]]) 739294Sandreas.hansson@arm.com 749259SAli.Saidi@ARM.com # If the input data is a 2d matrix, then it describes a bar 759259SAli.Saidi@ARM.com # chart with groups. The matrix being an array of groups of 769259SAli.Saidi@ARM.com # bars. 779259SAli.Saidi@ARM.com if dim == 2: 789259SAli.Saidi@ARM.com self.chartdata = transpose([data], axes=(2,0,1)) 799259SAli.Saidi@ARM.com print shape(self.chartdata) 809259SAli.Saidi@ARM.com 819259SAli.Saidi@ARM.com # If the input data is a 3d matrix, then it describes an array 829259SAli.Saidi@ARM.com # of groups of bars with each bar being an array of stacked 839259SAli.Saidi@ARM.com # values. 849259SAli.Saidi@ARM.com if dim == 3: 859259SAli.Saidi@ARM.com self.chartdata = transpose(data, axes=(1,2,0)) 869259SAli.Saidi@ARM.com print shape(self.chartdata) 879259SAli.Saidi@ARM.com 889259SAli.Saidi@ARM.com def get_data(self): 899259SAli.Saidi@ARM.com return self.inputdata 909259SAli.Saidi@ARM.com 919259SAli.Saidi@ARM.com data = property(get_data, set_data) 929259SAli.Saidi@ARM.com 939259SAli.Saidi@ARM.com # Graph the chart data. 949259SAli.Saidi@ARM.com # Input is a 3d matrix that describes a plot that has multiple 959259SAli.Saidi@ARM.com # groups, multiple bars in each group, and multiple values stacked 969259SAli.Saidi@ARM.com # in each bar. The underlying bar() function expects a sequence of 979259SAli.Saidi@ARM.com # bars in the same stack location and same group location, so the 989259SAli.Saidi@ARM.com # organization of the matrix is that the inner most sequence 999259SAli.Saidi@ARM.com # represents one of these bar groups, then those are grouped 1009259SAli.Saidi@ARM.com # together to make one full stack of bars in each group, and then 1019259SAli.Saidi@ARM.com # the outer most layer describes the groups. Here is an example 1029259SAli.Saidi@ARM.com # data set and how it gets plotted as a result. 1039259SAli.Saidi@ARM.com # 1049259SAli.Saidi@ARM.com # e.g. data = [[[10,11,12], [13,14,15], [16,17,18], [19,20,21]], 1059259SAli.Saidi@ARM.com # [[22,23,24], [25,26,27], [28,29,30], [31,32,33]]] 1069259SAli.Saidi@ARM.com # 1079259SAli.Saidi@ARM.com # will plot like this: 1089259SAli.Saidi@ARM.com # 1099259SAli.Saidi@ARM.com # 19 31 20 32 21 33 1109259SAli.Saidi@ARM.com # 16 28 17 29 18 30 1119259SAli.Saidi@ARM.com # 13 25 14 26 15 27 1129259SAli.Saidi@ARM.com # 10 22 11 23 12 24 1139259SAli.Saidi@ARM.com # 1149259SAli.Saidi@ARM.com # Because this arrangement is rather conterintuitive, the rearrange 1159259SAli.Saidi@ARM.com # function takes various matricies and arranges them to fit this 1169259SAli.Saidi@ARM.com # profile. 1179259SAli.Saidi@ARM.com # 1189259SAli.Saidi@ARM.com # This code deals with one of the dimensions in the matrix being 1199259SAli.Saidi@ARM.com # one wide. 1209259SAli.Saidi@ARM.com # 1219259SAli.Saidi@ARM.com def graph(self): 1229259SAli.Saidi@ARM.com if self.chartdata is None: 1239259SAli.Saidi@ARM.com raise AttributeError, "Data not set for bar chart!" 1249259SAli.Saidi@ARM.com 1259259SAli.Saidi@ARM.com need_subticks = True 1269259SAli.Saidi@ARM.com 1279259SAli.Saidi@ARM.com dim = len(shape(self.inputdata)) 1289259SAli.Saidi@ARM.com cshape = shape(self.chartdata) 1299259SAli.Saidi@ARM.com print cshape 1309259SAli.Saidi@ARM.com if dim == 1: 1319259SAli.Saidi@ARM.com colors = self.gen_colors(cshape[2]) 1329259SAli.Saidi@ARM.com colors = [ [ colors ] * cshape[1] ] * cshape[0] 1339259SAli.Saidi@ARM.com need_subticks = False 1349259SAli.Saidi@ARM.com 1359259SAli.Saidi@ARM.com if dim == 2: 1369259SAli.Saidi@ARM.com colors = self.gen_colors(cshape[0]) 1379259SAli.Saidi@ARM.com colors = [ [ [ c ] * cshape[2] ] * cshape[1] for c in colors ] 1389259SAli.Saidi@ARM.com 1399259SAli.Saidi@ARM.com if dim == 3: 1409259SAli.Saidi@ARM.com colors = self.gen_colors(cshape[1]) 1419259SAli.Saidi@ARM.com colors = [ [ [ c ] * cshape[2] for c in colors ] ] * cshape[0] 1429259SAli.Saidi@ARM.com 1439259SAli.Saidi@ARM.com colors = array(colors) 1449259SAli.Saidi@ARM.com 1459259SAli.Saidi@ARM.com self.figure = pylab.figure(figsize=self.chart_size) 1469259SAli.Saidi@ARM.com 1479259SAli.Saidi@ARM.com outer_axes = None 1489259SAli.Saidi@ARM.com inner_axes = None 1499259SAli.Saidi@ARM.com if need_subticks: 1509259SAli.Saidi@ARM.com self.metaaxes = self.figure.add_axes(self.figure_size) 1519259SAli.Saidi@ARM.com self.metaaxes.set_yticklabels([]) 1529259SAli.Saidi@ARM.com self.metaaxes.set_yticks([]) 1539259SAli.Saidi@ARM.com size = [0] * 4 1549259SAli.Saidi@ARM.com size[0] = self.figure_size[0] 1559259SAli.Saidi@ARM.com size[1] = self.figure_size[1] + .075 1569259SAli.Saidi@ARM.com size[2] = self.figure_size[2] 1579259SAli.Saidi@ARM.com size[3] = self.figure_size[3] - .075 1589259SAli.Saidi@ARM.com self.axes = self.figure.add_axes(size) 1599259SAli.Saidi@ARM.com outer_axes = self.metaaxes 1609259SAli.Saidi@ARM.com inner_axes = self.axes 1619259SAli.Saidi@ARM.com else: 1629259SAli.Saidi@ARM.com self.axes = self.figure.add_axes(self.figure_size) 1639259SAli.Saidi@ARM.com outer_axes = self.axes 1649259SAli.Saidi@ARM.com inner_axes = self.axes 1659259SAli.Saidi@ARM.com 1669259SAli.Saidi@ARM.com bars_in_group = len(self.chartdata) 1679259SAli.Saidi@ARM.com if bars_in_group < 5: 1689259SAli.Saidi@ARM.com width = 1.0 / ( bars_in_group + 1) 1699259SAli.Saidi@ARM.com center = width / 2 1709259SAli.Saidi@ARM.com else: 1719259SAli.Saidi@ARM.com width = .8 / bars_in_group 1729259SAli.Saidi@ARM.com center = .1 1739259SAli.Saidi@ARM.com 1749259SAli.Saidi@ARM.com bars = [] 1759259SAli.Saidi@ARM.com for i,stackdata in enumerate(self.chartdata): 1769259SAli.Saidi@ARM.com bottom = array([0.0] * len(stackdata[0]), Float) 1779259SAli.Saidi@ARM.com stack = [] 1789259SAli.Saidi@ARM.com for j,bardata in enumerate(stackdata): 1799259SAli.Saidi@ARM.com bardata = array(bardata) 1809259SAli.Saidi@ARM.com ind = arange(len(bardata)) + i * width + center 1819259SAli.Saidi@ARM.com bar = self.axes.bar(ind, bardata, width, bottom=bottom, 1829259SAli.Saidi@ARM.com color=colors[i][j]) 1839259SAli.Saidi@ARM.com if dim != 1: 1849259SAli.Saidi@ARM.com self.metaaxes.bar(ind, [0] * len(bardata), width) 1859259SAli.Saidi@ARM.com stack.append(bar) 1869259SAli.Saidi@ARM.com bottom += bardata 1879259SAli.Saidi@ARM.com bars.append(stack) 1889259SAli.Saidi@ARM.com 1899259SAli.Saidi@ARM.com if self.xlabel is not None: 1909259SAli.Saidi@ARM.com outer_axes.set_xlabel(self.xlabel) 1919259SAli.Saidi@ARM.com 1929259SAli.Saidi@ARM.com if self.ylabel is not None: 1939259SAli.Saidi@ARM.com inner_axes.set_ylabel(self.ylabel) 1949259SAli.Saidi@ARM.com 1959294Sandreas.hansson@arm.com if self.yticks is not None: 1969259SAli.Saidi@ARM.com ymin, ymax = self.axes.get_ylim() 1979259SAli.Saidi@ARM.com nticks = float(len(self.yticks)) 1989259SAli.Saidi@ARM.com ticks = arange(nticks) / (nticks - 1) * (ymax - ymin) + ymin 1999259SAli.Saidi@ARM.com inner_axes.set_yticks(ticks) 2009259SAli.Saidi@ARM.com inner_axes.set_yticklabels(self.yticks) 2019259SAli.Saidi@ARM.com elif self.ylim is not None: 2029259SAli.Saidi@ARM.com self.inner_axes.set_ylim(self.ylim) 2039259SAli.Saidi@ARM.com 2049259SAli.Saidi@ARM.com if self.xticks is not None: 2059259SAli.Saidi@ARM.com outer_axes.set_xticks(arange(cshape[2]) + .5) 2069259SAli.Saidi@ARM.com outer_axes.set_xticklabels(self.xticks) 2079259SAli.Saidi@ARM.com if self.xsubticks is not None: 2089259SAli.Saidi@ARM.com inner_axes.set_xticks(arange((cshape[0] + 1)*cshape[2])*width + 2*center) 2099259SAli.Saidi@ARM.com self.xsubticks.append('') 2109259SAli.Saidi@ARM.com inner_axes.set_xticklabels(self.xsubticks * cshape[0], fontsize=8) 2119259SAli.Saidi@ARM.com if self.legend is not None: 2129259SAli.Saidi@ARM.com if dim == 1: 2139259SAli.Saidi@ARM.com lbars = bars[0][0] 2149259SAli.Saidi@ARM.com if dim == 2: 2159259SAli.Saidi@ARM.com lbars = [ bars[i][0][0] for i in xrange(len(bars))] 2169259SAli.Saidi@ARM.com if dim == 3: 2179259SAli.Saidi@ARM.com number = len(bars[0]) 2189259SAli.Saidi@ARM.com lbars = [ bars[0][number - j - 1][0] for j in xrange(number)] 2199259SAli.Saidi@ARM.com 2209259SAli.Saidi@ARM.com self.figure.legend(lbars, self.legend, self.legend_loc, 2219259SAli.Saidi@ARM.com prop=FontProperties(size=self.legend_size)) 2229259SAli.Saidi@ARM.com 2239259SAli.Saidi@ARM.com if self.title is not None: 2249259SAli.Saidi@ARM.com self.axes.set_title(self.title) 2259259SAli.Saidi@ARM.com 2269259SAli.Saidi@ARM.com def savefig(self, name): 2279259SAli.Saidi@ARM.com self.figure.savefig(name) 2289259SAli.Saidi@ARM.com 2299259SAli.Saidi@ARM.com def savecsv(self, name): 2309259SAli.Saidi@ARM.com f = file(name, 'w') 2319259SAli.Saidi@ARM.com data = array(self.inputdata) 2329259SAli.Saidi@ARM.com dim = len(data.shape) 2339259SAli.Saidi@ARM.com 2349259SAli.Saidi@ARM.com if dim == 1: 2359259SAli.Saidi@ARM.com #if self.xlabel: 2369259SAli.Saidi@ARM.com # f.write(', '.join(list(self.xlabel)) + '\n') 2379259SAli.Saidi@ARM.com f.write(', '.join([ '%f' % val for val in data]) + '\n') 2389259SAli.Saidi@ARM.com if dim == 2: 2399259SAli.Saidi@ARM.com #if self.xlabel: 2409259SAli.Saidi@ARM.com # f.write(', '.join([''] + list(self.xlabel)) + '\n') 2419259SAli.Saidi@ARM.com for i,row in enumerate(data): 2429259SAli.Saidi@ARM.com ylabel = [] 2439259SAli.Saidi@ARM.com #if self.ylabel: 2449259SAli.Saidi@ARM.com # ylabel = [ self.ylabel[i] ] 2459259SAli.Saidi@ARM.com f.write(', '.join(ylabel + [ '%f' % val for val in row]) + '\n') 2469259SAli.Saidi@ARM.com if dim == 3: 2479259SAli.Saidi@ARM.com f.write("don't do 3D csv files\n") 2489259SAli.Saidi@ARM.com pass 2499259SAli.Saidi@ARM.com 2509259SAli.Saidi@ARM.com f.close() 2519259SAli.Saidi@ARM.com 2529259SAli.Saidi@ARM.comif __name__ == '__main__': 2539259SAli.Saidi@ARM.com from random import randrange 2549259SAli.Saidi@ARM.com import random, sys 2559259SAli.Saidi@ARM.com 2569405Sandreas.hansson@arm.com dim = 3 2579405Sandreas.hansson@arm.com number = 5 2589405Sandreas.hansson@arm.com 2599259SAli.Saidi@ARM.com args = sys.argv[1:] 2609259SAli.Saidi@ARM.com if len(args) > 3: 2619259SAli.Saidi@ARM.com sys.exit("invalid number of arguments") 2629259SAli.Saidi@ARM.com elif len(args) > 0: 2639259SAli.Saidi@ARM.com myshape = [ int(x) for x in args ] 2649259SAli.Saidi@ARM.com else: 2659259SAli.Saidi@ARM.com myshape = [ 3, 4, 8 ] 2669259SAli.Saidi@ARM.com 2679259SAli.Saidi@ARM.com # generate a data matrix of the given shape 2689406Sandreas.hansson@arm.com size = reduce(lambda x,y: x*y, myshape) 2699406Sandreas.hansson@arm.com #data = [ random.randrange(size - i) + 10 for i in xrange(size) ] 2709259SAli.Saidi@ARM.com data = [ float(i)/100.0 for i in xrange(size) ] 2719259SAli.Saidi@ARM.com data = reshape(data, myshape) 2729259SAli.Saidi@ARM.com 2739259SAli.Saidi@ARM.com # setup some test bar charts 274 if True: 275 chart1 = BarChart() 276 chart1.data = data 277 278 chart1.xlabel = 'Benchmark' 279 chart1.ylabel = 'Bandwidth (GBps)' 280 chart1.legend = [ 'x%d' % x for x in xrange(myshape[-1]) ] 281 chart1.xticks = [ 'xtick%d' % x for x in xrange(myshape[0]) ] 282 chart1.title = 'this is the title' 283 chart1.figure_size = [0.1, 0.2, 0.7, 0.85 ] 284 if len(myshape) > 1: 285 chart1.xsubticks = [ '%d' % x for x in xrange(myshape[1]) ] 286 chart1.graph() 287 chart1.savefig('/tmp/test1.png') 288 chart1.savefig('/tmp/test1.ps') 289 chart1.savefig('/tmp/test1.eps') 290 chart1.savecsv('/tmp/test1.csv') 291 292 if False: 293 chart2 = BarChart() 294 chart2.data = data 295 chart2.colormap = 'gray' 296 chart2.graph() 297 chart2.savefig('/tmp/test2.png') 298 chart2.savefig('/tmp/test2.ps') 299 300 pylab.myshow() 301