标量值 isnull()/isnan()/isinf()
Scalar-valued isnull()/isnan()/isinf()
在Pandas和Numpy中,有像np.isnan
、np.isinf
和pd.isnull
这样的向量化函数来检查数组、序列或数据帧的元素是否是各种missing/null/invalid.
他们确实在标量上工作。 pd.isnull(None)
简单地 returns True
而不是 pd.Series([True])
,这很方便。
但是假设我想知道 any 对象是否是这些空值之一;你不能用这些功能中的任何一个来做到这一点!那是因为他们会愉快地对各种数据结构进行矢量化。粗心地使用它们将不可避免地导致可怕的 "The truth value of a Series is ambiguous" 错误。
我想要的是这样一个函数:
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 的等价物。
是否有 "acceptable" 但官方的方法来使用与 NumPy 和 Pandas 相同的空检查逻辑,但严格用于标量?
你所要做的就是用一种方式包装 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
.
中使用递归来解决
这是对 的扩展。对于 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
标量值 isinf
和 isnan
可以直接在 math module.
中找到
可以轻松完成基本的标量空值检查:
from math import isnan
def is_scalar_null(x):
return x is None or (isinstance(x, float) and isnan(x))
这里可能有一些未捕获的边缘情况,但它在我的使用中效果很好。随着 Pandas 开始丰富他们在最新版本 (>= 0.25) 中对 "null" 数据的表示,这也可能会发生变化。
在Pandas和Numpy中,有像np.isnan
、np.isinf
和pd.isnull
这样的向量化函数来检查数组、序列或数据帧的元素是否是各种missing/null/invalid.
他们确实在标量上工作。 pd.isnull(None)
简单地 returns True
而不是 pd.Series([True])
,这很方便。
但是假设我想知道 any 对象是否是这些空值之一;你不能用这些功能中的任何一个来做到这一点!那是因为他们会愉快地对各种数据结构进行矢量化。粗心地使用它们将不可避免地导致可怕的 "The truth value of a Series is ambiguous" 错误。
我想要的是这样一个函数:
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 的等价物。
是否有 "acceptable" 但官方的方法来使用与 NumPy 和 Pandas 相同的空检查逻辑,但严格用于标量?
你所要做的就是用一种方式包装 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
.
这是对 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
标量值 isinf
和 isnan
可以直接在 math module.
可以轻松完成基本的标量空值检查:
from math import isnan
def is_scalar_null(x):
return x is None or (isinstance(x, float) and isnan(x))
这里可能有一些未捕获的边缘情况,但它在我的使用中效果很好。随着 Pandas 开始丰富他们在最新版本 (>= 0.25) 中对 "null" 数据的表示,这也可能会发生变化。