How to Deal With NaN Values — datatest 0.12.0.dev1 documentation (2024)

IEEE 754

While the behavior of NaN values can seem strange, it’s actuallythe result of an intentionally designed specification. The behaviorwas standardized in IEEE 754, a technical standards document first published in1985 and implemented by many popular programming languages (includingPython).

When checking certain types of data, you may encounter NaN values.Working with NaNs can be frustrating because they don’t always actas one might expect.

About NaN values:

Checking for NaN Values

To make sure data elements do not contain NaN values, you can usea helper function:

 1 2 3 4 5 6 7 8 91011
from math import isnanfrom datatest import validatedata = [5, 6, float('nan')]def not_nan(x): """Values should not be NaN.""" return not isnan(x)validate(data, not_nan)

You can also do this using an inverted Predicate match:

123456789
from math import isnanfrom datatest import validate, Predicatedata = [5, 6, float('nan')]requirement = ~Predicate(isnan)validate(data, requirement)

Accepting NaN Differences

If validation fails and returns NaN differences, you can acceptthem as you would any other difference:

123456789
from math import nanfrom datatest import validate, accepted, Extradata = [5, 6, float('nan')]requirement = {5, 6}with accepted(Extra(nan)): validate(data, requirement)

Like other values, NaNs can also be accepted as part of a list,set, or mapping of differences:

123456789
from math import nanfrom datatest import validate, accepted, Missing, Extradata = [5, 6, float('nan')]requirement = {5, 6, 7}with accepted([Missing(7), Extra(nan)]): validate(data, requirement)

Note

The math.nan value is new in Python 3.5. NaN values canalso be created in any Python version using float('nan').

Dropping NaNs Before Validation

Sometimes it’s OK to ignore NaN values entirely. If this isappropriate in your circ*mstance, you can simply remove allNaN records and validate the remaining data.

If you’re using Pandas, you can call the Series.dropna() andDataFrame.dropna() methods to drop records that contain NaNvalues:

 1 2 3 4 5 6 7 8 910
import pandas as pdfrom datatest import validatesource = pd.Series([1, 1, 2, 2, float('nan')])data = source.dropna() # Drop NaN valued elements.requirement = {1, 2}validate(data, requirement)

Requiring NaN Values

If necessary, it’s possible to require that NaNs appear in yourdata. But putting NaN values directly into a requirement canbe frought with problems and should usually be avoided. The mostrobust way to do this is by replacing NaN values with a specialtoken and then requiring the token.

Below, we define a custom NanToken object and use it to replaceactual NaN values.

If you’re using Pandas, you can call the Series.fillna() andDataFrame.fillna() methods to replace NaNs with a different value:

 1 2 3 4 5 6 7 8 91011121314151617
import pandas as pdfrom datatest import validateclass NanToken(object): def __repr__(self): return self.__class__.__name__NanToken = NanToken()source = pd.Series([1, 1, 2, 2, float('nan')])data = source.fillna(NanToken) # Replace NaNs with NanToken.requirement = {1, 2, NanToken}validate(data, requirement)

A Deeper Understanding

Equality: NaN ≠ NaN

NaN values don’t compare as equal to anything—even themselves:

>>> x = float('nan')>>> x == xFalse

To check if a value is NaN, it’s common for modules and packagesto provide a function for this purpose (e.g., math.isnan(),numpy.isnan(), pandas.isna(), etc.):

>>> import math>>> x = float('nan')>>> math.isnan(x)True

While NaN values cannot be compared directly, they can be comparedas part of a difference object. In fact, difference comparisons treatall NaN values as equal—even when the underlying type is different:

>>> import decimal, math, numpy>>> from datatest import Invalid>>> Invalid(math.nan) == Invalid(float('nan'))True>>> Invalid(math.nan) == Invalid(complex('nan'))True>>> Invalid(math.nan) == Invalid(decimal.Decimal('nan'))True>>> Invalid(math.nan) == Invalid(numpy.nan)True>>> Invalid(math.nan) == Invalid(numpy.float32('nan'))True>>> Invalid(math.nan) == Invalid(numpy.float64('nan'))True

Identity: NaN is NaN, Except When it Isn’t

Some packages provide a NaN constant that can be referenced inuser code (e.g., math.nan and numpy.nan).While it may be tempting to use these constants to check formatching NaN values, this approach is not reliable in practice.

To optimize performance, Numpy and Pandas must strictly manage thememory layouts of the data they contain. When numpy.nan isinserted into an ndarray or Series, the value is coerced into a compatible dtypewhen necessary. When a NaN’s type is coerced, a separate instanceis created and the ability to match using the is operatorno longer works as you might expect:

>>> import pandas as pd>>> import numpy as np>>> np.nan is np.nanTrue>>> s = pd.Series([10, 11, np.nan])>>> s[2]nan>>> s[2] is np.nanFalse

We can verify that the types are now different:

>>> type(np.nan)float>>> type(s[2])float64

Generally speaking, it is not safe to assume that NaN is NaN.This means that—for reliable validation—it’s best to removeNaN records entirely or replace them with some other value.

How to Deal With NaN Values — datatest 0.12.0.dev1 documentation (2024)
Top Articles
Latest Posts
Article information

Author: Dean Jakubowski Ret

Last Updated:

Views: 6331

Rating: 5 / 5 (70 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: Dean Jakubowski Ret

Birthday: 1996-05-10

Address: Apt. 425 4346 Santiago Islands, Shariside, AK 38830-1874

Phone: +96313309894162

Job: Legacy Sales Designer

Hobby: Baseball, Wood carving, Candle making, Jigsaw puzzles, Lacemaking, Parkour, Drawing

Introduction: My name is Dean Jakubowski Ret, I am a enthusiastic, friendly, homely, handsome, zealous, brainy, elegant person who loves writing and wants to share my knowledge and understanding with you.