Введение

В данной лабораторной работе рассматриваются методы классификации и анализа данных в языке R.

В работе используются:

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

Перед запуском отчета пакеты нужно один раз установить в консоли RStudio:

# Этот блок не выполняется при Knit, потому что стоит eval=FALSE.
# Команды ниже нужно выполнить один раз вручную в консоли RStudio.

install.packages("class")
install.packages("gmodels")
install.packages("caret")
install.packages("e1071")
install.packages("vegan")

Задание 2. Классификация методом k-ближайших соседей

Условие

Выполнить классификацию k-ближайших соседей с использованием функции knn() из пакета class на наборе данных iris.

Необходимо:

  • провести нормализацию данных;
  • разделить выборку на обучающую и тестовую;
  • оценить модель с использованием CrossTable() из пакета gmodels;
  • построить матрицу ошибок;
  • выполнить диагональную оценку качества прогноза.

Выполнение задания

# Подключаем пакет class.
# В нем находится функция knn(), которая выполняет классификацию
# методом k-ближайших соседей.

library(class)

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

library(gmodels)

# Подключаем пакет caret.
# Он нужен для функции confusionMatrix(),
# которая строит матрицу ошибок и считает метрики качества.

library(caret)
# Загружаем встроенный набор данных iris.
# Обычно он уже доступен в R, но data(iris) явно подгружает его.

data(iris)

# Смотрим структуру набора данных.
# str() показывает типы столбцов и общее устройство таблицы.

str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
# Выводим краткую статистику по каждому столбцу.
# Для числовых признаков показываются минимум, максимум, среднее, медиана и квартили.
# Для Species показывается количество объектов каждого класса.

summary(iris)
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##        Species  
##  setosa    :50  
##  versicolor:50  
##  virginica :50  
##                 
##                 
## 

В наборе iris содержится 150 объектов.
Первые четыре столбца являются числовыми признаками, а столбец Species содержит класс цветка.

Нормализация данных

Метод KNN работает на основе расстояний между объектами.
Если признаки находятся в разных масштабах, то признаки с большим диапазоном значений могут сильнее влиять на результат.

Поэтому перед применением KNN данные нормализуются.

# Создаем функцию нормализации.
# Эта функция переводит значения признака в диапазон от 0 до 1.
#
# min(x) — минимальное значение признака.
# max(x) — максимальное значение признака.
# x - min(x) — сдвигает значения так, чтобы минимум стал равен 0.
# max(x) - min(x) — диапазон значений признака.
# Деление на диапазон приводит значения к масштабу от 0 до 1.

normalize <- function(x) {
  (x - min(x)) / (max(x) - min(x))
}
# Берем только первые четыре столбца iris.
# Это числовые признаки: Sepal.Length, Sepal.Width, Petal.Length, Petal.Width.
# Столбец Species не нормализуем, потому что это не число, а класс.

iris_numeric <- iris[, 1:4]

# lapply() применяет функцию normalize к каждому числовому столбцу.
# as.data.frame() превращает результат обратно в таблицу.

iris_norm <- as.data.frame(
  lapply(iris_numeric, normalize)
)

# Выводим первые строки нормализованных данных.

head(iris_norm)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1   0.22222222   0.6250000   0.06779661  0.04166667
## 2   0.16666667   0.4166667   0.06779661  0.04166667
## 3   0.11111111   0.5000000   0.05084746  0.04166667
## 4   0.08333333   0.4583333   0.08474576  0.04166667
## 5   0.19444444   0.6666667   0.06779661  0.04166667
## 6   0.30555556   0.7916667   0.11864407  0.12500000
# Проверим нормализацию на примере Sepal.Length.
# До нормализации значения были в исходном диапазоне.

min(iris$Sepal.Length)
## [1] 4.3
max(iris$Sepal.Length)
## [1] 7.9
# После нормализации минимум должен быть 0, максимум должен быть 1.

min(iris_norm$Sepal.Length)
## [1] 0
max(iris_norm$Sepal.Length)
## [1] 1

Разделение на обучающую и тестовую выборки

# Фиксируем генератор случайных чисел.
# Это нужно, чтобы при каждом запуске отчета выборка делилась одинаково.

