写入Excel时,Numpy.float64会发生变化(.xlsx)

时间:2017-06-24 10:31:10

标签: python pandas numpy xlsxwriter xlwt

我注意到当某些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

2 个答案:

答案 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中的单元格,保存文件并检查输出来自行演示:

对于这样的文件:

enter image description here

你会得到这样的东西:

$ 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; stringattr += ' %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是二进制格式,浮点数将直接写入,无需转换。