Sklearn Агломеративная кластеризация Custom Affinity

Я пытаюсь использовать агломеративную кластеризацию с пользовательской метрикой расстояния (т.е. сродством), так как я хотел бы сгруппировать последовательность целых чисел по сходству последовательностей, а не по евклидову расстоянию, которое не имеет смысла.

Мои данные выглядят примерно так

>> dat.values 

array([[860, 261, 240, ..., 300, 241,   1],
   [860, 840, 860, ..., 860, 240,   1],
   [260, 860, 260, ..., 260, 220,   1],
   ...,
   [260, 260, 260, ..., 260, 260,   1],
   [260, 860, 260, ..., 840, 860,   1],
   [280, 240, 241, ..., 240, 260,   1]]) 

Я создал следующую функцию подобия

def sim(x, y): 
    return np.sum(np.equal(np.array(x), np.array(y)))/len(x)

Поэтому я просто возвращаю% совпадающих значений в двух последовательностях с помощью numpy и делаю следующий вызов

cluster = AgglomerativeClustering(n_clusters=5, affinity=sim, linkage='average')
cluster.fit(dat.values)

Но я получаю сообщение об ошибке

TypeError: sim() missing 1 required positional argument: 'y'

Я не уверен, почему я получаю эту ошибку; Я думал, что функция будет группировать пары строк, поэтому будет передан каждый требуемый аргумент.

Любая помощь в этом будет принята с благодарностью


person ApprenticeOfMathematics    schedule 19.12.2018    source источник


Ответы (2)


'affinity' в качестве вызываемого требует один вход X (который является вашей функцией или матрицей наблюдения), а затем вызывает расстояния между всеми точками (выборками) внутри него.

Итак, вам нужно изменить свой метод как:

# Your method to calculate distance between two samples
def sim(x, y): 
    return np.sum(np.equal(np.array(x), np.array(y)))/len(x)


# Method to calculate distances between all sample pairs
from sklearn.metrics import pairwise_distances
def sim_affinity(X):
    return pairwise_distances(X, metric=sim)

cluster = AgglomerativeClustering(n_clusters=5, affinity=sim_affinity, linkage='average')
cluster.fit(X)

Или вы можете использовать affinity='precomputed', как предложил @avchauzov. Для этого вам нужно будет передать предварительно рассчитанную матрицу расстояний для ваших наблюдений в fit(). Что-то вроде:

cluster = AgglomerativeClustering(n_clusters=5, affinity='precomputed', linkage='average')
distance_matrix = sim_affinity(X)
cluster.fit(distance_matrix)

Примечание. Вы указали сходство вместо расстояния. Поэтому убедитесь, что вы понимаете, как здесь будет работать кластеризация. Или, может быть, настроить функцию подобия, чтобы вернуть расстояние. Что-то вроде:

def sim(x, y): 
    # Subtracted from 1.0 (highest similarity), so now it represents distance
    return 1.0 - np.sum(np.equal(np.array(x), np.array(y)))/len(x)
person Vivek Kumar    schedule 19.12.2018
comment
Заметка оказалась очень полезной, спасибо! Я думал, что могу просто передать сходство, но вижу, что мне нужно вычесть из 1,0, чтобы получить то, что я хочу, плюс пример кода имеет смысл и работает. Ваше здоровье - person ApprenticeOfMathematics; 19.12.2018

Обычный способ сделать это — поставить affinity='precomputed и подогнать матрицу расстояний (см. пример здесь: https://gist.github.com/codehacken/8b9316e025beeabb082dda4d0654a6fa)

UPD В sklearn.hierarchical.py (https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/cluster/hierarchical.py#L460) вы можете видеть, что ваша пользовательская близость должна получить только X (не y) как ввод. И вход должен быть linkage_tree. Итак, вам нужно переписать функцию sim().

Но на мой взгляд первый способ гораздо удобнее.

person avchauzov    schedule 19.12.2018