set.seed(123)

# Создаем случайные индексы строк для обучающей выборки.
# nrow(iris_norm) — количество строк в таблице.
# 0.7 * nrow(iris_norm) — 70% всех строк.
# sample() случайно выбирает номера строк.

train_index <- sample(
  1:nrow(iris_norm),
  size = 0.7 * nrow(iris_norm)
)

# По выбранным индексам формируем обучающую выборку.
# Она будет использоваться алгоритмом KNN как известные примеры.

train_data <- iris_norm[train_index, ]

# Все строки, которые не попали в train_index, попадут в тестовую выборку.
# Минус перед train_index означает "взять все строки, кроме этих".

test_data <- iris_norm[-train_index, ]

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

train_labels <- iris$Species[train_index]

# Отдельно сохраняем правильные классы для тестовой выборки.
# Потом мы сравним их с предсказаниями модели.

test_labels <- iris$Species[-train_index]

# Выводим размеры обучающей и тестовой выборок.

nrow(train_data)
## [1] 105
nrow(test_data)
## [1] 45

Выполнение классификации KNN

# Выбираем количество соседей k.
# Часто для простого примера берут корень из размера обучающей выборки.
# round() округляет результат до целого числа.

k_value <- round(sqrt(nrow(train_data)))

k_value
## [1] 10
# Выполняем классификацию методом KNN.
#
# train = train_data — обучающие данные.
# test = test_data — объекты, для которых нужно предсказать класс.
# cl = train_labels — правильные классы обучающей выборки.
# k = k_value — количество ближайших соседей.
#
# Результат knn_result — предсказанные классы для объектов тестовой выборки.

knn_result <- knn(
  train = train_data,
  test = test_data,
  cl = train_labels,
  k = k_value
)

# Выводим предсказанные классы.

knn_result
##  [1] setosa     setosa     setosa     setosa     setosa     setosa    
##  [7] setosa     setosa     setosa     setosa     setosa     setosa    
## [13] setosa     setosa     versicolor versicolor versicolor versicolor
## [19] versicolor versicolor versicolor versicolor versicolor versicolor
## [25] versicolor versicolor versicolor virginica  versicolor versicolor
## [31] versicolor versicolor virginica  virginica  virginica  virginica 
## [37] virginica  virginica  virginica  virginica  virginica  versicolor
## [43] virginica  virginica  virginica 
## Levels: setosa versicolor virginica

Оценка модели с помощью CrossTable

# CrossTable() строит таблицу сравнения:
# x = test_labels — реальные классы;
# y = knn_result — предсказанные классы.
#
# В идеальном случае все значения должны находиться на главной диагонали.
# Значения вне диагонали показывают ошибки классификации.
#
# prop.chisq = FALSE отключает лишнюю статистику хи-квадрат,
# потому что в этом задании она не требуется.

CrossTable(
  x = test_labels,
  y = knn_result,
  prop.chisq = FALSE
)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  45 
## 
##  
##              | knn_result 
##  test_labels |     setosa | versicolor |  virginica |  Row Total | 
## -------------|------------|------------|------------|------------|
##       setosa |         14 |          0 |          0 |         14 | 
##              |      1.000 |      0.000 |      0.000 |      0.311 | 
##              |      1.000 |      0.000 |      0.000 |            | 
##              |      0.311 |      0.000 |      0.000 |            | 
## -------------|------------|------------|------------|------------|
##   versicolor |          0 |         17 |          1 |         18 | 
##              |      0.000 |      0.944 |      0.056 |      0.400 | 
##              |      0.000 |      0.944 |      0.077 |            | 
##              |      0.000 |      0.378 |      0.022 |            | 
## -------------|------------|------------|------------|------------|
##    virginica |          0 |          1 |         12 |         13 | 
##              |      0.000 |      0.077 |      0.923 |      0.289 | 
##              |      0.000 |      0.056 |      0.923 |            | 
##              |      0.000 |      0.022 |      0.267 |            | 
## -------------|------------|------------|------------|------------|
## Column Total |         14 |         18 |         13 |         45 | 
##              |      0.311 |      0.400 |      0.289 |            | 
## -------------|------------|------------|------------|------------|
## 
## 

Матрица ошибок

