各要素.count()
のリストをスキャンするため、使用しないでください。さらに、アイテムが入力に 3 回以上出現する場合、アイテムを複数回出力に追加します。
ここで、以前に見たアイテムのみを生成するジェネレーター関数を使用した方がよいでしょうが、一度だけです。
def unique_plurals(lst):
seen, seen_twice = set(), set()
seen_add, seen_twice_add = seen.add, seen_twice.add
for item in lst:
if item in seen and item not in seen_twice:
seen_twice_add(item)
yield item
continue
seen_add(item)
[list(unique_plurals(c)) for c in col]
これは、各リストを1 回だけ繰り返します ( a を使用する場合とは異なりますCounter()
)。
この方法ははるかに高速です。
>>> timeit('[[k for k, v in OrderedCounter(el).iteritems() if v != 1] for el in col]', 'from __main__ import col, OrderedCounter')
52.00807499885559
>>> timeit('[[k for k, v in Counter(el).iteritems() if v != 1] for el in col]', 'from __main__ import col, Counter')
15.766052007675171
>>> timeit('[list(unique_plurals(c)) for c in col]', 'from __main__ import col, unique_plurals')
6.946599006652832
>>> timeit('[list(unique_plurals_dict(c)) for c in col]', 'from __main__ import col, unique_plurals_dict')
6.557853937149048
これは、メソッドの約 8 倍、OrderedCounter
メソッドの 2.2 倍高速Counter
です。
ただし、Jon の one-dictionary-plus-counter メソッドはさらに高速です。
ただし、一度だけ表示される値を削除する必要があるだけで、残りは繰り返しを含めてそのままにしておく必要がある場合は、次を使用します (Jon から借用):
from itertools import count
from collections import defaultdict
def nonunique_plurals(lst):
seen = defaultdict(count)
for item in lst:
cnt = next(seen[item])
if cnt:
if cnt == 1:
# yield twice to make up for skipped first item
yield item
yield item
これにより、次が生成されます。
>>> [list(nonunique_plurals(c)) for c in col]
[['red', 'red', 'yellow', 'yellow'], ['pink', 'pink', 'brown', 'brown']]
>>> timeit('[non_uniques(c) for c in col]', 'from __main__ import col, non_uniques')
17.75499200820923
>>> timeit('[list(nonunique_plurals(c)) for c in col]', 'from __main__ import col, unique_plurals')
9.306739091873169
これは FMc によって提案されCounter()
たソリューションのほぼ 2 倍の速度ですが、順序は正確には保持されません。
>>> list(nonunique_plurals(['a', 'a', 'b', 'a', 'b', 'c']))
['a', 'a', 'a', 'b', 'b']
>>> non_uniques(['a', 'a', 'b', 'a', 'b', 'c'])
['a', 'a', 'b', 'a', 'b']