一种新的设计规范 nan_policy

中的许多函数 scipy.stats 有一个名为 nan_policy 它确定函数如何处理包含以下内容的数据 nan 。在本部分中,我们提供了本网站开发人员的指南,说明如何 nan_policy 旨在使用,以确保在将此参数添加到新函数时,我们保持一致的API。

基础API

该参数 nan_policy 接受三种可能的字符串: 'omit''raise''propagate' 。它们的含义是:

  • nan_policy='omit' :忽略出现的 nan 在输入中。如果输入包含以下内容,则不生成警告 nan (除非使用 nan 删除的值将生成警告)。例如,对于接受单个数组并返回标量(并忽略可能使用的 axis 目前):

    func([1.0, 3.0, np.nan, 5.0], nan_policy='omit')
    

    行为应与以下内容相同:

    func([1.0, 3.0, 5.0])
    

    更一般地,对于返回标量的函数, func(a, nan_policy='omit') 行为应该与 func(a[~np.isnan(a)])

    For functions that transform a vector to a new vector of the same size and for which each entry in the output array depends on more than just the corresponding value in the input array 1 (e.g. scipy.stats.zscore, scipy.stats.boxcox when lmbda is None),:

    y = func(a, nan_policy='omit')
    

    行为应与以下内容相同:

    nan_mask = np.isnan(a)
    y = np.empty(a.shape, dtype=np.float64)
    y[~nan_mask] = func(a[~nan_mask])
    y[nan_mask] = np.nan
    

    (通常, y 可能取决于 a 以及关于……的预期行为 func )。换句话说,一个 nan 在输入中给出了相应的 nan 在输出中,但它的存在 nan 不影响非`nan`值的计算。

    此属性的单元测试应用于测试处理 nan_policy

    对于返回标量并接受两个或多个参数但其值不相关的函数(例如 scipy.stats.ansariscipy.stats.f_oneway ),同样的思想也适用于每个输入数组。因此::

    func(a, b, nan_policy='omit')
    

    行为应与以下内容相同:

    func(a[~np.isnan(a)], b[~np.isnan(b)])
    

    对于具有以下项的输入 相关成对的 值(例如 scipy.stats.pearsonrscipy.stats.ttest_rel )建议的行为是省略所有与其相关的值 nan 。对于具有两个相关数组输入的函数,这意味着:

    y = func(a, b, nan_policy='omit')
    

    行为应与以下内容相同:

    hasnan = np.isnan(a) | np.isnan(b)  # Union of the isnan masks.
    y = func(a[~hasnan], b[~hasnan])
    

    此类函数的文档字符串应该清楚地说明此行为。

  • nan_policy='raise' :提高 ValueError

  • nan_policy='propagate' :传播 nan 值添加到输出。通常,这意味着只执行函数,而不检查 nan ,但是请看

    例如,这可能会导致意外输出。

nan_policy 与一个 axis 参数

这并不令人惊讶--当函数具有 axis 参数。例如,假设, func 将一维数组缩减为标量,并将n-d数组作为一维数组的集合进行处理,并使用 axis 指定要沿其应用缩减的轴的参数。如果是,请说::

func([1, 3, 4])     -> 10.0
func([2, -3, 8, 2]) ->  4.2
func([7, 8])        ->  9.5
func([])            -> -inf

然后::

func([[  1, nan,   3,   4],
      [  2,  -3,   8,   2],
      [nan,   7, nan,   8],
      [nan, nan, nan, nan]], nan_policy='omit', axis=-1)

必须给出结果::

np.array([10.0, 4.2, 9.5, -inf])

边缘情况

一个函数,该函数实现 nan_policy 参数应优雅地处理以下情况 all 输入数组中的值为 nan 。上述基本原则仍然适用:

func([nan, nan, nan], nan_policy='omit')

行为应与以下内容相同:

func([])

实际上,在添加 nan_policy 对于现有函数,发现该函数尚未以定义良好的方式处理这种情况并不少见,可能需要应用一些思想和设计来确保其正常工作。正确的行为(无论是返回 nan 、返回其他值、引发异常或其他内容)将根据具体情况确定。

为什么不 nan_policy 也适用于 inf

虽然我们在小学里学过“无穷大不是数”,但浮点值 naninf 在性质上是不同的。这些价值 inf-inf 其行为更像是常规浮点值,而不是 nan

  • 我们可以与之相提并论 inf 设置为其他浮点值,并且它的行为符合预期,例如 3 < inf 是真的。

  • 在大多数情况下,算术在 inf ,例如 inf + inf = inf-2*inf = -inf1/inf = 0 等。

  • 许多现有函数与“预期的”一起工作 infnp.log(inf) = infnp.exp(-inf) = 0np.array([1.0, -1.0, np.inf]).min() = -1.0 等。

所以虽然 nan 几乎总是意味着“出了问题”或“缺了什么”, inf 在许多情况下可以被视为有用的浮点值。

它与NumPy也是一致的 nan 不可忽略的功能 inf ::

>>> np.nanmax([1, 2, 3, np.inf, np.nan])
inf
>>> np.nansum([1, 2, 3, np.inf, np.nan])
inf
>>> np.nanmean([8, -np.inf, 9, 1, np.nan])
-inf

多么 not 要实施 nan_policy

在过去(可能是现在),一些人 stats 已处理的功能 nan_policy 通过使用掩码数组掩码 nan 值,然后使用 mstats 子包。此方法的问题在于,掩码数组代码可能会将 inf 设置为掩码值,这是我们不想做的(请参见上文)。这还意味着,如果不小心,返回值将是掩码数组,如果它们以常规数组传递,用户可能会感到惊讶。

脚注

1

如果输出的元素仅取决于输入的相应元素(例如 numpy.sinscipy.special.gamma ),那么就不需要 nan_policy 参数。