# confusionMatrix() строит матрицу ошибок.
# Первый аргумент — предсказанные классы.
# Второй аргумент — реальные классы.
#
# Матрица ошибок показывает, сколько объектов каждого класса
# было классифицировано правильно и неправильно.

knn_matrix <- confusionMatrix(
  knn_result,
  test_labels
)

knn_matrix
## Confusion Matrix and Statistics
## 
##             Reference
## Prediction   setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0         17         1
##   virginica       0          1        12
## 
## Overall Statistics
##                                           
##                Accuracy : 0.9556          
##                  95% CI : (0.8485, 0.9946)
##     No Information Rate : 0.4             
##     P-Value [Acc > NIR] : 2.842e-15       
##                                           
##                   Kappa : 0.9326          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: setosa Class: versicolor Class: virginica
## Sensitivity                 1.0000            0.9444           0.9231
## Specificity                 1.0000            0.9630           0.9688
## Pos Pred Value              1.0000            0.9444           0.9231
## Neg Pred Value              1.0000            0.9630           0.9688
## Prevalence                  0.3111            0.4000           0.2889
## Detection Rate              0.3111            0.3778           0.2667
## Detection Prevalence        0.3111            0.4000           0.2889
## Balanced Accuracy           1.0000            0.9537           0.9459

Диагональная оценка качества прогноза

# В матрице ошибок главная диагональ содержит правильные ответы.
# Например, если объект был setosa и предсказан как setosa,
# он попадает на главную диагональ.

diag_values <- diag(knn_matrix$table)

# Выводим количество правильно классифицированных объектов по каждому классу.

diag_values
##     setosa versicolor  virginica 
##         14         17         12
# colSums(knn_matrix$table) считает общее количество объектов каждого реального класса.
# Делим количество правильных ответов на общее количество объектов класса.
# Получаем качество прогноза отдельно по каждому классу.

diag_quality <- diag(knn_matrix$table) / colSums(knn_matrix$table)

# Умножаем на 100, чтобы получить проценты.

diag_quality_percent <- round(diag_quality * 100, 2)

diag_quality_percent
##     setosa versicolor  virginica 
##     100.00      94.44      92.31
# Общая точность модели хранится в knn_matrix$overall["Accuracy"].
# Она показывает долю всех правильно классифицированных объектов.

knn_accuracy <- knn_matrix$overall["Accuracy"]

knn_accuracy
##  Accuracy 
## 0.9555556

Вывод по заданию 2

В задании была выполнена классификация методом k-ближайших соседей.

Сначала данные были нормализованы, потому что KNN использует расстояния между объектами. После этого набор данных был разделен на обучающую и тестовую выборки в соотношении 70% и 30%.

С помощью функции knn() были получены предсказания для тестовой выборки. Для оценки качества использовались CrossTable() и confusionMatrix().

Диагональная оценка качества показала, сколько объектов каждого класса было классифицировано правильно. В целом метод KNN хорошо справился с классификацией набора данных iris.

Задание 3. Метод опорных векторов

Условие

Рассмотреть пример реализации метода опорных векторов с использованием функции svm() из пакета e1071.

Необходимо:

  • построить линейный классификатор;
  • выполнить прогнозирование;
  • выполнить перекрестную проверку с делением исходной выборки на 10 равных частей (cross = 10).

Выполнение задания

# Подключаем пакет e1071.
# Он содержит функцию svm(), которая реализует метод опорных векторов.

library(e1071)

# Пакет caret уже использовался выше.
# Здесь он снова нужен для confusionMatrix().

library(caret)
# Фиксируем случайность для воспроизводимости результата.

set.seed(42)

# Формируем случайные индексы для обучающей выборки.
# Используем 70% данных для обучения и 30% для тестирования.

svm_index <- sample(
  1:nrow(iris),
  size = 0.7 * nrow(iris)
)

# Создаем обучающую выборку.

train_svm <- iris[svm_index, ]

# Создаем тестовую выборку.

test_svm <- iris[-svm_index, ]

# Проверяем размеры выборок.

nrow(train_svm)
## [1] 105
nrow(test_svm)
## [1] 45

Построение линейного классификатора

