Рубрики
Без рубрики

Наука о данных с Python & R: Уменьшение размерности и кластеризация

Важным шагом в анализе данных является изучение и представление данных. В этом уроке мы увидим, как, комбинируя метод, называемый Анализ главных компонентов (PCA), вместе с кластером, мы можем представлять в двумерном пространстве данные, определенные в более высоком измерении, и в то же время иметь возможность группировать эти данные в аналогичные группы или кластеры и находить скрытые связи в наших данных.

Автор оригинала: Jose A Dianes.

Вступление

Важным шагом в анализе данных является изучение и представление данных. Мы уже видели некоторые концепции в исследовательском анализе данных и как их использовать как с Python, так и с R. В этом уроке мы увидим, как, комбинируя метод, называемый Анализ главных компонентов (PCA) вместе с Кластерным анализом мы можем представлять в двумерном пространстве данные, определенные в более высоком измерении , в то же время, быть в состоянии группировать эти данные в аналогичные группы или кластеры и находить скрытые связи в наших данных .

Более конкретно, PCA уменьшает размерность данных, находя основные компоненты . Это направления максимального изменения в наборе данных. Уменьшая исходные объекты или переменные набора данных до уменьшенного набора новых, основанных на основных компонентах, мы получаем минимальное количество переменных, которые сохраняют максимальное количество вариаций или информацию о том, как распределяются данные . Если мы получим только две из этих новых переменных, мы сможем представить каждый образец в наших данных в виде двумерной диаграммы (например, диаграммы рассеяния).

Как метод неконтролируемого анализа данных, кластеризация организует выборки данных по близости на основе своих переменных. Таким образом, мы сможем понять, как каждая точка данных связана друг с другом, и обнаружить группы похожих точек. Как только у нас будет каждая из этих групп или кластеров, мы сможем определить для них центроид-идеальную выборку данных, которая минимизирует сумму расстояний до каждой из точек данных в кластере. Анализируя переменные этих центроидов, мы сможем определить каждый кластер с точки зрения его характеристик.

Но на сегодня достаточно теории. Давайте воплотим эти идеи на практике, используя Python и R, чтобы лучше понять их, чтобы применить в будущем.

Весь исходный код для различных частей этой серии учебных пособий и приложений можно проверить на GitHub . Не стесняйтесь участвовать и делиться с нами своими успехами!

Подготовка Наших Данных По Туберкулезу

Мы продолжим использовать те же наборы данных, которые мы уже загрузили в партию введение фреймов данных . Веб-сайт Gapminder представляет себя как основанное на фактах мировоззрение . Это всеобъемлющий ресурс для сбора данных по показателям различных стран и территорий. Его раздел Data содержит список наборов данных, к которым можно получить доступ в виде страниц электронных таблиц Google (добавить &output=csv для загрузки в формате CSV). Каждый набор данных индикаторов помечен Поставщиком данных , Категорией и Подкатегорией .

Для этого урока мы будем использовать набор данных, связанный с распространенностью инфекционного туберкулеза:

Мы предлагаем читателю повторить этот процесс с новыми наборами данных о случаях и смертях и поделиться результатами.

Для того, чтобы сделать учебник самодостаточным, мы повторим здесь код, который получает и готовит бот наборов данных, на Python и R. Этот учебник посвящен изучению стран. Поэтому мы будем работать с наборами данных, где каждая выборка представляет собой страну, а каждая переменная-год.

R

В R вы используете read.csv для чтения CSV-файлов в переменные data.frame . Хотя функция R read.csv может работать с URL-адресами, https во многих случаях является проблемой для R, поэтому вам нужно использовать пакет, такой как RCurl, чтобы обойти его.

library(RCurl)
## Loading required package: bitops
# Get and process existing cases file
existing_cases_file <- getURL("https://docs.google.com/spreadsheets/d/1X5Jp7Q8pTs3KLJ5JBWKhncVACGsg5v4xu6badNs4C7I/pub?gid=0&output=csv")
existing_df <- read.csv(text = existing_cases_file, row.names=1, stringsAsFactor=F)
existing_df[c(1,2,3,4,5,6,15,16,17,18)] <- 
    lapply( existing_df[c(1,2,3,4,5,6,15,16,17,18)], 
            function(x) { as.integer(gsub(',', '', x) )})

Питон

Итак, сначала нам нужно загрузить данные электронной таблицы Google в формате CSV.

import urllib
    
tb_existing_url_csv = 'https://docs.google.com/spreadsheets/d/1X5Jp7Q8pTs3KLJ5JBWKhncVACGsg5v4xu6badNs4C7I/pub?gid=0&output=csv'
local_tb_existing_file = 'tb_existing_100.csv'
existing_f = urllib.urlretrieve(tb_existing_url_csv, local_tb_existing_file)

Теперь, когда он у нас есть локально, нам нужно прочитать CSV-файл как фрейм данных.

import pandas as pd
    
existing_df = pd.read_csv(
    local_tb_existing_file, 
    index_col = 0, 
    thousands  = ',')
existing_df.index.names = ['country']
existing_df.columns.names = ['year']

