def format_value(self, value, isToAlign=False, format_target="html"): """ Format a value nicely for human-readable output (including rounding). @param value: the value to format @param isToAlign: if True, spaces will be added to the returned String representation to align it to all other values in this column, correctly @param format_target the target the value should be formatted for @return: a formatted String representation of the given value. """ # Only format counts and measures if self.type.type != ColumnType.count and self.type.type != ColumnType.measure: return value if format_target not in POSSIBLE_FORMAT_TARGETS: raise ValueError('Unknown format target') if value is None or value == '': return '' # If the number ends with "s" or another unit, remove it. # Units should not occur in table cells, but in the table head. number_str = util.remove_unit(str(value).strip()) number = float(number_str) if isnan(number): return 'NaN' elif number == inf: return 'Inf' elif number == -inf: return '-Inf' # Apply the scale factor to the value if self.scale_factor is not None: number *= self.scale_factor number_of_significant_digits = self.number_of_significant_digits max_dec_digits = 0 if number_of_significant_digits is None and format_target is "tooltip_stochastic": return str(round(number, DEFAULT_TOOLTIP_PRECISION)) elif self.type.type == ColumnType.measure: if number_of_significant_digits is None and format_target is not "csv": number_of_significant_digits = DEFAULT_TIME_PRECISION max_dec_digits = self.type.max_decimal_digits if number_of_significant_digits is not None: current_significant_digits = _get_significant_digits(number_str) return _format_number(number, current_significant_digits, number_of_significant_digits, max_dec_digits, isToAlign, format_target) else: if number == float(number_str) or isnan(number) or isinf(number): # TODO remove as soon as scaled values are handled correctly return number_str if int(number) == number: number = int(number) return str(number)
def format_value(self, value, isToAlign=False, format_target="html"): """ Format a value nicely for human-readable output (including rounding). @param value: the value to format @param isToAlign: if True, spaces will be added to the returned String representation to align it to all other values in this column, correctly @param format_target the target the value should be formatted for @return: a formatted String representation of the given value. """ if format_target not in POSSIBLE_FORMAT_TARGETS: raise ValueError('Unknown format target') if value is None: return '' # If the number ends with "s" or another unit, remove it. # Units should not occur in table cells, but in the table head. number_str = util.remove_unit(str(value).strip()) try: number = float(number_str) except ValueError: # If value is no float, don't format it. return value # Apply the scale factor to the value if self.scale_factor is not None: number *= self.scale_factor number_of_significant_digits = self.number_of_significant_digits max_dec_digits = 0 if number_of_significant_digits is None and format_target is "tooltip_stochastic": return str(round(number, DEFAULT_TOOLTIP_PRECISION)) elif self.type.type == ColumnType.measure: if number_of_significant_digits is None and format_target is not "csv": number_of_significant_digits = DEFAULT_TIME_PRECISION max_dec_digits = self.type.max_decimal_digits if number_of_significant_digits is not None: current_significant_digits = _get_significant_digits(number_str) return _format_number(number, current_significant_digits, number_of_significant_digits, max_dec_digits, isToAlign, format_target) else: if number == float(number_str): # TODO remove as soon as scaled values are handled correctly return number_str if int(number) == number: number = int(number) return str(number)
def main(args=None): if args is None: args = sys.argv parser = argparse.ArgumentParser( fromfile_prefix_chars="@", description= """Create CSV tables for quantile plots with the results of a benchmark execution. The CSV tables are similar to those produced with table-generator, but have an additional first column with the index for the quantile plot, and they are sorted. The output is written to stdout. Part of BenchExec: https://github.com/sosy-lab/benchexec/""", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument( "result", metavar="RESULT", type=str, nargs="+", help="XML files with result produced by benchexec", ) parser.add_argument( "--correct-only", action="store_true", dest="correct_only", help= "only use correct results (recommended, implied if --score-based is used)", ) parser.add_argument( "--score-based", action="store_true", dest="score_based", help="create data for score-based quantile plot", ) parser.add_argument( "--sort-by", metavar="SORT", default="cputime", dest="column_identifier", type=str, help= "column identifier for sorting the values, e.g. 'cputime' or 'walltime'", ) options = parser.parse_args(args[1:]) # load results run_set_result = tablegenerator.RunSetResult.create_from_xml( options.result[0], tablegenerator.parse_results_file(options.result[0])) for results_file in options.result[1:]: run_set_result.append(results_file, tablegenerator.parse_results_file(results_file)) run_set_result.collect_data(options.correct_only or options.score_based) # select appropriate results if options.score_based: start_index = 0 index_increment = lambda run_result: run_result.score # noqa: E731 results = [] for run_result in run_set_result.results: if run_result.score is None: sys.exit("No score available for task {0}, " "cannot produce score-based quantile data.".format( run_result.task_id[0])) if run_result.category == result.CATEGORY_WRONG: start_index += run_result.score elif run_result.category == result.CATEGORY_MISSING: sys.exit("Property missing for task {0}, " "cannot produce score-based quantile data.".format( run_result.task_id[0])) elif run_result.category == result.CATEGORY_CORRECT: results.append(run_result) else: assert run_result.category in { result.CATEGORY_ERROR, result.CATEGORY_UNKNOWN, } else: start_index = 0 index_increment = lambda run_result: 1 # noqa: E731 if options.correct_only: results = [ run_result for run_result in run_set_result.results if run_result.category == result.CATEGORY_CORRECT ] else: results = run_set_result.results # sort data for quantile plot results.sort(key=get_extract_value_function(options.column_identifier)) # extract information which id columns should be shown for run_result in run_set_result.results: run_result.id = run_result.task_id relevant_id_columns = tablegenerator.select_relevant_id_columns(results) # write output index = start_index for run_result in results: index += index_increment(run_result) task_ids = ( task_id for task_id, show in zip(run_result.id, relevant_id_columns) if show) result_values = (util.remove_unit(value or "") for value in run_result.values) print(*itertools.chain([index], task_ids, result_values), sep="\t")
def _get_column_type_heur(column, column_values): if column.title == "status": return ColumnType.status column_type = column.type or None if column_type and column_type.type == ColumnType.measure: column_type = ColumnMeasureType(0) column_unit = column.unit # May be None column_source_unit = column.source_unit # May be None column_scale_factor = column.scale_factor # May be None column_max_int_digits = 0 column_max_dec_digits = 0 column_has_numbers = False column_has_decimal_numbers = False if column_unit: explicit_unit_defined = True else: explicit_unit_defined = False if column_scale_factor is None: explicit_scale_defined = False else: explicit_scale_defined = True for value in column_values: if value is None or value == "": continue value_match = REGEX_MEASURE.match(str(value)) # As soon as one row's value is no number, the column type is 'text' if value_match is None: return ColumnType.text else: column_has_numbers = True curr_column_unit = value_match.group(GROUP_UNIT) # If the units in two different rows of the same column differ, # 1. Raise an error if an explicit unit is defined by the displayUnit attribute # and the unit in the column cell differs from the defined sourceUnit, or # 2. Handle the column as 'text' type, if no displayUnit was defined for the column's values. # In that case, a unit different from the definition of sourceUnit does not lead to an error. if curr_column_unit: if column_source_unit is None and not explicit_scale_defined: column_source_unit = curr_column_unit elif column_source_unit != curr_column_unit: raise util.TableDefinitionError( "Attribute sourceUnit different from real source unit: {} and {} (in column {})" .format(column_source_unit, curr_column_unit, column.title)) if column_unit and curr_column_unit != column_unit: if explicit_unit_defined: _check_unit_consistency(curr_column_unit, column_source_unit, column) else: return ColumnType.text else: column_unit = curr_column_unit if column_scale_factor is None: column_scale_factor = _get_scale_factor( column_unit, column_source_unit, column) # Compute the number of decimal digits of the current value, considering the number of significant # digits for this column. # Use the column's scale factor for computing the decimal digits of the current value. # Otherwise, they might be different from output. scaled_value = float(util.remove_unit( str(value))) * column_scale_factor # Due to the scaling operation above, floats in the exponent notation may be created. Since this creates # special cases, immediately convert the value back to decimal notation. if value_match.group(GROUP_DEC_PART): # -1 since GROUP_DEC_PART includes the decimal point dec_digits_before_scale = len( value_match.group(GROUP_DEC_PART)) - 1 else: dec_digits_before_scale = 0 max_number_of_dec_digits_after_scale = max( 0, dec_digits_before_scale - ceil(log10(column_scale_factor))) scaled_value = "{0:.{1}f}".format( scaled_value, max_number_of_dec_digits_after_scale) scaled_value_match = REGEX_MEASURE.match(scaled_value) curr_dec_digits = _get_decimal_digits( scaled_value_match, column.number_of_significant_digits) column_max_dec_digits = max(column_max_dec_digits, curr_dec_digits) curr_int_digits = _get_int_digits(scaled_value_match) column_max_int_digits = max(column_max_int_digits, curr_int_digits) if (scaled_value_match.group(GROUP_DEC_PART) is not None or value_match.group(GROUP_DEC_PART) is not None or scaled_value_match.group(GROUP_SPECIAL_FLOATS_PART) is not None): column_has_decimal_numbers = True if not column_has_numbers: # only empty values return ColumnType.text if (column_has_decimal_numbers or column_max_dec_digits or int(column_scale_factor) != column_scale_factor # non-int scaling factor ): column_type = ColumnMeasureType(column_max_dec_digits) else: column_type = ColumnType.count column_width = column_max_int_digits if column_max_dec_digits: column_width += column_max_dec_digits + 1 return ( column_type, column_unit, column_source_unit, column_scale_factor, column_width, )
def format_value(self, value, format_target): """ Format a value nicely for human-readable output (including rounding). @param value: the value to format @param format_target the target the value should be formatted for @return: a formatted String representation of the given value. """ # Only format counts and measures if self.type.type != ColumnType.count and self.type.type != ColumnType.measure: return value if format_target not in POSSIBLE_FORMAT_TARGETS: raise ValueError("Unknown format target") if value is None or value == "": return "" if isinstance(value, str): # If the number ends with "s" or another unit, remove it. # Units should not occur in table cells, but in the table head. number_str = util.remove_unit(value.strip()) number = Decimal(number_str) elif isinstance(value, Decimal): number = value number_str = util.print_decimal(number) else: raise TypeError("Unexpected number type " + str(type(value))) if number.is_nan(): return "NaN" elif number == inf: return "Inf" elif number == -inf: return "-Inf" # Apply the scale factor to the value if self.scale_factor is not None: number *= self.scale_factor assert number.is_finite() if (self.number_of_significant_digits is None and self.type.type != ColumnType.measure and format_target == "tooltip_stochastic"): # Column of type count (integral values) without specified sig. digits. # However, we need to round values like stdev, so we just round somehow. return util.print_decimal(round(number, DEFAULT_TOOLTIP_PRECISION)) number_of_significant_digits = self.get_number_of_significant_digits( format_target) max_dec_digits = (self.type.max_decimal_digits if self.type.type == ColumnType.measure else 0) if number_of_significant_digits is not None: current_significant_digits = _get_significant_digits(number_str) return _format_number( number, current_significant_digits, number_of_significant_digits, max_dec_digits, format_target, ) else: return util.print_decimal(number)