从python中的列表中获取唯一值

Get unique values from a list in python

本问题已经有最佳答案,请猛点这里访问。

我想从以下列表中获取唯一值:

1
[u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']

我需要的输出是:

1
[u'nowplaying', u'PBS', u'job', u'debate', u'thenandnow']

此代码有效:

1
2
3
4
5
output = []
for x in trends:
    if x not in output:
        output.append(x)
print output

有没有更好的解决方案我应该使用?


首先正确声明您的列表,用逗号分隔。可以通过将列表转换为集合来获取唯一值。

1
2
3
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
myset = set(mylist)
print(myset)

如果进一步将其用作列表,则应通过执行以下操作将其转换回列表:

1
mynewlist = list(myset)

另一种可能,可能更快的方法是从一开始就使用集合,而不是列表。那么您的代码应该是:

1
2
3
4
output = set()
for x in trends:
    output.add(x)
print(output)

正如已经指出的那样,这些装置不能维持原来的顺序。如果您需要,您应该查看订购的设备。


为了与我将使用的类型保持一致:

1
mylist = list(set(mylist))


您的输出变量是什么类型?

python集就是您所需要的。这样声明输出:

1
output = set([]) # initialize an empty set

您可以使用output.add(elem)添加元素,并确保它们是唯一的。

警告:集合不保留列表的原始顺序。


您提供的示例与Python中的列表不对应。它类似于嵌套的dict,这可能不是您想要的。

Python列表:

1
a = ['a', 'b', 'c', 'd', 'b']

要获得唯一的项目,只需将其转换为一个集合(如果需要,可以将其重新转换为列表):

1
2
3
b = set(a)
print b
>>> set(['a', 'b', 'c', 'd'])


如果我们需要保持元素的顺序,那么这样做如何:

1
2
3
used = set()
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = [x for x in mylist if x not in used and (used.add(x) or True)]

还有一个使用reduce的解决方案,没有临时used变量。

1
2
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = reduce(lambda l, x: l.append(x) or l if x not in l else l, mylist, [])

更新-2019年3月

第三种解决方案,这是一个很好的解决方案,但有点慢,因为.index是O(n)。

1
2
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = [x for i, x in enumerate(mylist) if i == mylist.index(x)]

更新-2016年10月

另一个解决方案是使用reduce,但这次没有使用.append,这使得它更易于阅读和理解。

1
2
3
4
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = reduce(lambda l, x: l+[x] if x not in l else l, mylist, [])
#which can also be writed as:
unique = reduce(lambda l, x: l if x in l else l+[x], mylist, [])

注意:请记住,我们获得的可读性越高,脚本的性能就越差。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import timeit

setup ="mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']"

#10x to Michael for pointing out that we can get faster with set()
timeit.timeit('[x for x in mylist if x not in used and (used.add(x) or True)]', setup='used = set();'+setup)
0.4188511371612549

timeit.timeit('[x for x in mylist if x not in used and (used.append(x) or True)]', setup='used = [];'+setup)
0.6157128810882568

timeit.timeit('reduce(lambda l, x: l.append(x) or l if x not in l else l, mylist, [])', setup=setup)
1.8778090476989746

timeit.timeit('reduce(lambda l, x: l+[x] if x not in l else l, mylist, [])', setup=setup)
2.13108491897583

timeit.timeit('reduce(lambda l, x: l if x in l else l+[x], mylist, [])', setup=setup)
2.207760810852051

timeit.timeit('[x for i, x in enumerate(mylist) if i == mylist.index(x)]', setup=setup)
2.3621110916137695

回答意见

因为@monica问了一个很好的问题:"这是怎么工作的?"对于每个有问题的人来说。我将尝试更深入地解释这是如何运作的,以及这里发生了什么巫术;)

所以她首先问:

I try to understand why unique = [used.append(x) for x in mylist if x
not in used]
is not working.

好吧,它真的起作用了

1
2
3
4
5
6
7
>>> used = []
>>> mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
>>> unique = [used.append(x) for x in mylist if x not in used]
>>> print used
[u'nowplaying', u'PBS', u'job', u'debate', u'thenandnow']
>>> print unique
[None, None, None, None, None]