Мы указали index_col равным 0, так как мы хотим, чтобы названия стран были метками строк . Мы также указали разделитель thousands как”,”, поэтому Pandas автоматически анализирует ячейки как числа. Мы можем использовать head() для проверки первых нескольких строк.

existing_df.head()
страна
436 429 422 415 407 397 397 387 374 373 346 326 304 308 283 267 251 238 Афганистан
42 40 41 42 42 43 42 44 43 42 40 34 32 32 29 29 26 22 Албания
45 44 44 43 43 42 43 44 45 46 48 49 50 51 52 53 55 56 Алжир
42 14 4 18 17 22 0 25 12 8 8 6 5 6 9 11 9 5 Американское Самоа
39 37 35 33 32 30 28 23 24 22 20 20 21 18 19 18 17 19 Андорра

Уменьшение размерности с помощью PCA

В этом разделе мы хотим иметь возможность представлять каждую страну в двумерном пространстве. В нашем наборе данных каждая выборка представляет собой страну, определяемую 18 различными переменными, каждая из которых соответствует количеству случаев туберкулеза на 100 тысяч (существующих, новых, смертей) за данный год с 1990 по 2007 год. Эти переменные представляют собой не только общее количество или среднее значение в диапазоне 1990-2007 годов, но и все изменения во временных рядах и взаимосвязях внутри стран в данном году. Используя PCA, мы сможем свести эти 18 переменных только к двум из них, которые лучше всего улавливают эту информацию.

Для этого мы сначала расскажем, как выполнить PCA и построить первые два КОМПЬЮТЕРА в обоих, Python и R. Мы закроем раздел, проанализировав результирующий график и каждый из двух компьютеров.

R

Пакет R по умолчанию stats поставляется с функцией prcomp() для выполнения анализа основных компонентов. Это означает, что нам не нужно ничего устанавливать (хотя есть и другие варианты с использованием внешних пакетов). Это, пожалуй, самый быстрый способ сделать PCA, и я рекомендую вам вызвать ?prcomp в вашей консоли R, если вас интересуют подробности о том, как настроить процесс PCA с помощью этой функции.

pca_existing <- prcomp(existing_df, scale. = TRUE)

Полученный объект содержит несколько фрагментов информации, связанных с анализом основных компонентов. Нас интересуют оценки, которые у нас есть в pca_existing$x . У нас есть 18 различных основных компонентов. Помните, что общее количество ПК соответствует общему количеству переменных в наборе данных, хотя обычно мы хотим использовать не все из них, а только подмножество, соответствующее нашим целям.

В нашем случае мы будем использовать первые два. Сколько вариаций объясняется каждым из них? В R мы можем использовать для этого функцию plot , которая поставляется с результатом PCA.

plot(pca_existing)
введите описание изображения здесь

Большая часть вариаций объясняется первым ПК. Итак, давайте используем первые два компьютера, чтобы представить все наши страны на диаграмме рассеяния.

scores_existing_df <- as.data.frame(pca_existing$x)
# Show first two PCs for head countries
head(scores_existing_df[1:2])
##                      PC1          PC2
## Afghanistan    -3.490274  0.973495650
## Albania         2.929002  0.012141345
## Algeria         2.719073 -0.184591877
## American Samoa  3.437263  0.005609367
## Andorra         3.173621  0.033839606
## Angola         -4.695625  1.398306461

Теперь, когда они у нас есть в фрейме данных, мы можем использовать их с plot .

plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid")
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8)
введите описание изображения здесь

Давайте установим цвет, связанный со средним значением за все годы. Мы будем использовать функции rgb , ramp и rescale для создания цветовой палитры от желтого (более низкие значения) до синего (более высокие значения).

library(scales)
ramp <- colorRamp(c("yellow", "blue"))
colours_by_mean <- rgb( 
    ramp( as.vector(rescale(rowMeans(existing_df),c(0,1)))), 
    max = 255 )
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=colours_by_mean)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=colours_by_mean)
введите описание изображения здесь

Теперь давайте свяжем цвет с общей суммой.

ramp <- colorRamp(c("yellow", "blue"))
colours_by_sum <- rgb( 
    ramp( as.vector(rescale(rowSums(existing_df),c(0,1)))), 
    max = 255 )
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=colours_by_sum)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=colours_by_sum)
введите описание изображения здесь

И, наконец, давайте свяжем это с разницей между первым и прошлым годом, как простой способ измерить изменение во времени.

existing_df_change <- existing_df$X2007 - existing_df$X1990
ramp <- colorRamp(c("yellow", "blue"))
colours_by_change <- rgb( 
    ramp( as.vector(rescale(existing_df_change,c(0,1)))), 
    max = 255 )
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=colours_by_change)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=colours_by_change)
введите описание изображения здесь

У нас уже есть некоторые интересные выводы о том, что код PC1 и PC2 представляет собой представление о годах с 1990 по 2008 год. Мы объясним это сразу после того, как покажем, как выполнить уменьшение размерности с помощью Python.

Питон

Библиотека машинного обучения sklearn Python поставляется с реализацией PCA . Эта реализация использует scipy.linalg реализацию декомпозиции сингулярных значений|/. Он работает только для плотных массивов (см. numPy dense arrays или sparse array PCA , если вы используете разреженные массивы) и не масштабируется для больших размерных данных. Для больших размерных данных мы должны рассмотреть что-то вроде функций уменьшения размерности Spark|/. В нашем случае у нас всего 18 переменных, и это далеко не большое количество функций для современных библиотек машинного обучения и компьютерных возможностей.

