0

In python, how can I check a variable is a numerical type and has a meaningful value?

Here I mean by 'numerical type' those like int, float, and complex with all different bit length, and by 'meaningful value' that it is not nan or any other special values that can not be used for further computation.

(I guess this is such a common issue and there must be a duplicate question, but I did not find one after a quick search. Please let me know if there is a duplicate.)

norio
  • 3,652
  • 3
  • 25
  • 33
  • 1
    http://stackoverflow.com/questions/944700/how-to-check-for-nan-in-python – Will Sep 08 '16 at 16:21
  • 1
    also http://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-float-in-python and http://stackoverflow.com/questions/152580/whats-the-canonical-way-to-check-for-type-in-python – scrappedcola Sep 08 '16 at 16:23
  • 2
    Please consider [this answer](http://stackoverflow.com/a/154156/8747). Just say no. – Robᵩ Sep 08 '16 at 16:25
  • @Will I found in the post you referred to that the answer by DaveTheScientist is helpful. – norio Sep 08 '16 at 17:14
  • @Robᵩ Unfortunately I think I can't defend the principle you referred to in my context because `float('nan') < 1.0e-12` (for example) does not behave in the way I want. I want it to stop the execution or at least to be evaluated as `False`. – norio Sep 08 '16 at 17:20

4 Answers4

1
>>> from math import isnan
>>> isnan(float('nan'))
True
>>> isnan(1j.real)
False
>>> isnan(1j.imag)
False

Integers can never be NaNs.

Jonas Byström
  • 25,316
  • 23
  • 100
  • 147
1

Python 2.x and 3.x

import math
import numbers

def is_numerical(x):
    return isinstance(x, numbers.Number) and not isinstance(x, bool) and not math.isnan(abs(n)) and math.isfinite(abs(n))

Reason for the distinction is because Python 3 merged the long and int types into just int.

Edit: Added upon answer below using numbers.Number to exclude booleans.

sethmlarson
  • 923
  • 8
  • 21
1

It depends how thorough you want to be. Besides the builtin types (complex, float, and int) there are also other types that are considered numbers in python. For instance: fractions.Fraction, decimal.Decimal, and even bool can act as a number. Then you get external libraries that have their own numeric types. By far the biggest is numpy. With numpy some of its types will succeed isinstance checks, and other will not. For instance: isinstance(numpy.float64(10), float) is true, but isinstance(numpy.float32(10), float) is not. On top of all this you could even have a user defined class that acts like a number.

Python does provide one way of getting around this -- the numbers module. It provides several abstract types that represent different types of numbers. Any class that implements numeric functionality can register itself as being compatible with the relevant types. numbers.Number is the most basic, and therefore the one you're looking for. All you have to do is use it in your isinstance checks. eg.

from numbers import Number
from decimal import Decimal
from fractions import Fraction
import numpy

assert isinstance(1, Number)
assert isinstance(1.5, Number)  
assert isinstance(1+5j, Number)  
assert isinstance(True, Number)

assert isinstance(Decimal("1.23"), Number)
assert isinstance(Fraction(1, 2), Number)

assert isinstance(numpy.float64(10), Number)
assert isinstance(numpy.float32(10), Number)
assert isinstance(numpy.int32(10), Number)
assert isinstance(numpy.uint32(10), Number)

That still leaves us with the problem about whether the object is actually a number, rather than "not a number". The math.isnan function is good for this, but it requires that the number be convertible to a float (which not all numbers are). The big problem here is the complex type. There are a few ways around this: additional isinstance checks (but that comes with its own headaches), using abs, or testing for equality.

abs can be used on every numeric type (that I can think of). For most numbers it returns the positive version of the number, but for complex numbers it returns its magnitude (a float). So now we can do that isnan check. nan is also a special number in that it is the only number that is not equal to itself.

This means your final check might look like:

import math
import numbers

def number_is_not_nan(n):
    return isinstance(n, numbers.Number) and not math.isnan(abs(n))

def number_is_finite(n):
    return isinstance(n, numbers.Number) and not math.isfinite(abs(n))
Dunes
  • 37,291
  • 7
  • 81
  • 97
-1

I am answering to my own question. This is based on Seth Michael Larson's answer and DaveTheScientist's answer for another question. Considering that I need to be careful for float('inf') and float('-inf') as well as float('nan'), and that the passed argument x may be complex, I ended up writing the following function for the check.

def is_a_meaningful_number(x):
    import math
    if sys.version_info >= (3, 0, 0):
        NUMERIC_TYPES = (int, complex, float)
    else:
        NUMERIC_TYPES = (int, long, complex, float)
    return isinstance(x, NUMERIC_TYPES) and float('-inf') < abs(x) < float('inf')
Community
  • 1
  • 1
norio
  • 3,652
  • 3
  • 25
  • 33