【问题标题】:How can I prevent csv.DictWriter() or writerow() rounding my floats?如何防止 csv.DictWriter() 或 writerow() 舍入我的浮点数?
【发布时间】:2012-01-17 07:43:18
【问题描述】:

我有一本要写入 csv 文件的字典,但是当我将字典中的浮点数写入文件时,它们会被四舍五入。我想保持最大精度。

四舍五入发生在哪里,如何防止?

我做了什么

我关注了DictWriter example here,我在 Mac 上运行 Python 2.6.1(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

【问题讨论】:

  • 不是您的问题,但在 Python 2.x 中始终以 binary 模式打开 csv 文件('rb''wb'
  • 感谢您的提醒和审查我的问题。 +1

标签: python file-io csv floating-point rounding


【解决方案1】:

看起来 csv 使用的是 float.__str__ 而不是 float.__repr__

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

查看csv source,这似乎是一种硬连线行为。一种解决方法是在 csv 到达之前将所有浮点值转换为它们的 repr。使用类似: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

【讨论】:

  • 太棒了,完美运行!另外,感谢源链接。我仍在学习如何浏览 Python 文档,这对我来说有点尴尬。我还在字典中添加了一个日期时间,它被写为“datetime.date(2011, 12, 10)”,这是您提供的内容所期望的。我将把日期放在文件名中并以这种方式获取。极好的工作! +1
  • -1 很棒的大锤,工作不完美:“修复”浮动,破坏日期时间。
  • 是的,但我没有在原始问题中指定日期时间,因此原始答案不需要考虑。
  • 太棒了。我不知道这里的问题多久直接对源有贡献,但对于我的第一个问题,我很高兴我发布了它!过去几周我一直在使用 Python,我一直在使用它,现在我的更改(即您代表我所做的更改)已被合并到源代码中,我现在可以说我已经完全同化了通过 Python。 :) 再次感谢。
【解决方案2】:

这可行,但它可能不是最好/最有效的方法:

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

【讨论】:

  • 看起来你的浮点数也是四舍五入的,除非那是 getvalue() 的产物。我会调查的。
  • 与getvalue无关。 %f 格式在某些情况下仅使用 6 位小数。 “看起来”具有欺骗性;查看我的更新答案。
【解决方案3】:

这是一个已知的错误^H^H^H功能。根据the docs

"""... 值 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') 在所有 Python 版本上生成 True。这意味着您可以在 2.7 上编写文件,而在 2.6 上读取相同,反之亦然。 repr(a_float_object) 产生的准确性没有变化。

【讨论】:

  • 感谢您指出发生这种情况的原因。我可能已经看到“使用 str() 进行字符串化”,但我对 Python 的 n00bness 并没有引发 w.r.t 的标志。 str().
  • OP 表示他是 Python 新手。解决他的问题的工作代码是需要的,而不是漫不经心的学术答案。
  • OP 问题中的代码表明“Python 新手”是适度的;他似乎能够生成可以迭代字典并更新其值而无需手持的代码。
  • 虽然我经过数小时的搜索和调整才能做到这一点,但最终还是得到了它。直到我遇到了最初的问题,4 小时的拉毛都没有解决。您的两个答案都对您各自提出的不同观点有所帮助。感谢您提供有关 Python 2.7 的信息。不幸的是,我仅限于 2.6.2(在无法升级的目标机器上运行)。但我会在以后的脚本中记住这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 1970-01-01
  • 2016-12-09
  • 2012-02-05
相关资源
最近更新 更多