Форматирование десятичного выравнивания в Python - PullRequest
8 голосов
/ 22 июня 2009

Это должно быть легким.

Вот мой массив (точнее, метод генерации репрезентативных тестовых массивов):

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

Мне нужен список строк, в которых '\ n'.join (list_o_strings) будет печататься:

   79.9914
   20.8
  394.0
 4661.0
    5.0
 1725.751
  391.5
19061.0
11624.7
  353.92

Я хочу расположить пробел слева и справа (но не больше, чем необходимо).

Я хочу ноль после десятичной дроби, если это все, что после десятичной дроби.

Я не хочу научных обозначений.

.. и я не хочу терять какие-либо значимые цифры. (в 353.98000000000002 значение 2 не имеет значения)

Да, приятно хотеть ..

Python 2.5 * %g, %fx.x и т. Д. Либо сбивают с толку, либо не могут этого сделать. Я еще не пробовал import decimal. Я не вижу, чтобы NumPy тоже это делал (хотя array.__str__ и array.__repr__ выровнены по десятичному разряду (но иногда возвращают научное значение).

Да, и скорость имеет значение. Я имею дело с большими массивами здесь.

Мои текущие подходы к решению:

  1. str (a) и разобрать скобки NumPy
  2. для str (e) каждого элемента в массиве и разделения ('.'), Затем дополнения и восстановления
  3. в a.astype ('S' + str (i)), где i - максимум (len (str (a))), затем pad

Кажется, должно быть какое-то готовое решение ... (но не обязательно)

Лучшее предложение не выполняется, если dtype равно 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

Ответы [ 2 ]

10 голосов
/ 22 июня 2009

Извините, но после тщательного изучения я не могу найти способ выполнить требуемую задачу без минимальной постобработки (чтобы убрать конечные нули, которые вы не хотите видеть); что-то вроде:

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

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

print '\n'.join(thelist)

быстр и лаконичен, но нарушает ваше ограничение быть готовым - вместо этого это модульная комбинация общего форматирования (которая почти делает то, что вы хотите, но оставляет конечный ноль, который вы хотите скрыть) и RE для удаления нежелательных конечных нулей. Практически, я думаю, что это именно то, что вам нужно, но ваши условия, как указано, я считаю, слишком ограничены.

Редактировать : исходный вопрос был отредактирован, чтобы указать более значимые цифры, не требовать дополнительного начального пробела сверх того, что требуется для наибольшего числа, и предоставить новый пример (где мое предыдущее предложение, выше, не соответствовать желаемому результату). Работу по удалению начальных пробелов, которая является общей для набора строк, лучше всего выполнять с textwrap.dedent - но это работает с одной строкой (с символами новой строки), в то время как требуемый вывод представляет собой список строк. Нет проблем, мы просто соединим строки, выделим их и разделим снова:

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)

излучает:

      550.056103
     6773.83566
   601001.513
355425142.0
   707254.875
      883.174744
       82.232051
425076609.0
 62866263.5
      156.503068
2 голосов
/ 22 июня 2009

Форматирование строки Pythons может выводить только необходимые десятичные дроби (с% g) или использовать фиксированный набор десятичных знаков (с% f). Однако вы хотите распечатать только необходимые десятичные дроби, за исключением случаев, когда число является целым числом, тогда вам нужен один десятичный знак, и это делает его сложным.

Это означает, что вы получите что-то вроде:

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)

Сначала будет создана строка с 1 десятичным знаком, если значение является целым числом, или она будет печататься с автоматическими десятичными знаками (но только до 10 чисел), если это не дробное число. Наконец, он напечатает его, настроенный таким образом, чтобы выровнять десятичную точку.

Вероятно, однако, numpy на самом деле делает то, что вы хотите, потому что вы обычно хотите, чтобы он был в экспоненциальном режиме, если он слишком длинный.

...