快速Pythonic方法将许多字符串列表转换为浮点数列表,同时捕获ValueErrors

时间:2012-03-29 12:52:35

标签: python

我在Python中有大约5000万个字符串列表,如下所示:

["1", "1.0", "", "foobar", "3.0", ...]

我需要将这些转换为浮动列表和Nones,如下所示:

[1.0, 1.0, None, None, 3.0, ...]

目前我使用的代码如下:

def to_float_or_None(x):
    try:
        return float(x)
    except ValueError:
        return None

result = []
for record in database:
    result.append(map(to_float_or_None, record))

to_float_or_None函数总共花费大约750秒(根据cPro​​file)...有没有更快的方法来执行从字符串列表到浮点数/ Nones列表的转换?

更新
我已经将to_float_or_None函数确定为主要瓶颈。我发现使用map和使用列表推导之间的速度没有显着差异。 我应用了Paulo Scardine的提示来检查输入,它已经节省了1/4的时间。

def to_float_or_None(x):
    if not(x and x[0] in "0123456789."):
        return None
    try:
        return float(x)
    except:
        return None

发电机的使用对我来说是新的,所以感谢您提示Cpfohl和Lattyware!这确实加快了文件的读取速度,但我希望通过将字符串转换为浮点数/ Nones来节省一些内存。

4 个答案:

答案 0 :(得分:2)

编辑:我刚刚意识到我误解了这个问题,我们正在讨论列表清单,而不仅仅是清单。更新以适应这种情况。

你可以在这里使用list comprehension来制作一些faster的东西,并且更好阅读:

def to_float_or_None(x):
    try:
        return float(x)
    except ValueError:
        return None

database = [["1", "1.0", "", "foobar", "3.0"], ["1", "1.0", "", "foobar", "3.0"]]

result = [[to_float_or_None(item) for item in record] for record in database]

给我们:

[[1.0, 1.0, None, None, 3.0], [1.0, 1.0, None, None, 3.0]]

编辑:正如评论中Paolo Moretti所述,如果您想要绝对最快的结果,那么使用map may be faster,因为我们没有使用lambda函数:

def to_float_or_None(x):
    try:
        return float(x)
    except ValueError:
        return None

database = [["1", "1.0", "", "foobar", "3.0"], ["1", "1.0", "", "foobar", "3.0"]]

result = [list(map(to_float_or_None, record)) for record in database]

给我们相同的结果。但是,我要注意,过早优化是一件坏事。如果你已经将此确定为应用程序中的瓶颈,那么公平,但除此之外坚持使用更快的可读性。

我们仍然使用外部循环的列表推导,因为我们需要一个lambda函数再次使用map,因为它依赖于record

result = map(lambda record: map(to_float_or_None, record), database)

当然,如果你想懒惰地评估这些,你可以使用生成器表达式:

((to_float_or_None(item) for item in record) for record in database)

或者:

(map(to_float_or_None, record) for record in database)

除非您一次需要整个列表,否则这将是首选方法。

答案 1 :(得分:2)

我不了解性能方面,但这应该适用于您的情况。

list_floats = [to_float_or_None(item) for item in original_list]

答案 2 :(得分:2)

或者,如果您在列表中确实拥有那么多数据,可以使用pandas系列和apply() lambda函数进行转换:

import pandas,re

inlist = ["1", "1.0", "", "foobar", "3.0"] # or however long...
series = pandas.Series(inlist)
series.apply(lambda x: float(x) if re.match("^\d+?(\.\d+?)*$",x) else None)

Out[41]: 
0     1
1     1
2   NaN
3   NaN
4     3

许多其他优点 - 尤其是之后指定您希望如何处理missing values ...

答案 3 :(得分:2)

到目前为止给出的答案并没有完全回答这个问题。 try...catch与验证if then相比可能会导致不同的效果(请参阅:https://stackoverflow.com/a/5591737/456188)。总结答案:取决于失败与成功的比率以及两种情况下失败和成功的 MEASURED 时间。基本上我们不能回答这个问题,但我们可以告诉你如何:

  1. 查看一些具有代表性的案例以获得比率。
  2. 编写一个与if/then进行同等测试的try/catch优化它,然后测量to_float_or_None两个版本失败100次所需的时间,并测量两者的使用时间to_float_or_None的版本要成功100次。
  3. 做一点数学计算,找出哪个会更快。
  4. 关于列表理解问题的附注:

    根据您是否希望能够对其结果进行索引,或者您是否只想迭代它,生成器表达式实际上甚至比列表理解更好(只需替换[ { {1}} ] (个字符的字符。

    基本上没有时间创建,并且to_float_or_None(这是一个昂贵的部分)的实际执行可以延迟到它需要的结果。

    由于许多原因,这很有用,但如果您需要对其进行索引,则无法使用。它但是,允许您使用生成器压缩原始集合,这样您仍然可以访问原始字符串及其float_or_none结果。