# Создаем SVM-модель.
#
# Species ~ . означает:
# Species — целевая переменная,
# точка означает, что все остальные столбцы используются как признаки.
#
# data = train_svm — модель обучается на обучающей выборке.
# kernel = "linear" — используется линейное ядро.
# Это значит, что модель строит линейную разделяющую границу.
#
# scale = TRUE — признаки автоматически масштабируются.
# Это полезно, потому что SVM чувствителен к масштабу признаков.

svm_model <- svm(
  Species ~ .,
  data = train_svm,
  kernel = "linear",
  scale = TRUE
)

# Выводим краткую информацию о модели.

svm_model
## 
## Call:
## svm(formula = Species ~ ., data = train_svm, kernel = "linear", scale = TRUE)
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  linear 
##        cost:  1 
## 
## Number of Support Vectors:  25
# nSV показывает количество опорных векторов по каждому классу.
# sum() считает общее количество опорных векторов.
#
# Опорные векторы — это важные точки,
# которые находятся ближе всего к границе между классами.

sum(svm_model$nSV)
## [1] 25

Визуализация классификатора

# Строим график SVM-модели.
# Для визуализации выбираем два признака:
# Petal.Length и Petal.Width.
#
# На графике видно, как модель делит пространство признаков
# на области разных классов.

plot(
  svm_model,
  data = train_svm,
  formula = Petal.Width ~ Petal.Length,
  main = "SVM: линейный классификатор"
)

Прогнозирование на тестовой выборке

# predict() применяет обученную модель к тестовой выборке.
# В результате получаем предсказанный вид ириса для каждой строки test_svm.

svm_prediction <- predict(
  svm_model,
  test_svm
)

svm_prediction
##          7         11         12         19         23         25         28 
##     setosa     setosa     setosa     setosa     setosa     setosa     setosa 
##         31         37         39         45         46         51         52 
##     setosa     setosa     setosa     setosa     setosa versicolor versicolor 
##         56         59         62         63         66         67         70 
## versicolor versicolor versicolor versicolor versicolor versicolor versicolor 
##         75         78         79         82         91         95        101 
## versicolor  virginica versicolor versicolor versicolor versicolor  virginica 
##        102        106        108        112        113        116        117 
##  virginica  virginica  virginica  virginica  virginica  virginica  virginica 
##        119        125        127        133        134        137        141 
##  virginica  virginica  virginica  virginica versicolor  virginica  virginica 
##        143        147        148 
##  virginica  virginica  virginica 
## Levels: setosa versicolor virginica
# Строим матрицу ошибок для SVM.
# Сравниваем предсказания модели с реальными классами.

svm_matrix <- confusionMatrix(
  svm_prediction,
  test_svm$Species
)

svm_matrix
## Confusion Matrix and Statistics
## 
##             Reference
## Prediction   setosa versicolor virginica
##   setosa         12          0         0
##   versicolor      0         14         1
##   virginica       0          1        17
## 
## Overall Statistics
##                                           
##                Accuracy : 0.9556          
##                  95% CI : (0.8485, 0.9946)
##     No Information Rate : 0.4             
##     P-Value [Acc > NIR] : 2.842e-15       
##                                           
##                   Kappa : 0.9324          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: setosa Class: versicolor Class: virginica
## Sensitivity                 1.0000            0.9333           0.9444
## Specificity                 1.0000            0.9667           0.9630
## Pos Pred Value              1.0000            0.9333           0.9444
## Neg Pred Value              1.0000            0.9667           0.9630
## Prevalence                  0.2667            0.3333           0.4000
## Detection Rate              0.2667            0.3111           0.3778
## Detection Prevalence        0.2667            0.3333           0.4000
## Balanced Accuracy           1.0000            0.9500           0.9537
# Выводим общую точность SVM-модели.

svm_accuracy <- svm_matrix$overall["Accuracy"]

svm_accuracy
##  Accuracy 
## 0.9555556

Перекрестная проверка cross = 10

# Выполняем кросс-валидацию.
#
# cross = 10 означает, что обучающая выборка делится на 10 частей.
# Модель обучается на 9 частях и проверяется на оставшейся части.
# Это повторяется 10 раз.
#
# Такой подход дает более надежную оценку качества модели,
# чем одно случайное разбиение.

