Как найти документы, которые находятся в одном кластере с KMeans

Я собрал различные статьи вместе с базой Scikit-learn. Ниже приведены 15 лучших слов в каждом кластере:

Cluster 0: whales islands seaworld hurricane whale odile storm tropical kph mph pacific mexico orca coast cabos
Cluster 1: ebola outbreak vaccine africa usaid foundation virus cdc gates disease health vaccines experimental centers obama
Cluster 2: jones bobo sanford children carolina mississippi alabama lexington bodies crumpton mccarty county hyder tennessee sheriff
Cluster 3: isis obama iraq syria president isil airstrikes islamic li strategy terror military war threat al
Cluster 4: yosemite wildfire park evacuation dome firefighters blaze hikers cobb helicopter backcountry trails homes california evacuate

Я создаю матрицу "сумка слов" следующим образом:

hasher = TfidfVectorizer(max_df=0.5,
                             min_df=2, stop_words='english',
                             use_idf=1)
vectorizer = make_pipeline(hasher, TfidfTransformer())
# document_text_list is a list of all text in a given article
X_train_tfidf = vectorizer.fit_transform(document_text_list)

И затем запустите KMeans следующим образом:

km = sklearn.cluster.KMeans(init='k-means++', max_iter=10000, n_init=1,
                verbose=0, n_clusters=25)
km.fit(X_train_tfidf)

Я печатаю кластеры так:

print("Top terms per cluster:")
order_centroids = km.cluster_centers_.argsort()[:, ::-1]
terms = hasher.get_feature_names()
for i in range(25):
    print("Cluster %d:" % i, end='')
    for ind in order_centroids[i, :15]:
        print(' %s' % terms[ind], end='')
    print()

Однако я хотел бы знать, как определить, какие документы принадлежат одному кластеру, и, в идеале, их соответствующее расстояние до центра центроида (кластера).

Я знаю, что каждая строка сгенерированной матрицы (X_train_tfidf) соответствует документу, но нет очевидного способа вернуть эту информацию после выполнения алгоритма KMeans. Как я буду заниматься этим с помощью scikit-learn?

X_train_tfidf выглядит следующим образом:

X_train_tfidf:   (0, 4661)  0.0405014425985
  (0, 19271)    0.0914545222775
  (0, 20393)    0.287636818634
  (0, 56027)    0.116893929188
  (0, 30872)    0.137815327338
  (0, 35256)    0.0343461345507
  (0, 31291)    0.209804679792
  (0, 66008)    0.0643776635222
  (0, 3806) 0.0967713285061
  (0, 66338)    0.0532881852791
  (0, 65023)    0.0702918299573
  (0, 41785)    0.197672720592
  (0, 29774)    0.120772893833
  (0, 61409)    0.0268609667042
  (0, 55527)    0.134102682463
  (0, 40011)    0.0582437010271
  (0, 19667)    0.0234843097048
  (0, 51667)    0.128270976476
  (0, 52791)    0.57198926651
  (0, 15014)    0.149195054799
  (0, 18805)    0.0277497826525
  (0, 35939)    0.170775938672
  (0, 5808) 0.0473913910636
  (0, 24922)    0.0126531527875
  (0, 10346)    0.0200098997901
  : :
  (23945, 56927)    0.0595132327966
  (23945, 23259)    0.0100977769025
  (23945, 12515)    0.0482102583442
  (23945, 49709)    0.210139450446
  (23945, 28742)    0.0190221880312
  (23945, 16628)    0.137692798005
  (23945, 53424)    0.157029848335
  (23945, 30647)    0.104485375827
  (23945, 57512)    0.0569754813269
  (23945, 39389)    0.0158180459761
  (23945, 26093)    0.0153713768922
  (23945, 9787) 0.0963777149738
  (23945, 23260)    0.158336452835
  (23945, 50595)    0.0527243936945
  (23945, 42447)    0.0527515904547
  (23945, 2829) 0.0351677269698
  (23945, 2832) 0.0175929392039
  (23945, 52079)    0.0849796887889
  (23945, 13523)    0.0878730969786
  (23945, 57849)    0.133869666381
  (23945, 25064)    0.128424780903
  (23945, 31129)    0.0919760384953
  (23945, 65601)    0.0388718258746
  (23945, 1428) 0.391477289626
  (23945, 2152) 0.655211469073
  X_train_tfidf shape: (23946, 67816)

В ответ на ttttthomasssss Ответ:

Когда я пытаюсь запустить следующее:

X_cluster_0 = X_train_tfidf[cluster_0]

Я получаю сообщение об ошибке:

File "cluster.py", line 52, in main
    X_cluster_0 = X_train_tfidf[cluster_0]
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/sparse/csr.py", line 226, in __getitem__
    col = key[1]
IndexError: tuple index out of range

Глядя на структуру cluster_0:

(array([  858,  2012,  2256,  2762,  2920,  3770,  6052,  6174,  8296,
9494,  9966, 10085, 11914, 12117, 12633, 12727, 12993, 13527,
13754, 14186, 14669, 14713, 14973, 15071, 15157, 15208, 15926,
16300, 16301, 17138, 17556, 17775, 18236, 19057, 20106, 21014, 21080]),)

Это структура кортежа, которая имеет контент в 0-й позиции, поэтому я изменил строку на следующее:

X_cluster_0 = X_train_tfidf[cluster_0[0]]

