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

快速Pythonic方法,在捕获ValueErrors时将许多字符串列表转换为浮点列表,python,Python,我在Python中有大约5000万个字符串列表,如下所示: ["1", "1.0", "", "foobar", "3.0", ...] [1.0, 1.0, None, None, 3.0, ...] 我需要把这些变成一个浮动和非浮动的列表,像这样: ["1", "1.0", "", "foobar", "3.0", ...] [1.0, 1.0, None, None, 3.0, ...] 目前我使用的代码如下: def to_float_or_None(x): try:

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

["1", "1.0", "", "foobar", "3.0", ...]
[1.0, 1.0, None, None, 3.0, ...]
我需要把这些变成一个浮动和非浮动的列表,像这样:

["1", "1.0", "", "foobar", "3.0", ...]
[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_或_None函数总共需要750秒(根据cProfile)。。。有没有更快的方法来执行从字符串列表到浮点/非浮点列表的转换

更新
我已经将
to\u float\u或\u 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和厕所!这确实加快了文件的读取速度,但我希望通过将字符串转换为float/Nones来节省一些内存。

编辑:我刚刚意识到我误读了这个问题,我们讨论的是列表列表,而不仅仅是列表。更新以适应这一点

您可以在此处使用a来制作一些更易于阅读的内容:

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]]
编辑:如注释中所述,如果您希望获得绝对最快的结果,请使用
map
,因为我们不使用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)
或:


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

我不知道性能方面的情况,但这应该适用于您的情况

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

或者,如果列表中确实有那么多数据,可以使用诸如Series和
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

还有很多其他的好处——尤其是在以后指定如何处理这些问题时……

到目前为止给出的答案并不能完全回答这个问题<代码>尝试…捕获与验证相比,如果会导致不同的性能(请参阅:)。总结一下答案:取决于失败与成功的比率,以及在这两种情况下测量的失败和成功时间。基本上我们无法回答这个问题,但我们可以告诉您如何:

  • 看看几个有代表性的案例,得出一个比率
  • 编写一个测试与try/catch测试相同的
    if/then
    ,对其进行优化,然后测量两个版本的
    浮动或无失败100次所需的时间,并测量两个版本的
    浮动或无成功100次所需的时间
  • 做一点数学计算,找出哪个会更快
  • 关于列表理解问题的旁注:

    生成器表达式实际上比列表理解更好(只需将
    [
    ]
    字符替换为
    字符),这取决于您是否希望能够为此结果编制索引,或者您是否只想对其进行迭代

    创建基本上不需要时间,而to_float_或_None(这是昂贵的部分)的实际执行可以延迟到需要的结果


    出于许多原因,这是有用的,但如果您需要对其进行索引,它将不起作用。但是,它将允许您使用生成器压缩原始集合,以便您仍然可以访问原始字符串及其float\u或\u none结果。

    也许您可以在多个列表上并行运行它(如果您有>1个内核)。您的最后3行可以写为
    result=map(to\u float\u或\u none,数据库)
    [float(x)如果“0123456789”中的x和x[0]在您的列表中没有其他x]
    @PauloScardine这是一个黑客解决方案。这个怎么样:
    [“1”、“1.0”、“5 foobar”、“3.0”、“3.0”…]
    ?它将对您的解决方案抛出异常。@Lattyware更快?我不这么认为。map在某些情况下可能更快,在其他情况下列出理解。看到一些实际的基准会很好。在我的简单测试用例中,这比OP的版本稍微慢一点。首先,可读性应该胜过速度,除非你能证明这是一个瓶颈。也许在某些情况下,你会发现列表比较慢,但一般来说列表比较快——为什么会这样,请参阅我答案中的链接。这个问题特别是关于让事情变得更快。我引用:“有没有一种更快的方法来执行这个转换…?”(我的重点)。这是真的,但我的观点仍然成立-列表comps应该更快,因为循环是在较低的级别实现的,从而减少了开销。在某些情况下可能不是这样,但总的来说,它应该是最快的方法。你似乎断言你的方法是最快的方法。请给出一些实际的基准来支持这个特定问题。在我的简单测试用例中,这比OP的版本稍慢。我看不出使用映射或列表理解在速度上有任何区别,大部分时间都花在to_float_或_None函数上。这是真的-请求原谅,只有当异常是规则的异常时,not权限咒语才有效。在大多数情况下,这都是正确的——我认为,考虑到这些例子,我们也可以假设这种情况是正确的,但如果不是这样,那么可能值得首先验证,而不是在失败时捕获异常。这是唯一的答案,它不仅要求我重写“循环”,而且解决了实际功能。确实是c