2017-05-29 107 views
1

我在使用pandas.read_csv讀取CSV概率時遇到問題;其中一些值與> 1.0一起作爲浮點數讀取。瞭解pandas.read_csv()浮動解析

具體而言,我感到困惑的以下行爲:

>>> pandas.read_csv(io.StringIO("column\n0.99999999999999998"))["column"][0] 
1.0 
>>> pandas.read_csv(io.StringIO("column\n0.99999999999999999"))["column"][0] 
1.0000000000000002 
>>> pandas.read_csv(io.StringIO("column\n1.00000000000000000"))["column"][0] 
1.0 
>>> pandas.read_csv(io.StringIO("column\n1.00000000000000001"))["column"][0] 
1.0 
>>> pandas.read_csv(io.StringIO("column\n1.00000000000000008"))["column"][0] 
1.0 
>>> pandas.read_csv(io.StringIO("column\n1.00000000000000009"))["column"][0] 
1.0000000000000002 

默認浮法解析行爲似乎是不單調,尤其是開始0.9...一些值轉換到嚴格小於1.0更大的浮動,造成問題,例如當他們餵養sklearn.metrics

documentation指出read_csv有一個參數float_precision,可以用來選擇「的轉換器的C發動機應使用浮點值」,而這個設置爲'high'確實解決了我的問題。

不過,我想了解的默認行爲:

  1. 我在哪裏可以找到默認的浮動轉換器的源代碼?
  2. 我在哪裏可以找到有關默認浮動轉換器的預期行爲以及其他可能的選擇的文檔?
  3. 爲什麼最小有效位置的單個數字變化會跳過一個值?
  4. 爲什麼這個行爲是非單調的?

編輯關於「重複問題」:這不是重複的。我意識到浮點數學的侷限性。我是專門詢問有關大熊貓的默認解析機制,因爲內置float不顯示此行爲:

>>> float("0.99999999999999999") 
1.0 

...並我找不到文檔。

+0

可能重複[浮點數學是否被破壞?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – pvg

+4

@pvg,這不是一個騙局。 OP已經認真地演示了非單調行爲,並且從某些照明中提出要求,以便他們能夠高效地使用它。 –

+0

@StephenRauch它是一個愚蠢的或有相關的愚蠢。看看所需的精度和結果,這些都小於ieee double的epsilon。這不是一個明智的經營場所。 – pvg

回答

1

@MaxU已經表現出解析器的源代碼以及相關的標記生成器xstrtod所以我會專注於「爲什麼」部分:

xstrtod的代碼大致是這樣的(轉換爲純Python):

def xstrtod(p): 
    number = 0. 
    idx = 0 
    ndecimals = 0 

    while p[idx].isdigit(): 
     number = number * 10. + int(p[idx]) 
     idx += 1 

    idx += 1 

    while idx < len(p) and p[idx].isdigit(): 
     number = number * 10. + int(p[idx]) 
     idx += 1 
     ndecimals += 1 

    return number/10**ndecimals 

其再現了「問題」你看到:

print(xstrtod('0.99999999999999997')) # 1.0 
print(xstrtod('0.99999999999999998')) # 1.0 
print(xstrtod('0.99999999999999999')) # 1.0000000000000002 
print(xstrtod('1.00000000000000000')) # 1.0 
print(xstrtod('1.00000000000000001')) # 1.0 
print(xstrtod('1.00000000000000002')) # 1.0 
print(xstrtod('1.00000000000000003')) # 1.0 
print(xstrtod('1.00000000000000004')) # 1.0 
print(xstrtod('1.00000000000000005')) # 1.0 
print(xstrtod('1.00000000000000006')) # 1.0 
print(xstrtod('1.00000000000000007')) # 1.0 
print(xstrtod('1.00000000000000008')) # 1.0 
print(xstrtod('1.00000000000000009')) # 1.0000000000000002 
print(xstrtod('1.00000000000000019')) # 1.0000000000000002 

的問題似乎是在最後的地方9這改變了結果。因此,它的浮點精確度:

>>> float('100000000000000008') 
1e+17 
>>> float('100000000000000009') 
1.0000000000000002e+17 

這是在最後的地方,負責偏斜結果9


如果你想要高精確度,你可以定義自己的轉換器或使用Python提供的那些,即decimal.Decimal如果你想arbitary精度:

>>> import pandas 
>>> import decimal 
>>> converter = {0: decimal.Decimal} # parse column 0 as decimals 
>>> import io 
>>> def parse(string): 
...  return '{:.30f}'.format(pd.read_csv(io.StringIO(string), converters=converter)["column"][0]) 
>>> print(parse("column\n0.99999999999999998")) 
>>> print(parse("column\n0.99999999999999999")) 
>>> print(parse("column\n1.00000000000000000")) 
>>> print(parse("column\n1.00000000000000001")) 
>>> print(parse("column\n1.00000000000000008")) 
>>> print(parse("column\n1.00000000000000009")) 

它打印:

0.999999999999999980000000000000 
0.999999999999999990000000000000 
1.000000000000000000000000000000 
1.000000000000000010000000000000 
1.000000000000000080000000000000 
1.000000000000000090000000000000 

準確地表示輸入!

+0

我看了一下'xstrtod'源代碼(謝謝@MaxU);問題在於中間結果中存在很多不準確的可能性,尤其是'number',這會導致某些字符串被解析,導致它們不會導致最接近的可表示的float *。 (另外,指數沒有檢查溢出。) – user4235730

+0

在我看來,默認情況下應該使用「高」精度;這種行爲導致數字錯誤比我預期的更高並且難以檢測。 *最重要的是,這種行爲應該記錄在案。*(或者是否有人在這裏找到相關文件?) – user4235730

+0

不,我還沒有找到任何有關該行爲的文件。幸運的是,在大多數情況下,數字沒有超過10位小數,這些數字「足夠好」。這是您的優化:/ – MSeifert

1

如果你想了解它是如何工作 - 看source code - file "_libs/parsers.pyx" lines: 492-499 for Pandas 0.20.1

self.parser.double_converter_nogil = xstrtod # <------- default converter 
    self.parser.double_converter_withgil = NULL 
    if float_precision == 'high': 
     self.parser.double_converter_nogil = precise_xstrtod # <------- 'high' converter 
     self.parser.double_converter_withgil = NULL 
    elif float_precision == 'round_trip': # avoid gh-15140 
     self.parser.double_converter_nogil = NULL 
     self.parser.double_converter_withgil = round_trip 

Source code for xstrtod

Source code for precise_xstrtod