Я пытаюсь реализовать следующий (делительный) алгоритм кластеризации (ниже представлена краткая форма алгоритма, полное описание доступно здесь):
Начните с образца x, я = 1,..., n, рассматриваемого как единый кластер из n точек данных и матрицы D несходства, определенной для всех пар точек. Исправьте пороговое значение T для определения того, нужно ли разбить кластер.
-
Сначала определите расстояние между всеми парами точек данных и выберите пару с наибольшим расстоянием (Dmax) между ними.
-
Сравните Dmax с T. Если Dmax > T, то разделите один кластер на два, используя выбранную пару в качестве первых элементов в двух новых кластерах. Остальные n - 2 точки данных помещаются в один из двух новых кластеров. x_l добавляется в новый кластер, содержащий x_i, если D (x_i, x_l) < D (x_j, x_l), в противном случае добавляется в новый кластер, содержащий x_i.
-
На втором этапе значения D (x_i, x_j) находятся в одном из двух новых кластеров, чтобы найти пару в кластере с наибольшим расстоянием Dmax между ними. Если Dmax < T, разделение кластера останавливается и рассматривается другой кластер. Затем процедура повторяется в кластерах, сгенерированных на этой итерации.
Вывод - это иерархия кластеризованных записей данных. Я прошу совета, как реализовать алгоритм кластеризации.
EDIT 1: Я присоединяю функцию Python, которая определяет расстояние (коэффициент корреляции) и функцию, которая находит максимальное расстояние в матрице данных.
# Read data from GitHub
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/nico/collectiveintelligence-book/master/blogdata.txt', sep = '\t', index_col = 0)
data = df.values.tolist()
data = data[1:10]
# Define correlation coefficient as distance of choice
def pearson(v1, v2):
# Simple sums
sum1 = sum(v1)
sum2 = sum(v2)
# Sums of the squares
sum1Sq = sum([pow(v, 2) for v in v1])
sum2Sq = sum([pow(v, 2) for v in v2])
# Sum of the products
pSum=sum([v1[i] * v2[i] for i in range(len(v1))])
# Calculate r (Pearson score)
num = pSum - (sum1 * sum2 / len(v1))
den = sqrt((sum1Sq - pow(sum1,2) / len(v1)) * (sum2Sq - pow(sum2, 2) / len(v1)))
if den == 0: return 0
return num / den
# Find largest distance
dist={}
max_dist = pearson(data[0], data[0])
# Loop over upper triangle of data matrix
for i in range(len(data)):
for j in range(i + 1, len(data)):
# Compute distance for each pair
dist_curr = pearson(data[i], data[j])
# Store distance in dict
dist[(i, j)] = dist_curr
# Store max distance
if dist_curr > max_dist:
max_dist = dist_curr
РЕДАКТИРОВАТЬ 2: Вложенные ниже функции из ответа Dschoni.
# Euclidean distance
def euclidean(x,y):
x = numpy.array(x)
y = numpy.array(y)
return numpy.sqrt(numpy.sum((x-y)**2))
# Create matrix
def dist_mat(data):
dist = {}
for i in range(len(data)):
for j in range(i + 1, len(data)):
dist[(i, j)] = euclidean(data[i], data[j])
return dist
# Returns i & k for max distance
def my_max(dict):
return max(dict)
# Sort function
list1 = []
list2 = []
def sort (rcd, i, k):
list1.append(i)
list2.append(k)
for j in range(len(rcd)):
if (euclidean(rcd[j], rcd[i]) < euclidean(rcd[j], rcd[k])):
list1.append(j)
else:
list2.append(j)
ИЗМЕНИТЬ 3:
Когда я запускаю код, предоставляемый @Dschoni, алгоритм работает так, как ожидалось. Затем я изменил функцию create_distance_list
, чтобы мы могли вычислить расстояние между многомерными точками данных. Я использую эвклидовое расстояние. В примере с игрушками я загружаю данные iris
. Я группирую только первые 50 экземпляров набора данных.
import pandas as pd
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header = None, sep = ',')
df = df.drop(4, 1)
df = df[1:50]
data = df.values.tolist()
idl=range(len(data))
dist = create_distance_list(data)
print sort(dist, idl)
Результат выглядит следующим образом:
[[24], [17], [4], [7], [40], [13], [14], [15], [26, 27, 38], [3, 16, 39], [25], [42], [18, 20, 45], [43], [1, 2, 11, 46], [12, 37, 41] [5], [21], [22], [10, 23, 28, 29], [6, 34, 48], [0, 8, 33, 36, 44] [31], [32], [19], [30], [35], [9, 47]]
Некоторые точки данных все еще группируются вместе. Я решаю эту проблему, добавляя небольшой объем шума данных в словарь actual
в функции sort
:
# Add small random noise
for key in actual:
actual[key] += np.random.normal(0, 0.005)
Как правильно решить эту проблему?