You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
6.3 KiB
258 lines
6.3 KiB
import numpy |
|
import re |
|
|
|
|
|
def IsFloat(text): |
|
if text is None: |
|
return False |
|
try: |
|
float(text) |
|
return True |
|
except ValueError: |
|
return False |
|
|
|
|
|
def RemoveTrailingZeros(x): |
|
ret = x |
|
ret = re.sub('\.0*$', '', ret) |
|
ret = re.sub('(\.[1-9]*)0+$', '\\1', ret) |
|
return ret |
|
|
|
|
|
def HumanizeFloat(x, n=2): |
|
if not IsFloat(x): |
|
return x |
|
digits = re.findall('[0-9.]', str(x)) |
|
decimal_found = False |
|
ret = '' |
|
sig_figs = 0 |
|
for digit in digits: |
|
if digit == '.': |
|
decimal_found = True |
|
elif sig_figs != 0 or digit != '0': |
|
sig_figs += 1 |
|
if decimal_found and sig_figs >= n: |
|
break |
|
ret += digit |
|
return ret |
|
|
|
|
|
def GetNSigFigs(x, n=2): |
|
if not IsFloat(x): |
|
return x |
|
my_fmt = '%.' + str(n - 1) + 'e' |
|
x_string = my_fmt % x |
|
f = float(x_string) |
|
return f |
|
|
|
|
|
def GetFormattedPercent(baseline, other, bad_result='--'): |
|
result = '%8s' % GetPercent(baseline, other, bad_result) |
|
return result |
|
|
|
|
|
def GetPercent(baseline, other, bad_result='--'): |
|
result = bad_result |
|
if IsFloat(baseline) and IsFloat(other): |
|
try: |
|
pct = (float(other) / float(baseline) - 1) * 100 |
|
result = '%+1.1f' % pct |
|
except ZeroDivisionError: |
|
pass |
|
return result |
|
|
|
|
|
def FitString(text, length): |
|
if len(text) == length: |
|
return text |
|
elif len(text) > length: |
|
return text[-length:] |
|
else: |
|
fmt = '%%%ds' % length |
|
return fmt % text |
|
|
|
|
|
class TableFormatter(object): |
|
|
|
def __init__(self): |
|
self.d = '\t' |
|
self.bad_result = 'x' |
|
|
|
def GetTablePercents(self, table): |
|
# Assumes table is not transposed. |
|
pct_table = [] |
|
|
|
pct_table.append(table[0]) |
|
for i in range(1, len(table)): |
|
row = [] |
|
row.append(table[i][0]) |
|
for j in range(1, len(table[0])): |
|
c = table[i][j] |
|
b = table[i][1] |
|
p = GetPercent(b, c, self.bad_result) |
|
row.append(p) |
|
pct_table.append(row) |
|
return pct_table |
|
|
|
def FormatFloat(self, c, max_length=8): |
|
if not IsFloat(c): |
|
return c |
|
f = float(c) |
|
ret = HumanizeFloat(f, 4) |
|
ret = RemoveTrailingZeros(ret) |
|
if len(ret) > max_length: |
|
ret = '%1.1ef' % f |
|
return ret |
|
|
|
def TransposeTable(self, table): |
|
transposed_table = [] |
|
for i in range(len(table[0])): |
|
row = [] |
|
for j in range(len(table)): |
|
row.append(table[j][i]) |
|
transposed_table.append(row) |
|
return transposed_table |
|
|
|
def GetTableLabels(self, table): |
|
ret = '' |
|
header = table[0] |
|
for i in range(1, len(header)): |
|
ret += '%d: %s\n' % (i, header[i]) |
|
return ret |
|
|
|
def GetFormattedTable(self, |
|
table, |
|
transposed=False, |
|
first_column_width=30, |
|
column_width=14, |
|
percents_only=True, |
|
fit_string=True): |
|
o = '' |
|
pct_table = self.GetTablePercents(table) |
|
if transposed == True: |
|
table = self.TransposeTable(table) |
|
pct_table = self.TransposeTable(table) |
|
|
|
for i in range(0, len(table)): |
|
for j in range(len(table[0])): |
|
if j == 0: |
|
width = first_column_width |
|
else: |
|
width = column_width |
|
|
|
c = table[i][j] |
|
p = pct_table[i][j] |
|
|
|
# Replace labels with numbers: 0... n |
|
if IsFloat(c): |
|
c = self.FormatFloat(c) |
|
|
|
if IsFloat(p) and not percents_only: |
|
p = '%s%%' % p |
|
|
|
# Print percent values side by side. |
|
if j != 0: |
|
if percents_only: |
|
c = '%s' % p |
|
else: |
|
c = '%s (%s)' % (c, p) |
|
|
|
if i == 0 and j != 0: |
|
c = str(j) |
|
|
|
if fit_string: |
|
o += FitString(c, width) + self.d |
|
else: |
|
o += c + self.d |
|
o += '\n' |
|
return o |
|
|
|
def GetGroups(self, table): |
|
labels = table[0] |
|
groups = [] |
|
group_dict = {} |
|
for i in range(1, len(labels)): |
|
label = labels[i] |
|
stripped_label = self.GetStrippedLabel(label) |
|
if stripped_label not in group_dict: |
|
group_dict[stripped_label] = len(groups) |
|
groups.append([]) |
|
groups[group_dict[stripped_label]].append(i) |
|
return groups |
|
|
|
def GetSummaryTableValues(self, table): |
|
# First get the groups |
|
groups = self.GetGroups(table) |
|
|
|
summary_table = [] |
|
|
|
labels = table[0] |
|
|
|
summary_labels = ['Summary Table'] |
|
for group in groups: |
|
label = labels[group[0]] |
|
stripped_label = self.GetStrippedLabel(label) |
|
group_label = '%s (%d runs)' % (stripped_label, len(group)) |
|
summary_labels.append(group_label) |
|
summary_table.append(summary_labels) |
|
|
|
for i in range(1, len(table)): |
|
row = table[i] |
|
summary_row = [row[0]] |
|
for group in groups: |
|
group_runs = [] |
|
for index in group: |
|
group_runs.append(row[index]) |
|
group_run = self.AggregateResults(group_runs) |
|
summary_row.append(group_run) |
|
summary_table.append(summary_row) |
|
|
|
return summary_table |
|
|
|
# Drop N% slowest and M% fastest numbers, and return arithmean of |
|
# the remaining. |
|
@staticmethod |
|
def AverageWithDrops(numbers, slow_percent=20, fast_percent=20): |
|
sorted_numbers = list(numbers) |
|
sorted_numbers.sort() |
|
num_slow = int(slow_percent / 100.0 * len(sorted_numbers)) |
|
num_fast = int(fast_percent / 100.0 * len(sorted_numbers)) |
|
sorted_numbers = sorted_numbers[num_slow:] |
|
if num_fast: |
|
sorted_numbers = sorted_numbers[:-num_fast] |
|
return numpy.average(sorted_numbers) |
|
|
|
@staticmethod |
|
def AggregateResults(group_results): |
|
ret = '' |
|
if not group_results: |
|
return ret |
|
all_floats = True |
|
all_passes = True |
|
all_fails = True |
|
for group_result in group_results: |
|
if not IsFloat(group_result): |
|
all_floats = False |
|
if group_result != 'PASSED': |
|
all_passes = False |
|
if group_result != 'FAILED': |
|
all_fails = False |
|
if all_floats == True: |
|
float_results = [float(v) for v in group_results] |
|
ret = '%f' % TableFormatter.AverageWithDrops(float_results) |
|
# Add this line for standard deviation. |
|
### ret += " %f" % numpy.std(float_results) |
|
elif all_passes == True: |
|
ret = 'ALL_PASS' |
|
elif all_fails == True: |
|
ret = 'ALL_FAILS' |
|
return ret |
|
|
|
@staticmethod |
|
def GetStrippedLabel(label): |
|
return re.sub('\s*\S+:\S+\s*', '', label) |
|
### return re.sub("\s*remote:\S*\s*i:\d+$", "", label) |
|
|
|
@staticmethod |
|
def GetLabelWithIteration(label, iteration): |
|
return '%s i:%d' % (label, iteration)
|
|
|