]>
Commit | Line | Data |
---|---|---|
d3da7c4c | 1 | #!/usr/bin/env python |
1506a5ed JM |
2 | |
3 | ''' | |
4 | Use matplotlib to generate performance charts | |
84586c0f | 5 | Copyright (C) 2018 The noVNC Authors |
1d728ace | 6 | Licensed under MPL-2.0 (see docs/LICENSE.MPL-2.0) |
1506a5ed JM |
7 | ''' |
8 | ||
d3da7c4c | 9 | # a bar plot with errorbars |
12f4747c | 10 | import sys, json |
d3da7c4c JM |
11 | import numpy as np |
12 | import matplotlib.pyplot as plt | |
13 | from matplotlib.font_manager import FontProperties | |
14 | ||
15 | def usage(): | |
303819ea | 16 | print "%s json_file level1 level2 level3 [legend_height]\n\n" % sys.argv[0] |
d3da7c4c JM |
17 | print "Description:\n" |
18 | print "level1, level2, and level3 are one each of the following:\n"; | |
19 | print " select=ITEM - select only ITEM at this level"; | |
20 | print " bar - each item on this level becomes a graph bar"; | |
21 | print " group - items on this level become groups of bars"; | |
22 | print "\n"; | |
23 | print "json_file is a file containing json data in the following format:\n" | |
24 | print ' {'; | |
25 | print ' "conf": {'; | |
26 | print ' "order_l1": ['; | |
27 | print ' "level1_label1",'; | |
28 | print ' "level1_label2",'; | |
29 | print ' ...'; | |
30 | print ' ],'; | |
31 | print ' "order_l2": ['; | |
32 | print ' "level2_label1",'; | |
33 | print ' "level2_label2",'; | |
34 | print ' ...'; | |
35 | print ' ],'; | |
36 | print ' "order_l3": ['; | |
37 | print ' "level3_label1",'; | |
38 | print ' "level3_label2",'; | |
39 | print ' ...'; | |
40 | print ' ]'; | |
41 | print ' },'; | |
42 | print ' "stats": {'; | |
43 | print ' "level1_label1": {'; | |
44 | print ' "level2_label1": {'; | |
45 | print ' "level3_label1": [val1, val2, val3],'; | |
46 | print ' "level3_label2": [val1, val2, val3],'; | |
47 | print ' ...'; | |
48 | print ' },'; | |
49 | print ' "level2_label2": {'; | |
50 | print ' ...'; | |
51 | print ' },'; | |
52 | print ' },'; | |
53 | print ' "level1_label2": {'; | |
54 | print ' ...'; | |
55 | print ' },'; | |
56 | print ' ...'; | |
57 | print ' },'; | |
58 | print ' }'; | |
59 | sys.exit(2) | |
60 | ||
61 | def error(msg): | |
62 | print msg | |
63 | sys.exit(1) | |
64 | ||
65 | ||
66 | #colors = ['#ff0000', '#0863e9', '#00f200', '#ffa100', | |
67 | # '#800000', '#805100', '#013075', '#007900'] | |
68 | colors = ['#ff0000', '#00ff00', '#0000ff', | |
69 | '#dddd00', '#dd00dd', '#00dddd', | |
70 | '#dd6622', '#dd2266', '#66dd22', | |
71 | '#8844dd', '#44dd88', '#4488dd'] | |
72 | ||
73 | if len(sys.argv) < 5: | |
74 | usage() | |
75 | ||
76 | filename = sys.argv[1] | |
77 | L1 = sys.argv[2] | |
78 | L2 = sys.argv[3] | |
79 | L3 = sys.argv[4] | |
80 | if len(sys.argv) > 5: | |
81 | legendHeight = float(sys.argv[5]) | |
82 | else: | |
83 | legendHeight = 0.75 | |
84 | ||
85 | # Load the JSON data from the file | |
86 | data = json.loads(file(filename).read()) | |
87 | conf = data['conf'] | |
88 | stats = data['stats'] | |
89 | ||
90 | # Sanity check data hierarchy | |
91 | if len(conf['order_l1']) != len(stats.keys()): | |
92 | error("conf.order_l1 does not match stats level 1") | |
93 | for l1 in stats.keys(): | |
94 | if len(conf['order_l2']) != len(stats[l1].keys()): | |
95 | error("conf.order_l2 does not match stats level 2 for %s" % l1) | |
96 | if conf['order_l1'].count(l1) < 1: | |
97 | error("%s not found in conf.order_l1" % l1) | |
98 | for l2 in stats[l1].keys(): | |
99 | if len(conf['order_l3']) != len(stats[l1][l2].keys()): | |
100 | error("conf.order_l3 does not match stats level 3") | |
101 | if conf['order_l2'].count(l2) < 1: | |
102 | error("%s not found in conf.order_l2" % l2) | |
103 | for l3 in stats[l1][l2].keys(): | |
104 | if conf['order_l3'].count(l3) < 1: | |
105 | error("%s not found in conf.order_l3" % l3) | |
106 | ||
107 | # | |
108 | # Generate the data based on the level specifications | |
109 | # | |
110 | bar_labels = None | |
111 | group_labels = None | |
112 | bar_vals = [] | |
113 | bar_sdvs = [] | |
114 | if L3.startswith("select="): | |
115 | select_label = l3 = L3.split("=")[1] | |
116 | bar_labels = conf['order_l1'] | |
117 | group_labels = conf['order_l2'] | |
118 | bar_vals = [[0]*len(group_labels) for i in bar_labels] | |
119 | bar_sdvs = [[0]*len(group_labels) for i in bar_labels] | |
120 | for b in range(len(bar_labels)): | |
121 | l1 = bar_labels[b] | |
122 | for g in range(len(group_labels)): | |
123 | l2 = group_labels[g] | |
124 | bar_vals[b][g] = np.mean(stats[l1][l2][l3]) | |
125 | bar_sdvs[b][g] = np.std(stats[l1][l2][l3]) | |
126 | elif L2.startswith("select="): | |
127 | select_label = l2 = L2.split("=")[1] | |
128 | bar_labels = conf['order_l1'] | |
129 | group_labels = conf['order_l3'] | |
130 | bar_vals = [[0]*len(group_labels) for i in bar_labels] | |
131 | bar_sdvs = [[0]*len(group_labels) for i in bar_labels] | |
132 | for b in range(len(bar_labels)): | |
133 | l1 = bar_labels[b] | |
134 | for g in range(len(group_labels)): | |
135 | l3 = group_labels[g] | |
136 | bar_vals[b][g] = np.mean(stats[l1][l2][l3]) | |
137 | bar_sdvs[b][g] = np.std(stats[l1][l2][l3]) | |
138 | elif L1.startswith("select="): | |
139 | select_label = l1 = L1.split("=")[1] | |
140 | bar_labels = conf['order_l2'] | |
141 | group_labels = conf['order_l3'] | |
142 | bar_vals = [[0]*len(group_labels) for i in bar_labels] | |
143 | bar_sdvs = [[0]*len(group_labels) for i in bar_labels] | |
144 | for b in range(len(bar_labels)): | |
145 | l2 = bar_labels[b] | |
146 | for g in range(len(group_labels)): | |
147 | l3 = group_labels[g] | |
148 | bar_vals[b][g] = np.mean(stats[l1][l2][l3]) | |
149 | bar_sdvs[b][g] = np.std(stats[l1][l2][l3]) | |
150 | else: | |
151 | usage() | |
152 | ||
153 | # If group is before bar then flip (zip) the data | |
154 | if [L1, L2, L3].index("group") < [L1, L2, L3].index("bar"): | |
155 | bar_labels, group_labels = group_labels, bar_labels | |
156 | bar_vals = zip(*bar_vals) | |
157 | bar_sdvs = zip(*bar_sdvs) | |
158 | ||
159 | print "bar_vals:", bar_vals | |
160 | ||
161 | # | |
162 | # Now render the bar graph | |
163 | # | |
164 | ind = np.arange(len(group_labels)) # the x locations for the groups | |
165 | width = 0.8 * (1.0/len(bar_labels)) # the width of the bars | |
166 | ||
167 | fig = plt.figure(figsize=(10,6), dpi=80) | |
168 | plot = fig.add_subplot(1, 1, 1) | |
169 | ||
170 | rects = [] | |
171 | for i in range(len(bar_vals)): | |
172 | rects.append(plot.bar(ind+width*i, bar_vals[i], width, color=colors[i], | |
173 | yerr=bar_sdvs[i], align='center')) | |
174 | ||
175 | # add some | |
176 | plot.set_ylabel('Milliseconds (less is better)') | |
177 | plot.set_title("Javascript array test: %s" % select_label) | |
178 | plot.set_xticks(ind+width) | |
179 | plot.set_xticklabels( group_labels ) | |
180 | ||
181 | fontP = FontProperties() | |
182 | fontP.set_size('small') | |
183 | plot.legend( [r[0] for r in rects], bar_labels, prop=fontP, | |
184 | loc = 'center right', bbox_to_anchor = (1.0, legendHeight)) | |
185 | ||
186 | def autolabel(rects): | |
187 | # attach some text labels | |
188 | for rect in rects: | |
189 | height = rect.get_height() | |
190 | if np.isnan(height): | |
191 | height = 0.0 | |
192 | plot.text(rect.get_x()+rect.get_width()/2., height+20, '%d'%int(height), | |
193 | ha='center', va='bottom', size='7') | |
194 | ||
195 | for rect in rects: | |
196 | autolabel(rect) | |
197 | ||
198 | # Adjust axis sizes | |
199 | axis = list(plot.axis()) | |
200 | axis[0] = -width # Make sure left side has enough for bar | |
201 | #axis[1] = axis[1] * 1.20 # Add 20% to the right to make sure it fits | |
202 | axis[2] = 0 # Make y-axis start at 0 | |
203 | axis[3] = axis[3] * 1.10 # Add 10% to the top | |
204 | plot.axis(axis) | |
205 | ||
206 | plt.show() |