При использовании этой реализации PCA нам необходимо заранее указать количество основных компонентов, которые мы хотим использовать. Затем мы можем просто вызвать метод fit () | с нашим фреймом данных и проверить результаты.

from sklearn.decomposition import PCA
    
pca = PCA(n_components=2)
pca.fit(existing_df)
    PCA(copy=True, n_components=2, whiten=False)

Это дает нам объект, который мы можем использовать для преобразования наших данных, вызвав transform .

existing_2d = pca.transform(existing_df)

Или мы могли бы просто вызвать fit_transform , чтобы выполнить оба шага в одном вызове .

В обоих случаях мы получим представление нашего фрейма данных в более низком измерении в виде массива numPy. Давайте поместим его в новый фрейм данных.

existing_df_2d = pd.DataFrame(existing_2d)
existing_df_2d.index = existing_df.index
existing_df_2d.columns = ['PC1','PC2']
existing_df_2d.head()
страна
-732.215864 203.381494 Афганистан
613.296510 4.715978 Албания
569.303713 -36.837051 Алжир
717.082766 5.464696 Американское Самоа
661.802241 11.037736 Андорра

Мы также можем напечатать объясненный коэффициент дисперсии следующим образом.

print(pca.explained_variance_ratio_) 
    [ 0.91808789  0.060556  ]

Мы видим, что первый ПК уже объясняет почти 92% дисперсии, в то время как на второй приходится еще 6% в общей сложности почти 98% между ними двумя.

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

%matplotlib inline
    
ax = existing_df_2d.plot(kind='scatter', x='PC2', y='PC1', figsize=(16,8))
    
for i, country in enumerate(existing_df.index):
    ax.annotate(
        country, 
        (existing_df_2d.iloc[i].PC2, existing_df_2d.iloc[i].PC1)
    )
введите описание изображения здесь

Теперь давайте создадим пузырьковую диаграмму, установив размер точки на значение, пропорциональное среднему значению за все годы в этой конкретной стране. Сначала нам нужно добавить новый столбец, содержащий масштабированное среднее значение по стране за все годы.

from sklearn.preprocessing import normalize
    
existing_df_2d['country_mean'] = pd.Series(existing_df.mean(axis=1), index=existing_df_2d.index)
country_mean_max = existing_df_2d['country_mean'].max()
country_mean_min = existing_df_2d['country_mean'].min()
country_mean_scaled = 
    (existing_df_2d.country_mean-country_mean_min) / country_mean_max
existing_df_2d['country_mean_scaled'] = pd.Series(
        country_mean_scaled, 
        index=existing_df_2d.index) 
existing_df_2d.head()
страна
-732.215864 203.381494 Афганистан 353.333333 0.329731
613.296510 4.715978 Албания 36.944444 0.032420
569.303713 -36.837051 Алжир 47.388889 0.042234
717.082766 5.464696 Американское Самоа 12.277778 0.009240
661.802241 11.037736 Андорра 25.277778 0.021457

Теперь мы готовы построить график, используя этот размер переменной (на этот раз мы опустим названия стран, так как они нас не так интересуют).

existing_df_2d.plot(
    kind='scatter', 
    x='PC2', 
    y='PC1', 
    s=existing_df_2d['country_mean_scaled']*100, 
    figsize=(16,8))
введите описание изображения здесь

Давайте сделаем то же самое с суммой, а не со средним значением.

existing_df_2d['country_sum'] = pd.Series(
    existing_df.sum(axis=1), 
    index=existing_df_2d.index)
country_sum_max = existing_df_2d['country_sum'].max()
country_sum_min = existing_df_2d['country_sum'].min()
country_sum_scaled =
    (existing_df_2d.country_sum-country_sum_min) / country_sum_max
existing_df_2d['country_sum_scaled'] = pd.Series(
        country_sum_scaled, 
        index=existing_df_2d.index)
existing_df_2d.plot(
    kind='scatter', 
    x='PC2', y='PC1', 
    s=existing_df_2d['country_sum_scaled']*100, 
    figsize=(16,8))
введите описание изображения здесь

И, наконец, давайте свяжем размер с изменением в период с 1990 по 2007 год. Обратите внимание, что в масштабированной версии значения, близкие к нулю, будут ссылаться на значения с отрицательными значениями в исходной немасштабированной версии, поскольку мы масштабируем до диапазона [0,1].

existing_df_2d['country_change'] = pd.Series(
    existing_df['2007']-existing_df['1990'], 
    index=existing_df_2d.index)
country_change_max = existing_df_2d['country_change'].max()
country_change_min = existing_df_2d['country_change'].min()
country_change_scaled = 
    (existing_df_2d.country_change - country_change_min) / country_change_max
existing_df_2d['country_change_scaled'] = pd.Series(
        country_change_scaled, 
        index=existing_df_2d.index)
