在Pandas和Numpy中,有矢量化函数,例如np.isnan
,np.isinf
和pd.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相同的空检查逻辑,但严格用于标量?
答案 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)
标量值isinf
和isnan
可以直接在math module中找到。
可以很容易地完成基本的标量空检查:
from math import isnan
def is_scalar_null(x):
return x is None or (isinstance(x, float) and isnan(x))
这里可能存在一些未捕获的边缘情况,但是在我的用法中效果很好。随着熊猫开始在最新版本(> = 0.25)中丰富其“空”数据的表示形式,这也可能会发生变化。