2012-04-05 155 views
3

通常,NaN(不是數字)通過計算傳播,因此我不需要在每個步驟中檢查NaN。這幾乎總是有效,但顯然有例外。例如:通過計算傳播NaN

>>> nan = float('nan') 
>>> pow(nan, 0) 
1.0 

我發現這個following comment

安靜的NaN通過算術運算傳播允許在操作的序列的末尾而不 廣泛的測試期間檢測到的 錯誤中間階段。然而,注意取決於語言和函數 ,NaN可以默默地被 移除,該表達式將給出所有其他 浮點值的恆定結果,例如, NaN^0,其可以被定義爲1,所以在 通常需要對集合INVALID標誌進行後續測試以檢測引入NaN的所有情況。

爲了滿足那些希望更加嚴格地解釋功能應該如何工作的人的需求,2008標準定義了兩個額外的功能:功能 和功能 。 pown(x,n)其中指數必須是一個整數,並且powr(x,y)只要參數是NaN就會返回一個NaN,否則指數會得到一個不確定的形式。

有沒有辦法通過Python來檢查上面提到的INVALID標誌?另外,還有沒有其他方法可以捕捉NaN不會傳播的情況?

動機:我決定使用NaN丟失數據。在我的應用程序中,缺少輸入應導致結果丟失。除了我所描述的,它效果很好。

回答

3

我意識到一個月過去了,但我遇到了類似的問題(即pow(float('nan'), 1)在某些Python實現中拋出異常,例如Jython 2.52b2),並且我發現上述答案並不是那麼簡單,我非常想找到的東西。

使用6502建議的MissingData類型似乎是要走的路,但我需要一個具體的例子。我試圖伊桑Furman的零式類卻發現,這不與任何算術運算工作,因爲它不強制數據類型(見下文),而且我也不喜歡,它明確指定這是重寫每一個算術函數。

從Ethan的例子開始,調整代碼我發現here,我到了下面的課。雖然這個類很受評論,但您可以看到它實際上只有少量的功能代碼。

其要點是: 1.使用裹脅()返回用於混合型的(例如無數據+浮動)算術操作的兩個無數據對象,並且兩個字符串用於基於字符串(例如CONCAT)操作。 2.使用GETATTR()返回一個可調用無數據()對象對於所有其他屬性/方法訪問 3.使用()調用來實現無數據的所有其他方法()對象:由返回NoData()對象

以下是其使用的一些示例。

>>> nd = NoData() 
>>> nd + 5 
NoData() 
>>> pow(nd, 1) 
NoData() 
>>> math.pow(NoData(), 1) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: nb_float should return float object 
>>> nd > 5 
NoData() 
>>> if nd > 5: 
...  print "Yes" 
... else: 
...  print "No" 
... 
No 
>>> "The answer is " + nd 
'The answer is NoData()' 
>>> "The answer is %f" % (nd) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: float argument required, not instance 
>>> "The answer is %s" % (nd) 
'The answer is ' 
>>> nd.f = 5 
>>> nd.f 
NoData() 
>>> nd.f() 
NoData() 

我注意到,使用戰俘與無數據()調用**操作,因此與無數據的工作,但使用math.pow不會因爲它首先嚐試無數據()對象轉換爲浮動。我很高興使用非數學pow - 希望6502等他們使用math.pow,當他們在上面的評論中有pow問題時。

其他問題,我想不出解決的辦法是與格式(%F)運算符...的無數據的任何方法被調用在這種情況下使用,如果你不操作只是失敗提供一個浮動。無論如何,這是班級本身。

class NoData(): 
"""NoData object - any interaction returns NoData()""" 
def __str__(self): 
    #I want '' returned as it represents no data in my output (e.g. csv) files 
    return ''   

def __unicode__(self): 
    return '' 

def __repr__(self): 
    return 'NoData()' 

def __coerce__(self, other_object): 
    if isinstance(other_object, str) or isinstance(other_object, unicode): 
     #Return string objects when coerced with another string object. 
     #This ensures that e.g. concatenation operations produce strings. 
     return repr(self), other_object 
    else: 
     #Otherwise return two NoData objects - these will then be passed to the appropriate 
     #operator method for NoData, which should then return a NoData object 
     return self, self 

def __nonzero__(self): 
    #__nonzero__ is the operation that is called whenever, e.g. "if NoData:" occurs 
    #i.e. as all operations involving NoData return NoData, whenever a 
    #NoData object propagates to a test in branch statement.  
    return False   

def __hash__(self): 
    #prevent NoData() from being used as a key for a dict or used in a set 
    raise TypeError("Unhashable type: " + self.repr()) 

def __setattr__(self, name, value): 
    #This is overridden to prevent any attributes from being created on NoData when e.g. "NoData().f = x" is called 
    return None  

def __call__(self, *args, **kwargs): 
    #if a NoData object is called (i.e. used as a method), return a NoData object 
    return self  