existing_df_2d[['country_change','country_change_scaled']].head()
страна
Афганистан -198 0.850840
Албания -20 1.224790
Алжир 11 1.289916
Американское Самоа -37 1.189076
Андорра -20 1.224790
existing_df_2d.plot(
    kind='scatter', 
    x='PC2', y='PC1', 
    s=existing_df_2d['country_change_scaled']*100, 
    figsize=(16,8))
введите описание изображения здесь

Результаты PCA

Из графиков, которые мы сделали в Python и R, мы можем подтвердить, что наибольшее изменение происходит вдоль оси y, которую мы назначили PC1. Мы видели, что первый компьютер уже объясняет почти 92% дисперсии, в то время как второй составляет еще 6% в общей сложности почти 98% между ними двумя. На самом верху наших графиков мы увидели значительную концентрацию стран, большинство из которых были развитыми. В то время как мы спускаемся по этой оси, число стран более редкое, и они принадлежат к менее развитым регионам мира.

При окрашивании/калибровке точек с использованием двух абсолютных величин, таких как среднее и общее число случаев, мы можем видеть, что направления также соответствуют изменению этих величин.

Более того, при использовании цвета/размера для кодирования разницы в количестве случаев с течением времени (2007 минус 1990) градиент цвета в основном менялся в направлении второго основного компонента, причем более положительные значения (т. Е. увеличение числа случаев) окрашивались в синий цвет или увеличивались в размере . То есть, в то время как первый ПК фиксирует большую часть вариаций в нашем наборе данных, и эта вариация основана на общем количестве случаев в диапазоне 1990-2007 годов, второй ПК в значительной степени зависит от изменений с течением времени.

В следующем разделе мы попытаемся выяснить другие отношения между странами.

Изучение структуры данных с помощью кластеризации k-средних

В этом разделе мы будем использовать кластеризацию k-means для группировки стран на основе того, насколько схожа их ситуация из года в год. То есть мы будем группировать данные на основе 18 переменных, которые у нас есть. Затем мы воспользуемся назначением кластера, чтобы раскрасить предыдущую 2D-диаграмму, чтобы обнаружить скрытые взаимосвязи в наших данных и лучше понять ситуацию в мире в отношении заболевания туберкулезом.

При использовании k-средних нам нужно определить правильное количество групп для нашего случая. Это можно сделать более или менее точно, перебрав различные значения числа групп и сравнив величину, называемую суммой квадратов расстояний внутри кластера для каждой итерации. Это квадратная сумма расстояний до центра кластера для каждого члена кластера. Конечно, это расстояние минимально, когда количество кластеров становится равным количеству выборок, но мы не хотим туда попасть. Обычно мы останавливаемся, когда улучшение этого значения начинает уменьшаться с меньшей скоростью.

Однако мы будем использовать более интуитивный подход, основанный на нашем понимании ситуации в мире и характера результатов, которых мы хотим достичь. Иногда это способ анализа данных, особенно при выполнении исследовательских задач. Использовать имеющиеся у нас знания о природе наших данных-это всегда хорошо.

R

Получение кластеров в R так же просто, как вызов k означает . Функция имеет несколько параметров, но мы просто будем использовать все значения по умолчанию и начнем пробовать с разными значениями k.

Давайте начнем с k=3 , предполагая, что, по крайней мере, это страны в действительно плохой ситуации, страны в хорошей ситуации и некоторые из них между ними.

set.seed(1234)
existing_clustering <- kmeans(existing_df, centers = 3)

Результат содержит список с компонентами:

  • кластер : вектор целых чисел, указывающий кластер, в который выделена каждая точка.
  • центры : Матрица кластерных центров.
  • внутри : сумма квадратов расстояний внутри кластера для каждого кластера.
  • размер : количество точек в каждом кластере.

Давайте раскрасим нашу предыдущую диаграмму рассеяния в зависимости от того, к какому кластеру принадлежит каждая страна.

existing_cluster_groups <- existing_clustering$cluster
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=existing_cluster_groups)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=existing_cluster_groups)
введите описание изображения здесь

Большинство кластеров основаны на первом ПК. Это означает, что кластеры просто определяются с точки зрения общего числа случаев на 100 тыс., а не того, как данные развивались во времени (PC2). Итак, давайте попробуем с k=4 и посмотрим, будут ли некоторые из этих кластеров уточнены в направлении второго ПК.

set.seed(1234)
existing_clustering <- kmeans(existing_df, centers = 4)
existing_cluster_groups <- existing_clustering$cluster
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=existing_cluster_groups)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=existing_cluster_groups)
введите описание изображения здесь

Есть еще уточнение, но опять же в сторону первого ПК. Давайте попробуем тогда с k=5 .

set.seed(1234)
existing_clustering <- kmeans(existing_df, centers = 5)
existing_cluster_groups <- existing_clustering$cluster
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=existing_cluster_groups)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=existing_cluster_groups)
введите описание изображения здесь

Вот и все. Прямо посередине у нас есть кластер, который был разделен на два разных в направлении второго ПК. Что, если мы попробуем с k=6 ?

set.seed(1234)
existing_clustering <- kmeans(existing_df, centers = 6)
existing_cluster_groups <- existing_clustering$cluster
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=existing_cluster_groups)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=existing_cluster_groups)
введите описание изображения здесь

