Numpy:检查float数组是否包含整数

f8rj6qna  于 2023-08-05  发布在  其他
关注(0)|答案(5)|浏览(89)

在Python中,可以使用n.is_integer()检查float是否包含整数值,基于以下QA:How to check if a float value is a whole number的数据。
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的方式检查浮点数的小数部分?

eaf3rand

eaf3rand1#

据我所知,没有这样的函数返回一个布尔数组,指示浮点数是否有小数部分。我能找到的最接近的是np.modf,它返回小数和整数部分,但它创建了两个浮点数组(至少暂时),所以它可能不是最好的内存方式。
如果你在工作中感到快乐,你可以尝试以下方法:

>>> np.mod(x, 1, out=x)
>>> mask = (x == 0)

字符串
这应该比使用round或floor保存内存(在这里必须保留x),但当然会丢失原始的x
另一个选择是要求在Numpy中实现它,或者自己实现它。

w51jfk4q

w51jfk4q2#

我需要这个问题的答案,原因略有不同:检查何时可以将整个浮点数数组转换为整数而不丢失数据。
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的东西来替换减法,以在一定的容差范围内进行检查(当然,您应该小心,因为截断不是正确的舍入!).

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解析)的开销。

kiayqfof

kiayqfof3#

虽然(x % 1) == 0的公认方法已经足够了,但让我感到困扰的是,在numpy中没有办法在本机实现这一点,特别是考虑到vanilla python中存在float.is_integer
因此,我对numpy支持的浮点格式(float16float32float64float128(实际上是extended precision))和how to write a 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;
}

字符串
我继续将这个函数及其兄弟函数 Package 在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。

k5ifujac

k5ifujac4#

你也可以在列表解析中使用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])

字符串
the answer using mod 1相比,对于给定的4个值的示例(5.66 us vs 8.03 us),这稍微快一点,对于1000个值的数组,速度快3倍以上。

n3schb8v

n3schb8v5#

受公认答案的启发,这里有一个使用%运算符的非就地版本:

modulus = x % 1
mask = modulus == 0

字符串
或者更简洁地说

mask = (x % 1) == 0

相关问题