Python:如何通过子字符串相关性对字符串列表进行排序?

时间:2017-12-06 19:57:59

标签: python sorting

我有一些字符串列表,例如:

["foo bar SOME baz TEXT bob",
"SOME foo bar baz bob TEXT",
"SOME foo TEXT",
"foo bar SOME TEXT baz",     
"SOME TEXT"]

我希望它按照SOME TEXT子串的精确度排序(大写并不重要)。像这样的顺序:

["SOME TEXT",
"foo bar SOME TEXT baz",
"SOME foo TEXT",
"foo bar SOME baz TEXT bob",
"SOME foo bar baz bob TEXT"]

这个想法是 - 最好的分数得到与子串字位置最匹配的字符串。对于更大量的"马虎"子字符串之间的单词 - 它得到的排序越低
我找到了一些像fuzzysetLevenshtein distance这样的库,但我不确定这是我需要的。根据我的理解,我知道我要排序的确切子字符串和那些libs搜索相似的单词。

实际上我需要在我的Django项目中进行一些数据库查询(Postgresql)之后进行此类操作。我已经尝试使用其ORM进行全文搜索,但没有得到相关的排序顺序(它不能计算子字符串之间的距离)。接下来我尝试了Haystack + Whoosh,但此时此刻并没有找到如何做到这一点的信息。所以现在的想法是获取查询集,然后将其排序出数据库(是的,我知道这可能是一个糟糕的决定,但是现在我希望它只是工作)。但是,如果有人告诉我如何在任何技术中做到这一点,我在这里提到 - 这也将是非常酷。谢谢!

P.S。子串的长度应该是最多20个字符串中的2-10个字。

3 个答案:

答案 0 :(得分:5)

您可以使用difflib.SequenceMatcher来实现与所需输出非常相似的内容:

>>> import difflib
>>> l = ["foo bar SOME baz TEXT bob", "SOME foo bar baz bob TEXT", "SOME foo TEXT", "foo bar SOME TEXT baz", "SOME TEXT"]
>>> sorted(l, key=lambda z: difflib.SequenceMatcher(None, z, "SOME TEXT").ratio(), reverse=True)
['SOME TEXT', 'SOME foo TEXT', 'foo bar SOME TEXT baz', 'foo bar SOME baz TEXT bob', 'SOME foo bar baz bob TEXT']

如果你不能说明唯一的区别是两个元素"foo bar SOME TEXT baz""SOME foo TEXT"的位置与你想要的输出相比是交换的。

答案 1 :(得分:1)

查看友好社区sorting tutorial。您需要使用密钥进行排序。这是一个微不足道的功能,可以给你这个想法;它找到两个单词之间的距离,并将其作为差异指标返回。

sentence = ["foo bar SOME baz TEXT bob",
            "SOME foo bar baz bob TEXT",
            "SOME foo TEXT",
            "foo bar SOME TEXT baz",
            "SOME TEXT"]

def match_score(sentence):
    some_pos = sentence.find("SOME")
    text_pos = sentence.find("TEXT")
    return abs(text_pos - some_pos)

sentence.sort(key = lambda x: match_score(x))

for item in sentence:
    print(item)

输出:

foo bar SOME TEXT baz
SOME TEXT
foo bar SOME baz TEXT bob
SOME foo TEXT
SOME foo bar baz bob TEXT

答案 2 :(得分:0)

这是我的看法。

l = ["foo bar SOME baz TEXT bob",
"SOME foo bar baz bob TEXT",
"SOME foo TEXT",
"foo bar SOME TEXT baz",     
"SOME TEXT"]

l.sort(key=lambda x: (x.find("SOME")-x.find("TEXT"))*0.9-0.1*x.find("SOME"), reverse=True)

print(l)

输出:

['SOME TEXT', 'foo bar SOME TEXT baz', 'SOME foo TEXT', 'foo bar SOME baz TEXT bob', 'SOME foo bar baz bob TEXT']

所以我们所做的就是根据主要权重将列表排序为“SOME”和“TEXT”之间的距离,并对字符串中“SOME”的出现进行一些小的权重。

另一种更长的方法是首先根据SOME和TEXT之间的距离对列表进行分组。然后根据“SOME”的位置对每个组进行排序。