0

まず第一に、これに関連するかなりの数の質問を見てきました (文字列をフロートに変換するなど)。同様の問題)。私は解決策を作成しましたが、それが 1) パフォーマンスと 2) pythonic エレガンスの点で最適な解決策であるかどうか疑問に思っています。

要するに問題:

  • 私はさまざまなソースからデータを取得します。これらは辞書付きのリストになります (行/列テーブルのセットアップとして)。
  • 多様性は、固定の入力タイプ (基本的には string、boolean、int、float の可能性があります) に依存できないことを意味しますが、ユーザーはどの列 (dict のキー) が値であるかを指定できます。
  • 次に、これを実際の値の型に変換する必要があります (ここでは数億行のデータについて話しているため、パフォーマンスが重要です)。
  • 入力が実数でない場合 ('aaa' など)、None を返す必要があります。
  • 通貨記号と桁区切り記号 (削除する必要があります)、および小数点記号 (ドットでない場合は、標準のドットに置き換える必要があります) がある場合があります。

だから私は何を作った:

import ast
import types
NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType)

def mk_value(s, currency_sign='', thousand_sep='', decimal_sep='.'):
    if isinstance(s, bool): # make boolean into a 0/1 value
            if s:
                result = 1
            else:
                result = 0
    elif isinstance(s, NumberTypes): # keep numbers as/is
        result = s
    else: # convert a string
        # prepare the string for conversion
        if currency_sign != '':
            s = s.replace(currency_sign, '')
        if thousand_sep != '':
            s = s.replace(thousand_sep, '')
        if decimal_sep != '.':
            s = s.replace(decimal_sep, '.')        
        s = s.strip()
        # convert the string
        if s == '':
            result = None
        else:
            try:
                # convert the string by a safe evaluation
                result = ast.literal_eval(s)
                # check if result of the evaluation is a number type
                if not isinstance(result, NumberTypes):
                    result = None
            except ValueError:
                # if the conversion gave an error, the string is not a number
                result = None
    return result

次の方法でテストできます。

mk_value(True)
mk_value(1234)
mk_value(1234.56)
mk_value('1234')
mk_value('1234.56')
mk_value('1,234.56') # without an explicit decimal separator this is not a number
mk_value('1.234.567,89 EUR', currency_sign='EUR', thousand_sep='.', decimal_sep=',') # all exceptions

したがって、これは機能します(私が見る限り)。しかし、これは最良の/最もpythonicな方法ですか? もっと速い方法はありますか?これについて Cython を調べる必要がありますか? これを改善するためのアイデアは本当に役に立ちます!

ブラジル

カースト

編集: Andrew と WoLpH の提案に基づいてコードを更新しました。次のようになります。

import types
NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType)

def mk_value(s, currency_sign='', thousand_sep='', decimal_sep='.'):
    if isinstance(s, bool): # make boolean into a 0/1 value
            if s:
                result = 1
            else:
                result = 0
    elif isinstance(s, NumberTypes): # keep numbers as/is
        result = s
    else: # convert a string
        # prepare the string for conversion
        if currency_sign:
            s = s.replace(currency_sign, '')
        if thousand_sep:
            s = s.replace(thousand_sep, '')
        if decimal_sep != '.':
            s = s.replace(decimal_sep, '.')        
        s = s.strip()
        # convert the string
        if not s: # if the string is empty, it's not a number
            result = None
        else:
            try: # try int
                result = int(s)
            except ValueError:
                try: # if there's an error, try float
                    result = float(s)
                except ValueError:                    
                    # if the conversion gave an error, the string is not a number
                    result = None
    return result

以前のコードのパフォーマンスは次のとおりです。

>>> timeit.timeit("mk_value(1234)", 'from __main__ import mk_value', number=100000)
0.050575971603393555
>>> timeit.timeit("mk_value(1234.56)", 'from __main__ import mk_value', number=100000)
0.07073187828063965
>>> timeit.timeit("mk_value('1234')", 'from __main__ import mk_value', number=100000)
0.8333430290222168
>>> timeit.timeit("mk_value('1234.56')", 'from __main__ import mk_value', number=100000)
0.8230760097503662
>>> timeit.timeit("mk_value('1,234.56', thousand_sep=',')", 'from __main__ import mk_value', number=100000)
0.9358179569244385

新しいコードのパフォーマンス:

>>> timeit.timeit("mk_value(1234)", 'from __main__ import mk_value', number=100000)
0.04723405838012695
>>> timeit.timeit("mk_value(1234.56)", 'from __main__ import mk_value', number=100000)
0.06952905654907227
>>> timeit.timeit("mk_value('1234')", 'from __main__ import mk_value', number=100000)
0.1798090934753418
>>> timeit.timeit("mk_value('1234.56')", 'from __main__ import mk_value', number=100000)
0.45616698265075684
>>> timeit.timeit("mk_value('1,234.56', thousand_sep=',')", 'from __main__ import mk_value', number=100000)
0.5290899276733398

したがって、それははるかに高速です。最も複雑なものではほぼ2倍、intでははるかに高速です(try/exceptロジックの最初のものだと思います)。ご意見をお寄せいただきありがとうございます。

誰かがさらに改善する方法について素晴らしいアイデアを持っているかどうかを確認するために、今のところ開いたままにします:) 少なくとも、これが将来他の人に役立つことを願っています(これは非常に一般的な問題でなければなりません)

4

2 に答える 2