Мы получаем некоторое диагональное разделение во втором верхнем кластере. Это, безусловно, содержит некоторую интересную информацию, но давайте вернемся к нашему случаю k=5 , и позже мы увидим, как использовать другой процесс уточнения с кластерами, слишком тесными, как у нас в верхней части графика.

set.seed(1234)
existing_clustering <- kmeans(existing_df, centers = 5)
existing_cluster_groups <- existing_clustering$cluster
plot(PC1~PC2, data=scores_existing_df, 
     main= "Existing TB cases per 100K distribution",
     cex = .1, lty = "solid", col=existing_cluster_groups)
text(PC1~PC2, data=scores_existing_df, 
     labels=rownames(existing_df),
     cex=.8, col=existing_cluster_groups)
введите описание изображения здесь

Питон

Снова мы будем использовать sklearn , в данном случае его k-означает кластеризацию реализацию, чтобы выполнить нашу кластеризацию на данных TB. Поскольку мы уже определились с рядом кластеров из 5, мы сразу же будем использовать его здесь.

from sklearn.cluster import KMeans
    
kmeans = KMeans(n_clusters=5)
clusters = kmeans.fit(existing_df)

Теперь нам нужно сохранить назначения кластеров вместе с каждой страной в нашем фрейме данных . Метки кластеров возвращаются в clusters.labels_ .

existing_df_2d['cluster'] = pd.Series(clusters.labels_, index=existing_df_2d.index)

И теперь мы готовы к построению графика, используя столбец кластера в качестве цвета.

import numpy as np
    
existing_df_2d.plot(
        kind='scatter',
        x='PC2',y='PC1',
        c=existing_df_2d.cluster.astype(np.float), 
        figsize=(16,8))
введите описание изображения здесь

Результат почти такой же, как и при использовании R, с различиями в цвете и без названий стран, которые мы решили здесь не включать, чтобы лучше видеть цвета. В следующем разделе мы подробно проанализируем каждый кластер.

Кластерная интерпретация

Большая часть работы в этом разделе посвящена индексации и построению графиков фреймов данных. В самом коде нет ничего сложного, поэтому мы возьмем один из наших языков и выполним все это (на этот раз мы будем использовать R).

Чтобы проанализировать каждый кластер, давайте добавим столбец в наш фрейм данных, содержащий идентификатор кластера. Мы будем использовать это для подмножества.

existing_df$cluster <- existing_clustering$cluster
table(existing_df$cluster)
## 
##  1  2  3  4  5 
## 16 30 20 51 90

Последняя строка показывает, сколько стран у нас в каждом кластере.

Диаграмма сравнения Центроидов

Давайте начнем с создания линейной диаграммы, которая сравнивает временные ряды для каждого центроида кластера. Эта диаграмма поможет нам лучше понять результаты нашего кластера.

xrange <- 1990:2007
plot(xrange, existing_clustering$centers[1,], 
     type='l', xlab="Year", 
     ylab="New cases per 100K", 
     col = 1, 
     ylim=c(0,1000))
for (i in 2:nrow(existing_clustering$centers)) {
    lines(xrange, existing_clustering$centers[i,],
    col = i)
}
legend(x=1990, y=1000, 
       lty=1, cex = 0.5,
       ncol = 5,
       col=1:(nrow(existing_clustering$centers)+1),
       legend=paste("Cluster",1:nrow(existing_clustering$centers)))
введите описание изображения здесь

Кластер 1

Кластер 1 содержит всего 16 стран. Это:

rownames(subset(existing_df, cluster==1))
##  [1] "Bangladesh"       "Bhutan"           "Cambodia"        
##  [4] "Korea, Dem. Rep." "Djibouti"         "Kiribati"        
##  [7] "Mali"             "Mauritania"       "Namibia"         
## [10] "Philippines"      "Sierra Leone"     "South Africa"    
## [13] "Swaziland"        "Timor-Leste"      "Togo"            
## [16] "Zambia"

Центроид, который их представляет,:

existing_clustering$centers[1,]
##    X1990    X1991    X1992    X1993    X1994    X1995    X1996    X1997 
## 764.0000 751.1875 734.9375 718.0625 701.6875 687.3125 624.7500 621.6250 
##    X1998    X1999    X2000    X2001    X2002    X2003    X2004    X2005 
## 605.1875 609.4375 622.0000 635.5000 604.2500 601.1250 597.3750 601.1250 
##    X2006    X2007 
## 600.2500 595.7500

Это, безусловно, страны с наибольшим количеством случаев заболевания туберкулезом каждый год. На графике видно, что это верхняя линия, хотя количество случаев постепенно снижается.

Кластер 2

Кластер 2 содержит 30 стран. Это:

rownames(subset(existing_df, cluster==2))
##  [1] "Afghanistan"           "Angola"               
##  [3] "Bolivia"               "Cape Verde"           
##  [5] "China"                 "Gabon"                
##  [7] "Gambia"                "Ghana"                
##  [9] "Guinea-Bissau"         "Haiti"                
## [11] "India"                 "Indonesia"            
## [13] "Laos"                  "Liberia"              
## [15] "Madagascar"            "Malawi"               
## [17] "Mongolia"              "Myanmar"              
## [19] "Nepal"                 "Niger"                
## [21] "Pakistan"              "Papua New Guinea"     
## [23] "Peru"                  "Sao Tome and Principe"
## [25] "Solomon Islands"       "Somalia"              
## [27] "Sudan"                 "Thailand"             
## [29] "Tuvalu"                "Viet Nam"

