Автор оригинала: 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 . Не стесняйтесь участвовать и делиться с нами своими успехами!