我有许多人的地址列表(每个地址1-8个地址),我正在尝试确定每个人拥有的唯一地址的数量。
这是一个人的示例地址数据集
#df[df['ID'] =='12345'][['address','zip]].values
addresses = [['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”,门牌号码,街道名称,邮政信箱等,以便我可以单独比较它们(代码找到here)
从上面的数据可以看出,地址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. ]])
然后对这些进行聚类,我认为亲和力聚类可能是最好的方法 - 聚类计数是可变的,它适用于距离,并且可以识别原型示例,然后我可以使用“最佳”地址来表示集群。但是,我得到了一些奇怪的结果 - affinityprop clusterer为这些数据生成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)
看this question,似乎问题是群集器正在添加小的随机起点,并将完全相似的记录计算为退化。
有没有解决方法或者我应该放弃亲和力聚类并坚持使用DBSCAN?
答案 0 :(得分:3)
虽然我怀疑这个问题会随着不同群体的大样本而消失(参见下面的示例),但在您的情况下,您似乎希望增加damping
因子以获得所需结果。从.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]