10

我需要将冷线索与我们客户的数据库进行匹配。

潜在客户大量来自第三方提供商(数千条记录),销售要求我们(用他们的话说)“过滤掉我们的客户”,这样他们就不会试图将我们的服务出售给老客户。

显然,线索中有拼写错误。Charles 变成了 Charlie,Joseph 变成了 Joe,等等。所以我不能只做一个过滤器,比较lead_first_name 和 client_first_name 等等。

我需要使用某种字符串相似性机制。

现在我正在使用可爱的difflib将潜在客户的名字和姓氏与使用Client.objects.all(). 它可以工作,但是由于客户端的数量,它往往很慢。

我知道大多数 sql 数据库都有 soundex 和差异函数。在下面的更新中查看我对它的测试 - 它的效果不如 difflib。

还有其他解决方案吗?有更好的解决方案吗?

编辑:

Soundex,至少在我的数据库中,表现不如 difflib。

这是一个简单的测试 - 在包含“Joseph Lopes”的表中查找“Joe Lopes”:

with temp (first_name, last_name) as (
select 'Joseph', 'Lopes'
union
select 'Joe', 'Satriani'
union
select 'CZ', 'Lopes'
union
select 'Blah', 'Lopes'
union
select 'Antonio', 'Lopes'
union
select 'Carlos', 'Lopes'
)
select first_name, last_name
  from temp
 where difference(first_name+' '+last_name, 'Joe Lopes') >= 3
 order by difference(first_name+' '+last_name, 'Joe Lopes')

以上返回“Joe Satriani”作为唯一匹配。即使将相似度阈值降低到 2 也不会将“Joseph Lopes”作为潜在匹配返回。

但是 difflib 做得更好:

difflib.get_close_matches('Joe Lopes', ['Joseph Lopes', 'Joe Satriani', 'CZ Lopes', 'Blah Lopes', 'Antonio Lopes', 'Carlos Lopes'])
['Joseph Lopes', 'CZ Lopes', 'Carlos Lopes']

在 gruszczy 的回复后编辑:

在自己编写之前,我在所有知识的存储库中寻找并找到了 Levenshtein Distance 的 T-SQL 实现。

在测试它时,它仍然不会比 difflib 做更好的匹配工作。

这使我研究了 difflib 背后的算法。它似乎是Ratcliff -Obershelp算法的修改版本。

不幸的是,我似乎找不到其他善良的灵魂已经创建了基于 difflib 的 T-SQL 实现......我会尽可能地尝试一下。

如果在接下来的几天内没有其他人提出更好的答案,我会将其授予 gruszczy。谢谢,好心的先生。

4

4 回答 4

2

soundex不会帮助你,因为它是一种语音算法。Joe 和 Joseph 在语音上并不相似,因此 soundex 不会将它们标记为相似。

您可以尝试在 PostgreSQL 中实现的Levenshtein distance 。也许也在您的数据库中,如果没有,您应该能够编写一个存储过程,它将计算两个字符串之间的距离并将其用于您的计算。

于 2010-08-04T14:16:05.883 回答
2

从 Django 1.10 开始可以使用trigram_similar查找,请参阅PostgreSQL 特定查找全文搜索的文档

于 2016-07-25T15:22:59.363 回答
1

如果您需要使用 django 和 postgres 到达那里并且不想使用 1.10 trigram-similarity https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/lookups/#trigram-similarity中引入的,您可以使用 Levensthein 实现,如下所示:

需要扩展的模糊字符串匹配

您需要在 psql 中将 postgres 扩展添加到您的数据库:

CREATE EXTENSION fuzzystrmatch;

让我们定义自定义函数,我们可以注释查询集。它只需要一个参数 search_term 并使用 postgres levenshtein 函数(参见文档):

from django.db.models import Func

class Levenshtein(Func):
    template = "%(function)s(%(expressions)s, '%(search_term)s')"
    function = "levenshtein"

    def __init__(self, expression, search_term, **extras):
        super(Levenshtein, self).__init__(
            expression,
            search_term=search_term,
            **extras
        )

然后在项目的任何其他地方,我们只需导入定义的 Levenshtein 和 F 来传递 django 字段。

from django.db.models import F

Spot.objects.annotate(
    lev_dist=Levenshtein(F('name'), 'Kfaka')
).filter(
    lev_dist__lte=2
)
于 2018-04-06T15:27:40.673 回答
0

根据andilabs的回答,您可以使用 Levenshtein 函数来创建您的自定义函数。Postgres doc表示Levenshtein函数如下:

levenshtein(text source, text target, int ins_cost, int del_cost, int sub_cost) returns int levenshtein(text source, text target) returns int

andilabs answer可以使用唯一的第二个功能。如果你想要一个更高级的插入/删除/替换成本搜索,你可以重写这样的函数:

from django.db.models import Func

class Levenshtein(Func):
    template = "%(function)s(%(expressions)s, '%(search_term)s', %(ins_cost)d, %(del_cost)d, %(sub_cost)d)"
    function = 'levenshtein'

    def __init__(self, expression, search_term, ins_cost=1, del_cost=1, sub_cost=1, **extras):
        super(Levenshtein, self).__init__(
            expression,
            search_term=search_term,
            ins_cost=ins_cost,
            del_cost=del_cost,
            sub_cost=sub_cost,
            **extras
        )

并调用函数:

from django.db.models import F

Spot.objects.annotate(
    lev_dist=Levenshtein(F('name'), 'Kfaka', 3, 3, 1)  # ins = 3, del = 3, sub = 1
).filter(
    lev_dist__lte=2
)
于 2021-04-02T09:29:05.390 回答