blob: b3b82f094ab6a966cb48608b44f4f0affccdb3c5 [file] [log] [blame]
asharif265cda32013-02-15 21:57:02 +00001import numpy
2import re
3
4def IsFloat(text):
5 if text is None:
6 return False
7 try:
8 float(text)
9 return True
10 except ValueError:
11 return False
12
13
14def RemoveTrailingZeros(x):
15 ret = x
16 ret = re.sub("\.0*$", "", ret)
17 ret = re.sub("(\.[1-9]*)0+$", "\\1", ret)
18 return ret
19
20
21def HumanizeFloat(x, n=2):
22 if not IsFloat(x):
23 return x
24 digits = re.findall("[0-9.]", str(x))
25 decimal_found = False
26 ret = ""
27 sig_figs = 0
28 for digit in digits:
29 if digit == ".":
30 decimal_found = True
31 elif sig_figs != 0 or digit != "0":
32 sig_figs += 1
33 if decimal_found and sig_figs >= n:
34 break
35 ret += digit
36 return ret
37
38
39def GetNSigFigs(x, n=2):
40 if not IsFloat(x):
41 return x
42 my_fmt = "%." + str(n-1) + "e"
43 x_string = my_fmt % x
44 f = float(x_string)
45 return f
46
47
48def GetFormattedPercent(baseline, other, bad_result="--"):
49 result = "%8s" % GetPercent(baseline, other, bad_result)
50 return result
51
52
53def GetPercent(baseline, other, bad_result="--"):
54 result = bad_result
55 if IsFloat(baseline) and IsFloat(other):
56 try:
57 pct = (float(other)/float(baseline) - 1) * 100
58 result = "%+1.1f" % pct
59 except ZeroDivisionError:
60 pass
61 return result
62
63
64def FitString(text, length):
65 if len(text) == length:
66 return text
67 elif len(text) > length:
68 return text[-length:]
69 else:
70 fmt = "%%%ds" % length
71 return fmt % text
72
73
74class TableFormatter(object):
75 def __init__(self):
76 self.d = "\t"
77 self.bad_result = "x"
78
79 def GetTablePercents(self, table):
80 # Assumes table is not transposed.
81 pct_table = []
82
83 pct_table.append(table[0])
84 for i in range(1, len(table)):
85 row = []
86 row.append(table[i][0])
87 for j in range (1, len(table[0])):
88 c = table[i][j]
89 b = table[i][1]
90 p = GetPercent(b, c, self.bad_result)
91 row.append(p)
92 pct_table.append(row)
93 return pct_table
94
95 def FormatFloat(self, c, max_length=8):
96 if not IsFloat(c):
97 return c
98 f = float(c)
99 ret = HumanizeFloat(f, 4)
100 ret = RemoveTrailingZeros(ret)
101 if len(ret) > max_length:
102 ret = "%1.1ef" % f
103 return ret
104
105 def TransposeTable(self, table):
106 transposed_table = []
107 for i in range(len(table[0])):
108 row = []
109 for j in range(len(table)):
110 row.append(table[j][i])
111 transposed_table.append(row)
112 return transposed_table
113
114 def GetTableLabels(self, table):
115 ret = ""
116 header = table[0]
117 for i in range(1, len(header)):
118 ret += "%d: %s\n" % (i, header[i])
119 return ret
120
121 def GetFormattedTable(self, table, transposed=False,
122 first_column_width=30, column_width=14,
123 percents_only=True,
124 fit_string=True):
125 o = ""
126 pct_table = self.GetTablePercents(table)
127 if transposed == True:
128 table = self.TransposeTable(table)
129 pct_table = self.TransposeTable(table)
130
131 for i in range(0, len(table)):
132 for j in range(len(table[0])):
133 if j == 0:
134 width = first_column_width
135 else:
136 width = column_width
137
138 c = table[i][j]
139 p = pct_table[i][j]
140
141 # Replace labels with numbers: 0... n
142 if IsFloat(c):
143 c = self.FormatFloat(c)
144
145 if IsFloat(p) and not percents_only:
146 p = "%s%%" % p
147
148 # Print percent values side by side.
149 if j != 0:
150 if percents_only:
151 c = "%s" % p
152 else:
153 c = "%s (%s)" % (c, p)
154
155 if i == 0 and j != 0:
156 c = str(j)
157
158 if fit_string:
159 o += FitString(c, width) + self.d
160 else:
161 o += c + self.d
162 o += "\n"
163 return o
164
165 def GetGroups(self, table):
166 labels = table[0]
167 groups = []
168 group_dict = {}
169 for i in range(1, len(labels)):
170 label = labels[i]
171 stripped_label = self.GetStrippedLabel(label)
172 if stripped_label not in group_dict:
173 group_dict[stripped_label] = len(groups)
174 groups.append([])
175 groups[group_dict[stripped_label]].append(i)
176 return groups
177
178 def GetSummaryTableValues(self, table):
179 # First get the groups
180 groups = self.GetGroups(table)
181
182 summary_table = []
183
184 labels = table[0]
185
186 summary_labels = ["Summary Table"]
187 for group in groups:
188 label = labels[group[0]]
189 stripped_label = self.GetStrippedLabel(label)
190 group_label = "%s (%d runs)" % (stripped_label, len(group))
191 summary_labels.append(group_label)
192 summary_table.append(summary_labels)
193
194 for i in range(1, len(table)):
195 row = table[i]
196 summary_row = [row[0]]
197 for group in groups:
198 group_runs = []
199 for index in group:
200 group_runs.append(row[index])
201 group_run = self.AggregateResults(group_runs)
202 summary_row.append(group_run)
203 summary_table.append(summary_row)
204
205 return summary_table
206
207 # Drop N% slowest and M% fastest numbers, and return arithmean of
208 # the remaining.
209 @staticmethod
210 def AverageWithDrops(numbers, slow_percent=20, fast_percent=20):
211 sorted_numbers = list(numbers)
212 sorted_numbers.sort()
213 num_slow = int(slow_percent/100.0 * len(sorted_numbers))
214 num_fast = int(fast_percent/100.0 * len(sorted_numbers))
215 sorted_numbers = sorted_numbers[num_slow:]
216 if num_fast:
217 sorted_numbers = sorted_numbers[:-num_fast]
218 return numpy.average(sorted_numbers)
219
220 @staticmethod
221 def AggregateResults(group_results):
222 ret = ""
223 if not group_results:
224 return ret
225 all_floats = True
226 all_passes = True
227 all_fails = True
228 for group_result in group_results:
229 if not IsFloat(group_result):
230 all_floats = False
asharif0a5843b2013-02-15 21:59:10 +0000231 if group_result != "PASSED":
asharif265cda32013-02-15 21:57:02 +0000232 all_passes = False
asharif0a5843b2013-02-15 21:59:10 +0000233 if group_result != "FAILED":
asharif265cda32013-02-15 21:57:02 +0000234 all_fails = False
235 if all_floats == True:
236 float_results = [float(v) for v in group_results]
237 ret = "%f" % TableFormatter.AverageWithDrops(float_results)
238 # Add this line for standard deviation.
239### ret += " %f" % numpy.std(float_results)
240 elif all_passes == True:
241 ret = "ALL_PASS"
242 elif all_fails == True:
243 ret = "ALL_FAILS"
244 return ret
245
246 @staticmethod
247 def GetStrippedLabel(label):
248 return re.sub("\s*\S+:\S+\s*", "", label)
249### return re.sub("\s*remote:\S*\s*i:\d+$", "", label)
250
251 @staticmethod
252 def GetLabelWithIteration(label, iteration):
253 return "%s i:%d" % (label, iteration)