Центроид, который их представляет,:

existing_clustering$centers[2,]
##    X1990    X1991    X1992    X1993    X1994    X1995    X1996    X1997 
## 444.5000 435.2000 426.1667 417.4000 409.2333 400.5667 378.6000 365.3667 
##    X1998    X1999    X2000    X2001    X2002    X2003    X2004    X2005 
## 358.0333 354.4333 350.6000 326.7333 316.1667 308.5000 297.8667 288.8000 
##    X2006    X2007 
## 284.9667 280.8000

Это относительно большой кластер. По-прежнему страны с большим количеством случаев, но определенно меньше, чем в первом кластере. Мы видим здесь такие страны, как Индия или Китай, более крупные страны на земле (из предыдущего учебника мы знаем, что сам Китай сократил свои случаи на 85%) и американские страны, такие как Перу или Боливия. На самом деле, это кластер с самым быстрым уменьшением числа существующих случаев, как мы видим на линейном графике.

Кластер 3

Это очень важно. Кластер 3 содержит всего 20 стран. Это:

rownames(subset(existing_df, cluster==3))
##  [1] "Botswana"                 "Burkina Faso"            
##  [3] "Burundi"                  "Central African Republic"
##  [5] "Chad"                     "Congo, Rep."             
##  [7] "Cote d'Ivoire"            "Congo, Dem. Rep."        
##  [9] "Equatorial Guinea"        "Ethiopia"                
## [11] "Guinea"                   "Kenya"                   
## [13] "Lesotho"                  "Mozambique"              
## [15] "Nigeria"                  "Rwanda"                  
## [17] "Senegal"                  "Uganda"                  
## [19] "Tanzania"                 "Zimbabwe"

Центроид, который их представляет,:

existing_clustering$centers[3,]
##  X1990  X1991  X1992  X1993  X1994  X1995  X1996  X1997  X1998  X1999 
## 259.85 278.90 287.30 298.05 309.00 322.95 335.00 357.65 369.65 410.85 
##  X2000  X2001  X2002  X2003  X2004  X2005  X2006  X2007 
## 422.25 463.75 492.45 525.25 523.60 519.90 509.80 513.50

Это единственный кластер, в котором число случаев увеличилось за последние годы, и к 2007 году он вот-вот займет первое место. Каждая из этих стран, вероятно, находится в центре гуманитарного кризиса и, вероятно, страдает от других инфекционных заболеваний, таких как ВИЧ. Здесь мы можем подтвердить, что PC2 кодирует в основном это, процент изменения во времени числа существующих случаев.

Кластер 4

Четвертый кластер включает 51 страну.

rownames(subset(existing_df, cluster==4))
##  [1] "Armenia"                  "Azerbaijan"              
##  [3] "Bahrain"                  "Belarus"                 
##  [5] "Benin"                    "Bosnia and Herzegovina"  
##  [7] "Brazil"                   "Brunei Darussalam"       
##  [9] "Cameroon"                 "Comoros"                 
## [11] "Croatia"                  "Dominican Republic"      
## [13] "Ecuador"                  "El Salvador"             
## [15] "Eritrea"                  "Georgia"                 
## [17] "Guam"                     "Guatemala"               
## [19] "Guyana"                   "Honduras"                
## [21] "Iraq"                     "Kazakhstan"              
## [23] "Kyrgyzstan"               "Latvia"                  
## [25] "Lithuania"                "Malaysia"                
## [27] "Maldives"                 "Micronesia, Fed. Sts."   
## [29] "Morocco"                  "Nauru"                   
## [31] "Nicaragua"                "Niue"                    
## [33] "Northern Mariana Islands" "Palau"                   
## [35] "Paraguay"                 "Qatar"                   
## [37] "Korea, Rep."              "Moldova"                 
## [39] "Romania"                  "Russian Federation"      
## [41] "Seychelles"               "Sri Lanka"               
## [43] "Suriname"                 "Tajikistan"              
## [45] "Tokelau"                  "Turkmenistan"            
## [47] "Ukraine"                  "Uzbekistan"              
## [49] "Vanuatu"                  "Wallis et Futuna"        
## [51] "Yemen"

Представлен его центроидом.

existing_clustering$centers[4,]
##     X1990     X1991     X1992     X1993     X1994     X1995     X1996 
## 130.60784 133.41176 125.60784 127.54902 124.82353 127.70588 121.68627 
##     X1997     X1998     X1999     X2000     X2001     X2002     X2003 
## 130.50980 125.82353 124.45098 110.58824 106.60784 121.09804 103.01961 
##     X2004     X2005     X2006     X2007 
## 101.80392  97.29412  96.17647  91.68627