svm_cross <- svm(
  Species ~ .,
  data = train_svm,
  kernel = "linear",
  scale = TRUE,
  cross = 10
)

# accuracies содержит точность для каждого из 10 разбиений.

svm_cross$accuracies
##  [1] 100.00000  90.90909  90.00000 100.00000 100.00000  90.90909 100.00000
##  [8] 100.00000  90.00000  90.90909
# Считаем среднюю точность по всем 10 разбиениям.

mean_cross_accuracy <- mean(svm_cross$accuracies)

mean_cross_accuracy
## [1] 95.27273
# tot.acc — итоговая точность кросс-валидации,
# которую возвращает сама функция svm().

svm_cross$tot.acc
## [1] 95.2381

Вывод по заданию 3

В задании был рассмотрен метод опорных векторов.

С помощью функции svm() была построена модель линейной классификации. Для обучения использовалась обучающая выборка, а качество проверялось на тестовой выборке.

Матрица ошибок показала, что SVM хорошо классифицирует объекты из набора iris.

Также была выполнена перекрестная проверка с параметром cross = 10. Она позволяет оценить качество модели более надежно, потому что модель проверяется на разных частях данных.

Задание 4. Метод главных компонент

Условие

Выполнить расчет главных компонент с использованием пакета vegan и функции rda().

Необходимо:

  • построить ординационную диаграмму методом PCA;
  • сделать выводы.

Выполнение задания

# Подключаем пакет vegan.
# В нем находится функция rda().
# В данном задании она используется для расчета PCA.

library(vegan)

Подготовка данных

# Для PCA берем только числовые признаки.
# Столбец Species не используется, потому что он является категориальным классом.

iris_num <- iris[, 1:4]

# Выводим первые строки числовых данных.

head(iris_num)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1          5.1         3.5          1.4         0.2
## 2          4.9         3.0          1.4         0.2
## 3          4.7         3.2          1.3         0.2
## 4          4.6         3.1          1.5         0.2
## 5          5.0         3.6          1.4         0.2
## 6          5.4         3.9          1.7         0.4
# Смотрим описательную статистику числовых признаков.

summary(iris_num)
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500
# Считаем корреляцию между признаками.
# Корреляция показывает, насколько признаки связаны друг с другом.
#
# Значения, близкие к 1, означают сильную положительную связь.
# Значения, близкие к -1, означают сильную отрицательную связь.
# Значения около 0 означают слабую связь.

cor(iris_num)
##              Sepal.Length Sepal.Width Petal.Length Petal.Width
## Sepal.Length    1.0000000  -0.1175698    0.8717538   0.8179411
## Sepal.Width    -0.1175698   1.0000000   -0.4284401  -0.3661259
## Petal.Length    0.8717538  -0.4284401    1.0000000   0.9628654
## Petal.Width     0.8179411  -0.3661259    0.9628654   1.0000000

Расчет PCA с помощью rda()

# rda() из пакета vegan может выполнять PCA.
#
# scale = TRUE означает, что признаки будут масштабированы.
# Это важно, потому что признаки имеют разные диапазоны значений.
#
# PCA создает новые переменные — главные компоненты.
# Они являются комбинациями исходных признаков.

pca_result <- rda(
  iris_num,
  scale = TRUE
)

pca_result
## 
## Call: rda(X = iris_num, scale = TRUE)
## 
##               Inertia Rank
## Total               4     
## Unconstrained       4    4
## 
## Inertia is correlations
## 
## Eigenvalues for unconstrained axes:
##    PC1    PC2    PC3    PC4 
## 2.9185 0.9140 0.1468 0.0207
# summary() показывает подробную информацию о PCA:
# собственные значения,
# долю объясняемой дисперсии,
# вклад главных компонент.

summary(pca_result)
## 
## Call:
## rda(X = iris_num, scale = TRUE) 
## 
## Partitioning of correlations:
##               Inertia Proportion
## Total               4          1
## Unconstrained       4          1
## 
## Eigenvalues, and their contribution to the correlations 
## 
## Importance of components:
##                          PC1    PC2     PC3      PC4
## Eigenvalue            2.9185 0.9140 0.14676 0.020715
## Proportion Explained  0.7296 0.2285 0.03669 0.005179
## Cumulative Proportion 0.7296 0.9581 0.99482 1.000000

