我注意到当某些Numpy float64值保存为Excel文件(通过Pandas DataFrame)时,它们会被更改。首先我认为这与Excel中的一些不精确有关,但Excel似乎将浮点数编码为双精度,所以我对此观察有点困惑。
>>> import numpy as np
>>> import pandas as pd
# Create a floating point number that exhibits the problem.
>>> ba = bytearray(['\x53', '\x2a', '\xb0', '\x49', '\xf3', '\x79', '\x90', '\x40'])
>>> ba
bytearray(b'S*\xb0I\xf3y\x90@')
>>> f = np.frombuffer(ba)
>>> f[0]
1054.4875857854684
# Write to dataframe to save as Excel file.
>>> df = pd.DataFrame({'a': f})
>>> df.to_excel('test.xlsx', engine='xlsxwriter')
# Read excel file (when viewing the file in LibreOffice, the
# value isn't 1054.4875857854684 any more).
>>> df2 = pd.read_excel('test.xlsx')
>>> df2.ix[0,'a']
1054.4875857854699
>>> df2.ix[0,'a'] == f[0]
False
为什么不能从之前编写的Excel中读取相同的float64?
我还尝试使用Openpyxl
(。xlsx格式)和Xlwt
(。xls格式)作为引擎。虽然前者产生与xlsxwriter
相同的错误结果,但Xlwt
实际上按预期工作并根据确切的变量值写入浮点数。我可能会错过.xlsx
格式编写器引擎的参数吗?
# this uses the xlwt engine
>>> df.to_excel('test.xls')
>>> df2 = pd.read_excel('test.xls')
>>> df2.ix[0,'a'] == f[0]
True
答案 0 :(得分:1)
我还尝试使用Openpyxl(.xlsx格式)和Xlwt(.xls格式)作为引擎。虽然前者产生了与xlsxwriter相同的错误结果,但Xlwt实际上按预期工作并根据确切的变量值写入浮点数。
不同之处在于.xls是二进制文件格式,IEEE 754 double的64位表示精确写入文件,可以读回相同的64位。
.xlsx文件格式是zip容器中的文本XML文件的集合。因为这样的双精度被写为double的字符串表示(使用类似'%.16g'
的格式)并通过将该字符串表示转换回double来读入。这实际上是双打的失败过程,因为绝大多数IEEE 754数字都没有精确的字符串表示。
例如,如果您在示例中使用numpy数字并使用不同的精度对其进行格式化,则会得到不同的表示形式:
>>> '%.16g' % f[0]
'1054.487585785468'
>>> '%.17g' % f[0]
'1054.4875857854684'
>>> '%.18g' % f[0]
'1054.48758578546835'
您也可以通过将1054.4875857854684
粘贴到Excel中的单元格,保存文件并检查输出来自行演示:
对于这样的文件:
你会得到这样的东西:
$ unzip numpy.xlsx -d numpy
$ xmllint --format numpy/xl/worksheets/sheet1.xml | grep 1054
<v>1054.4875857854599</v>
当您使用Pandas读回文件时,这或多或少是您所看到的。
答案 1 :(得分:0)
在对Pandas和XlsxWriter进行一些挖掘之后,我基本上发现了从numpy.float64
到.xlsx文件的两个转换步骤:
1)numpy.float64
=&gt; float
pandas/io/excel.py
(不失真伪)
def _conv_value(val):
# Convert numpy types to Python types for the Excel writers.
if com.is_integer(val):
val = int(val)
elif com.is_float(val):
val = float(val)
elif com.is_bool(val):
val = bool(val)
elif isinstance(val, Period):
val = "%s" % val
elif com.is_list_like(val):
val = str(val)
return val
2)float
=&gt; string
(attr += ' %s="%s"' % (key, value)
)。这是改变精度的地方(在xlswriter/xmlwriter.py
)
def _xml_number_element(self, number, attributes=[]):
# Optimised tag writer for <c> cell number elements in the inner loop.
attr = ''
for key, value in attributes:
value = self._escape_attributes(value)
attr += ' %s="%s"' % (key, value)
self.fh.write("""<c%s><v>%.15g</v></c>""" % (attr, number))
因此序列化(步骤2)是精度改变的地方。我猜,因为xls是二进制格式,浮点数将直接写入,无需转换。