Как я могу предотвратить округление моих чисел csv.DictWriter () или writerow ()? - PullRequest
10 голосов
/ 10 декабря 2011

У меня есть словарь, который я хочу записать в CSV-файл, но значения в словаре округляются, когда я записываю их в файл.Я хочу сохранить максимальную точность.

Где происходит округление и как я могу его предотвратить?

Что я сделал

Я следовал примеру DictWriter здесь и я использую Python 2.6.1 на Mac (10.6 - Snow Leopard).


# my import statements
import sys
import csv

Вот что содержит мой словарь (d):

>>> d = runtime.__dict__
>>> d
{'time_final': 1323494016.8556759,
'time_init': 1323493818.0042379,
'time_lapsed': 198.85143804550171}

Значения действительно являются числами с плавающей запятой:

>>> type(runtime.time_init)
<type 'float'>

Затем я настраиваю свой писатель и записываю заголовок и значения:

f = open(log_filename,'w')
fieldnames = ('time_init', 'time_final', 'time_lapsed')
myWriter = csv.DictWriter(f, fieldnames=fieldnames)
headers = dict( (n,n) for n in fieldnames )
myWriter.writerow(headers)
myWriter.writerow(d)
f.close()

Но когда я смотрю в выходной файл, я получаю округлениечисла (т. е. числа с плавающей запятой):

time_init,time_final,time_lapsed
1323493818.0,1323494016.86,198.851438046

Ответы [ 3 ]

5 голосов
/ 10 декабря 2011

Похоже, csv использует float .__ str __ вместо float .__ repr __ :

>>> print repr(1323494016.855676)
1323494016.855676
>>> print str(1323494016.855676)
1323494016.86

Глядя на csv source , это похоже на аппаратное поведение.Обходной путь - привести все значения с плавающей точкой к их repr до того, как csv доберется до него.Используйте что-то вроде: d = dict((k, repr(v)) for k, v in d.items()).

Вот отработанный пример:

import sys, csv

d = {'time_final': 1323494016.8556759,
     'time_init': 1323493818.0042379,
     'time_lapsed': 198.85143804550171
}

d = dict((k, repr(v)) for k, v in d.items())

fieldnames = ('time_init', 'time_final', 'time_lapsed')
myWriter = csv.DictWriter(sys.stdout, fieldnames=fieldnames)
headers = dict( (n,n) for n in fieldnames )
myWriter.writerow(headers)
myWriter.writerow(d)

Этот код производит следующий вывод:

time_init,time_final,time_lapsed
1323493818.0042379,1323494016.8556759,198.85143804550171

Более усовершенствованный подходбудет заботиться только о замене поплавков:

d = dict((k, (repr(v) if isinstance(v, float) else str(v))) for k, v in d.items())

Обратите внимание, я только что исправил эту проблему для Py2.7.3, поэтому в будущем это не должно быть проблемой.См http://hg.python.org/cpython/rev/bf7329190ca6

2 голосов
/ 10 декабря 2011

Это известная ошибка ^ H ^ H ^ Hfeature. Согласно документам :

"" "... значение None записывается как пустая строка. [Snip] Все другие нестроковые данные перед записью преобразуются в str ()." ""

Не полагайтесь на преобразования по умолчанию. Используйте repr() для поплавков. unicode объекты нуждаются в специальной обработке; см. руководство Проверьте, примет ли получатель файла формат по умолчанию datetime.x объектов для x in (datetime, date, time, timedelta).

Обновление

Для объектов с плавающей точкой "%f" % value является , а не хорошей заменой repr(value). Критерий - может ли потребитель файла воспроизвести исходный объект с плавающей точкой. repr(value) гарантирует это. "%f" % value нет.

# Python 2.6.6
>>> nums = [1323494016.855676, 1323493818.004238, 198.8514380455017, 1.0 / 3]
>>> for v in nums:
...     rv = repr(v)
...     fv = "%f" % v
...     sv = str(v)
...     print rv, float(rv) == v, fv, float(fv) == v, sv, float(sv) == v
...
1323494016.8556759 True 1323494016.855676 True 1323494016.86 False
1323493818.0042379 True 1323493818.004238 True 1323493818.0 False
198.85143804550171 True 198.851438 False 198.851438046 False
0.33333333333333331 True 0.333333 False 0.333333333333 False

Обратите внимание, что в приведенном выше примере отображается при проверке полученных строк, что ни один из %f случаев не работал. До версии 2.7 Python repr всегда использовал 17 значащих десятичных цифр. В 2.7 это было изменено на использование минимального количества цифр, которое все еще гарантировало float(repr(v)) == v. Разница составляет , а не ошибка округления.

# Python 2.7 output
1323494016.855676 True 1323494016.855676 True 1323494016.86 False
1323493818.004238 True 1323493818.004238 True 1323493818.0 False
198.8514380455017 True 198.851438 False 198.851438046 False
0.3333333333333333 True 0.333333 False 0.333333333333 False

Обратите внимание на улучшенные результаты repr() в первом столбце выше.

Обновление 2 в ответ на комментарий "" "И спасибо за информацию о Python 2.7. К сожалению, я ограничен до 2.6.2 (работает на целевом компьютере, который не может быть обновлен) Но я буду помнить это для будущих сценариев. "" "

Это не имеет значения. float('0.3333333333333333') == float('0.33333333333333331') производит True на всех версиях Python. Это означает, что вы можете написать свой файл на 2.7, и он будет читать то же самое на 2.6, или наоборот. В точности нет изменений в том, что repr(a_float_object) производит.

1 голос
/ 10 декабря 2011

Это работает, но это, вероятно, не самый лучший / самый эффективный способ:

>>> f = StringIO()
>>> w = csv.DictWriter(f,fieldnames=headers)
>>> w.writerow(dict((k,"%f"%d[k]) for k in d.keys()))
>>> f.getvalue()
'1323493818.004238,1323494016.855676,198.851438\r\n'
...