Этот кластер довольно близок к последнему и более крупному. Он содержит много американских стран, некоторые европейские страны и т. Д. Некоторые из них крупные и богатые, такие как Россия или Бразилия. Структурно разница со странами кластера 5 может заключаться в большем числе случаев на 100 тыс. Они также, по-видимому, уменьшают число случаев несколько быстрее, чем кластер 5. Эти две причины заставили k-means объединить их в другую группу.

Кластер 5

Последний и более крупный кластер содержит 90 стран.

rownames(subset(existing_df, cluster==5))
##  [1] "Albania"                          "Algeria"                         
##  [3] "American Samoa"                   "Andorra"                         
##  [5] "Anguilla"                         "Antigua and Barbuda"             
##  [7] "Argentina"                        "Australia"                       
##  [9] "Austria"                          "Bahamas"                         
## [11] "Barbados"                         "Belgium"                         
## [13] "Belize"                           "Bermuda"                         
## [15] "British Virgin Islands"           "Bulgaria"                        
## [17] "Canada"                           "Cayman Islands"                  
## [19] "Chile"                            "Colombia"                        
## [21] "Cook Islands"                     "Costa Rica"                      
## [23] "Cuba"                             "Cyprus"                          
## [25] "Czech Republic"                   "Denmark"                         
## [27] "Dominica"                         "Egypt"                           
## [29] "Estonia"                          "Fiji"                            
## [31] "Finland"                          "France"                          
## [33] "French Polynesia"                 "Germany"                         
## [35] "Greece"                           "Grenada"                         
## [37] "Hungary"                          "Iceland"                         
## [39] "Iran"                             "Ireland"                         
## [41] "Israel"                           "Italy"                           
## [43] "Jamaica"                          "Japan"                           
## [45] "Jordan"                           "Kuwait"                          
## [47] "Lebanon"                          "Libyan Arab Jamahiriya"          
## [49] "Luxembourg"                       "Malta"                           
## [51] "Mauritius"                        "Mexico"                          
## [53] "Monaco"                           "Montserrat"                      
## [55] "Netherlands"                      "Netherlands Antilles"            
## [57] "New Caledonia"                    "New Zealand"                     
## [59] "Norway"                           "Oman"                            
## [61] "Panama"                           "Poland"                          
## [63] "Portugal"                         "Puerto Rico"                     
## [65] "Saint Kitts and Nevis"            "Saint Lucia"                     
## [67] "Saint Vincent and the Grenadines" "Samoa"                           
## [69] "San Marino"                       "Saudi Arabia"                    
## [71] "Singapore"                        "Slovakia"                        
## [73] "Slovenia"                         "Spain"                           
## [75] "Sweden"                           "Switzerland"                     
## [77] "Syrian Arab Republic"             "Macedonia, FYR"                  
## [79] "Tonga"                            "Trinidad and Tobago"             
## [81] "Tunisia"                          "Turkey"                          
## [83] "Turks and Caicos Islands"         "United Arab Emirates"            
## [85] "United Kingdom"                   "Virgin Islands (U.S.)"           
## [87] "United States of America"         "Uruguay"                         
## [89] "Venezuela"                        "West Bank and Gaza"

Представлен его центроидом.

existing_clustering$centers[5,]
##    X1990    X1991    X1992    X1993    X1994    X1995    X1996    X1997 
## 37.27778 35.68889 35.73333 34.40000 33.51111 32.42222 30.80000 30.51111 
##    X1998    X1999    X2000    X2001    X2002    X2003    X2004    X2005 
## 29.30000 26.77778 24.35556 23.57778 22.02222 20.93333 20.48889 19.92222 
##    X2006    X2007 
## 19.25556 19.11111

Этот кластер слишком велик и неоднороден и, вероятно, нуждается в дальнейшей доработке. Тем не менее, это хорошая группировка по сравнению с другими удаленными кластерами. В любом случае он содержит страны с меньшим количеством существующих случаев в нашем наборе.

Второй уровень кластеризации

Так что давайте сделаем это быстро. Давайте вновь объединим 90 стран в нашем Кластере 5, чтобы еще больше усовершенствовать их. В качестве числа кластеров будем использовать 2. Нам просто интересно посмотреть, есть ли на самом деле два разных кластера в кластере 5. Читатель, конечно, может попытаться пойти дальше и использовать более 2 центров.

# subset the original dataset
cluster5_df <- subset(existing_df, cluster==5)
# do the clustering
set.seed(1234)
cluster5_clustering <- kmeans(cluster5_df[,-19], centers = 2)
# assign sub-cluster number to the data set for Cluster 5
cluster5_df$cluster <- cluster5_clustering$cluster

Теперь мы можем построить их график, чтобы увидеть, есть ли фактические различия.

xrange <- 1990:2007
plot(xrange, cluster5_clustering$centers[1,], 
     type='l', xlab="Year", 
     ylab="Existing cases per 100K", 
     col = 1, 
     ylim=c(0,200))
for (i in 2:nrow(cluster5_clustering$centers)) {
    lines(xrange, cluster5_clustering$centers[i,],
    col = i)
}
legend(x=1990, y=200, 
       lty=1, cex = 0.5,
       ncol = 5,
       col=1:(nrow(cluster5_clustering$centers)+1),
       legend=paste0("Cluster 5.",1:nrow(cluster5_clustering$centers)))