Объясняемая дисперсия

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

eigen_values <- as.numeric(eigenvals(pca_result))

# Считаем долю дисперсии каждой компоненты в процентах.

variance_percent <- eigen_values / sum(eigen_values) * 100

# Считаем накопленную долю дисперсии.
# Она показывает, сколько информации объясняют первые компоненты вместе.

cumulative_percent <- cumsum(variance_percent)

# Собираем результаты в таблицу.

variance_table <- data.frame(
  component = paste0("PC", 1:length(eigen_values)),
  eigenvalue = round(eigen_values, 3),
  variance_percent = round(variance_percent, 2),
  cumulative_percent = round(cumulative_percent, 2)
)

variance_table
##   component eigenvalue variance_percent cumulative_percent
## 1       PC1      2.918            72.96              72.96
## 2       PC2      0.914            22.85              95.81
## 3       PC3      0.147             3.67              99.48
## 4       PC4      0.021             0.52             100.00
# Строим столбчатый график.
# Он показывает, какую долю информации объясняет каждая главная компонента.
#
# Обычно первые компоненты объясняют большую часть информации,
# а следующие компоненты дают все меньше нового.

barplot(
  height = variance_percent,
  names.arg = paste0("PC", 1:length(variance_percent)),
  main = "Доля объясняемой дисперсии",
  xlab = "Главная компонента",
  ylab = "Дисперсия, %",
  ylim = c(0, 100)
)

Ординационная диаграмма PCA

# Строим ординационную диаграмму.
# На ней объекты располагаются в пространстве главных компонент.
#
# Близко расположенные точки похожи друг на друга.
# Далеко расположенные точки отличаются сильнее.

plot(
  pca_result,
  scaling = 2,
  main = "Ординационная диаграмма PCA"
)

# Получаем координаты объектов на главных компонентах.
# display = "sites" означает, что нужны координаты наблюдений.
# scaling = 2 используется для удобного отображения на PCA-графике.

pca_scores <- scores(
  pca_result,
  display = "sites",
  scaling = 2
)

# Превращаем виды ириса в числовые значения для цветов.
# setosa, versicolor и virginica будут окрашены разными цветами.

species_colors <- as.numeric(iris$Species)

# Строим график объектов по первым двум главным компонентам.
# PC1 — первая главная компонента.
# PC2 — вторая главная компонента.

plot(
  pca_scores[, 1],
  pca_scores[, 2],
  col = species_colors,
  pch = 19,
  main = "PCA: расположение объектов по главным компонентам",
  xlab = "PC1",
  ylab = "PC2"
)

# Добавляем легенду, чтобы было понятно,
# какой цвет соответствует какому виду ириса.

legend(
  "topright",
  legend = levels(iris$Species),
  col = 1:3,
  pch = 19
)

Вывод по заданию 4

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

Для анализа использовались только числовые признаки набора iris. Столбец Species не использовался при расчете PCA, потому что он является категориальной переменной.

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

По ординационной диаграмме видно, что вид setosa хорошо отделяется от остальных видов. Виды versicolor и virginica расположены ближе друг к другу, поэтому они могут частично пересекаться.

Метод PCA полезен для визуализации многомерных данных и понимания структуры признаков.

Общий вывод

В лабораторной работе были выполнены задания по классификации и анализу данных в R.

В задании 2 был использован метод k-ближайших соседей. Данные были нормализованы, разделены на обучающую и тестовую выборки, после чего была построена модель классификации. Качество модели было оценено с помощью CrossTable(), матрицы ошибок и диагональной оценки качества прогноза.

В задании 3 был рассмотрен метод опорных векторов. С помощью функции svm() был построен линейный классификатор. Также была выполнена перекрестная проверка с делением выборки на 10 частей.

В задании 4 был выполнен расчет главных компонент методом PCA с использованием функции rda() из пакета vegan. Были построены график объясняемой дисперсии и ординационная диаграмма.

В результате работы были получены практические навыки классификации, оценки качества моделей, кросс-валидации и визуализации многомерных данных.