标量值的isull()/ isnan()/ isinf()

时间:2018-11-01 12:28:28

标签: python pandas numpy missing-data

在Pandas和Numpy中,有矢量化函数,例如np.isnannp.isinfpd.isnull,用于检查数组,序列或数据框的元素是否为各种缺失/空/无效。

它们确实在标量上工作。 pd.isnull(None)只是返回True而不是pd.Series([True]),这很方便。

但是,假设我想知道 any 对象是否为这些空值之一;使用任何这些功能都无法做到这一点!这是因为它们将很高兴地对各种数据结构进行矢量化处理。粗心地使用它们将不可避免地导致可怕的“系列的真值不明确”错误。

我想要的是这样的功能:

assert not is_scalar_null(3)
assert not is_scalar_null([1,2])
assert not is_scalar_null([None, 1])
assert not is_scalar_null(pd.Series([None, 1]))
assert not is_scalar_null(pd.Series([None, None]))
assert is_scalar_null(None)
assert is_scalar_null(np.nan)

在内部,Pandas函数pandas._lib.missing.checknull会做正确的事情:

import pandas._libs.missing as libmissing
libmissing.checknull(pd.Series([1,2]))  # correctly returns False

但是使用它通常是不好的做法。根据Python命名约定,_lib是私有的。我也不确定Numpy的等效功能。

是否有一种“可接受”但正式的方式来使用与NumPy和Pandas相同的空检查逻辑,但严格用于标量?

3 个答案:

答案 0 :(得分:3)

所有您需要做的就是包装pd.isnull,以防万一它可迭代,它将被强制按元素进行检查。这样,您将始终获得标量布尔值作为输出。

from collections import Iterable

def is_scalar_null(value):
    if isinstance(value, Iterable):
        return all(not pd.isnull(v) for v in value)
    return not pd.isnull(value)

assert is_scalar_null(3)
assert is_scalar_null([1, 2])
assert is_scalar_null(pd.Series([1]))
assert not is_scalar_null(None)
assert not is_scalar_null(np.nan)
assert not is_scalar_null([np.nan, 1])
assert not is_scalar_null(pd.Series([np.nan, 1]))

然后您可以修补实际的pd.isnull,但我不能说我建议这样做。

from collections import Iterable

orig_pd_is_null = pd.isnull

def is_scalar_null(value):
    if isinstance(value, Iterable):
        return all(not orig_pd_is_null(v) for v in value)
    return not orig_pd_is_null(value)

pd.isnull = is_scalar_null

assert pd.isnull(3)
assert pd.isnull([1, 2])
assert pd.isnull(pd.Series([1]))
assert not pd.isnull(None)
assert not pd.isnull(np.nan)
assert not pd.isnull([np.nan, 1])
assert not pd.isnull(pd.Series([np.nan, 1]))

如果嵌套的可迭代对象,这种方法可能可能会破坏,但是可以通过在is_scalar_null中使用递归来解决。

答案 1 :(得分:1)

这是@DeepSpace's solution的扩展。对于NumPy数组以及扩展的数字Pandas系列,可以使用numba进行JIT编译循环。 all / any的生成器理解通常效率较低,并且当NaN的值接近数组结尾时,代价通常过高。

例如,在极端情况下,我们会看到约240倍的性能差异:

from collections import Iterable
from numba import njit

def any_null(arr):
    for i in range(len(arr)):
        if np.isnan(arr[i]):
            return True
    return False

def is_scalar_null(value, jit_flag=True):
    checker = njit(any_null) if jit_flag else any_null
    if isinstance(value, pd.Series):
        return checker(value.values)
    elif isinstance(value, np.ndarray):
        return checker(value)
    elif isinstance(value, Iterable):
        return all(not pd.isnull(v) for v in value)
    return not pd.isnull(value)

np.random.seed(0)
A = np.random.random(10**7)
A[-1] = np.nan

%timeit is_scalar_null(A, jit_flag=True)  # 74.3 ms per loop
%timeit is_scalar_null(A, jit_flag=False) # 17.6 s per loop

答案 2 :(得分:0)

标量值isinfisnan可以直接在math module中找到。

可以很容易地完成基本的标量空检查:

from math import isnan

def is_scalar_null(x):
    return x is None or (isinstance(x, float) and isnan(x))

这里可能存在一些未捕获的边缘情况,但是在我的用法中效果很好。随着熊猫开始在最新版本(> = 0.25)中丰富其“空”数据的表示形式,这也可能会发生变化。