On Sat, Dec 28, 2019 at 11:29 PM Steve Barnes <[email protected]>
wrote:
>
>
> How about something along the lines of:
>
> def isnan(num):
>
> """ Attempt at generic NaN check """
>
> if hasattr(num, "is_nan"): # Does it have its own method?
>
> return num.is_nan()
>
> else:
>
> return num != num
>
> Then decimal would work nicely as would any potential types that are not
> self-equal would just need to define their own method albeit with a
> prescribed name. Of course it will return false for non-numeric types such
> as strings.
>
one trick here is sequences with a NaN in them:
In [56]: [1, 2, 3, float('NaN')] != [1, 2, 3, float('NaN')]
Out[56]: True
Which is why I'm leaning toward checking for a Number type:
def is_nan(num):
"""
This version works as well
"""
try:
return num.is_nan()
except AttributeError:
if isinstance(num, Number):
return num != num
return False
This doesn't work with single-element numpy arrays (it does with numpy
scalars). Which is maybe fine -- at least for the statistics module, as
folks using numpy are probaly using a numpy statistics package anyway.
Though I'm not sure why it's better than:
def is_nan(num):
"""
This version works for everything I've tried
"""
try:
return num.is_nan()
except AttributeError:
if isinstance(num, complex):
return cmath.isnan(num)
try:
return math.isnan(num)
except:
return False
Which passes all the tests I came up with.
(code enclosed)
-CHB
--
Christopher Barker, PhD
Python Language Consulting
- Teaching
- Scientific Software Development
- Desktop GUI and Web Development
- wxPython, numpy, scipy, Cython
"""
some tests of an is_nan function
"""
import math
import cmath
from decimal import Decimal
from fractions import Fraction
from numbers import Number
import numpy as np
import pytest
def is_nan(num):
"""
This version works for everything I've tried
"""
try:
return num.is_nan()
except AttributeError:
if isinstance(num, complex):
return cmath.isnan(num)
try:
return math.isnan(num)
except:
return False
# def is_nan(num):
# """
# This version works as well
# """
# try:
# return num.is_nan()
# except AttributeError:
# if isinstance(num, Number):
# return num != num
# return False
nan_vals = [Decimal('nan'),
Decimal('snan'),
float('nan'),
np.nan,
np.array(np.nan), # zero dim array
np.array([np.nan, ]), # one element array
np.array([np.nan]).reshape((1, 1, 1)), # one element 3d array
np.float32(np.nan), # numpy scalar
np.array(np.nan, dtype=np.float128),
complex(float('nan'), 1),
complex(0, float('nan')),
complex(float('nan'), float('nan')),
]
non_nan_vals = [34,
10**1000, # an int too big for a float
3.4,
np.array(1.2e34), # zero dim array
np.array([1.2e34, ]), # one element array
np.array([1.2e34]).reshape((1, 1, 1)), # one element 3d array
np.float32(1.234), # numpy scalar
np.array(1.23, dtype=np.float32),
np.array(1e200, dtype=np.float128) * 1e200, # a numpy float128 too big for a float
np.array([1.23, 3.45], dtype=np.float128)[0],
Decimal('1e500'),
Fraction(1, 10),
Fraction(1, 1000000)**100, # fraction too small for a float
Fraction(10, 1) ** 500, # fraction too large for a float
complex(1, 2),
complex(-123, 154),
"a string", # a string can never be a NaN
[1, 2, 3], # nor is a list
[1, 2, 3, float('NaN')], # even if it has a NaN in it.
np.array([np.nan, np.nan]), # more than one element array -- non NaN
]
exception_raising = []
@pytest.mark.parametrize("num", nan_vals)
def test_nan(num):
assert is_nan(num)
@pytest.mark.parametrize("num", non_nan_vals)
def test_not_nan(num):
assert not is_nan(num)
@pytest.mark.parametrize("num, exp", exception_raising)
def test_exception(num, exp):
with pytest.raises(Exception):
is_nan(num)
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/PGDUY4ZRJX7VSKO62CRJDUOISA5PPA2S/
Code of Conduct: http://python.org/psf/codeofconduct/