如何加快列表理解速度

Tig*_*er1 3 python list-comprehension list python-2.7

以下是我的清单:

col = [['red', 'yellow', 'blue', 'red', 'green', 'yellow'],
       ['pink', 'orange', 'brown', 'pink', 'brown']
      ]
Run Code Online (Sandbox Code Playgroud)

我的目标是消除每个列表中出现一次的项目.

这是我的代码:

eliminate = [[w for w in c if c.count(w)>1]for c in col]

Output: [['red', 'red', 'yellow','yellow'], ['pink','pink', 'brown','brown']]
Run Code Online (Sandbox Code Playgroud)

该代码适用于小数据集,例如上面的列表,但是,我的数据集非常大.每个列表最多包含1000个项目.

有没有办法让上面的代码更快?比如将代码分解为两个或更多个for循环,因为我的理解是正常的for循环比列表理解更快.

有什么建议?谢谢.

Jon*_*nts 9

我试着OrderedCounter去避免重复的.count()电话:

from collections import OrderedDict, Counter

col=[['red', 'yellow', 'blue', 'red', 'green', 'yellow'],['pink', 'orange', 'brown', 'pink', 'brown']]

class OrderedCounter(Counter, OrderedDict):
    pass

new = [[k for k, v in OrderedCounter(el).iteritems() if v != 1] for el in col]
# [['red', 'yellow'], ['pink', 'brown']]
Run Code Online (Sandbox Code Playgroud)

如果我们只是希望迭代一次,那么(类似于Martijn的 - 加上少用套装):

from itertools import count
def unique_plurals(iterable):
    seen = {}
    return [el for el in iterable if next(seen.setdefault(el, count())) == 1]

new = map(unique_plurals, col)
Run Code Online (Sandbox Code Playgroud)

这在指定需要多少次出现方面更灵活,并且保留一个dict而不是多个sets.


Mar*_*ers 7

不要使用,.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]
Run Code Online (Sandbox Code Playgroud)

这只迭代每个列表一次(与使用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
Run Code Online (Sandbox Code Playgroud)

这比OrderedCounter方法快8倍,是方法的2.2倍Counter.

但是,Jon的一个字典加计数器方法仍然更快!

但是,如果您只需要消除仅出现一次但保持其余值不变的值(包括重复值),那么您将使用(从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
Run Code Online (Sandbox Code Playgroud)

这会产生:

>>> [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
Run Code Online (Sandbox Code Playgroud)

这几乎是FMc提出Counter()解决方案速度的两倍,但它并没有精确地保留顺序:

>>> 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']
Run Code Online (Sandbox Code Playgroud)