如何合并列表中的类似项目

时间:2011-03-20 17:51:28

标签: python arrays algorithm string list

我在Google上找不到任何相关内容,所以我希望在这里找到一些帮助:)

我有一个Python列表如下:

[['hoose', 200], ["Bananphone", 10], ['House', 200], ["Bonerphone", 10], ['UniqueValue', 777] ...]

我有一个函数可以返回两个字符串之间的Levenshtein距离,对于House而言它会返回2,等等。

现在我想合并levenshtein得分为f.e的列表元素。 < 5,while(!)添加他们的分数,因此对于结果列表我想要以下内容:

[['hoose', 400], ["Bananaphone", 20], ['UniqueValue', 777], ...]

[['House', 400], ["Bonerphone", 20], ['UniqueValue', 777], ...]  

只要他们的价值被添加就没关系。

列表中只有2个项目非常相似,因此任何一个项目的链式效果与很多其他项目相似都不会出现。

6 个答案:

答案 0 :(得分:8)

为了从我的评论中提出要点,我只是从here抓住了距离的实现,并计算了一些距离:

d('House', 'hoose') = 2
d('House', 'trousers') = 4
d('trousers', 'hoose') = 5

现在,假设您的阈值为4.您必须合并Househoose,以及Housetrousers,但不是 trousershoose。你确定这样的事情永远不会发生在你的数据上吗?

最后,我认为更多的是聚类问题,因此您可能需要研究聚类算法。 SciPy提供了hierarchical clustering的实现,可以使用自定义距离函数(请注意,对于较大的数据集,这可能非常慢 - 它也会消耗大量内存)。

主要问题是决定群集质量的衡量标准,因为没有一个正确的解决方案可以解决您的问题。 This paper(pdf)为您提供了解问题的起点。

答案 1 :(得分:4)

与其他评论一样,我不确定这样做有多大意义,但我认为这是一个可以做你想要的解决方案。这是非常低效的 - O(n 2 )其中n是列表中的单词数 - 但我不确定是否有更好的方法:

data = [['hoose', 200],
        ["Bananphone", 10],
        ['House', 200],
        ["Bonerphone", 10],
        ['UniqueValue', 777]]

already_merged = []

for word, score in data:
    added_to_existing = False
    for merged in already_merged:
        for potentially_similar in merged[0]:
            if levenshtein(word, potentially_similar) < 5:
                merged[0].add(word)
                merged[1] += score
                added_to_existing = True
                break
        if added_to_existing:
            break
    if not added_to_existing:
        already_merged.append([set([word]),score])

print already_merged

输出结果为:

[[set(['House', 'hoose']), 400], [set(['Bonerphone', 'Bananphone']), 20], [set(['UniqueValue']), 777]]

这种方法的一个明显问题是,您正在考虑的单词可能与您已经考虑过的许多不同单词集足够接近,但这段代码只会将其归入第一个单词中。它找到了。我已为Space_C0wb0y's answer投票+1;)

答案 2 :(得分:2)

import Levenshtein
import operator
import cluster

class Item(object):
    @classmethod
    def fromList(cls,lst):
        return cls(lst[0][0], lst[0][1], lst[1])

    def __init__(self, name, val=0, score=0):
        super(Item,self).__init__()
        self.name     = name
        self.val      = val
        self.score    = score

    def dist(self, other):
        return 100 if other is self else Levenshtein.distance(self.name, other.name)

    def __str__(self):
        return "('{0}', {1})".format(self.name, self.val)

def main():
    myList = [
        [['hoose', 5], 200],
        [['House', 5], 200],
        [["Bananaphone", 5], 10],
        [['trousers', 5], 100]
    ]
    items = [Item.fromList(i) for i in myList]

    cl = cluster.HierarchicalClustering(items, (lambda x,y: x.dist(y)))
    for group in cl.getlevel(5):
        groupScore = sum(item.score for item in group)
        groupStr   = ', '.join(str(item) for item in group)
        print "{0}: {1}".format(groupScore, groupStr)

if __name__=="__main__":
    main()

返回

10: ('Bananaphone', 5)
500: ('trousers', 5), ('hoose', 5), ('House', 5)

答案 3 :(得分:0)

蓝图:

result = dict()
for item in [[['hoose', 5], 200], [['House', 5], 200], [["Bananaphone", 5], 10], ...]:

   key = item[0] # ('hoose', 5)
   value = item[1] # 200

   if key in result:
       result[key] = 0
   result[key] += value

可能需要调整用于解压缩内部列表项的代码。

答案 4 :(得分:0)

你没有说清单中的项目数量,但我猜测n ^ 2的复杂性是可以的。

你也没有说你是否想要比较所有可能的对或只是相邻的对。我假设所有配对。

所以这就是这个想法:

  1. 取第一项,并针对所有其他项目计算lev分数。
  2. 将所有分数小于5的项目合并,将其从列表中删除并汇总其分数。
  3. 在合并列表中,选择下一个项目,将该项目与除刚刚检查过的项目之外的所有项目进行比较。
  4. 重复直到列表中没有项目

答案 5 :(得分:0)

@Mark Longair我在python 3.5中遇到了一些错误,因此我如下进行了纠正:

import Levenshtein
data = [['hoose', 200],
       ["Bananphone", 10],
       ['House', 200],
       ["Bonerphone", 10],
       ['UniqueValue', 777]]

already_merged = []

for word, score in data:
    added_to_existing = False
    for merged in already_merged:
        for potentially_similar in merged[0]:
            if Levenshtein.distance(word, potentially_similar) < 5:
                merged[0].add(word)
                merged[1] += score
                added_to_existing = True
                break
        if added_to_existing:
            break
    if not added_to_existing:
        already_merged.append([set([word]),score])

print (already_merged)

@Mark感谢您提供这种简单的解决方案。