普通文本  |  259行  |  6.34 KB

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)