mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 1 | # Copyright Martin J. Bligh (mbligh@google.com), 2007 |
| 2 | |
| 3 | """ |
| 4 | Class to draw gnuplot graphs for autotest performance analysis. |
| 5 | Not that generic - specifically designed to to draw graphs of one type, |
| 6 | but probably adaptable. |
| 7 | """ |
| 8 | |
| 9 | import subprocess, sys, os |
mbligh | 16ae926 | 2007-09-21 00:53:08 +0000 | [diff] [blame] | 10 | from math import sqrt |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 11 | Popen = subprocess.Popen |
| 12 | |
mbligh | 16ae926 | 2007-09-21 00:53:08 +0000 | [diff] [blame] | 13 | def avg_dev(values): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 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); |
mbligh | 16ae926 | 2007-09-21 00:53:08 +0000 | [diff] [blame] | 20 | |
| 21 | |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 22 | class gnuplot: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 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 |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 33 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 34 | def xtics(self): |
| 35 | count = 1 |
| 36 | tics = [] |
mbligh | 781f52f | 2009-01-07 16:47:24 +0000 | [diff] [blame] | 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)) |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 41 | count += 1 |
| 42 | return tics |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 43 | |
| 44 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 45 | def add_dataset(self, title, labeled_values): |
| 46 | """ |
| 47 | Add a data line |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 48 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 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) |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 63 | |
| 64 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 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') |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 87 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 88 | self.xlabels = self.xsort(list(self.xvalues)) |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 89 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 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())) |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 92 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 93 | plot_lines = ['"-" title "%s"' % t for t in self.data_titles] |
| 94 | g.write('plot ' + ', '.join(plot_lines) + '\n') |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 95 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 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') |
mbligh | f28cb4b | 2007-09-20 17:33:19 +0000 | [diff] [blame] | 105 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 106 | g.close() |
| 107 | if not test: |
| 108 | sts = os.waitpid(p.pid, 0) |