将浮点数转换为字符串numba python numpy数组

c86crjj0  于 2023-08-05  发布在  Python
关注(0)|答案(5)|浏览(170)

我正在运行一个@nb.njit函数,试图在其中将一个整数放入字符串数组中。

import numpy as np
import numba as nb

@nb.njit(nogil=True)
def func():
    my_array = np.empty(6, dtype=np.dtype("U20"))
    my_array[0] = np.str(2.35646)
    return my_array

if __name__ == '__main__':
    a = func()
    print(a)

字符串
我得到以下错误:

numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Invalid use of Function(<class 'str'>) with argument(s) of type(s): (float64)


我应该使用哪个函数在numba中执行从floatstring的转换?

vltsax25

vltsax251#

numpy.str功能目前还不支持。所有supported numpy functions的列表可以在Numba的网站上找到。
也不支持内置的str。这可以在supported Python features page上检查。
要做到这一点,唯一的方法是以某种方式创建一个将浮点数转换为字符串的函数,只使用Numba支持的Python和Numpy的功能。
在进入这个方向之前,我仍然要重新考虑将浮点数转换为字符串的必要性。它可能不是很有效,并且由于将浮点数转换为字符串而增加了一些开销,您可能会失去一些函数的好处。
当然,如果不了解更多关于该项目的信息,这很难说。

pgky5nke

pgky5nke2#

我想要一个浮点数到字符串的转换,它提供了一个像“23.45”这样的字符串,带有两个小数位。我的解决方案是这样的。也许它能帮助某人。

def floatToString(self, floatnumber:float32) -> str:
        stringNumber:str = ""
        whole:int = math.floor(floatnumber)
        frac:int = 0
        digits:float = float(floatnumber % 1)
        digitsTimes100:float = float(digits) * float(100.0)
        if digitsTimes100 is not None:
            frac = math.floor(digitsTimes100)
        stringNumber = str(whole)+"."+str(frac)
        return stringNumber

字符串
要注意四舍五入的问题,但这对我来说已经足够了。

k2arahey

k2arahey3#

提供了一种相对通用的方法:

import math
import numba as nb

@nb.njit
def cut_trail(f_str):
    cut = 0
    for c in f_str[::-1]:
        if c == "0":
            cut += 1
        else:
            break
    if cut == 0:
        for c in f_str[::-1]:
            if c == "9":
                cut += 1
            else:
                cut -= 1
                break
    if cut > 0:
        f_str = f_str[:-cut]
    if f_str == "":
        f_str = "0"
    return f_str

@nb.njit
def float2str(value):
    if math.isnan(value):
        return "nan"
    elif value == 0.0:
        return "0.0"
    elif value < 0.0:
        return "-" + float2str(-value)
    elif math.isinf(value):
        return "inf"
    else:
        max_digits = 16
        min_digits = -4
        e10 = math.floor(math.log10(value)) if value != 0.0 else 0
        if min_digits < e10 < max_digits:
            i_part = math.floor(value)
            f_part = math.floor((1 + value % 1) * 10.0 ** max_digits)
            i_str = str(i_part)
            f_str = cut_trail(str(f_part)[1:max_digits - e10])
            return i_str + "." + f_str
        else:
            m10 = value / 10.0 ** e10
            exp_str_len = 4
            i_part = math.floor(m10)
            f_part = math.floor((1 + m10 % 1) * 10.0 ** max_digits)
            i_str = str(i_part)
            f_str = cut_trail(str(f_part)[1:max_digits])
            e_str = str(e10)
            if e10 >= 0:
                e_str = "+" + e_str
            return i_str + "." + f_str + "e" + e_str

字符串
与纯Python相比,它的精确度较低,速度也相对较慢(大约是纯Python的3倍):

numbers = (
    math.nan, math.inf, -math.inf, 0.0, 1.0, 1.000001, -2000.000014, 1234567890.12345678901234567890,
    1234567890.12345678901234567890e10, 1234567890.12345678901234567890e-30,
    1.234e-200, 1.234e200
)
k = 32
for number in numbers:
    print(f"{number!r:{k}}  {str(number)!r:{k}}  {float2str(number)!r:{k}}")
# nan                               'nan'                             'nan'                           
# inf                               'inf'                             'inf'                           
# -inf                              '-inf'                            '-inf'                          
# 0.0                               '0.0'                             '0.0'                           
# 1.0                               '1.0'                             '1.0'                           
# 1.000001                          '1.000001'                        '1.000001'                      
# -2000.000014                      '-2000.000014'                    '-2000.0000139'                 
# 1234567890.1234567                '1234567890.1234567'              '1234567890.123456'             
# 1.2345678901234567e+19            '1.2345678901234567e+19'          '1.234567890123456e+19'         
# 1.2345678901234568e-21            '1.2345678901234568e-21'          '1.234567890123457e-21'         
# 1.234e-200                        '1.234e-200'                      '1.234e-200'                    
# 1.234e+200                        '1.234e+200'                      '1.2339e+200'            

%timeit -n 10 -r 10 [float2str(x) for x in numbers]
# 10 loops, best of 10: 18.1 µs per loop
%timeit -n 10 -r 10 [str(x) for x in numbers]
# 10 loops, best of 10: 6.5 µs per loop


但在Numba中为浮点型参数本机实现str()之前,可以将其用作一种解决方法。
注意,边缘情况被相对粗略地处理,并且在最后1-2个数字中的错误相对频繁。

jgovgodb

jgovgodb4#

虽然这并没有解决float-to-str转换,但我认为值得一提的是,在Numba的后续版本中,您可以在整数上使用str()

import numba as nb

@nb.njit
def int2str(value):
    return str(value)

n = 10
print([int2str(x) for x in range(-n, n)])
# ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

n = 1000
%timeit [str(x) for x in range(-n, n)]
# 1000 loops, best of 5: 391 µs per loop
%timeit [int2str(x) for x in range(-n, n)]
# 1000 loops, best of 5: 813 µs per loop

字符串
这比纯Python慢,但它可能有助于确保njit()加速。
同样的功能还没有为浮动实现。

mqxuamgl

mqxuamgl5#

下面是一个简单的尝试:
1.更简单(如果它对你很重要)
1.可以更改精度
1.考虑NaN值
1.当第一个小数为0时工作(如0.05)
1.在Numba中编译

@nb.jit
def f2s(f, precision=2) :
    if np.isnan(f) :
        return 'NaN'
    s = str(int(np.floor(f))) + '.'
    digits = f%1
    for _ in range(precision) :
        digits *= 10
        s += str(int(np.floor(digits)))
    return s

字符串
如果愿意,可以将int(np.floor(_))替换为math.floor(_)
请参考@norok2答案,以获得更强大/复杂的版本(带有科学符号等...)

相关问题