2009-06-22 11 views
8

Este debería ser fácil.Formato de alineación decimal en Python

Aquí es mi array (más bien, un método de generación de matrices de ensayo representativos):

>>> ri = numpy.random.randint 
>>> ri2 = lambda x: ''.join(ri(0,9,x).astype('S')) 
>>> a = array([float(ri2(x)+ '.' + ri2(y)) for x,y in ri(1,10,(10,2))]) 
>>> a 
array([ 7.99914000e+01, 2.08000000e+01, 3.94000000e+02, 
     4.66100000e+03, 5.00000000e+00, 1.72575100e+03, 
     3.91500000e+02, 1.90610000e+04, 1.16247000e+04, 
     3.53920000e+02]) 

Quiero una lista de cadenas, donde '\ n'.join (list_o_strings) imprimirían:

79.9914 
    20.8 
    394.0 
4661.0 
    5.0 
1725.751 
    391.5 
19061.0 
11624.7 
    353.92 

Quiero espaciar la almohadilla a la izquierda y a la derecha (pero no más de lo necesario).

Quiero un cero después del decimal si eso es todo lo que está después del decimal.

No deseo notación científica.

..y no quiero perder ningún dígito significativo. (En la 353,98000000000002 2 no es significativo)

Sí, es bueno querer ..

Python 2.5 de %g, %fx.x, etc me están confundiendo a cualquiera, o no puede hacerlo. No he intentado import decimal todavía. No puedo ver que NumPy lo hace bien (aunque, la array.__str__ y array.__repr__ están alineados decimal (pero a veces volver científica).

Ah, y la velocidad cuenta. Estoy tratando con grandes matrices aquí.

Mis principios de solución son: ('')

  1. a STR (a) y el análisis sintáctico de los soportes de NumPy
  2. a STR (e) cada elemento de la matriz y se dividió a continuación, almohadilla y reconstruir
  3. a a.astype ('S' + str (i)) donde i es el máximo (len (str (a))), y luego pad

Parece que debe haber un poco de la plataforma solución por ahí ... (aunque no necesario)

Top sugerencia cuando falla con float64 dtype es:

>>> a 
array([ 5.50056103e+02, 6.77383566e+03, 6.01001513e+05, 
     3.55425142e+08, 7.07254875e+05, 8.83174744e+02, 
     8.22320510e+01, 4.25076609e+08, 6.28662635e+07, 
     1.56503068e+02]) 
>>> ut0 = re.compile(r'(\d)0+$') 
>>> thelist = [ut0.sub(r'\1', "%12f" % x) for x in a] 
>>> print '\n'.join(thelist) 
    550.056103 
6773.835663 
601001.513 
355425141.8471 
707254.875038 
    883.174744 
    82.232051 
425076608.7676 
62866263.55 
    156.503068 
+0

Por favor, publique el código que no funciona. –

Respuesta

9

lo sentimos, pero después de una investigación exhaustiva que no puede encontrar ninguna manera de realizar la tarea que usted requiere sin un mínimo de postprocesamiento (para quitar los ceros finales que no desea ver); algo así como:

import re 
ut0 = re.compile(r'(\d)0+$') 

thelist = [ut0.sub(r'\1', "%12f" % x) for x in a] 

print '\n'.join(thelist) 

es rápida y concisa, pero rompe su limitación de ser "off-the-shelf" - es, en cambio, una combinación modular del formato general (que casi hace lo que quiere, pero las hojas cero final que desea ocultar) y un RE para eliminar ceros finales no deseados. Prácticamente, creo que hace exactamente lo que necesita, pero las condiciones que acabo de mencionar son, creo, excesivamente limitadas.

Editar pregunta: la pregunta original se editó para especificar dígitos más significativos, no requiere espacio adicional adicional más allá de lo requerido para el número más grande y proporciona un nuevo ejemplo (donde mi sugerencia anterior, no coincide con la deseada salida). El trabajo de eliminar espacios en blanco iniciales que es común a un grupo de cadenas se realiza mejor con textwrap.dedent, pero eso funciona en una sola cadena (con líneas nuevas) mientras que el resultado requerido es una lista de cadenas. No hay problema, sólo tendremos que poner las líneas juntas, dedent ellos, y los divide otra vez:

import re 
import textwrap 

a = [ 5.50056103e+02, 6.77383566e+03, 6.01001513e+05, 
     3.55425142e+08, 7.07254875e+05, 8.83174744e+02, 
     8.22320510e+01, 4.25076609e+08, 6.28662635e+07, 
     1.56503068e+02] 

thelist = textwrap.dedent(
     '\n'.join(ut0.sub(r'\1', "%20f" % x) for x in a)).splitlines() 

print '\n'.join(thelist) 

emite:

 550.056103 
    6773.83566 
    601001.513 
355425142.0 
    707254.875 
     883.174744 
     82.232051 
425076609.0 
62866263.5 
     156.503068 
+0

No puedo garantizar que% 12f no pierda dígitos significativos. (Hice una edición y cambié la forma en que se generaron las matrices de prueba para reflejar esto.) Si aumento a% 20 o más para garantizar esto, entonces simplemente hay demasiado relleno a la izquierda. (quiero que el valor más grande no tenga espacios iniciales) ¡También tomaré soluciones de respaldo del armario! – Paul

2

pitones formato de cadenas puede tanto imprimir sólo los decimales necesarios (con % g) o usa un conjunto fijo de decimales (con% f). Sin embargo, desea imprimir solo los decimales necesarios, excepto si el número es un número entero, entonces quiere un decimal, y eso lo hace complejo.

Esto significa que podría terminar con algo como:

def printarr(arr): 
    for x in array: 
     if math.floor(x) == x: 
      res = '%.1f' % x 
     else: 
      res = '%.10g' % x 
     print "%*s" % (15-res.find('.')+len(res), res) 

Esto crea primero una cadena, ya sea con 1 decimal, si el valor es un número entero, o se imprimirá con decimales automáticas (pero sólo hasta 10 números) si no es un número fraccionario. Finalmente lo imprimirá, ajustado para que el punto decimal esté alineado.

Probablemente, numpy realmente hace lo que quiere, porque normalmente quiere que esté en modo exponencial si es demasiado largo.