2017-06-24 99 views
4

我注意到當某些Numpy float64值保存爲Excel文件(通過Pandas DataFrame)時,它們會發生變化。首先,我認爲這與Excel中的一些不精確有關,但Excel似乎將浮點數編碼爲雙精度,所以我對這個觀察有點困惑。Numpy.float64在寫入Excel時發生變化(.xlsx)

>>> 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\[email protected]') 
>>> 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 

回答

1

我也試圖與Openpyxl格式(.xlsx格式)和Xlwt(.xls格式)的發動機。雖然前者產生與xlsxwriter相同的錯誤結果,但Xlwt實際上按預期工作,並根據確切的變量值寫入浮點數。

區別在於.xls是一種二進制文件格式,而IEEE 754 double的64位表示形式完全寫入文件,並且可以回讀到相同的64位。

但.xlsx文件格式是一個zip容器中的文本XML文件的集合。因爲這樣的雙打被寫爲雙精度的字符串表示(使用像'%.16g'這樣的格式)並通過將該字符串表示轉換回雙精度來讀入。這實質上是雙工的洛西過程,因爲絕大多數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> 

這或多或少是當你讀迴文件中使用熊貓,你所看到的。

+0

在實現中,我看着你將'XlsxWriter'中的float格式化部分保留爲undefined。 'attr + ='%s =「%s」'%(key,value)'使用上帝知道什麼樣的格式化精度... – orange

+0

該代碼是針對XML屬性的。 double值使用'%.16g'格式化。從實驗中,這與Excel相匹配(儘管對於某些數字,它似乎使用'%.17g')。我沒有辦法知道,但Excel可能使用專用函數寫入雙打而不是'sprintf()'。至少從性能的角度來看,這是有意義的。無論哪種方式,這都是雙精度的邊緣,所以有些損失是不可避免的。如果您有興趣,您應該在Excel中創建數字並查看輸出XML中的表示形式。 – jmcnamara

0

在熊貓和XlsxWriter一些挖後,我基本上發現了兩個轉換步驟從numpy.float64到.xlsx文件:

1)numpy.float64 =>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 =>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是一種二進制格式,所以float將被直接寫入,而不用轉換。

相關問題