Кластеризация распространения сродства для адресов
у меня есть список адресов для многих людей (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", номер дома, название улицы, почтовый ящик и т. д., чтобы я мог сравнить их по отдельности (код найден здесь)
как вы можете видеть из данных выше адреса 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)
смотрим этот вопрос, кажется, что проблемы-это кластеризатор добавление небольших случайных начальных точек и подсчет совершенно похожих записей как вырождений.
есть ли способ обойти это или я должен просто отказаться от кластеризации сродства и придерживаться DBSCAN?
1 ответов
хотя я подозреваю, что эта проблема исчезнет с большими выборками разных групп (см. пример ниже), в вашем случае похоже, что вы захотите увеличить 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]