[PYTHON MATRIX-SIG] Pretty Printer

Konrad HINSEN hinsenk@ere.umontreal.ca
Wed, 7 Feb 1996 19:00:54 -0500


And here's another pretty printer. Float and complex formatting is
much improved, but there is still no line wrapping. Precision control
works, however.

Note that due to the problem in compress() complex arrays will still
often be printed with an inappropriate format. It is also still
impossible to print a float array whose elements are all zero. This
will be possible with one of Jim's upcoming bug fixes.

Line width and precision are currently taken from global variables in
sys if these exist and no explicit parameters are given. If the
general opinion favors Numeric, I'll be happy to change this.  For now
you should not forget to put an "import sys" in your Numeric.py.

Before I forget: Precision is indicated by an integer specifying
the number of decimal digits after the decimal point. If there
are good arguments for another specification, I'll be happy to
change this.

I am now quite happy with formatting of floats and complex numbers,
although I admit that I haven't done extensive tests. I hope
there won't be complaints about speed or lack thereof.


I'd appreciate any feedback on how you like the float formatting.  I
now remove all trailing zeros, although Python scalars get printed
with at least one. Would you prefer full compatibility in this case?
Or would you prefer to have no decimal point if there are no
fractional digits?


And now the code:

---------------------------------------------------------------------------
import sys

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:  # nothing for 'O'
	return str(a)
    line_width = item_length*items_per_line - (type != 'c')
    if line_width > max_line_width:
	pass
    return _arrayToString(a, format_function, len(a.shape), line_width)[:-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_width):
    if rank == 0:
	return str(a[0])
    elif rank == 1:
	s = ''
	for i in range(a.shape[0]):
	    s = s + format_function(a[i])
	if s[-1] == ' ': s = s[:-1]
	s = s + '\n'
    else:
	s = ''
	for i in range(a.shape[0]-1):
	    s = s + _arrayToString(a[i], format_function, rank-1, line_width)
	    if rank == 3:
		s = s + '\n'
	    elif rank > 3:
		s = s + (rank-3)*(line_width*'-'+'\n')
	s = s + _arrayToString(a[a.shape[0]-1], format_function,
			       rank-1, line_width)
    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
=================