[PYTHON MATRIX-SIG] Final (!?) pretty printer

Konrad HINSEN hinsenk@ere.umontreal.ca
Thu, 8 Feb 1996 18:49:21 -0500


Here it is: the fully functional pretty printer for Python arrays.
Unless there are bugs to be fixed or complaints or requests for
reasonable new features, I'd like to leave it at this stage. Line
wrapping is now implemented and respects the maximum line width
whenever possible, but it still puts at least one element on each
line.

Note that continuation lines are indented by at least 6 characters
in such a way as to make sure that their items do *not* align with
the main lines. This makes visual parsing easier.

And now, as you expected, the code:


def arrayToString(a, max_line_width = None, precision = None):
    if not max_line_width:
	try:
	    max_line_width = sys.output_line_width
	except AttributeError:
	    max_line_width = 77
    if not precision:
	try:
	    precision = sys.float_output_precision
	except AttributeError:
	    precision = 8
    if a.contiguous():
	data = a.reshape(None)
    else:
	data = a.copy().reshape(None)
    type = a.typecode()
    items_per_line = a.shape[-1]
    if type == 'b' or type == '1' or type == 's' or type == 'i' \
       or type == 'l':
	max_str_len = max(len(str(maximum.reduce(data))),
			  len(str(minimum.reduce(data))))
	format = '%' + str(max_str_len) + 'd'
	item_length = max_str_len+1
	format_function = lambda x, f = format: _formatInteger(x, f)
    elif type == 'f' or type == 'd':
	format, item_length = _floatFormat(data, precision)
	format_function = lambda x, f = format: _formatFloat(x, f)
    elif type == 'F' or type == 'D':
	real_format, real_item_length = _floatFormat(data.real, precision,
						     sign=0)
	imag_format, imag_item_length = _floatFormat(data.imaginary, precision,
						     sign=1)
	item_length = real_item_length + imag_item_length + 2
	format_function = lambda x, f1 = real_format, f2 = imag_format: \
			  _formatComplex(x, f1, f2)
    elif type == 'c':
	format = '%s'
	item_length = 1
	format_function = lambda x, f = format: _formatCharacter(x, f)
    else:  # give up on 'O'
	return str(a)
    final_spaces = (type != 'c')
    line_width = item_length*items_per_line - final_spaces
    if line_width > max_line_width:
	indent = 6
	if indent == item_length:
	    indent = 8
	items_first = (max_line_width+final_spaces)/item_length
	if items_first < 1: items_first = 1
	items_continuation = (max_line_width+final_spaces-indent)/item_length
	if items_continuation < 1: items_continuation = 1
	line_width = max(item_length*items_first,
			 item_length*items_continuation+indent) - final_spaces
	number_of_lines = 1 + (items_per_line-items_first +
			       items_continuation-1)/items_continuation
	line_format = (number_of_lines, items_first, items_continuation,
		       indent, line_width)
    else:
	line_format = (1, items_per_line, 0, 0, line_width)
    return _arrayToString(a, format_function, len(a.shape), line_format)[:-1]

def _floatFormat(data, precision, sign = 0):
    exp_format = 0
    non_zero = abs(compress(data.notEqual(0), data))
    if len(non_zero) == 0:
	max_val = 0.
	min_val = 0.
    else:
	max_val = maximum.reduce(non_zero)
	min_val = minimum.reduce(non_zero)
	if max_val >= 1.e12 or min_val < 0.0001 or max_val/min_val > 1000.:
	    exp_format = 1
    if exp_format:
	large_exponent = 0 < min_val < 1e-99 or max_val >= 1e100
	max_str_len = 8 + precision + large_exponent
	if sign: format = '%+'
	else: format = '%'
	format = format + str(max_str_len) + '.' + str(precision) + 'e'
	if large_exponent: format = format + '3'
	item_length = max_str_len + 1
    else:
	format = '%.' + str(precision) + 'f'
	precision = min(precision, apply(max, tuple(map(lambda x, p=precision,
							f=format: _digits(x,p,f),
							data))))
	max_str_len = len(str(int(max_val))) + precision + 2
	if sign: format = '%#+'
	else: format = '%#'
	format = format + str(max_str_len) + '.' + str(precision) + 'f'
	item_length = max_str_len + 1
    return (format, item_length)

def _digits(x, precision, format):
    s = format % x
    zeros = len(s)
    while s[zeros-1] == '0': zeros = zeros-1
    return precision-len(s)+zeros

def _arrayToString(a, format_function, rank, line_format):
    if rank == 0:
	return str(a[0])
    elif rank == 1:
	s = ''
	items = line_format[1]
	indent = 0
	index = 0
	for j in range(line_format[0]):
	    s = s + indent * ' '
	    for i in range(items):
		s = s + format_function(a[index])
		index = index + 1
		if index == a.shape[0]: break
	    if s[-1] == ' ': s = s[:-1]
	    s = s + '\n'
	    items = line_format[2]
	    indent = line_format[3]
    else:
	s = ''
	for i in range(a.shape[0]-1):
	    s = s + _arrayToString(a[i], format_function, rank-1, line_format)
	    if rank == 3:
		s = s + '\n'
	    elif rank > 3:
		s = s + (rank-3)*(line_format[4]*'-'+'\n')
	s = s + _arrayToString(a[a.shape[0]-1], format_function,
			       rank-1, line_format)
    return s

def _formatInteger(x, format):
    return format % x + ' '

def _formatFloat(x, format, strip_zeros = 1):
    if format[-1] == '3':
	format = format[:-1]
	s = format % x
	third = s[-3]
	if third == '+' or third == '-':
	    s = s[1:-2] + '0' + s[-2:]
    elif format[-1] == 'f':
	s = format % x
	if strip_zeros:
	    zeros = len(s)
	    while s[zeros-1] == '0': zeros = zeros-1
	    s = s[:zeros] + (len(s)-zeros)*' '
    else:
	s = format % x
    return s + ' '

def _formatComplex(x, real_format, imag_format):
    r = _formatFloat(x.real, real_format)[:-1]
    i = _formatFloat(x.imag, imag_format, 0)[:-1]
    spaces = 0
    while r[spaces] == ' ': spaces = spaces + 1
    r = spaces*' ' + '(' + r[spaces:]
    if imag_format[-1] == 'f':
	zeros = len(i)
	while i[zeros-1] == '0': zeros = zeros-1
	i = i[:zeros] + 'j)' + (len(i)-zeros)*' '
    else:
	i = i + 'j)'
    return r + i + ' '

def _formatCharacter(x, format):
    return format % x

-------------------------------------------------------------------------------
Konrad Hinsen                     | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie             | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal            | Fax:  +1-514-343-7586
C.P. 6128, succ. Centre-Ville     | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7             | Francais (phase experimentale)
-------------------------------------------------------------------------------

=================
MATRIX-SIG  - SIG on Matrix Math for Python

send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================