【问题标题】:How to remove duplicates from a list in python [duplicate]如何从python中的列表中删除重复项[重复]
【发布时间】:2013-09-16 18:49:36
【问题描述】:

我有一个清单:list = ['item1', 'item2', 'item3', 'item4']

我想比较所有项目的相似度。

如果item2item3相似,则结果为list = ['item1', 'item2', 'item4']

编辑:

抱歉,我的问题令人困惑。

列表项是一组三元组。我想删除列表中的类似项目。

list = [('very','beauty','place'),('very','good','place'),('another','trigram','item')]

计算该列表中每个pair-item的jaccard相似度,如果pair-item的jaccard分数> 0.4,我称之为相似。在此示例中,item1 和 item2 相似。我想要的最后一个输出是:

list = [('very','beauty','place'),('another','trigram','item')]

这是计算jaccard分数的方法:

def compute_jaccard_index(set_1, set_2):
   n = len(set_1.intersection(set_2))
   return n / float(len(set_1) + len(set_2) - n)

【问题讨论】:

  • 你认为什么是“相似”?
  • 它是传递的吗?你有更好的例子(包括代码)吗?
  • item2item3 相似时,你如何决定这两个元素中的哪些应该保留,哪些应该删除?
  • 另外,为什么每个人都认为“相似”意味着相同,因此认为集合是合适的解决方案?
  • “相似”是可传递的吗?如果a 类似于b 并且b 类似于ca 类似于c?例如,如果“first”与“fist”相似,“fist”与“list”相似,那么“first”与“list”相似?

标签: python list iteration


【解决方案1】:

此解决方案将继续查看两个元素的 pairs,直到它查看所有对而不过滤任何元素。这不是一个有效的解决方案,因为它将继续一遍又一遍地查看相同的对,而且它也没有利用可能的传递性。但这是一个开始。

>>> from itertools import combinations
>>> def filterSimilar (d):
        while True:
            filteredOne = False
            for s, t in combinations(d, 2):
                if isSimilar(s, t):
                    d.remove(t)
                    filteredOne = True
                    break
            if not filteredOne:
                break
>>> d = ['asdf', 'asxf', 'foo', 'bar', 'baz']   
>>> filterSimilar(d)
>>> d
['asdf', 'foo', 'bar']

isSimilar 的一个可能示例实现如下,它使用两个字符串之间的 Levenshtein 距离:

def levenshteinDistance (s, t):
    if len(s) == 0:
        return len(t)
    if len(t) == 0:
        return len(s)
    return min(levenshteinDistance(s[:-1], t) + 1, levenshteinDistance(s, t[:-1]) + 1, levenshteinDistance(s[:-1], t[:-1]) + (0 if s[-1] == t[-1] else 1))

def isSimilar (s, t):
    return levenshteinDistance(s, t) < 2

(请注意,我在此示例中使用的 Levenshtein 距离不是传递比较的示例)


使用您的 compute_jaccard_index 函数,isSimilar 函数现在看起来像这样:

def isSimilar (s, t):
    return compute_jaccard_index(s, t) > .4

然后用于您的示例数据:

>>> lst = [{'very','beauty','place'},{'very','good','place'},{'another','trigram','item'}]
>>> filterSimilar(lst)
>>> lst
[{'very', 'beauty', 'place'}, {'item', 'trigram', 'another'}]

【讨论】:

  • def filterSimilar(d) 错过return d
  • 还有什么方法可以加快速度吗?
  • 它没有丢失返回,因为它改变了传递的列表。一个简单的让它更快的方法是记住你已经处理了哪些比较;或者不是在删除某些内容时重新启动,而是记住,当将来的比较触及它时跳过它,最后在最后删除它。
【解决方案2】:

如果这些项目是字符串或数字,您正在寻找内置的 set。

例如:

In [1]: foo = [1, 32, 4, 5, 6, 5]

In [2]: set(foo)
Out[2]: {1, 4, 5, 6, 32}

In [3]: list(set(foo))
Out[3]: [32, 1, 4, 5, 6]

这取决于你所说的“相似”是什么意思。

【讨论】:

  • 不是算子,但是是的。
【解决方案3】:

如果您使用相似函数而不是直接相等比较,这将起作用:

itemsToRemove = []
n = len(list)
for i in range(n):
  for j in range(i+1,n):
      if(similarTest(list[i], list[j]):
        itemsToRemove.append(list[i])
        break
return [item for item in list if item not in itemsToRemove]

当然,如果您真的想像其他人建议的那样删除相同的项目,那么集合会很好用。

【讨论】:

  • 使用某种散列解决方案和字典几乎肯定会更好。此外,使用 itertools 计算笛卡尔积可能会更好。
  • @Marcin 你不能在两个单独的元素之间散列相似性。
  • @poke 不,实际上你可以。这就是散列函数的作用。
  • @Marcin:例如,假设我定义两个字符串“相似”,如果它们的 Levenshtein 距离小于或等于 2。如果相似性不是等价关系,则散列不会保留它(虽然有一些使用哈希的近似值)。在这种情况下,您可以做得比这更好,例如使用 k-d 树。但是,如果您对相似性的结构一无所知,如果它只是您调用的一个黑盒函数,那么这已经差不多了。
  • @SteveJessop 没错。但是,等价关系是更常见的情况。在这种情况下,您确实可以“散列两个单独元素之间的相似性”。
【解决方案4】:

您可以使用set。它将从列表中删除所有重复的元素。

>>>list = [1,2,3,4,4,5,2,3,1]
>>>list =set(list)
>>>list
set([1, 2, 3, 4, 5])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    • 2014-09-30
    • 2019-12-07
    • 2011-09-29
    • 2013-08-03
    • 2011-02-20
    相关资源
    最近更新 更多