问题是,我们不能在unique变量内得到所需的结果,而只能在used变量内得到。这是因为在清单理解期间,.append修改used变量并返回None

因此,为了将结果放入unique变量,并且仍然使用与.append(x) if x not in used相同的逻辑,我们需要将这个.append调用移到列表理解的右侧,并在左侧返回x

但如果我们太幼稚了,只需要:

1
2
3
>>> unique = [x for x in mylist if x not in used and used.append(x)]
>>> print unique
[]

我们什么也得不到。

同样,这是因为.append方法返回None,它在我们的逻辑表达式上给出了如下的外观:

1
x not in used and None

这基本上总是:

  • xused中时,计算为False
  • x不在used中时,计算为None
  • 在这两种情况下(False/None,这将被视为falsy值,因此我们将得到一个空列表。

    但是,当x不在used中时,为什么要对None进行评估?有人可能会问。

    因为这就是python的短路操作符的工作原理。

    The expression x and y first evaluates x; if x is false, its value is
    returned; otherwise, y is evaluated and the resulting value is
    returned.

    因此,当不使用x时(即当其True时),将对下一部分或表达式进行评估(used.append(x)并返回其值(None)。

    但这正是我们想要的,为了从一个重复的列表中获得唯一的元素,我们只想在我们第一次遇到这些元素时,将它们添加到一个新的列表中。

    所以我们只想在x不在used的情况下评估used.append(x),如果有办法把这个None值变成truthy值,我们会很好的,对吧?

    是的,这里是第二类short-circuit运营商的所在地。

    The expression x or y first evaluates x; if x is true, its value is
    returned; otherwise, y is evaluated and the resulting value is
    returned.

    我们知道.append(x)永远是falsy,所以如果我们在他旁边加一个or,我们就会得到下一个部分。这就是我们写作的原因:

    1
    x not in used and (used.append(x) or True)

    因此,只有当表达的第一部分(x not in used)True时,我们才能评价used.append(x),得到True

    在第二种方法中,使用reduce方法也可以看到类似的情况。

    1
    2
    3
    4
    5
    (l.append(x) or l) if x not in l else l
    #similar as the above, but maybe more readable
    #we return l unchanged when x is in l
    #we append x to l and return l when x is not in l
    l if x in l else (l.append(x) or l)

    我们在哪里:

  • l中追加x,当x不在l中时返回l。多亏了or声明,对.append进行了评估,然后返回l
  • xl中时,返回l未触及

  • 维持秩序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # oneliners
    # slow -> . --- 14.417 seconds ---
    [x for i, x in enumerate(array) if x not in array[0:i]]

    # fast -> . --- 0.0378 seconds ---
    [x for i, x in enumerate(array) if array.index(x) == i]

    # multiple lines
    # fastest -> --- 0.012 seconds ---
    uniq = []
    [uniq.append(x) for x in array if x not in uniq]
    uniq

    订单无关紧要:

    1
    2
    # fastest-est -> --- 0.0035 seconds ---
    list(set(array))


    这是一个简单的解决方案-

    1
    2
    list=[u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
    list=set(list)


    从列表中获取唯一元素

    1
    mylist = [1,2,3,4,5,6,6,7,7,8,8,9,9,10]

    Using Simple Logic from Sets - Sets are unique list of items

    1
    2
    3
    4
    mylist=list(set(mylist))

    In [0]: mylist
    Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    Using Simple Logic

    1
    2
    3
    4
    5
    6
    7
    newList=[]
    for i in mylist:
        if i not in newList:
            newList.append(i)

    In [0]: mylist
    Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    Using pop method
    ->pop removes the last or indexed item and displays that to user. video

    1
    2
    3
    4
    5
    6
    7
    8
    9
    k=0
    while k < len(mylist):
        if mylist[k] in mylist[k+1:]:
            mylist.pop(mylist[k])
        else:
            k=k+1

    In [0]: mylist
    Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    Using Numpy

    1
    2
    3
    4
    5
    import numpy as np
    np.unique(mylist)

    In [0]: mylist
    Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    参考文献


    集合-独特元素的无序集合。元素列表可以传递给集合的构造函数。所以,通过使用重复元素传递列表,我们使用唯一元素进行设置,然后将其转换回列表,然后使用唯一元素获取列表。关于性能和内存开销,我什么也不能说,但我希望,对于小的列表来说,这并不重要。

    1
    list(set(my_not_unique_list))

    简明扼要。


    如果您在代码中使用了numpy(对于大量数据来说这可能是一个不错的选择),请签出numpy.unique:

    1
    2
    3
    4
    5
    >>> import numpy as np
    >>> wordsList = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
    >>> np.unique(wordsList)
    array([u'PBS', u'debate', u'job', u'nowplaying', u'thenandnow'],
          dtype='<U10')

    (http://docs.scipy.org/doc/numpy/reference/generated/numpy.unique.html)

    如您所见,numpy不仅支持数字数据,还可以使用字符串数组。当然,结果是一个麻木的数组,但并不重要,因为它的行为仍然像一个序列:

    1
    2
    3
    4
    5
    6
    7
    8
    >>> for word in np.unique(wordsList):
    ...     print word
    ...
    PBS
    debate
    job
    nowplaying
    thenandnow

    如果您真的想要一个普通的python列表,那么您可以随时调用list()。

    但是,结果是自动排序的,从上面的代码片段中可以看到。如果需要保留列表顺序,请检查无排序的numpy unique。


    仅使用列表压缩的相同顺序唯一列表。

    1
    2
    3
    4
    5
    6
    7
    8
    > my_list = [1, 2, 1, 3, 2, 4, 3, 5, 4, 3, 2, 3, 1]
    > unique_list = [
    >    e
    >    for i, e in enumerate(my_list)
    >    if my_list.index(e) == i
    > ]
    > unique_list
    [1, 2, 3, 4, 5]

    enumerates给出了索引i和元素e作为tuple

    my_list.index返回e的第一个索引。如果第一个索引不是i,那么当前迭代的e不是列表中的第一个e

    编辑

    我要注意的是,这不是一个很好的方法,从性能上来说。这只是一种仅使用列表压缩实现它的方法。


    通过使用python字典的基本属性:

    1
    2
    3
    inp=[u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
    d={i for i in inp}
    print d

    输出将是:

    1
    set([u'nowplaying', u'job', u'debate', u'PBS', u'thenandnow'])


    首先,您给出的示例不是有效的列表。

    1
    example_list = [u'nowplaying',u'PBS', u'PBS', u'nowplaying', u'job', u'debate',u'thenandnow']

    假设上面是示例列表。然后,您可以使用下面的方法作为give-itertools示例文档,该示例文档可以返回唯一的值,并根据您的需要保留顺序。这里的iterable是示例清单

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from itertools import ifilterfalse

    def unique_everseen(iterable, key=None):
       "List unique elements, preserving order. Remember all elements ever seen."
        # unique_everseen('AAAABBBCCDAABBB') --> A B C D
        # unique_everseen('ABBCcAD', str.lower) --> A B C D
        seen = set()
        seen_add = seen.add
        if key is None:
            for element in ifilterfalse(seen.__contains__, iterable):
                seen_add(element)
                yield element
        else:
            for element in iterable:
                k = key(element)
                if k not in seen:
                    seen_add(k)
                    yield element


    1
    2
    3
    4
    5
    6
    def get_distinct(original_list):
        distinct_list = []
        for each in original_list:
            if each not in distinct_list:
                distinct_list.append(each)
        return distinct_list


    set可以帮助您从列表中筛选出重复的元素。它对strinttuple元素很有效,但如果您的列表中包含dict或其他list元素,则最终会得到TypeError例外。

    以下是处理某些(不是全部)非哈希类型的常规订单保留解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def unique_elements(iterable):
        seen = set()
        result = []
        for element in iterable:
            hashed = element
            if isinstance(element, dict):
                hashed = tuple(sorted(element.iteritems()))
            elif isinstance(element, list):
                hashed = tuple(element)
            if hashed not in seen:
                result.append(element)
                seen.add(hashed)
        return result

    作为奖励,Counter是一种简单的方法,可以同时获得唯一值和每个值的计数:

    1
    2
    3
    from collections import Counter
    l = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
    c = Counter(l)

    1
    2
    def setlist(lst=[]):
       return list(set(lst))


    除了前面的答案,也就是说你可以把你的列表转换成集合,你也可以这样做。

    1
    2
    mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenadnow']
    mylist = [i for i in set(mylist)]

    输出将是

    1
    [u'nowplaying', u'job', u'debate', u'PBS', u'thenadnow']

    但秩序不会被保留。

    另一个简单的答案可能是(不使用集合)

    1
    2
    >>> t = [v for i,v in enumerate(mylist) if mylist.index(v) == i]
    [u'nowplaying', u'PBS', u'job', u'debate', u'thenadnow']

    我很惊讶到目前为止还没有人给出直接的订单保留答案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def unique(sequence):
       """Generate unique items from sequence in the order of first occurrence."""
        seen = set()
        for value in sequence:
            if value in seen:
                continue

            seen.add(value)

            yield value

    它将生成值,因此它不仅可以与列表一起工作,例如unique(range(10))。要获得列表,只需调用list(unique(sequence)),如下所示:

    1
    2
    >>> list(unique([u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']))
    [u'nowplaying', u'PBS', u'job', u'debate', u'thenandnow']

    它要求每个项目都是可散列的,并且不仅仅是可比较的,但是Python中的大多数内容都是可散列的,它是O(n)而不是O(n^2),所以对于长列表来说,它可以很好地工作。


    要从列表中获取唯一值,请使用以下代码:

    1
    2
    3
    trends = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
    output = set(trends)
    output = list(output)

    重要:如果列表中的任何项都不可哈希(对于可变类型,例如list或dict),则上述方法将不起作用。

    1
    2
    3
    4
    5
    trends = [{'super':u'nowplaying'}, u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
    output = set(trends)
    Traceback (most recent call last):
      File"<stdin>", line 1, in <module>
      TypeError: unhashable type: 'dict'

    这意味着您必须确保trends列表始终只包含可哈希项,否则您必须使用更复杂的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from copy import deepcopy

    try:
        trends = [{'super':u'nowplaying'}, [u'PBS',], [u'PBS',], u'nowplaying', u'job', u'debate', u'thenandnow', {'super':u'nowplaying'}]
        output = set(trends)
        output = list(output)
    except TypeError:
        trends_copy = deepcopy(trends)
        while trends_copy:
            trend = trends_copy.pop()
            if trends_copy.count(trend) == 0:
                output.append(trend)
    print output

    您可以使用集合。为了清楚起见,我正在解释列表和集合之间的区别。集合是唯一元素的无序集合。列表是元素的有序集合。所以,

    1
    2
    3
    4
        unicode_list=[u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job',u'debate', u'thenandnow']
        list_unique=list(set(unicode_list))
        print list_unique
    [u'nowplaying', u'job', u'debate', u'PBS', u'thenandnow']

    但是:不要在命名变量时使用list/set。它将导致错误:例如:在上面的列表中不是使用列表,而是使用Unicode列表。

    1
    2
    3
    4
    5
    list=[u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job',u'debate', u'thenandnow']
            list_unique=list(set(list))
            print list_unique
        list_unique=list(set(list))
    TypeError: 'list' object is not callable

    使用set来消除列表重复,返回为列表

    1
    2
    3
    def get_unique_list(lst):
            if isinstance(lst,list):
                return list(set(lst))


  • 在代码开头,只需声明输出列表为空:output=[]
  • 您可以使用此代码来代替您的代码trends=list(set(trends))

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from collections import OrderedDict


    seq = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']

    # Unordered (hashable items)
    list(set(seq))
    # Out: ['thenandnow', 'PBS', 'debate', 'job', 'nowplaying']

    # Order-preserving
    list(OrderedDict.fromkeys(seq))
    # Out: ['nowplaying', 'PBS', 'job', 'debate', 'thenandnow']

    或者在python 3.6+中:

    1
    2
    3
    # Order-preserving
    list(dict.fromkeys(seq))
    # Out: ['nowplaying', 'PBS', 'job', 'debate', 'thenandnow']


    我的解决方案是检查内容的唯一性,但保留原始顺序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def getUnique(self):
        notunique = self.readLines()
        unique = []
        for line in notunique: # Loop over content
            append = True # Will be set to false if line matches existing line
            for existing in unique:
                if line == existing: # Line exists ? do not append and go to the next line
                    append = False
                    break # Already know file is unique, break loop
            if append: unique.append(line) # Line not found? add to list
        return unique

    编辑:通过使用字典键来检查是否存在,而不是对每行执行一个完整的文件循环,可能会更有效,我不会对大型集使用我的解决方案。


    如果要从列表中获取唯一元素并保持其原始顺序,则可以使用Python标准库中的OrderedDict数据结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from collections import OrderedDict

    def keep_unique(elements):
        return list(OrderedDict.fromkeys(elements).keys())

    elements = [2, 1, 4, 2, 1, 1, 5, 3, 1, 1]
    required_output = [2, 1, 4, 5, 3]

    assert keep_unique(elements) == required_output

    实际上,如果您使用的是python≥3.6,那么可以使用plain dict来实现这一点:

    1
    2
    def keep_unique(elements):
        return list(dict.fromkeys(elements).keys())

    在引入了口述的"紧凑"表示之后,这就成为可能。在这里看看。尽管这"考虑了实施细节,不应依赖"。


    集合是有序和唯一元素的集合。因此,可以使用如下设置获取唯一列表:

    1
    unique_list = list(set([u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']))


    我知道这是一个古老的问题,但这里有我独特的解决方案:类继承!:

    1
    2
    3
    4
    5
    6
    class UniqueList(list):
        def appendunique(self,item):
            if item not in self:
                self.append(item)
                return True
            return False

    然后,如果要唯一地将项目附加到列表中,只需在单音列表中调用AppendUnique。因为它继承了一个列表,所以它基本上就像一个列表,所以您可以使用index()等函数,并且因为它返回true或false,所以您可以确定追加是成功的(唯一项)还是失败的(已经在列表中)。

    要从列表中获取项目的唯一列表,请使用for循环将项目追加到单音列表(然后复制到列表)。

    示例用法代码:

    1
    2
    3
    4
    5
    6
    7
    unique = UniqueList()

    for each in [1,2,2,3,3,4]:
        if unique.appendunique(each):
            print 'Uniquely appended ' + str(each)
        else:
            print 'Already contains ' + str(each)

    印刷品:

    1
    2
    3
    4
    5
    6
    Uniquely appended 1
    Uniquely appended 2
    Already contains 2
    Uniquely appended 3
    Already contains 3
    Uniquely appended 4

    正在复制到列表:

    1
    2
    3
    4
    5
    6
    7
    unique = UniqueList()

    for each in [1,2,2,3,3,4]:
        unique.appendunique(each)

    newlist = unique[:]
    print newlist

    印刷品:

    1
    [1, 2, 3, 4]

    长数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    s = np.empty(len(var))

    s[:] = np.nan

    for  x in  set(var):

        x_positions = np.where(var==x)

        s[x_positions[0][0]]=x


    sorted_var=s[~np.isnan(s)]

    使用以下功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def uniquefy_list(input_list):
    """
    This function  takes a list as input and return a list containing only unique elements from the input list

    """

    output_list=[]
    for elm123 in input_list:
        in_both_lists=0
        for elm234 in output_list:
            if elm123 == elm234:
                in_both_lists=1
                break
        if in_both_lists == 0:
            output_list.append(elm123)

    return output_list

    尝试这个函数,它类似于您的代码,但它是一个动态范围。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def unique(a):

        k=0
        while k < len(a):
            if a[k] in a[k+1:]:
                a.pop(k)
            else:
                k=k+1



        return a