基于编辑距离纯逻辑实现相似地址聚类

举报
小小明-代码实体 发表于 2022/09/01 23:53:46 2022/09/01
【摘要】 需求背景: 香港公司发来的账单中,有很多相对的地址却使用的不同的派送方式采用了不同的收费,这部分数据明显存在问题需要与香港公司进行确认。所以我们需要将所有相同的地址聚类在一起判断为相同的地址。 上...

需求背景:

image-20220831113813080

香港公司发来的账单中,有很多相对的地址却使用的不同的派送方式采用了不同的收费,这部分数据明显存在问题需要与香港公司进行确认。所以我们需要将所有相同的地址聚类在一起判断为相同的地址。

上图中展示了一种极度简单的情况,只需要将文本所有空格去掉即可找出来,但是部分地址是仅仅差几个汉字字符仍然是相同的地址,为了最高的准确度我们使用编辑距离计算地址间的相似度更佳。

前面已经写过一篇文章:《相似文本聚类与调参

这篇文章的方法优势在于几百万条地址数据时也能快速计算出结果,但是不调参的情况下准确性一般,调参操作比较复杂。

不过今天我们需要处理的地址数量在几万以内,不是特别多,所以完全可以使用编辑距离算法暴力遍历。

首先我们读取数据:

import pandas as pd

df = pd.read_csv("相似地址.csv")
df

  
 
  • 1
  • 2
  • 3
  • 4

image-20220831152227113

为了提高识别的准确率,我们可以事先对地址数据作一些预处理,经观察可以看到部分地址包含小数点、空白字符以及最后面的附加费用信息,例如:

image-20220831152738586

我们可以通过代码将其替换掉:

import re
# 去除小数点和空白字符
df["入仓/派送地址"] = df["入仓/派送地址"].str.replace("[\.\s]+", "", regex=True)
# 五个以上汉字之前的-可以删除
df["入仓/派送地址"] = df["入仓/派送地址"].str.replace(
    "-+([一-龟]{5,})", r"\1", regex=True)
# -后面不是数字或字母,则-后面的全部去掉
df["入仓/派送地址"] = df["入仓/派送地址"].str.replace("-+[^\da-zA-Z].*$", "", regex=True)
# copy后面的全部去掉
df["入仓/派送地址"] = df["入仓/派送地址"].str.replace(
    "[-+]*COPY.+$", "", flags=re.IGNORECASE, regex=True)
# $ 标识后面的全部去掉
df["入仓/派送地址"] = df["入仓/派送地址"].str.replace("[-+]*\$.+$", "", regex=True)
# +标识后面带数字的全部去掉
df["入仓/派送地址"] = df["入仓/派送地址"].str.replace("\+\d.+$", "", regex=True)
df

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可以清晰的看到,对应的非地址数据已经被清理:

image-20220831152917702

各位读取应该根据数据的实际情况编写正则清理非地址数据。

下面我们基于编辑距离开始对相似地址聚类,这里我们需要先安装fuzz:

pip install fuzzywuzzy

  
 
  • 1

然后就可以使用内部计算编辑距离的类:

from Levenshtein._levenshtein import ratio

ratio("葵涌永立街30-40號美基工業大廈5樓", "葵涌永立街30-40號美基工業大廈五樓")

  
 
  • 1
  • 2
  • 3
0.9473684210526315

  
 
  • 1

开始计算,这里我们定义相似度超过0.7的地址被认为是相同的地址:

from Levenshtein._levenshtein import ratio

nums = {}
addrs = df["入仓/派送地址"].values
x = 0
for i in range(addrs.shape[0]-1):
    for j in range(i+1, addrs.shape[0]):
        if i in nums and j in nums:
            continue
        if ratio(addrs[i], addrs[j]) >= 0.7:
            num = nums.get(i, nums.get(j))
            if num is None:
                num = x
                x += 1
            nums[i] = nums[j] = num
len(nums), x

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
(689, 184)

  
 
  • 1

仅0.5秒的时间已经计算出结果,共689地址出现重复,其中有183个不同的地址。

然后我们可以排序并保存结果:

df["重复编号"] = pd.Series(nums)
df = df.convert_dtypes()
df.sort_values("重复编号", inplace=True)
df.to_excel("相似地址聚类.xlsx", index=False)

  
 
  • 1
  • 2
  • 3
  • 4

image-20220831154214794

可以看到一些比较相似的地方都被顺利找到。

文章来源: xxmdmst.blog.csdn.net,作者:小小明-代码实体,版权归原作者所有,如需转载,请联系作者。

原文链接:xxmdmst.blog.csdn.net/article/details/126625350

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。