2009-06-22 6 views
8

Cette devrait être être facile.Mise en forme de l'alignement décimal en Python

Voici mon tableau (plutôt une méthode de génération des tableaux de test représentatifs):

>>> 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]) 

Je veux une liste de chaînes où \ n'.join (list_o_strings) imprimeraient:

79.9914 
    20.8 
    394.0 
4661.0 
    5.0 
1725.751 
    391.5 
19061.0 
11624.7 
    353.92 

Je veux à l'espace pad à gauche et le droit (mais pas plus que nécessaire).

Je veux un zéro après la virgule si c'est tout ce qui est après la virgule.

Je ne veux pas de notation scientifique.

..et je ne veux pas perdre de chiffres significatifs. (En 353,98000000000002 2 est non significatif)

Oui, il est bon de vouloir ..

Python 2.5 de %g, %fx.x, etc. sont soit moi befuddling, ou ne peut le faire. Je n'ai pas encore essayé import decimal. Je ne vois pas que NumPy-t-il, soit (bien que, la array.__str__ et array.__repr__ sont alignés décimales (mais retournent parfois scientifique).

Oh, et la vitesse compte. Je fais face à de grands tableaux ici.

Mes approches actuelles de la solution sont les suivants:

  1. à str (a) et analyser hors des supports de numpy
  2. à str (e) chaque élément de la matrice et split ('') puis pad et reconstruire
  3. à a.astype ('S' + str (i)) où i est le maximum (len (str (a))), puis pad

Il semble qu'il devrait y en avoir dans le commerce solution là-bas ... (mais pas obligatoire)

suggestion Top échoue lorsque dtype est float64:

>>> 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

Veuillez poster le code qui ne fonctionne pas. –

Répondre

9

Désolé, mais après enquête approfondie je ne peux pas trouver un moyen d'accomplir la tâche dont vous avez besoin sans un minimum de post-traitement (pour supprimer les zéros de fin que vous ne voulez pas voir); quelque chose comme:

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

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

print '\n'.join(thelist) 

est rapide et concise, mais votre casse contrainte d'être « off-the-shelf » - il est, au contraire, une combinaison modulaire de mise en forme générale (qui fait presque ce que vous voulez, mais les feuilles zéro final que vous voulez cacher) et un RE pour supprimer les zéros fin indésirables. En pratique, je pense que c'est exactement ce dont vous avez besoin, mais vos conditions, comme je l'ai dit, sont surestimées.

Modifier: a été modifié question initiale de préciser les chiffres les plus importants, ne nécessitent pas d'espace de premier plan supplémentaire au-delà de ce qui est nécessaire pour le plus grand nombre, et de fournir un nouvel exemple (où ma suggestion précédente, ci-dessus, ne correspond pas au désiré sortie). Le travail de suppression des espaces de début qui est commun à un ensemble de chaînes est mieux effectué avec textwrap.dedent - mais cela fonctionne sur une seule chaîne (avec des retours à la ligne) alors que la sortie requise est une liste de chaînes. Pas de problème, nous allons simplement mettre ensemble les lignes, dedent les, et les séparer à nouveau:

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) 

émet:

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

Je ne peux pas garantir que% 12f ne perdra pas de chiffres significatifs. (J'ai fait une modification et changé la façon dont mes tableaux de test sont générés pour refléter ceci.) Si j'augmente à 20% ou plus pour le garantir, alors il y a simplement trop de remplissage à gauche. (Je veux la plus grande valeur pour ne pas avoir d'espaces de premier plan) Je vais prendre des solutions de back-of-the-placard aussi! – Paul

2

Pythons mise en forme de chaîne peut à la fois imprimer uniquement les décimales nécessaires (avec % g) ou utilisez un ensemble fixe de décimales (avec% f). Cependant, vous ne voulez imprimer que les décimales nécessaires, sauf si le nombre est un nombre entier, alors vous voulez un décimal, et cela le rend complexe.

Cela signifie que vous finiriez avec quelque chose comme:

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) 

Ce sera d'abord créer une chaîne, soit avec 1 décimale, si la valeur est un nombre entier, ou il sera imprimé avec des décimales automatiques (mais seulement jusqu'à 10 chiffres) si ce n'est pas un nombre fractionnaire. Enfin, il va l'imprimer, ajusté de sorte que le point décimal sera aligné.

Probablement, cependant, numpy fait réellement ce que vous voulez, parce que vous voulez généralement qu'il soit en mode exponentiel si c'est trop long.