Я вытаскиваю "документы" из базы данных, из которой я могу легко получить индекс из (итерации предоставленного массива до тех пор, пока не найду соответствующий документ [предполагая, конечно, что scikit не изменяет порядок документов в матрице]). Поэтому я не понимаю, что именно представляет X_cluster_0. X_cluster_0 имеет следующую структуру:

  X_cluster_0:   (0, 42726) 0.741747456202
  (0, 13535)    0.115880661286
  (0, 17447)    0.117608794277
  (0, 44849)    0.414829246262
  (0, 14574)    0.10214258736
  (0, 17317)    0.0634383214735
  (0, 17935)    0.0591234431875
  : :
  (17, 33867)   0.0174155914371
  (17, 48916)   0.0227046046275
  (17, 59132)   0.0168864861723
  (17, 40860)   0.0485813219503
  (17, 63725)   0.0271415763987
  (18, 45019)   0.490135684209
  (18, 36168)   0.14595160766
  (18, 52304)   0.139590524213
  (18, 63586)   0.16501953796
  (18, 28709)   0.15075416279
  (18, 11495)   0.0926490431993
  (18, 40860)   0.124236878928

Вычисление расстояния до Centroid

В настоящее время запущенный предлагаемый код (distance = euclidean(X_cluster_0[0], km.cluster_centers_[0])) приводит к следующей ошибке:

File "cluster.py", line 68, in main
    distance = euclidean(X_cluster_0[0], km.cluster_centers_[0])
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/spatial/distance.py", line 211, in euclidean
    dist = norm(u - v)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/sparse/compressed.py", line 197, in __sub__
    raise NotImplementedError('adding a nonzero scalar to a '
NotImplementedError: adding a nonzero scalar to a sparse matrix is not supported

Вот что выглядит km.cluster_centers:

km.cluster_centers: [  9.47080802e-05   2.53907413e-03   0.00000000e+00 ...,   0.00000000e+00
   0.00000000e+00   0.00000000e+00]

Я предполагаю, что проблема, с которой я сейчас сталкиваюсь, состоит в том, как извлечь i-й элемент матрицы (предполагая обход матрицы слева направо). Любой уровень вложенности индекса, который я указываю, не имеет никакого значения (т.е. X_cluster_0[0], X_cluster_0[0][0] и X_cluster_0[0][0][0] все дают мне ту же распечатанную матричную структуру, которая изображена выше).

Ответ 1

Вы можете использовать функцию fit_predict() для выполнения кластеризации и получения индексов результирующих кластеров.

Получение индекса кластера для каждого документа

Вы можете попробовать следующее:

km = sklearn.cluster.KMeans(init='k-means++', max_iter=10000, n_init=1,
                verbose=0, n_clusters=25)
clusters = km.fit_predict(X_train_tfidf)

# Note that your input data has dimensionality m x n and the clusters array has dimensionality m x 1 and contains the indices for every document
print X_train_tfidf.shape
print clusters.shape

# Example to get all documents in cluster 0
cluster_0 = np.where(clusters==0) # don't forget import numpy as np

# cluster_0 now contains all indices of the documents in this cluster, to get the actual documents you'd do:
X_cluster_0 = X_train_tfidf[cluster_0]

Поиск расстояния каждого документа до каждого центроида

Вы можете получить центроиды, выполнив centroids = km.cluster_centers_, который в вашем случае должен иметь размерность 25 (количество кластеров) x n (количество функций). Для вычисления, например, евклидова расстояния документа до центра тяжести, вы можете использовать SciPy (можно найти docs для scipy различных метрик расстояния здесь):

# Example, distance for 1 document to 1 cluster centroid
from scipy.spatial.distance import euclidean

distance = euclidean(X_cluster_0[0], km.cluster_centers_[0])
print distance

Обновление: Расстояния с разреженными и плотными матрицами

Метрики расстояния в scipy.spatial.distance требуют, чтобы входные матрицы были плотными матрицами, поэтому, если X_cluster_0 - разреженная матрица, вы можете либо преобразовать матрицу в плотную матрицу:

d = euclidean(X_cluster_0.A[0], km.cluster_centers_[0]) # Note the .A on X_cluster_0
print d

В качестве альтернативы вы можете использовать функцию scikit euclidean_distances(), которая также работает с разреженными матрицами:

from sklearn.metrics.pairwise import euclidean_distances

D = euclidean_distances(X_cluster_0.getrow(0), km.cluster_centers_[0]) 
# This would be the equivalent expression to the above scipy example, however note that euclidean_distances returns a matrix and not a scalar
print D

Обратите внимание, что с помощью метода scikit вы можете сразу вычислить всю матрицу расстояний:

D = euclidean_distances(X_cluster_0, km.cluster_centers_)
print D

Обновление: структура и тип X_cluster_0:

X_cluster_0, а также X_train_tfidf являются разреженными матрицами (см. docs: scipy.sparse.csr.csr_matrix).

Интерпретация дампа, такого как

(0, 13535)    0.115880661286
(0, 17447)    0.117608794277
(0, 44849)    0.414829246262
(0, 14574)    0.10214258736
.             .
.             .

будет следующим: (0, 13535) относится к документу 0 и функции 13535, поэтому номер строки 0 и номер столбца 13535 в вашей сумке словной матрицы. Следующее число с плавающей запятой 0.115880661286 представляет оценку tf-idf для этой функции в данном документе.

Чтобы узнать точное слово, которое вы могли бы попробовать сделать hasher.get_feature_names()[13535] (сначала проверьте len(hasher.get_feature_names()), чтобы узнать, сколько у вас функций).

Если ваша переменная corpus document_text_list - это список списков, то соответствующий документ будет просто document_text_list[0].