bugfix> python > 投稿

私はこのようなリストとしてオーダーブックから値を取得します:

list1 = [...,'ethbtc', '0.077666', '10', '0.077680', '15',...]
------------------------ ^ symbol ----- ^ value ----- ^ quantity--

このリストには約100個のシンボルがあり、各シンボルには40個の値があります。それらは常に同じ順序です。
残高の100%を支払う場合、この瞬間にシステムが購入する最高価格を調べたいと思います。

ですから、0.077666で11 ETHを購入したい場合、実際の価格は0.077680になります。最初の価格では10 ETHしか利用できないからです。
それは現時点で多くなるため、私は平均を取得したくない

私のコードにはネストされたforループがあり、2つのリストをループします。

  1. coinlist = 100個すべてのシンボルがこの symbollist = [ethbtc, eoseth,...] のようにリストされている場所  
  2. a と呼ばれるインデックスのリスト値と数量は常に同じ場所にあるため
    a = ['1', '3', '5', ...]  

私のコード:

for symbolnow in symbollist:
sumlist = []
    for i in a:
        quantity = float(list1[list1.index(symbolnow) + (i+1)] if symbolnow in list1 else 0)
        sumlist.append(quantity)
        if sum(sumlist) > mycurrentbalance:
            maxvalue = float(list1[list1.index(symbolnow) + i] if symbolnow in list1 else -1)
            break
        else:
            maxvalue = -1

それで、このコードは何をしますか:
1)シンボルリスト内のすべてのシンボルをループします
2)見つかったすべてのシンボルについて、利用可能な数量を探します
3)残高(つまり、10 ETH)がqtyより小さい場合、ループが中断します
4)そうでない場合、十分になるまで、合計リスト内のすべての数量を検索および要約し続けます。

コードは意図したとおりに動作しますが、それほど高速ではありません。予想通り list1.index 実行に時間がかかります。

質問
より高速なコードはどのように機能しますか。このシナリオまたは正規表現でリストの理解が優れていますか?私のコードは非常にいですか?

前もって感謝します!

編集:
入力と目的の出力を明確にするためのサンプル:

list1 = [...,'ethbtc', '0.077666', '1', '0.077680', '1.5', '0.077710', '3', '0.078200', '4',...]
mycurrentbalance = 5.5 <-残高はETHにあります
list1 の3番目のエントリごとETHの数量であるため、リストでは ['1', '1.5', '3', '4'] になります

したがって、すべてのETH(この場合は5.5)を販売したい場合、最大値は '0.077710'になります

list1 'ethbtc' の前後に100個のシンボルが含まれている他の値の量と記号があります

