我如何检查一个列表是否有任何重复,并返回一个没有重复的新列表?


当前回答

也有使用Pandas和Numpy的解决方案。它们都返回numpy数组,所以如果你想要一个列表,你必须使用.tolist()函数。

t=['a','a','b','b','b','c','c','c']
t2= ['c','c','b','b','b','a','a','a']

熊猫的解决方案

唯一使用Pandas函数():

import pandas as pd
pd.unique(t).tolist()
>>>['a','b','c']
pd.unique(t2).tolist()
>>>['c','b','a']

Numpy解决方案

使用numpy函数unique()。

import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']

注意,numpy.unique()也对值进行排序。所以列表t2是有序返回的。如果你想保留顺序,可以这样回答:

_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']

与其他解决方案相比,该解决方案并不那么优雅,但是,与pandas.unique()相比,numpy.unique()还允许您检查嵌套数组在选定的轴上是否唯一。

其他回答

另一种做法:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]
def remove_duplicates(input_list):
  if input_list == []:
    return []
  #sort list from smallest to largest
  input_list=sorted(input_list)
  #initialize ouput list with first element of the       sorted input list
  output_list = [input_list[0]]
  for item in input_list:
    if item >output_list[-1]:
      output_list.append(item)
  return output_list   

在这个答案中,将有两个部分:两个唯一的解,和一个特定解的速度图。

删除重复项

这些答案大多只删除可哈希的重复项,但这个问题并不意味着它不需要可哈希项,这意味着我将提供一些不需要可哈希项的解决方案。

集合。Counter是标准库中的一个功能强大的工具,可以完美地实现这一点。只有另一种解决方案里面有Counter。然而,该解决方案也仅限于可哈希键。

为了在Counter中允许不可哈希键,我创建了一个Container类,它将尝试获取对象的默认哈希函数,但如果失败,它将尝试其标识函数。它还定义了一个eq和一个散列方法。这应该足以在我们的解决方案中允许不可散列项。不可哈希对象将被视为可哈希对象。但是,这个哈希函数对不可哈希对象使用identity,这意味着两个相等的不可哈希对象将不起作用。我建议您重写它,并将其更改为使用等效可变类型的哈希(例如,如果my_list是一个列表,则使用hash(tuple(my_list))。

我也得到了两个解。另一个解决方案是保持条目的顺序,使用OrderedDict和Counter的子类,命名为'OrderedCounter'。下面是函数:

from collections import OrderedDict, Counter

class Container:
    def __init__(self, obj):
        self.obj = obj
    def __eq__(self, obj):
        return self.obj == obj
    def __hash__(self):
        try:
            return hash(self.obj)
        except:
            return id(self.obj)

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)
    
def remd(sequence):
    cnt = Counter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

def oremd(sequence):
    cnt = OrderedCounter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

Remd为非有序排序,oremd为有序排序。你可以清楚地看出哪个更快,但我还是会解释的。非有序排序稍微快一些,因为它不存储条目的顺序。

现在,我还想展示每个答案的速度比较。我现在就做。

哪个函数是最快的?

为了去除重复,我从几个答案中收集了10个函数。我计算了每个函数的速度,并使用matplotlib.pyplot将其放入一个图形中。

我把它分成三轮画图。hashable是任何可以哈希的对象,unhashable是任何不能哈希的对象。有序序列是保持有序的序列,无序序列不保持有序。现在,这里有更多的术语:

Unordered Hashable适用于任何删除重复项的方法,它不一定要保持顺序。它不需要为不可hashables工作,但它可以。

Ordered Hashable适用于任何保持列表中元素顺序的方法,但它不一定适用于unhashables,但它可以。

Ordered Unhashable是任何保持列表中项目顺序的方法,适用于unhashables。

y轴是花费的秒数。

x轴是函数作用的数字。

我用以下理解为无序哈希和有序哈希生成序列:[list(range(x)) + list(range(x)) for x in range(0,1000,10)]

对于有序的不可哈希对象:[[list(range(y)) + list(range(y)) For y in range(x)] For x in range(0,1000,10)]

请注意,在范围内有一个步骤,因为如果没有它,这将花费10倍的时间。也因为在我个人看来,我认为它可能看起来更容易阅读。

还要注意,图例上的键是我试图猜测的函数实现中最重要的部分。至于哪个功能是最好的还是最差的呢?图表说明了一切。

解决了这个问题,下面是图表。

无序Hashables

(放大)

命令Hashables

(放大)

命令Unhashables

(放大)

如果你想保持顺序,不使用任何外部模块,这里有一个简单的方法:

>>> t = [1, 9, 2, 3, 4, 5, 3, 6, 7, 5, 8, 9]
>>> list(dict.fromkeys(t))
[1, 9, 2, 3, 4, 5, 6, 7, 8]

注意:这种方法保留了出现的顺序,因此,如上所示,9将在1之后,因为它是第一次出现。然而,这和你做的结果是一样的

from collections import OrderedDict
ulist=list(OrderedDict.fromkeys(l))

但它更短,跑得更快。

这是因为每次fromkeys函数尝试创建一个新键时,如果值已经存在,它就会简单地覆盖它。然而,这不会影响字典,因为fromkeys创建的字典中所有键的值都为None,因此有效地消除了所有重复的值。

我的列表中有一个字典,所以我不能使用上面的方法。我得到了错误:

TypeError: unhashable type:

如果你关心顺序和/或某些项是不可散列的。那么你可能会发现这个很有用:

def make_unique(original_list):
    unique_list = []
    [unique_list.append(obj) for obj in original_list if obj not in unique_list]
    return unique_list

有些人可能认为带副作用的列表理解不是一个好的解决方案。这里有一个替代方案:

def make_unique(original_list):
    unique_list = []
    map(lambda x: unique_list.append(x) if (x not in unique_list) else False, original_list)
    return unique_list