2012-04-05 149 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不能用作字典键,也不能存储在集合中。