Numpy:检查浮点数组是否包含整数

16

在Python中,可以使用n.is_integer()检查float是否包含整数值,这是基于如何检查浮点值是否为整数的问答。

NumPy是否有类似的操作可应用于数组?是否有一些方法可以实现以下功能:

>>> x = np.array([1.0 2.1 3.0 3.9])
>>> mask = np.is_integer(x)
>>> mask
array([True, False, True, False], dtype=bool)

有可能做类似于这样的事情

>>> mask = (x == np.floor(x))
或者
>>> mask = (x == np.round(x))

但它们涉及调用额外的方法并创建一堆可能可以避免的临时数组。

NumPy是否有一个矢量化函数,以类似于Python的 float.is_integer 的方式检查浮点数的小数部分?

5个回答

11
据我所知,没有这样的函数可以返回一个布尔数组,以指示浮点数是否具有分数部分。我能找到的最接近的是np.modf,它返回小数部分和整数部分,但这会创建两个浮点数数组(至少暂时),因此在内存方面可能不是最佳选择。
如果你乐意在同一位置工作,你可以尝试类似这样的方法:
>>> np.mod(x, 1, out=x)
>>> mask = (x == 0)

相较于使用round或floor(需要保留x的值),这应该可以节省内存,但你当然会失去原来的x。

另一个选择是要求在Numpy中实现它,或自己实现。


1
有趣的是你提到了这个。我之所以问,是因为我正在考虑做这件事。 - Mad Physicist
修改:不会更快吗?应该用位于运算符吧? 修改:哦,对了,它们本质上只适用于整数,而不是浮点数!最近花了太多时间在JS整数强制转换方面了... - Job

5

我有一个问题需要解答:如何在不丢失数据的情况下将整个浮点数数组转换为整数。

Hunse的回答对我几乎有用,但显然我不能使用就地技巧,因为我需要能够撤销操作:

if np.all(np.mod(x, 1) == 0):
    x = x.astype(int)

接下来,我想到了以下选项,这个选项在许多情况下可能更快:

x_int = x.astype(int)
if np.all((x - x_int) == 0):
    x = x_int

原因在于模运算比减法慢。然而,现在我们提前将其转换为整数 - 相对而言,我不知道这个操作有多快。但是如果大多数的数组都是整数(在我的情况下是这样),后者几乎肯定更快。
另一个好处是,您可以使用类似np.isclose的东西替换subraction以在一定容差范围内检查(当然,在这里应该小心,因为截断不是适当的舍入!)。
x_int = x.astype(int)
if np.all(np.isclose(x, x_int, 0.0001)):
    x = x_int

编辑:如果适用于您的用例,则更慢但可能值得的是,也可以单独转换整数。

x_int = x.astype(int)
safe_conversion = (x - x_int) == 0
# if we can convert the whole array to integers, do that
if np.all(safe_conversion):
    x = x_int.tolist()
else:
    x  = x.tolist()
    # if there are _some_ integers, convert them
    if np.any(safe_conversion):
        for i in range(len(x)):
            if safe_conversion[i]:
                x[i] = int(x[i])

作为一个例子,这很重要:这对我很有用,因为我有稀疏数据(即大多数都是零),我将其转换为JSON一次,并在服务器上重复使用。对于浮点数,ujson 将它们转换为 [ ...,0.0,0.0,0.0,... ],对于整数,则结果为[...,0,0,0,...],从而可以节省字符串中字面量数量的一半以上。这样可以减少服务器端(较短的字符串)和客户端(较短的字符串,假定 JSON 解析速度稍快)的开销。

1
刚意识到这是我自己的问题。感谢你给出的精彩回答。 - Mad Physicist
1
不客气!:) 我刚刚意识到,如果 一些 元素可以安全地转换,那么这样做所增加的开销对我来说是值得的,因此我还添加了如何执行此操作的代码。 - Job
1
刚试了一下并进行了一些基准测试。对我来说,减法始终比取模快(2-3倍),无论整数值浮点数的数量如何。但我认为还有一个更优化的答案:def is_int_valued(x): return np.all(x == np.floor(x)) - ivirshup
啊,这样就避免了浮点数到整数的转换,以及比较时再次转换,所以这很有道理!但是,如果你期望进行转换,那么可能不是最优的,因为你还需要再次进行整数转换。我猜每个用例都需要基准测试 :) - Job

3
虽然接受的方法 (x % 1) == 0 相当合适,但让我困扰的是在numpy中没有原生地实现这个方法的方式,尤其是考虑到在纯Python中已经存在 float.is_integer 的情况下。
我因此对numpy支持的浮点格式(float16, float32, float64, float128 (实际上是扩展精度))以及如何编写ufunc进行了一些研究。
结果是,对于足够小以适应相应无符号整数类型的IEEE754浮点数(在普通计算机上几乎包括所有float64之前),可以通过一些简单的位操作进行检查。例如,这里是一个C99函数,可以非常快速地告诉您的float32是否包含整数值:
#include <stdint.h>

int is_integer(float n)
{
    uint32_t k = ((union { float n; uint32_t k; }){n}).k;

    // Zero when everything except sign bit is zero
    if((k & 0x7FFFFFFF) == 0) return 1;

    uint32_t exponent = k & 0x7F800000;

    // NaN or Inf when the exponent bits are all ones
    // Guaranteed fraction when exponent < 0
    if(exponent == 0x7F800000 || exponent < 0x3F800000) return 0;
    // Guaranteed integer when exponent >= FLT_MANT_DIG - 1
    if(exponent >= 0x4B000000) return 1;
    // Otherwise, check that the significand bits past the exponent are zeros
    return (k & (0x7FFFFF >> ((exponent >> 23) - 0x7F))) == 0;
}

我在这个函数和它的兄弟函数中新增了一个ufunc封装,你可以在这里找到:https://gitlab.com/madphysicist/is_integer_ufunc。一个很好的特性是,这个ufunc对于所有整数类型都返回True,而不是引发错误。另一个特性是,它的速度比(x % 1) == 0快5倍到40倍不等,取决于dtype和输入大小。
根据链接的教程,你可以通过 python setup.py {build_ext --inplace, build, install} 来安装,具体取决于你有多需要它。也许我应该看看numpy社区是否有兴趣将这个ufunc包含在内。

2

你也可以在列表推导式中使用Python方法。

>>> x = np.array([1.0, 2.1, 3.0, 3.9])
>>> mask = np.array([val.is_integer() for val in x])
>>> mask
array([ True, False,  True, False])

与使用mod 1的答案相比,对于给定的4个值的示例,这种方法略微更快(5.66微秒与8.03微秒),并且对于包含1000个值的数组,速度快了3倍以上。


0
受到被接受的答案的启发,这里提供一个使用%运算符的非就地版本:
modulus = x % 1
mask = modulus == 0

更简洁地说
mask = (x % 1) == 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接