使用“安全”的方式将float64转换为int64

4
我希望将一个NumPy(版本1.11.0)数组从float64转换为int64。我希望这个操作能够在整数上执行,但在非整数上失败。
我原以为可以使用casting=safe,但很明显我的想法是错误的...
我希望以下代码可以正常运行:
np.array([1.0]).astype(int, casting='safe')

而这将会失败:

np.array([1.1]).astype(int, casting='safe')

但他们都以此错误失败:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-51-7261ddf00794> in <module>()
    1 import numpy as np
    2 print(np.__version__)
----> 3 np.array([1.0]).astype(int, casting='safe')

TypeError: Cannot cast array from dtype('float64') to dtype('int64') according to the rule 'safe'

我猜我对安全转换的理解有误,因此这可能不是实现这一目标的最佳方法。有没有更好的方法使第一个示例正常工作但第二个示例失败?


2
我认为 safe 是用于转换时不会丢失任何信息的强制类型转换。它并不关心值,只关心类型。换句话说,float -> int 并不是对于整个 float 域来说都是安全的转换。但反过来应该是安全的。 - ebolyen
2个回答

4

我不知道如何在NumPy中直接完成此操作。我找到的最快的方法是将一个浮点型dtype数组整数转换为int dtype数组:

import numpy as np

def float_to_int(array):
    int_array = array.astype(int, casting='unsafe', copy=True)
    if not np.equal(array, int_array).all():
        raise TypeError("Cannot safely convert float array to int dtype. "
                        "Array must only contain whole numbers.")
    return int_array

测试正确性:

In [3]: whole_numbers = np.arange(1000000, dtype=float)

In [4]: fractional = np.arange(0, 100000, 0.1, dtype=float)

In [5]: float_to_int(whole_numbers)
Out[5]: array([     0,      1,      2, ..., 999997, 999998, 999999])

In [6]: float_to_int(fractional)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-0a7807a592b7> in <module>()
----> 1 float_to_int(fractional)

<ipython-input-2-953668ae0922> in float_to_int(array)
      2         int_array = array.astype(int, casting='unsafe', copy=True)
      3         if not np.equal(array, int_array).all():
----> 4                 raise TypeError("Cannot safely convert float array to int dtype. "
      5                                 "Array must only contain whole numbers.")
      6         return int_array

TypeError: Cannot safely convert float array to int dtype. Array must only contain whole numbers.

基于https://dev59.com/hlsW5IYBdhLWcg3wX2Zz#35042794https://dev59.com/2XNA5IYBdhLWcg3wgeEd#7236784,我尝试使用np.modf实现,但这比以上解决方案更慢:

def float_to_int_mod(array):
    mod_array, int_array = np.modf(array)
    if not np.equal(mod_array, 0).all():
        raise TypeError("Cannot safely convert float array to int dtype. "
                        "Array must only contain whole numbers.")
    return int_array.astype(int, casting='unsafe', copy=True)

运行时比较:

In [8]: %timeit float_to_int(whole_numbers)
100 loops, best of 3: 2.75 ms per loop

In [9]: %timeit float_to_int_mod(whole_numbers)
100 loops, best of 3: 8.74 ms per loop

这是使用Python 3和numpy 1.11.0进行测试的。


2
你已经接近成功了:
In [7]: a = np.array([1.1, 2.2, 3.3])

In [8]: a.astype(int) == a
Out[8]: array([False, False, False], dtype=bool)

In [9]: if (a.astype(int) != a).any():
   ...:     raise ValueError("Conversion failed")
   ...: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-9-3628e8ae2ce8> in <module>()
      1 if (a.astype(int) != a).any():
----> 2     raise ValueError("Conversion failed")
      3 

ValueError: Conversion failed

In [10]: b = np.array([1.0, 2.0, 3.0])

In [11]: b.astype(int) == b
Out[11]: array([ True,  True,  True], dtype=bool)

“安全”与“不安全”的转换仅涉及数据类型,而不是数值。因此,将float64转换为int64是“不安全”的,因为至少存在一个浮点数无法完全损失地转换为整数。


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