МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ Федеральное государственное бюджетное образовательное учреждение высшего образования «Сибирский государственный университет науки и технологий имени академика М.Ф. Решетнева» Институт информатики и телекоммуникаций Кафедра информатики и вычислительной техники ОТЧЕТ ПО ЛАБОРАТОРНОЙ РАБОТЕ Технологии обработки информации Применение методов кластерного анализа в программировании Руководитель А.В. Проскурин подпись, дата Обучающийся БИСЗ18-01, 18733040 номер группы, зачетной книжки инициалы, фамилия А.И.Киреев подпись, дата Красноярск 2020 г. инициалы, фамилия ЦЕЛЬ РАБОТЫ Целью работы является изучение алгоритма кластеризации k-means, а также использование методов программирования для реализации алгоритмов поиска кластеров в заданном наборе данных. ЗАДАНИЕ НА ЛАБОРАТОРНУЮ РАБОТУ Разработать программный продукт для реализации методов кластерного анализа на любом языке программирования, позволяющий выполнить кластеризацию объектов по критериям в соответствии с вариантом задания. Вариант задания соответствует номеру в списке группы. При нехватке заданий вариант задания вычисляется как: (номер_в_списке_группы % количество_вариантов). Предоставить пользователю выбор метода расчета расстояния между объектами (два из представленных, в соответствии с вариантом задания): 1. Евклидово расстояние. 2. Квадрат евклидова расстояния. 3. Манхэттенское расстояние. 4. Расстояние Чебышева. 5. Степенное расстояние. 6. Процент несогласия. Использовать один из указанных метод кластеризации: a) Метод ближнего соседа. b) Метод наиболее удаленного соседа. c) Метод кластеризации k-means. Реализовать один из указанных методов отображения результатов кластеризации (помимо численного): I. Построить дендрограмму. II. Построить диаграмму рассеивания. Варианты заданий: Вариант 6 Мера расстояния 3 Метод кластеризации Отображение результатов с I 2 В отчете отобразить указанный вариант задания, теоретический материал и методы расчета, соответствующие реализованным методам, пример расчета и скриншоты программного продукта с результатами расчетов. Размерность и количество исходных данных можно задать в программе, сами исходные данные пользователь должен иметь возможность выбрать в процессе решения задачи. 3 ХОД РАБОТЫ Кластеризация - многомерная статистическая процедура, выполняющая сбор данных, содержащих информацию о выборке объектов, и затем упорядочивающая объекты в сравнительно однородные группы. Задача кластеризации относится к статистической обработке. К наиболее простым и эффективным алгоритмам кластеризации относится k-means, или в русскоязычном варианте k-средних. Он состоит из четырех шагов. 7. Задается число кластеров k, которое должно быть сформировано из объектов исходной выборки. Случайным образом выбирается k записей, которые будут служить начальными центрами кластеров. Начальные точки, и которых потом вырастает кластер, часто называют «семенами». Каждая такая запись представляет собой своего рода «эмбрион» кластера, состоящий только из одного элемента. Для каждой записи исходной выборки определяется ближайший к ней центр кластера. Производится вычисление центроидов – центров тяжести кластеров. Это делается путем определения среднего для значений каждого признака всех записей в кластере. Затем старый центр кластера смещается в его центроид. Таким образом, центроиды становятся новыми центрами кластеров для следующей итерации алгоритма. Шаги 3 и 4 повторяются до тех пор, пока выполнение алгоритма не будет прервано, либо пока не будет выполнено условие в соответствии с некоторым критерием сходимости. Остановка алгоритма производится, когда границы кластеров и расположение центроидов перестают изменяться, т.е. на каждой итерации в каждом кластере остается один и тот же набор записей. Квадрат евклидова расстояния. Иногда может возникнуть желание возвести в квадрат стандартное евклидово расстояние, чтобы придать большие веса более отдаленным друг от друга объектам. Это расстояние вычисляется следующим образом: Dx, y xi yi 2 i Расстояние городских кварталов (манхэттенское расстояние). Это расстояние является просто средним разностей по координатам. В большинстве случаев эта мера расстояния приводит к таким же результатам, как и для обычного расстояния Евклида. Однако отметим, что для этой меры влияние отдельных больших разностей (выбросов) уменьшается (так как они не возводятся в квадрат). Манхэттенское расстояние вычисляется по формуле: Dx, y xi yi i 4 1. Решение тестовой задачи В качестве примера задачи рассмотрим кластеризацию точек: Точки x 1 2 3 4 5 6 7 14 y 2 16 6 4 12 15 17 12 13 11 12 13 10 11 9 10 8 6 4 2 0 0 5 10 15 20 Эти семь точек визуально собираются в два кластера – левый и правый. Вычислим расстояния между точками: Квадратное Евклидовое расстояние: Точки 1 2 13 x y 1 2 3 4 5 6 7 2 1 6 4 12 15 16 13 11 12 14 10 16 9 2,24 4,12 2,24 10,44 13,34 14,56 2 16 11 14,14 10,05 12,37 4,12 5,10 2,00 3 6 12 4,12 5,10 2,83 6,32 9,85 10,44 4 4 13 2,00 3,61 2,24 8,54 11,40 12,65 5 12 10 10,44 11,05 6,32 8,94 6,71 4,12 6 15 11 13,15 14,00 9,06 11,40 3,16 7 16 9 14,56 15,13 10,44 13,00 4,12 7,07 2,24 Манхэттенские расстояния: Точки 1 2 13 x y 1 2 3 4 5 6 7 2 1 6 4 12 15 16 13 11 12 13 10 11 9 3 5 2 13 15 18 2 16 11 16 3 6 12 5 6 11 14 5 1 2 3 8 10 13 4 4 13 2 5 3 11 13 16 5 12 10 13 12 8 11 4 5 6 15 11 15 14 10 13 4 7 16 9 18 17 13 16 5 3 3 Видим, что в обоих вариантах определения расстояния ближайшие точки – 1 и 4. Они первыми объединятся в правый кластер. Далее идут: 5 Евклидовы расстояния 1 и 4 – в левый кластер 3 и 4 – в левый кластер 2 и 6 – в правый кластер 2 и 7 – в правый кластер 3 и 5 – кластеры объединяются Манхэттенские расстояния 1 и 4 – в левый кластер 3 и 4 – в левый кластер 2 и 7 – в правый кластер 2 и 5 – в правый кластер 3 и 5 – кластеры объединяются Дендрограммы: Евклидовы расстояния 1 4 3 5 6 Манхэттенские расстояния 2 7 1 4 3 5 Разработаем программу для автоматизации такого анализа. 2. Код программы: Файл ClusterObject.cs using using using using using System; System.Collections.Generic; System.Linq; System.Text; System.Threading.Tasks; namespace CluaterAnalyse { public class ClusterObject { public int Index; public double[] Values; public int[] ClusterIndexes; public ClusterObject() { Index = -1; Values = null; ClusterIndexes = null; } public ClusterObject(int index, double[] values, int clusterCount) { Index = index; Values = values; ClusterIndexes = new int[clusterCount]; for (int i=0; i<clusterCount; i++) 6 6 2 7 ClusterIndexes[i] = index; } // Квадрат Евклидово расстояние public double GetEuclideDistanceToObject(ClusterObject anObject) { double distance = 0; for (int i = 0; i < Values.Count(); i++) { double difference = anObject.Values[i] - this.Values[i]; distance += difference * difference; } distance = Math.Sqrt(distance); return distance; } // Манхэттенское расстояние public double GetManhattenDistanceToObject(ClusterObject anObject) { double distance = 0; for (int i = 0; i < Values.Count(); i++) { double difference = anObject.Values[i] - this.Values[i]; distance += Math.Abs(difference); } return distance; } } } Файл Cluster.cs using using using using using System; System.Collections.Generic; System.Linq; System.Text; System.Threading.Tasks; namespace CluaterAnalyse { public class Cluster { public List<ClusterObject> Items; private int _index; public int Index { get { return _index; } } public Cluster() { Items = new List<ClusterObject>(); _index = -1; } public Cluster(int index, ClusterObject anObject) { Items = new List<ClusterObject>(); Items.Add(anObject); _index = index; } int Count { get { return Items.Count; } 7 } // Добавление объекта public void Add(int step, ClusterObject anObject) { for (int i = step; i < anObject.ClusterIndexes.Length; i++) anObject.ClusterIndexes[i] = Index; Items.Add(anObject); } // Добавление объектов другого кластера (слияние) public void AddCluster(int step, Cluster cluster) { for (int i = 0; i < cluster.Count; i++) Add(step, cluster.Items[i]); } // Евклидово расстояние между кластерами public double GetEuclideDistanceToCluster(Cluster cluster) { double distance = Items[0].GetEuclideDistanceToObject(cluster.Items[0]); for (int i = 0; i < Count; i++) for (int j = 0; j < cluster.Count; j++) { double d = Items[i].GetEuclideDistanceToObject(cluster.Items[j]); if (d < distance) distance = d; } return distance; } // Манхэттенское расстояние между кластерами public double GetManhattenDistanceToCluster(Cluster cluster) { double distance = Items[0].GetManhattenDistanceToObject(cluster.Items[0]); for (int i = 0; i < Count; i++) for (int j = 0; j < cluster.Count; j++) { double d = Items[i].GetManhattenDistanceToObject(cluster.Items[j]); if (d < distance) distance = d; } return distance; } // Расстояние между кластерами (true - Евклидово, false - Манхэттенское) public double GetDistanceToCluster(Cluster cluster, bool Euclide) { if (Euclide) return GetEuclideDistanceToCluster(cluster); else return GetManhattenDistanceToCluster(cluster); } // Информация о составе кластера public override string ToString() { string s = "{ "; for (int i = 0; i < Count; i++) s += (Items[i].Index + 1).ToString() + ", "; s = s.Remove(s.Length - 2); s += " }"; return s; } } } Файл Dendrogramm.cs 8 using using using using using using using using using System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Threading.Tasks; System.Windows.Forms; namespace CluaterAnalyse { public partial class Dendrogramm : Form { private Cluster Cluster; private int ItemCount; private Label[] Labels; // Создание формы public Dendrogramm(Cluster cluster) { InitializeComponent(); Cluster = cluster; // Merge = merge; ItemCount = cluster.Items.Count(); Labels = new Label[ItemCount]; Labels[0] = label1; this.SuspendLayout(); for (int i = 1; i < ItemCount; i++) { Labels[i] = new Label(); Labels[i].AutoSize = true; Labels[i].Name = $"label_{i}"; Labels[i].Size = new System.Drawing.Size(13, 13); Labels[i].TabIndex = i + 1; Labels[i].Text = (i + 1).ToString(); Labels[i].Parent = this; } this.ResumeLayout(false); this.PerformLayout(); } // Изменение размеров - рисуем дандрограмму private void Dendogramm_Resize(object sender, EventArgs e) { Dendogramm_Paint(sender, null); } // Прорисовка окна - рисуем дендрограмму private void Dendogramm_Paint(object sender, PaintEventArgs e) { int width = this.ClientRectangle.Width / (ItemCount); int height = panel1.ClientRectangle.Height / (ItemCount + 1); panel1.Width = ClientRectangle.Width; panel1.Height = ClientRectangle.Height - 40; panel1.Refresh(); Graphics graph = panel1.CreateGraphics(); // Рисуем метки и первые черточки for (int i = 0; i < ItemCount; i++) { Labels[i].Location = new System.Drawing.Point(width * i + width / 2 - Labels[i].Width / 2, ClientRectangle.Height - 20); int index = Cluster.Items[i].Index + 1; 9 Labels[i].Text = index.ToString(); graph.DrawLine( new Pen(Color.Black), width * i + width / 2, (ItemCount + 1) * height, width * i + width / 2, ItemCount * height); } // Рисуем дерево for (int i = 0; i < ItemCount; i++) { int prevCluster = Cluster.Items[i].ClusterIndexes[0]; int prevClusterSite = -1; for (int k = 0; k < ItemCount; k++) { if (prevCluster == Cluster.Items[k].Index) { prevClusterSite = k; break; } } for (int step = 1; step < ItemCount; step++) { int nextCluster = Cluster.Items[i].ClusterIndexes[step]; int nextClusterSite = -1; for (int k = 0; k < ItemCount; k++) { if (nextCluster == Cluster.Items[k].Index) { nextClusterSite = k; break; } } graph.DrawLine( new Pen(Color.Black), width * prevClusterSite + width / 2, (ItemCount + 1 - step) * height, width * nextClusterSite + width / 2, (ItemCount + 1 - step) * height); graph.DrawLine( new Pen(Color.Black), width * nextClusterSite + width / 2, (ItemCount + 1 - step) * height, width * nextClusterSite + width / 2, (ItemCount - step) * height); prevCluster = nextCluster; prevClusterSite = nextClusterSite; } } } } } Файл Form1.cs using using using using using using using using using System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Threading.Tasks; System.Windows.Forms; namespace CluaterAnalyse { public partial class Form1 : Form 10 { // Массив кластеров private Cluster[] Clusters; public Form1() { InitializeComponent(); Clusters = null; } // Изменение числа признаков private void textBox1_Leave(object sender, EventArgs e) { try { int n = int.Parse(textBox1.Text); grid.ColumnCount = n; for (int i = 0; i < n; i++) grid.Columns[i].HeaderText = $"Признак {i + 1}"; } catch (Exception ex) { if (MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) == DialogResult.OK) { textBox1.Select(); textBox1.SelectAll(); } else textBox1.Text = grid.ColumnCount.ToString(); } } // Изменение числа объектов private void textBox2_Leave(object sender, EventArgs e) { try { int n = int.Parse(textBox2.Text); grid.RowCount = n; for (int i = 0; i < n; i++) grid.Rows[i].HeaderCell.Value = (i + 1).ToString(); } catch (Exception ex) { if (MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) == DialogResult.OK) { textBox2.Select(); textBox2.SelectAll(); } else textBox2.Text = grid.RowCount.ToString(); } } // Загрузка формы private void Form1_Load(object sender, EventArgs e) { textBox1_Leave(sender, e); textBox2_Leave(sender, e); } // Кнопка Сгенерировать private void button1_Click(object sender, EventArgs e) 11 { int MaxValue = grid.RowCount * grid.ColumnCount; Random random = new Random(); for (int row = 0; row < grid.RowCount; row++) for (int column = 0; column < grid.ColumnCount; column++) grid.Rows[row].Cells[column].Value = random.Next(MaxValue); } // Кнопка Очистить private void button2_Click(object sender, EventArgs e) { for (int row = 0; row < grid.RowCount; row++) for (int column = 0; column < grid.ColumnCount; column++) grid.Rows[row].Cells[column].Value = ""; } // Кнопка Выполнить анализ private void button3_Click(object sender, EventArgs e) { int count = grid.RowCount; int valueCount = grid.ColumnCount; Clusters = new Cluster[count]; // Считывание данных for (int i = 0; i < count; i++) { double[] values = new double[valueCount]; for (int j = 0; j < valueCount; j++) try { values[j] = double.Parse(grid[j, i].Value.ToString()); } catch (Exception ex) { if (i == count - 1) { count--; break; } else { grid[j, i].Selected = true; MessageBox.Show(ex.Message, "Ошибка считывания данных", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } Clusters[i] = new Cluster(i, new ClusterObject(i, values, count)); } PrintClusters(count); int step = 0; // Объединяем наиболее близкие кластеры while (count > 1) { step++; double shortestDistance = Clusters[0].GetDistanceToCluster(Clusters[1], radioButton1.Checked); int cluster1 = 0; int cluster2 = 1; for (int i = 0; i < count-1; i++) for (int j=i+1; j < count; j++) { 12 double distance = Clusters[i].GetDistanceToCluster(Clusters[j], radioButton1.Checked); if (distance < shortestDistance) { shortestDistance = distance; cluster1 = i; cluster2 = j; } } textBox3.AppendText($"Слияние кластеров {Clusters[cluster1].Index + 1} и {Clusters[cluster2].Index + 1}.\r\n"); Clusters[cluster1].AddCluster(step, Clusters[cluster2]); for (int i = cluster2 + 1; i < count; i++) Clusters[i - 1] = Clusters[i]; count--; Clusters[count] = null; PrintClusters(count); button4.Enabled = true; } } // Вывод информации о текущем состоянии всех кластеров void PrintClusters(int count) { textBox3.AppendText("Текущее состояние кластеров:\r\n"); for (int i = 0; i<count; i++) { textBox3.AppendText($" Кластер {i+1}: {Clusters[i].ToString()}\r\n"); } } private void grid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { button4.Enabled = false; } private void grid_ColumnAdded(object sender, DataGridViewColumnEventArgs e) { button4.Enabled = false; } private void grid_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e) { button4.Enabled = false; } // Добавление строки в таблицу private void grid_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e) { int n = grid.RowCount; textBox2.Text = n.ToString(); grid.Rows[n - 1].HeaderCell.Value = n.ToString(); button4.Enabled = false; } private void button4_Click(object sender, EventArgs e) { Dendrogramm dendogramm = new Dendrogramm(Clusters[0]); dendogramm.ShowDialog(); } } } 13 3. Скриншоты работы программы 1. Евклидовы расстояния 2. Манхэттенские расстояния Видим, что полученные результаты совпадают с выполненным вручную решением тестовой задачи. 14 ОТВЕТЫ НА КОНТРОЛЬНЫЕ ВОПРОСЫ 1. Группировка данных (постановка задачи и ограничения). В отличие от задач классификации, кластерный анализ не требует априорных предположений о наборе данных, не накладывает ограничения на представление исследуемых объектов, позволяет анализировать показатели различ15 ных типов данных (интервальным данным, частотам, бинарным данным). При этом необходимо помнить, что переменные должны измеряться в сравнимых шкалах. Кластерный анализ позволяет сокращать размерность данных, делать ее наглядной. Кластерный анализ может применяться к совокупностям временных рядов, здесь могут выделяться периоды схожести некоторых показателей и определяться группы временных рядов со схожей динамикой. Кластерный анализ параллельно развивался в нескольких направлениях, таких как биология, психология и др., поэтому у большинства методов существует по два и более названий. Это существенно затрудняет работу при использовании кластерного анализа. Задачи кластерного анализа можно объединить в следующие группы: 1) Разработка типологии или классификации. 2) Исследование полезных концептуальных схем группирования объектов. 3) Представление гипотез на основе исследования данных. 4) Проверка гипотез или исследований для определения, действительно ли типы (группы), выделенные тем или иным способом, присутствуют в имеющихся данных. Как правило, при практическом использовании кластерного анализа одновременно решается несколько из указанных задач. 2. Понятие кластерного анализа. Термин кластерный анализ (впервые ввел Tryon, 1939) в действительности включает в себя набор различных алгоритмов классификации. Общий вопрос, задаваемый исследователями во многих областях, состоит в том, как организовать наблюдаемые данные в наглядные структуры, т.е. развернуть таксономии. Например, биологи ставят цель разбить животных на различные виды, чтобы содержательно описать различия между ними. В соответствии с современной системой, принятой в биологии, человек принадлежит к приматам, млекопитающим, амниотам, позвоночным и животным. Заметьте, что в этой классификации, чем выше уровень агрегации, тем меньше сходства между членами в соответствующем классе. Человек имеет больше сходства с другими приматами (т.е. с обезьянами), чем с "отдаленными" членами семейства млекопитающих (например, собаками) и т.д. 3. Задачи кластерного анализа. 1) Разработка типологии или классификации. 2) Исследование полезных концептуальных схем группирования объектов. 3) Представление гипотез на основе исследования данных. 4) Проверка гипотез или исследований для определения, действительно ли типы (группы), выделенные тем или иным способом, присутствуют в имеющихся данных. 4. Способы отображения результатов кластеризации. Дендрограмма и диаграмма рассеивания. 5. Понятие и принцип построения дендрограммы. 16 Дендрограмма (dendrogram) - древовидная диаграмма, содержащая n уровней, каждый из которых соответствует одному из шагов процесса последовательного укрупнения кластеров. Дендрограмму также называют древовидной схемой, деревом объединения кластеров, деревом иерархической структуры. Дендрограмма представляет собой вложенную группировку объектов, которая изменяется на различных уровнях иерархии. Существует много способов построения дендрограмм. В дендрограмме объекты могут располагаться вертикально или горизонтально. 6. Методы оценки схожести объектов. Евклидово расстояние. Квадрат евклидова расстояния. Манхэттенское расстояние. Расстояние Чебышева. Степенное расстояние. Процент несогласия. 7. Метрика расстояния Махаланобиса. Расстояние Махалано́биса — мера расстояния между векторами случайных величин, обобщающая понятие евклидова расстояния. 8. Метод кластеризации k-means. К наиболее простым и эффективным алгоритмам кластеризации относится k-means, или в русскоязычном варианте k-средних. Он состоит из четырех шагов. Задается число кластеров k, которое должно быть сформировано из объектов исходной выборки. Случайным образом выбирается k записей, которые будут служить начальными центрами кластеров. Начальные точки, и которых потом вырастает кластер, часто называют «семенами». Каждая такая запись представляет собой своего рода «эмбрион» кластера, состоящий только из одного элемента. Для каждой записи исходной выборки определяется ближайший к ней центр кластера. Производится вычисление центроидов – центров тяжести кластеров. Это делается путем определения среднего для значений каждого признака всех записей в кластере. Затем старый центр кластера смещается в его центроид. Таким образом, центроиды становятся новыми центрами кластеров для следующей итерации алгоритма. Шаги 3 и 4 повторяются до тех пор, пока выполнение алгоритма не будет прервано, либо пока не будет выполнено условие в соответствии с некоторым критерием сходимости. Остановка алгоритма производится, когда границы кластеров и расположение центроидов перестают изменяться, т.е. на каждой итерации в каждом кластере остается один и тот же набор записей. Алгоритм k-means обычно находит набор стабильных кластеров за несколько десятков итераций. В качестве критерия сходимости чаще всего используется сумма квадратов расстояний между центроидом кластера и всеми вошедшими в него записями. Алго17 ритм остановится тогда, когда ошибка Е достигнет достаточно малого значения. 9. Методы ближайших и удаленных соседей. Метод ближнего соседа или одиночная связь. Здесь расстояние между двумя кластерами определяется расстоянием между двумя наиболее близкими объектами (ближайшими соседями) в различных кластерах. Этот метод позволяет выделять кластеры сколь угодно сложной формы при условии, что различные части таких кластеров соединены цепочками близких друг к другу элементов. В результате работы этого метода кластеры представляются длинными «цепочками» или «волокнистыми» кластерами, «сцепленными вместе» только отдельными элементами, которые случайно оказались ближе остальных друг к другу. Метод наиболее удаленных соседей или полная связь. Здесь расстояния между кластерами определяются наибольшим расстоянием между любыми двумя объектами в различных кластерах (т.е. «наиболее удаленными соседями»). Метод хорошо использовать, когда объекты действительно происходят из различных «рощ». Если же кластеры имеют в некотором роде удлиненную форму или их естественный тип является «цепочечным», то этот метод не следует использовать. 18 ВЫВОДЫ Изучили алгоритм кластеризации k-means, а также использование методов программирования для реализации алгоритмов поиска кластеров в заданном наборе данных. 19