快速检查字符串是否可以在python中转换为float或int

时间:2014-07-26 20:28:49

标签: python

如果可以转换,我需要将大型数组中的所有字符串转换为int或float类型。通常,人们建议使用try-except或regex方法(如Checking if a string can be converted to float in Python中所述),但事实证明它非常慢。

问题是:如何以最快的方式编写代码?

我发现字符串有.isdigit()方法。花车有类似的东西吗?

这是当前(慢)代码。

    result = []
    for line in lines:
        resline = []
        for item in line:
            try:
                resline.append(int(item))       
            except:        
                try:
                    resline.append(float(item))     
                except:
                    resline.append(item)            
        result.append(resline)          
    return np.array(result)

还有一些证据(https://stackoverflow.com/a/2356970/3642151),正则表达式方法甚至更慢。

3 个答案:

答案 0 :(得分:2)

您的返回值显示您正在使用NumPy。因此,您应该使用np.loadtxtnp.genfromtxt(使用dtype=None参数)将行加载到NumPy数组中。 dtype=None参数会自动检测字符串是否可以转换为floatint

np.loadtxt速度更快,所需内存少于np.genfromtxt,但要求您指定dtype - 没有dtype=None自动dtype-detection选项。请参阅Joe Kington's post for a comparsion

如果您发现使用np.loadtxt加载CSV或np.genfromtxt仍然太慢,那么使用Panda的read_csv function is much much faster,但(当然)首先需要您install Pandas ,结果将是Pandas DataFrame,而不是NumPy数组。 DataFrames有许多不错的功能(并且可以转换为NumPy数组),因此您可能会发现这不仅在加载速度方面而且在数据操作方面都具有优势。


顺便说一句,如果你没有在通话中指定dtype

np.array(data)

然后np.array对所有数据使用单个dtype。如果您的数据包含整数和浮点数,那么np.array将返回一个浮点数dtype的数组:

In [91]: np.array([[1, 2.0]]).dtype
Out[91]: dtype('float64')

更糟糕的是,如果您的数据包含数字和字符串,np.array(data)将返回字符串dtype数组:

In [92]: np.array([[1, 2.0, 'Hi']]).dtype
Out[92]: dtype('S32')

因此,通过检查哪些字符串intsfloats在最后一行中被破坏,您所做的所有艰苦工作。 np.genfromtxt(..., dtype=None)通过返回结构化数组(具有异类dtype的数组)解决了这个问题。

答案 1 :(得分:2)

尝试分析您的Python脚本,您会发现try... exceptfloatint不是您脚本中最耗时的调用。

import random
import string
import cProfile

def profile_str2float(calls):
    for x in xrange(calls):
        str2float(random_str(100))

def str2float(string):
    try:
        return float(string)
    except ValueError:
        return None

def random_str(length):
    return ''.join(random.choice(string.lowercase) for x in xrange(length))

cProfile.run('profile_str2float(10**5)', sort='cumtime')

运行此脚本我得到以下结果:

         40400003 function calls in 14.721 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   14.721   14.721 <string>:1(<module>)
        1    0.126    0.126   14.721   14.721 str2float.py:5(profile_str2float)
   100000    0.111    0.000   14.352    0.000 str2float.py:15(random_str)
   100000    1.413    0.000   14.241    0.000 {method 'join' of 'str' objects}
 10100000    4.393    0.000   12.829    0.000 str2float.py:16(<genexpr>)
 10000000    7.115    0.000    8.435    0.000 random.py:271(choice)
 10000000    0.760    0.000    0.760    0.000 {method 'random' of '_random.Random' objects}
 10000000    0.559    0.000    0.559    0.000 {len}
   100000    0.242    0.000    0.242    0.000 str2float.py:9(str2float)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

从累积时间统计数据可以看出,str2float函数消耗的CPU时间不多,在100.000次调用中它几乎不使用250ms。

答案 2 :(得分:2)

所有概括都是错误的(具有讽刺意味)。人们不能说try: except:总是比正则表达更快,反之亦然。在您的情况下,正则表达式并不过分,并且比try: except:方法快得多。但是,根据我们在您的问题的评论部分中的讨论,我继续实施了一个有效执行此转换的C库(因为我在SO上看到了很多这个问题);该库名为fastnumbers。以下是使用try: except:方法,使用正则表达式和使用fastnumbers的时序测试。


from __future__ import print_function
import timeit

prep_code = '''\
import random
import string
x = [''.join(random.sample(string.ascii_letters, 7)) for _ in range(10)]
y = [str(random.randint(0, 1000)) for _ in range(10)]
z = [str(random.random()) for _ in range(10)]
'''

try_method = '''\
def converter_try(vals):
    resline = []
    for item in vals:
        try:
            resline.append(int(item))
        except ValueError:
            try:
                resline.append(float(item))
            except ValueError:
                resline.append(item)

'''

re_method = '''\
import re
int_match = re.compile(r'[+-]?\d+$').match
float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match
def converter_re(vals):
    resline = []
    for item in vals:
        if int_match(item):
            resline.append(int(item))
        elif float_match(item):
            resline.append(float(item))
        else:
            resline.append(item)

'''

fn_method = '''\
from fastnumbers import fast_real
def converter_fn(vals):
    resline = []
    for item in vals:
        resline.append(fast_real(item))

'''

print('Try with non-number strings', timeit.timeit('converter_try(x)', prep_code+try_method), 'seconds')
print('Try with integer strings', timeit.timeit('converter_try(y)', prep_code+try_method), 'seconds')
print('Try with float strings', timeit.timeit('converter_try(z)', prep_code+try_method), 'seconds')
print()
print('Regex with non-number strings', timeit.timeit('converter_re(x)', prep_code+re_method), 'seconds')
print('Regex with integer strings', timeit.timeit('converter_re(y)', prep_code+re_method), 'seconds')
print('Regex with float strings', timeit.timeit('converter_re(z)', prep_code+re_method), 'seconds')
print()
print('fastnumbers with non-number strings', timeit.timeit('converter_fn(x)', prep_code+fn_method), 'seconds')
print('fastnumbers with integer strings', timeit.timeit('converter_fn(y)', prep_code+fn_method), 'seconds')
print('fastnumbers with float strings', timeit.timeit('converter_fn(z)', prep_code+fn_method), 'seconds')
print()

我的机器上的输出如下所示:

Try with non-number strings 55.1374599934 seconds
Try with integer strings 11.8999788761 seconds
Try with float strings 41.8258318901 seconds

Regex with non-number strings 11.5976541042 seconds
Regex with integer strings 18.1302199364 seconds
Regex with float strings 19.1559209824 seconds

fastnumbers with non-number strings 4.02173805237 seconds
fastnumbers with integer strings 4.21903610229 seconds
fastnumbers with float strings 4.96900391579 seconds

有些事情很清楚

    对于非数字输入,
  • try: except: 非常慢;正则表达式轻松击败
  • 如果不需要提出例外,
  • try: except:会变得更有效率
  • fastnumbers在所有情况下都打败裤子

因此,如果您不想使用fastnumbers,则需要评估您是否更有可能遇到无效字符串或有效字符串,并根据该算法选择算法。