回答 3 件
  • user3080953からの回答に加えて、データを前処理する必要があるのは、それがより効率的であるだけでなく、複雑さを処理するのに役立つからです。ここでは、リストのデコードとデータの使用という2つのことを同時に行っています。最初にデコードしてから使用します。

    私の意見では、ターゲット形式は次のようになります。

    prices_and_quantities_by_symbol = {
        'ethbtc': {
            'prices':[0.077666, 0.077680, 0.077710, 0.078200], 
            'quantities':[1, 1.5, 3, 4]
        }, 
        'btceth': {
            ...
        }, 
    ...}
    
    

    今、あなたはする必要があります:

    for symbol, prices_and_quantities in prices_and_quantities_by_symbol.items(): # O(len(symbol_list))
        total = 0
        for p, q in zip(prices_and_quantities["prices"], prices_and_quantities["quantities"]): # O(len(quantities))
            total += q # the running sum
            if total >= my_current_balance:
                yield symbol, p # this will yield the symbol and the associated max_value
                break
    
    

    ターゲット形式でデータを取得する方法は?リストを繰り返し処理し、シンボルが見つかったら、次のシンボルまで値と数量の保存を開始します。

    prices_and_quantities_by_symbol = {}
    symbol_set = (symbol_list) # O(len(symbol_list))
    for i, v in enumerate(list1): # O(len(list1))
        if v in symbol_set:  # amortized O(1) lookup
            current_prices = []
            current_quantities = []
            current_start = i+1
            prices_and_quantities_by_symbol[v] = {
                'prices':current_prices, 
                'quantities':current_quantities
            }
        else: # a value or a quantity
            (current_prices if (i-current_start)%2==0 else current_quantities).append(float(v))
    
    

    特に、量/値のリストが長い場合は、わずかですが興味深い最適化が行われます。数量を保存するのではなく、現在の数量の合計を保存します。

    prices_and_running_total_by_symbol = {
        'ethbtc': {
            'prices':[0.077666, 0.077680, 0.077710, 0.078200], 
            'running_total':[1, 2.5, 5.5, 9.5]
        }, 
        'btceth': {
            ...
        }, 
    ...}
    
    

    これで、 bisect を使用してmax_valueを非常にすばやく見つけることができます 。 bisect.bisect_left(rts, my_current_balance) であるため、コードが理解しやすくなります。  最初の実行合計 >= my_current_balance のインデックスを返します :

    for symbol, prices_and_running_totals in prices_and_running_totals_by_symbol.items(): # O(len(symbol_list))
        ps = prices_and_running_totals["prices"]
        rts = prices_and_running_totals["running_total"]
        i = bisect.bisect_left(rts, my_current_balance) # O(log(len(rts)))
        yield symbol, ps[i] # this will yield the symbol and the associated max_value
    
    

    積算合計を作成するには、価格と数量を別々に処理する必要があります。

    # O(len(list1))
    ...
    if v in symbol_set:  # amortized O(1) lookup*
        ...
    elif (i-current_start)%2==0:
        current_prices.append(float(v))
    else:
        current_running_totals.append((current_running_totals[-1] if current_running_totals else 0.0) + float(v))
    
    

    すべてを関数(あるいは、クラスのメソッド)に入れます:

    prices_and_running_totals_by_symbol = process_data(list1)
    for symbol, max_value in symbols_max_values(prices_and_running_totals_by_symbol, my_current_balance):
        print(symbol, max_value)
    
    

    問題を2つの部分(デコードと使用)に分割することで、コードが速くなり、(私の意見では)理解しやすくなることがわかります(コメントは入れませんでしたが、あるべきです)。

  • 前処理 list1  それを辞書に保存します。これは、 list1 のみを反復処理することを意味します  内部ループが実行されるたびに1回ではなく1回。

    price_dict = {'ethbtc': ['0.077666', '10', '0.077680', '15'], 'btceth': [...], ...}
    
    

    a を反復処理する代わりに 、 range を反復処理する  (Python 3)または xrange  (Python 2)。これはリストの代わりにイテレーターを使用し、コードをより柔軟にします。

    range(0, len(price_dict[symbol]), 2)
    
    

  • あなたの場合、固定間隔がある場合、スライスオブジェクトを使用すると「a」ループに役立つと思います。以下に示すように、リストスライスをオブジェクトに保存できます(また、他の1つまたは2つのヒント)。上記のユーザーに同意します。その入力データを前処理する機会がある場合、本当に必要です。 pandasライブラリを使用することをお勧めします。これは非常に高速ですが、辞書でも値のハッシュ化が可能になるためです。

    input_data = ['ethbtc', '0.0776666', '10', '0.077680', '15']  # Give your variables meaningful names
    length = 20 # a variable to store how long a list of values is for a particular symbol.
    for symbol in symbollist: # Use meaningful names if loops too
        start = input_data.index(symbol)  # break up longer lines
        # Some exception handling here
        indxs = slice(start: start+length:2) # python lets you create slice objects
        quantities = [float(number) for number in input_data[indxs]]
        if sum(quantities) > mycurrentbalance:
            # Whatever code here
            ....
    
    

あなたの答え