代码之家  ›  专栏  ›  技术社区  ›  Peter Hansen

不进行静默的NAN/INF转换,将字符串转换为浮点

  •  1
  • Peter Hansen  · 技术社区  · 15 年前

    我想使用python 2.6和更高版本将字符串转换为float,但是 没有 默默无语地转换像 'NaN' 'Inf' 浮动对象。我确实希望它们被静默地忽略,就像对任何不能作为浮点表示的文本一样。

    2.6之前, float("NaN") 将在Windows上引发ValueError。现在它返回一个float,math.isnan()为其返回true,这对我的应用程序来说不是很有用的行为。(正如所指出的,这始终是一种依赖于平台的行为,但无论发生在何处,出于我的目的,都将其视为一种不受欢迎的行为。)

    以下是我目前的情况:

    import math
    def get_floats(source):
        for text in source.split():
            try:
                val = float(text)
                if math.isnan(val) or math.isinf(val):
                    raise ValueError
                yield val
            except ValueError:
                pass
    

    这是一个生成器,我可以提供一个字符串,其中包含表示实数的空格分隔序列。我希望它只生成那些纯数字表示浮点的字段,如“1.23”或“-34e6”,但不包括“nan”或“-inf”。那些根本不浮动的东西,例如“你好”,也应该被忽略。

    测试用例:

    assert list(get_floats('1.23 foo -34e6 NaN -Inf')) == [1.23, -34000000.0]
    

    请建议您认为更优雅的替代方案,即使它们涉及“先看后跳”(在Python中通常被认为是一种较小的方法)。

    编辑 为了澄清诸如“hello”之类的非浮动文本也应该被安静地忽略。其目的是只提取那些实数,而忽略其他一切。

    4 回复  |  直到 7 年前
        1
  •  2
  •   user97370    15 年前

    我会这样写的。我认为它结合了简洁性和可读性。

    def is_finite(x):
        return not math.isnan(x) and not math.isinf(x)
    
    def get_floats(source):
        for x in source.split():
            try:
                yield float(x)
            except ValueError:
                pass
    
    def get_finite_floats(source):
        return (x for x in get_floats(source) if is_finite(x))
    
        2
  •  1
  •   unutbu    15 年前

    这是一个很小的建议,但是 continue 比引发异常要快一点:

    def get_floats(source):
        for text in source.split():
            try:
                val = float(text)
                if math.isnan(val) or math.isinf(val): continue
                yield val
            except ValueError:
                pass
    

    使用 raise ValueError :

    % python -mtimeit -s'import test' "list(test.get_floats('1.23 -34e6 NaN -Inf Hello'))"
    10000 loops, best of 3: 22.3 usec per loop
    

    使用 持续 :

    % python -mtimeit -s'import test' "list(test.get_floats_continue('1.23 -34e6 NaN -Inf Hello'))"
    100000 loops, best of 3: 17.2 usec per loop
    
        3
  •  0
  •   Peter Hansen    15 年前

    我投票赞成保罗·汉金的可读性答案,不过如果我不想把代码分割得这么多的话,这里有一个我的原始版本的变体,它就不那么笨拙了。

    def get_only_numbers(source):
        '''yield all space-separated real numbers in source string'''
        for text in source.split():
            try:
                val = float(text)
            except ValueError:
                pass  # ignore non-numbers
            else:
                # "NaN", "Inf" get converted: explicit test to ignore them
                if not math.isnan(val) and not math.isinf(val):
                    yield val
    

    但与我最初的生活还是没有什么不同。

        4
  •  0
  •   agcala    7 年前

    怎么样

    for line in tf.readlines():
      data =[]
      for x in line.strip().split(','):
        if x.replace('.','',1).isdecimal():
            data.append(float(x))