簡體   English   中英

快速檢查字符串是否可以在python中轉換為float或int

[英]Fast checking if a string can be converted to float or int in python

我需要將大數組中的所有字符串都轉換為int或float類型,如果它們可以轉換的話。 通常,人們建議使用try-except或regex方法(例如在檢查字符串是否可以在Python中轉換為float的方法 ),但是事實證明它非常慢。

問題是:如何以最快的方式編寫該代碼?

我發現有一個字符串的.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 )表明正則表達式的方法甚至更慢。

您的返回值表明您正在使用NumPy。 因此,您應該使用np.loadtxtnp.genfromtxt (帶有dtype dtype=None參數)將行加載到NumPy數組中。 dtype=None參數將自動檢測字符串是否可以轉換為floatint

np.loadtxt是速度更快,需要內存少np.genfromtxt ,但需要你指定的dtype -有沒有dtype=None自動D型檢測選項。 請參閱Joe Kington的帖子進行比較

如果您發現使用np.loadtxtnp.genfromtxt加載CSV仍然太慢,則使用Panda的read_csv函數要快得多 ,但是(當然)需要先安裝Pandas ,結果是Pandas DataFrame ,而不是NumPy數組。 DataFrame具有許多不錯的功能(並且可以轉換為NumPy數組),因此您可能會發現這不僅在加載速度方面而且在數據處理方面都是一個優勢。


順便說一句,如果您未在通話中指定dtype

np.array(data)

然后np.array對所有數據使用單個np.array 如果您的數據同時包含int和float,則np.array將返回一個帶有float np.array的數組:

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

更糟糕的是,如果您的數據包含數字和字符串,則np.array(data)將返回字符串np.array(data)的數組:

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

因此,您檢查所有字符串為intsfloats所有艱苦工作都會在最后一行被銷毀。 np.genfromtxt(..., dtype=None)通過返回結構化數組(具有異類dtype的數組np.genfromtxt(..., dtype=None)來解決此問題。

嘗試對您的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。

所有歸納都是錯誤的(具有諷刺意味的是)。 不能說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 ,則需要評估您是否更有可能遇到無效字符串或有效字符串,並以此為基礎選擇算法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM