ドキュメントには例がありません...キーに基づいて_bisect.insort_left)_
_をどのように使用しますか?
キーに基づいて挿入しようとしています。
_bisect.insort_left(data, ('brown', 7))
_
_data[0]
_に挿入を挿入します。
ドキュメントから...
_
bisect.insort_left(
_a、x、lo = 0、hi = len(a) _)
_
挿入xaにソート順に挿入します。 aがすでにソートされていると仮定すると、これはa.insert(bisect.bisect_left(a, x, lo, hi), x)
と同等です。 O(log n)検索は、低速なO(n)挿入ステップによって支配されることに注意してください。
使用例:
_>>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
>>> data.sort(key=lambda r: r[1])
>>> keys = [r[1] for r in data] # precomputed list of keys
>>> data[bisect_left(keys, 0)]
('black', 0)
>>> data[bisect_left(keys, 1)]
('blue', 1)
>>> data[bisect_left(keys, 5)]
('red', 5)
>>> data[bisect_left(keys, 8)]
('yellow', 8)
>>>
_
_('brown', 7)
_を使用して、data
のソートされたリストで_('red', 5)
_の後に_bisect.insort_left
_を配置したい。現時点ではbisect.insort_left(data, ('brown', 7))
は_('brown', 7)
_を_data[0]
_に配置します。なぜなら、挿入にキーを使用していないためです...ドキュメントは、キーを使用して挿入を行うことを示していません。
これは基本的に _SortedCollection recipe
_ が bisect
のドキュメント がで言及することと同じことを行います以下も参照してください:キー機能をサポートする最後のセクション。
行われているのは、パフォーマンスを向上させるために、ソートされたkeys
リストと並行して個別のソートされたdata
リストが維持されることです(各挿入の前にキーリストを作成するよりも高速ですが、保持して更新することは厳密には必要ありません)。 ActiveStateレシピはこれをクラス内にカプセル化しましたが、以下のコードでは、渡される2つの独立した独立したリストにすぎません(両方が保持されている場合よりも、同期が外れるのは簡単です)レシピのクラスのインスタンス)。
_from bisect import bisect_left
def insert(seq, keys, item, keyfunc=lambda v: v):
"""Insert an item into a sorted list using a separate corresponding
sorted keys list and a keyfunc() to extract the key from each item.
Based on insert() method in SortedCollection recipe:
http://code.activestate.com/recipes/577197-sortedcollection/
"""
k = keyfunc(item) # Get key.
i = bisect_left(keys, k) # Determine where to insert item.
keys.insert(i, k) # Insert key of item to keys list.
seq.insert(i, item) # Insert the item itself in the corresponding place.
# Initialize the sorted data and keys lists.
data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
data.sort(key=lambda r: r[1]) # Sort data by key value
keys = [r[1] for r in data] # Initialize keys list
print(data) # -> [('black', 0), ('blue', 1), ('red', 5), ('yellow', 8)]
insert(data, keys, ('brown', 7), keyfunc=lambda x: x[1])
print(data) # -> [('black', 0), ('blue', 1), ('red', 5), ('brown', 7), ('yellow', 8)]
_
次の質問:
_bisect.insort_left
_は使用できますか?
いいえ、bisect.insort_left()
関数を使用してこれを行うことはできません。キー関数をサポートするように記述されていないためです。代わりに、渡されたアイテム全体を比較して挿入します。x
、その_if a[mid] < x:
_ステートメントの配列内のアイテム全体の1つを使用します。 _Lib/bisect.py
_ のbisect
モジュールのソースを見ると、私の意味がわかります。
関連する抜粋は次のとおりです。
_def insort_left(a, x, lo=0, hi=None):
"""Insert item x in list a, and keep it sorted assuming a is sorted.
If x is already in a, insert it to the left of the leftmost x.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if a[mid] < x: lo = mid+1
else: hi = mid
a.insert(lo, x)
_
上記を変更して、オプションのキー関数引数を受け入れ、使用することができます。
_def my_insort_left(a, x, lo=0, hi=None, keyfunc=lambda v: v):
x_key = keyfunc(x) # Get comparison value.
. . .
if keyfunc(a[mid]) < x_key: # Compare key values.
lo = mid+1
. . .
_
...次のように呼び出します。
_my_insort_left(data, ('brown', 7), keyfunc=lambda v: v[1])
_
実際には、不要な一般性を犠牲にして効率を上げるためにカスタム関数を作成する場合は、一般的なキー関数の引数を追加せずに、すべてをハードコーディングしてデータで必要な方法を操作できますあなたが持っているフォーマット。これにより、挿入中にキー関数を繰り返し呼び出すオーバーヘッドを回避できます。
_def my_insort_left(a, x, lo=0, hi=None):
x_key = x[1] # Key on second element of each item in sequence.
. . .
if a[mid][1] < x_key: lo = mid+1 # Compare second element to key.
. . .
_
... keyfuncを渡さずにこの方法で呼び出されました:
_my_insort_left(data, ('brown', 7))
_
__getitem__
および__len__
を実装するクラスでイテラブルをラップできます。これにより、bisect_left
でキーを使用する機会が得られます。イテラブルとキー関数を引数として取るようにクラスを設定した場合。
これをinsort_left
で使用できるように拡張するには、insert
メソッドを実装する必要があります。ここでの問題は、その場合、insort_left
は、キーがメンバーであるオブジェクトを含むリストにキー引数を挿入しようとすることです。
例はより明確です
from bisect import bisect_left, insort_left
class KeyWrapper:
def __init__(self, iterable, key):
self.it = iterable
self.key = key
def __getitem__(self, i):
return self.key(self.it[i])
def __len__(self):
return len(self.it)
def insert(self, index, item):
print('asked to insert %s at index%d' % (item, index))
self.it.insert(index, {"time":item})
timetable = [{"time": "0150"}, {"time": "0250"}, {"time": "0350"}, {"time": "0450"}, {"time": "0550"}, {"time": "0650"}, {"time": "0750"}]
bslindex = bisect_left(KeyWrapper(timetable, key=lambda t: t["time"]), "0359")
islindex = insort_left(KeyWrapper(timetable, key=lambda t: t["time"]), "0359")
自分のinsert
メソッドで、タイムテーブルディクショナリに固有にする必要がある方法を確認してください。それ以外の場合は、insort_left
が"0359"
を挿入しようとします{"time": "0359"}
を挿入しますか?
これを回避する方法は、比較用のダミーオブジェクトを作成し、KeyWrapper
から継承してinsert
をオーバーライドするか、何らかのファクトリ関数を渡してオブジェクトを作成することです。これらの方法はいずれも、慣用的なpythonの観点からは、特に望ましくありません。
したがって、最も簡単な方法は、KeyWrapper
をbisect_left
とともに使用することです。これにより、挿入インデックスが返され、自分で挿入を実行できます。これを専用の関数で簡単にラップできます。
例えば.
bslindex = bisect_left(KeyWrapper(timetable, key=lambda t: t["time"]), "0359")
timetable.insert(bslindex, {"time":"0359"})
この場合は、insert
を実装しないようにしてください。誤ってKeyWrapper
をinsort_left
のような変更関数に誤って渡した場合、おそらくこれは実行されないはずです。正しいこと。
サンプルデータを使用するには
from bisect import bisect_left
class KeyWrapper:
def __init__(self, iterable, key):
self.it = iterable
self.key = key
def __getitem__(self, i):
return self.key(self.it[i])
def __len__(self):
return len(self.it)
data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
data.sort(key=lambda c: c[1])
newcol = ('brown', 7)
bslindex = bisect_left(KeyWrapper(data, key=lambda c: c[1]), newcol[1])
data.insert(bslindex, newcol)
print(data)
あなたの目標がリストを維持することである場合キーでソート、bisect insertのような通常の操作を実行し、削除して更新します sortedcontainers はあなたに合うと思いますも必要であり、O(n)挿入を回避します。
クラスに比較メソッドを追加します
これは、特にクラスが既にあり、そこからキーでソートしたい場合は特に、最も苦痛の少ない方法です。
#!/usr/bin/env python3
import bisect
import functools
@functools.total_ordering
class MyData:
def __init__(self, color, number):
self.color = color
self.number = number
def __lt__(self, other):
return self.number < other .number
def __str__(self):
return '{} {}'.format(self.color, self.number)
mydatas = [
MyData('red', 5),
MyData('blue', 1),
MyData('yellow', 8),
MyData('black', 0),
]
mydatas_sorted = []
for mydata in mydatas:
bisect.insort(mydatas_sorted, mydata)
for mydata in mydatas_sorted:
print(mydata)
出力:
black 0
blue 1
red 5
yellow 8
参照: クラスの「有効化」比較
Python 3.5.2。