table_formatter.py revision f81680c018729fd4499e1e200d04b48c4b90127c
1import 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 231 if group_result != "PASSED": 232 all_passes = False 233 if group_result != "FAILED": 234 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) 254