1# Copyright Martin J. Bligh (mbligh@google.com), 2007 2 3""" 4Class to draw gnuplot graphs for autotest performance analysis. 5Not that generic - specifically designed to to draw graphs of one type, 6but probably adaptable. 7""" 8 9import subprocess, sys, os 10from math import sqrt 11Popen = subprocess.Popen 12 13def avg_dev(values): 14 if len(values) == 0: 15 return (0,0) 16 average = float(sum(values)) / len(values) 17 sum_sq_dev = sum( [(x - average) ** 2 for x in values] ) 18 std_dev = sqrt(sum_sq_dev / float(len(values))); 19 return (average, std_dev); 20 21 22class gnuplot: 23 def __init__(self, title, xlabel, ylabel, xsort = sorted, size = "1180,900", keytitle = None): 24 self.title = title 25 self.xlabel = xlabel 26 self.ylabel = ylabel 27 self.data_titles = [] 28 self.datasets = [] 29 self.xsort = xsort 30 self.xvalues = set([]) 31 self.size = size 32 self.keytitle = keytitle 33 34 def xtics(self): 35 count = 1 36 tics = [] 37 for label in self.xlabels: 38 # prepend 2 blanks to work around gnuplot bug 39 # in placing X axis legend over X tic labels 40 tics.append('" %s" %d' % (label, count)) 41 count += 1 42 return tics 43 44 45 def add_dataset(self, title, labeled_values): 46 """ 47 Add a data line 48 49 title: title of the dataset 50 labeled_values: dictionary of lists 51 { label : [value1, value2, ... ] , ... } 52 """ 53 if not labeled_values: 54 raise "plotgraph:add_dataset - dataset was empty! %s" %\ 55 title 56 self.data_titles.append(title) 57 data_points = {} 58 for label in labeled_values: 59 point = "%s %s" % avg_dev(labeled_values[label]) 60 data_points[label] = point 61 self.xvalues.add(label) 62 self.datasets.append(data_points) 63 64 65 def plot(self, cgi_header = False, output = None, test = None): 66 if cgi_header: 67 print "Content-type: image/png\n" 68 sys.stdout.flush() 69 if test: 70 g = open(test, 'w') 71 else: 72 p = Popen("/usr/bin/gnuplot", stdin = subprocess.PIPE) 73 g = p.stdin 74 g.write('set terminal png size %s\n' % self.size) 75 if self.keytitle: 76 g.write('set key title "%s"\n' % self.keytitle) 77 g.write('set key outside\n') # outside right 78 else: 79 g.write('set key below\n') 80 g.write('set title "%s"\n' % self.title) 81 g.write('set xlabel "%s"\n' % self.xlabel) 82 g.write('set ylabel "%s"\n' % self.ylabel) 83 if output: 84 g.write('set output "%s"\n' % output) 85 g.write('set style data yerrorlines\n') 86 g.write('set grid\n') 87 88 self.xlabels = self.xsort(list(self.xvalues)) 89 90 g.write('set xrange [0.5:%f]\n' % (len(self.xvalues)+0.5)) 91 g.write('set xtics rotate (%s)\n' % ','.join(self.xtics())) 92 93 plot_lines = ['"-" title "%s"' % t for t in self.data_titles] 94 g.write('plot ' + ', '.join(plot_lines) + '\n') 95 96 for dataset in self.datasets: 97 count = 1 98 for label in self.xlabels: 99 if label in dataset: 100 data = dataset[label] 101 g.write("%d %s\n" % (count, str(data))) 102 count += 1 103 sys.stdout.flush() 104 g.write('e\n') 105 106 g.close() 107 if not test: 108 sts = os.waitpid(p.pid, 0) 109