введите описание изображения здесь

На самом деле в наших данных есть разные тенденции. Мы видим, что в нашем первоначальном кластере 5 есть группа стран, которые сокращают число случаев заболевания более быстрыми темпами, пытаясь догнать страны с меньшим числом существующих случаев заболевания туберкулезом на 100 тыс.

rownames(subset(cluster5_df, cluster5_df$cluster==2))
##  [1] "Albania"                          "Algeria"                         
##  [3] "Anguilla"                         "Argentina"                       
##  [5] "Bahamas"                          "Belize"                          
##  [7] "Bulgaria"                         "Colombia"                        
##  [9] "Egypt"                            "Estonia"                         
## [11] "Fiji"                             "French Polynesia"                
## [13] "Hungary"                          "Iran"                            
## [15] "Japan"                            "Kuwait"                          
## [17] "Lebanon"                          "Libyan Arab Jamahiriya"          
## [19] "Mauritius"                        "Mexico"                          
## [21] "New Caledonia"                    "Panama"                          
## [23] "Poland"                           "Portugal"                        
## [25] "Saint Vincent and the Grenadines" "Samoa"                           
## [27] "Saudi Arabia"                     "Singapore"                       
## [29] "Slovakia"                         "Slovenia"                        
## [31] "Spain"                            "Syrian Arab Republic"            
## [33] "Macedonia, FYR"                   "Tonga"                           
## [35] "Tunisia"                          "Turkey"                          
## [37] "United Arab Emirates"             "Venezuela"                       
## [39] "West Bank and Gaza"

В то время как страны с меньшим количеством случаев заболевания, а также более медленными темпами снижения.

rownames(subset(cluster5_df, cluster5_df$cluster==1))
##  [1] "American Samoa"           "Andorra"                 
##  [3] "Antigua and Barbuda"      "Australia"               
##  [5] "Austria"                  "Barbados"                
##  [7] "Belgium"                  "Bermuda"                 
##  [9] "British Virgin Islands"   "Canada"                  
## [11] "Cayman Islands"           "Chile"                   
## [13] "Cook Islands"             "Costa Rica"              
## [15] "Cuba"                     "Cyprus"                  
## [17] "Czech Republic"           "Denmark"                 
## [19] "Dominica"                 "Finland"                 
## [21] "France"                   "Germany"                 
## [23] "Greece"                   "Grenada"                 
## [25] "Iceland"                  "Ireland"                 
## [27] "Israel"                   "Italy"                   
## [29] "Jamaica"                  "Jordan"                  
## [31] "Luxembourg"               "Malta"                   
## [33] "Monaco"                   "Montserrat"              
## [35] "Netherlands"              "Netherlands Antilles"    
## [37] "New Zealand"              "Norway"                  
## [39] "Oman"                     "Puerto Rico"             
## [41] "Saint Kitts and Nevis"    "Saint Lucia"             
## [43] "San Marino"               "Sweden"                  
## [45] "Switzerland"              "Trinidad and Tobago"     
## [47] "Turks and Caicos Islands" "United Kingdom"          
## [49] "Virgin Islands (U.S.)"    "United States of America"
## [51] "Uruguay"

Однако мы вряд ли получим эти кластеры, просто увеличив в 1 число центров в нашем первом процессе кластеризации с исходным набором данных. Как мы уже говорили, кластер 5 казался очень сплоченной группой по сравнению с более отдаленными странами. Этот двухэтапный процесс кластеризации является полезным методом, который мы можем использовать с любым набором данных, который мы хотим исследовать.

Выводы

Я надеюсь, что вам понравилось это исследование данных так же, как и мне. Мы видели, как использовать Python и R для выполнения анализа основных компонентов и кластеризации наших данных о туберкулезе. Хотя мы не выступаем за использование одной платформы над другой, иногда легче выполнять некоторые задачи на одной из них. Опять же, мы видим, что интерфейс программирования в Python является более последовательным (мы будем иметь это более ясно после большего количества учебных пособий, сравнивающих Python и R), четким и модульным. Однако R был создан для выполнения задач статистики и аналитики, и чаще всего у нас будет способ сделать что-то в одном или двух вызовах функций.

Что касается кластеризации PCA и k-средних, то первый метод позволил нам построить распределение всех стран в двумерном пространстве на основе их эволюции числа случаев в диапазоне 18 лет. таким образом, мы увидели, как общее число случаев в основном определяет главный компонент (т. Е. Направление наибольшей вариации), в то время как процент изменения во времени влияет на второй компонент.

Затем мы использовали кластеризацию k-средних, чтобы сгруппировать страны по тому, насколько схожи их случаи в каждом году. Таким образом, мы обнаружили некоторые интересные взаимосвязи и, самое главное, лучше поняли ситуацию в мире в отношении этой важной болезни. Мы видели, как большинство стран улучшилось за рассматриваемый нами промежуток времени, но мы также смогли обнаружить группу стран с высокой распространенностью этого заболевания, которые, отнюдь не улучшая свое положение, увеличивают число случаев заболевания.

Помните, что весь исходный код для различных частей этой серии учебных пособий и приложений можно проверить на GitHub . Не стесняйтесь участвовать и делиться с нами своими успехами!