我有一份许多人的地址列表(每人1到8个地址),我想确定每个人拥有的唯一地址数量。
这里是一个人的地址数据样本
#df[df['ID'] =='12345'][['address','zip]].valuesaddresses = [['PULMONARY MED ASSOC MED GROUP INC 1485 RIVER PARK DR STE 200', '95815'], ['1485 RIVER PARK DRIVE SUITE 200', '95815'], ['1485 RIVER PARK DR SUITE 200', '95815'], ['3637 MISSION AVE SUITE 7', '95608']]
我有一个地址解析器,可以将地址的不同部分分开,如“attn”、门牌号、街道名称、邮政信箱等,以便我可以单独比较它们(代码见这里)
从上面的数据可以看出,地址1-3可能是相同的,而地址4是不同的。
我编写了以下相似度计算方法——权重没有特别的魔力,只是基于我的直觉认为哪些是最重要的
def calcDistance(a1, a2,z1,z2, parser): z1 = str(z1) z2 = str(z2) add1 = parser.parse(a1) add2 = parser.parse(a2) zip_dist = 0 if z1 == z2 else distance.levenshtein(z1,z2) zip_weight = .4 attn_dist = distance.levenshtein(add1['attn'],add2['attn']) if add1['attn'] and add2['attn'] else 0 attn_weight = .1 if add1['attn'] and add2['attn'] else 0 suite_dist = distance.levenshtein(add1['suite_num'],add2['suite_num']) if add1['suite_num'] and add2['suite_num'] else 0 suite_weight = .1 if add1['suite_num'] and add2['suite_num'] else 0 street_dist = distance.levenshtein(add1['street_name'],add2['street_name']) if add1['street_name'] and add2['street_name'] else 0 street_weight = .3 if add1['street_name'] and add2['street_name'] else 0 house_dist = distance.levenshtein(add1['house'],add2['house']) if add1['house'] and add2['house'] else 0 house_weight = .1 if add1['house'] and add2['house'] else 0 weight = (zip_dist * zip_weight + attn_dist * attn_weight + suite_dist * suite_weight + street_dist * street_weight + house_dist * house_weight ) / (zip_weight +attn_weight + suite_weight + street_weight + house_weight ) return weight
将此函数应用于我的每个地址,你可以看到地址1-3完全相似是正确的,而地址4略有不同。
similarity = -1*np.array([[calcDistance(a1[0],a2[0],a1[1],a2[1],addr_parser) for a1 in addresses] for a2 in addresses])print similarity array([[-0. , -0. , -0. , -5.11111111], [-0. , -0. , -0. , -5.11111111], [-0. , -0. , -0. , -5.11111111], [-5.11111111, -5.11111111, -5.11111111, -0. ]])
为了对这些地址进行聚类,我认为亲和聚类可能是最好的方法——聚类数量是可变的,它可以处理距离,并且可以识别出典型的例子,我可以使用“最佳”地址来代表聚类。然而,我得到了一些奇怪的结果——亲和聚类器对这些数据产生了3个聚类,而不是2个。
affprop = sklearn.cluster.AffinityPropagation(affinity="precomputed", damping=.5)affprop.fit(similarity)print affprop.labels_array([0, 0, 1, 2], dtype=int64)
相反,DBSCAN正确地聚类成了两个
dbscan = sklearn.cluster.DBSCAN(min_samples=1)dbscan.fit(similarity)print dbscan.labels_array([0, 0, 0, 1], dtype=int64)
查看这个问题,看起来问题是聚类器添加了小的随机起始点,并将完全相似的记录视为退化情况。
有没有办法解决这个问题,或者我应该放弃亲和聚类而坚持使用DBSCAN?
回答:
虽然我认为随着不同组的样本增多(见下面的例子),这个问题会消失,但在你的情况下,看起来你需要增加damping
因子来获得所需的结果。从0.95开始,你可以得到正确的分组:
>>> affprop = sklearn.cluster.AffinityPropagation(affinity="precomputed", damping=.95)>>> affprop.fit(similarity)AffinityPropagation(affinity='precomputed', convergence_iter=15, copy=True, damping=0.95, max_iter=200, preference=None, verbose=False)>>> print affprop.labels_[0 0 0 1]
正如我最初提到的,随着你向数据集中添加更多不同的数据,这个问题似乎会消失。查看你引用的问题中的例子,我们看到他们最初也有同样的问题:
>>> c = [[0], [0], [0], [0], [0], [0], [0], [0]]>>> af = sklearn.cluster.AffinityPropagation (affinity = 'euclidean').fit (c)>>> print (af.labels_)[0 1 0 1 2 1 1 0]
这在增加阻尼后消失了:
>>> c = [[0], [0], [0], [0], [0], [0], [0], [0]]>>> af = sklearn.cluster.AffinityPropagation (affinity = 'euclidean', damping=.99).fit (c)>>> print (af.labels_)[0 0 0 0 0 0 0 0]
或者当我们引入更多组时:
>>> c = [[0], [0], [0], [1], [2], [1], [2], [1]]>>> af = sklearn.cluster.AffinityPropagation (affinity = 'euclidean', damping=.5).fit (c)>>> print (af.labels_)[0 0 0 2 1 2 1 2]