def __getattr__(self,name): 
    #For all other attribute accesses or method accesses, return a NoData object. 
    #Remember that the NoData object can be called (__call__), so if a method is called, 
    #a NoData object is first returned and then called. This works for operators, 
    #so e.g. NoData() + 5 will: 
    # - call NoData().__coerce__, which returns a (NoData, NoData) tuple 
    # - call __getattr__, which returns a NoData object 
    # - call the returned NoData object with args (self, NoData) 
    # - this call (i.e. __call__) returns a NoData object 

    #For attribute accesses NoData will be returned, and that's it. 

    #print name #(uncomment this line for debugging purposes i.e. to see that attribute was accessed/method was called) 
    return self 
+0

我的意思是Jython 2.5.2b2,而不是2.52b2 – jcdude 2012-05-03 12:43:12

2

如果它只是pow()給你頭痛,你可以很容易地重新定義它在任何你喜歡的情況下返回NaN

def pow(x, y): 
    return x ** y if x == x else float("NaN") 

如果NaN可以作爲一個指數也願意要以檢查;這會引發一個例外情況,除非基數爲1(顯然理論上1對任何能力,即使是非數字的能力都是1)。

(當然pow()其實需要三個操作數,第三個可選,其中遺漏我會離開作爲練習...)

不幸的是,**操作者具有相同的行爲,而且也沒有辦法重新定義對於內置數字類型。抓住這個的可能性是編寫一個float的子類,它實現了__pow__()__rpow__(),並將這個類用於你的NaN值。

Python似乎沒有提供對計算設置的任何標誌的訪問;即使是這樣,在每個單獨的操作之後,你都必須檢查它。

事實上,在進一步的考慮,我認爲最好的解決方案可能是簡單地使用虛擬類的實例的缺失值。 Python會扼殺你對這些值所做的任何操作,引發異常,並且你可以捕獲異常並返回默認值或其他值。如果缺少所需的值,沒有理由繼續進行其餘的計算,所以異常應該沒問題。

+0

我不明白這是如何工作的。 'NaN!= NaN',所以你的'if'永遠是真的。 – Duncan 2012-04-05 19:18:53

+0

只需用'x == x'替換'x!= NaN'。 – max 2012-04-05 19:19:30

+0

而我不確定;也許'pow'是唯一的一個,也許它不是......我想用'NaN'來填充缺失的數據,因爲它聽起來很整潔,不太實際...... :( – max 2012-04-05 19:20:21

2

爲什麼使用NaN已經有另一種語義,而不是使用自己定義的類MissingData類的實例?

MissingData實例定義操作得到傳播應該很容易......

+0

我不敢相信我沒有想到這一點,現在用ABC,甚至不難定義所有的算術運算,對吧? – max 2012-04-05 19:27:12

+0

或者正如我在我剛剛編輯的那樣我自己的回答,甚至不對'MissingData'類實施任何操作,只要讓Python在嘗試時提出任何異常在計算中使用這些對象之一,捕捉它並提供默認值。 – kindall 2012-04-05 19:34:13

+0

我實際上需要對MissingValue進行操作,因爲在每次中間計算時都必須捕獲異常,這有點過分。簡單地讓MissingValue傳播,然後讓MissingValue填充結果數據集會好得多。 – max 2012-04-05 19:40:20

2

要回答你的問題:沒有,沒有辦法用正常浮動,檢查標誌。您可以使用Decimal類,但是,它提供更多的控制。 。 。但有點慢。

你的另一種選擇是使用EmptyDataNull類,比如這個:

class NullType(object): 
    "Null object -- any interaction returns Null" 
    def _null(self, *args, **kwargs): 
     return self 
    __eq__ = __ne__ = __ge__ = __gt__ = __le__ = __lt__ = _null 
    __add__ = __iadd__ = __radd__ = _null 
    __sub__ = __isub__ = __rsub__ = _null 
    __mul__ = __imul__ = __rmul__ = _null 
    __div__ = __idiv__ = __rdiv__ = _null 
    __mod__ = __imod__ = __rmod__ = _null 
    __pow__ = __ipow__ = __rpow__ = _null 
    __and__ = __iand__ = __rand__ = _null 
    __xor__ = __ixor__ = __rxor__ = _null 
    __or__ = __ior__ = __ror__ = _null 
    __divmod__ = __rdivmod__ = _null 
    __truediv__ = __itruediv__ = __rtruediv__ = _null 
    __floordiv__ = __ifloordiv__ = __rfloordiv__ = _null 
    __lshift__ = __ilshift__ = __rlshift__ = _null 
    __rshift__ = __irshift__ = __rrshift__ = _null 
    __neg__ = __pos__ = __abs__ = __invert__ = _null 
    __call__ = __getattr__ = _null 

    def __divmod__(self, other): 
     return self, self 
    __rdivmod__ = __divmod__ 

    if sys.version_info[:2] >= (2, 6): 
     __hash__ = None 
    else: 
     def __hash__(yo): 
      raise TypeError("unhashable type: 'Null'") 

    def __new__(cls): 
     return cls.null 
    def __nonzero__(yo): 
     return False 
    def __repr__(yo): 
     return '<null>' 
    def __setattr__(yo, name, value): 
     return None 
    def __setitem___(yo, index, value): 
     return None 
    def __str__(yo): 
     return '' 
NullType.null = object.__new__(NullType) 
Null = NullType() 

您可能需要改變__repr____str__方法。此外,請注意,Null不能用作字典鍵,也不能存儲在集合中。