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