Введение

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

В ходе работы применяются следующие пакеты:

Основная цель работы — познакомиться с базовыми методами анализа признаков и научиться оформлять результаты в виде RMarkdown-отчета.

Задание 1. Графический анализ данных с помощью caret

Постановка задачи

Необходимо установить и подключить пакет caret, выполнить команду names(getModelInfo()), а затем провести графический анализ искусственно созданного набора данных.

Для анализа используются данные:

sample_x <- matrix(rnorm(50 * 5), ncol = 5)
sample_y <- factor(rep(c("A", "B"), 25))

Также требуется сохранить построенные графики в формате .jpg.

Выполнение

# Проверка и установка пакета при необходимости
if (!require(caret, quietly = TRUE)) {
  install.packages("caret")
}
library(caret)

Выведем несколько первых названий моделей, доступных в пакете caret:

available_models <- names(getModelInfo())
head(available_models, 10)
##  [1] "ada"         "AdaBag"      "AdaBoost.M1" "adaboost"    "amdai"      
##  [6] "ANFIS"       "avNNet"      "awnb"        "awtan"       "bag"

Сформируем данные для анализа:

set.seed(321)
sample_x <- matrix(rnorm(50 * 5), ncol = 5)
sample_y <- factor(rep(c("A", "B"), 25))

Построим график попарных зависимостей:

featurePlot(
  x = sample_x,
  y = sample_y,
  plot = "pairs"
)

Сохраним его в файл:

jpeg("caret_pairs_plot.jpg")
featurePlot(x = sample_x, y = sample_y, plot = "pairs")
dev.off()
## png 
##   2

Построим график плотности распределения:

featurePlot(
  x = sample_x,
  y = sample_y,
  plot = "density"
)

Сохраним график плотности:

jpeg("caret_density_plot.jpg")
featurePlot(x = sample_x, y = sample_y, plot = "density")
dev.off()
## png 
##   2

Построим boxplot-график:

featurePlot(
  x = sample_x,
  y = sample_y,
  plot = "box"
)

Сохраним boxplot:

jpeg("caret_box_plot.jpg")
featurePlot(x = sample_x, y = sample_y, plot = "box")
dev.off()
## png 
##   2

Вывод

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

Функция featurePlot() позволила построить несколько типов графиков:

  • pairs показывает взаимосвязи между признаками;
  • density отображает распределение значений по классам;
  • box позволяет сравнить разброс значений внутри групп.

Поскольку исходные данные были созданы случайным образом, выраженного разделения между классами A и B не видно. Это означает, что признаки в данном примере не позволяют надежно отличить один класс от другого.

Задание 2. Оценка важности признаков с помощью FSelector

Постановка задачи

Необходимо использовать пакет FSelector и определить важность признаков для задачи классификации на наборе данных iris.

Выполнение

if (!require(FSelector, quietly = TRUE)) {
  install.packages("FSelector")
}
library(FSelector)

Загрузим набор данных iris:

data(iris)
head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa

Рассчитаем информативность признаков:

iris_importance <- information.gain(Species ~ ., data = iris)
iris_importance
##              attr_importance
## Sepal.Length       0.4521286
## Sepal.Width        0.2672750
## Petal.Length       0.9402853
## Petal.Width        0.9554360

Отсортируем признаки по убыванию важности:

iris_importance_sorted <- iris_importance[order(-iris_importance$attr_importance), , drop = FALSE]
iris_importance_sorted
##              attr_importance
## Petal.Width        0.9554360
## Petal.Length       0.9402853
## Sepal.Length       0.4521286
## Sepal.Width        0.2672750

Вывод

Для оценки значимости признаков была использована функция information.gain(). Она показывает, насколько каждый признак полезен для разделения объектов на классы.

По результатам анализа наиболее информативными признаками оказались Petal.Width и Petal.Length. Эти признаки лучше всего помогают различать виды ирисов.

Признак Sepal.Width имеет наименьшее значение важности. Это говорит о том, что ширина чашелистика хуже помогает при классификации по сравнению с характеристиками лепестков.

Задание 3. Дискретизация переменной с помощью arules

Постановка задачи

Необходимо выполнить преобразование непрерывной переменной в категориальную с помощью функции discretize() из пакета arules.

Используются четыре метода:

  • interval;
  • frequency;
  • cluster;
  • fixed.

Для работы используется набор данных iris.

Выполнение

if (!require(arules, quietly = TRUE)) {
  install.packages("arules")
}
library(arules)

Создадим копию набора данных:

data(iris)
iris_data <- iris

Выполним дискретизацию переменной Sepal.Length различными методами:

iris_data$Sepal_interval <- discretize(
  iris_data$Sepal.Length,
  method = "interval",
  breaks = 3
)

iris_data$Sepal_frequency <- discretize(
  iris_data$Sepal.Length,
  method = "frequency",
  breaks = 3
)

iris_data$Sepal_cluster <- discretize(
  iris_data$Sepal.Length,
  method = "cluster",
  breaks = 3
)

iris_data$Sepal_fixed <- discretize(
  iris_data$Sepal.Length,
  method = "fixed",
  breaks = c(-Inf, 5, 6, Inf)
)

Посмотрим первые строки полученного набора данных:

head(iris_data)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal_interval
## 1          5.1         3.5          1.4         0.2  setosa      [4.3,5.5)
## 2          4.9         3.0          1.4         0.2  setosa      [4.3,5.5)
## 3          4.7         3.2          1.3         0.2  setosa      [4.3,5.5)
## 4          4.6         3.1          1.5         0.2  setosa      [4.3,5.5)
## 5          5.0         3.6          1.4         0.2  setosa      [4.3,5.5)
## 6          5.4         3.9          1.7         0.4  setosa      [4.3,5.5)
##   Sepal_frequency Sepal_cluster Sepal_fixed
## 1       [4.3,5.4)    [4.3,5.33)       [5,6)
## 2       [4.3,5.4)    [4.3,5.33)    [-Inf,5)
## 3       [4.3,5.4)    [4.3,5.33)    [-Inf,5)
## 4       [4.3,5.4)    [4.3,5.33)    [-Inf,5)
## 5       [4.3,5.4)    [4.3,5.33)       [5,6)
## 6       [5.4,6.3)   [5.33,6.27)       [5,6)

Сравним распределение значений по группам:

table(iris_data$Sepal_interval)
## 
## [4.3,5.5) [5.5,6.7) [6.7,7.9] 
##        52        70        28
table(iris_data$Sepal_frequency)
## 
## [4.3,5.4) [5.4,6.3) [6.3,7.9] 
##        46        53        51
table(iris_data$Sepal_cluster)
## 
##  [4.3,5.33) [5.33,6.27)  [6.27,7.9] 
##          46          53          51
table(iris_data$Sepal_fixed)
## 
## [-Inf,5)    [5,6) [6, Inf] 
##       22       61       67

Вывод

В данном задании была выполнена дискретизация переменной Sepal.Length, то есть числовые значения были преобразованы в интервальные категории.

Метод interval разбивает диапазон значений на интервалы одинаковой ширины. Он удобен своей простотой, но не учитывает, сколько объектов попадает в каждый интервал.

Метод frequency старается распределить наблюдения по группам более равномерно. Поэтому он полезен, если важно получить интервалы примерно с одинаковым количеством объектов.

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

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

Задание 4. Отбор признаков с помощью Boruta

Постановка задачи

Необходимо установить пакет Boruta, выполнить отбор признаков для набора данных Ozone, построить график и сделать выводы.

Выполнение

if (!require(Boruta, quietly = TRUE)) {
  install.packages("Boruta")
}
if (!require(mlbench, quietly = TRUE)) {
  install.packages("mlbench")
}
library(Boruta)
library(mlbench)

Загрузим набор данных Ozone и удалим пропущенные значения:

data("Ozone")
ozone_data <- na.omit(Ozone)
str(ozone_data)
## 'data.frame':    203 obs. of  13 variables:
##  $ V1 : Factor w/ 12 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ V2 : Factor w/ 31 levels "1","2","3","4",..: 5 6 7 8 9 12 13 14 15 16 ...
##  $ V3 : Factor w/ 7 levels "1","2","3","4",..: 1 2 3 4 5 1 2 3 4 5 ...
##  $ V4 : num  5 6 4 4 6 6 5 4 4 7 ...
##  $ V5 : num  5760 5720 5790 5790 5700 5720 5760 5780 5830 5870 ...
##  $ V6 : num  3 4 6 3 3 3 6 6 3 2 ...
##  $ V7 : num  51 69 19 25 73 44 33 19 19 19 ...
##  $ V8 : num  54 35 45 55 41 51 51 54 58 61 ...
##  $ V9 : num  45.3 49.6 46.4 52.7 48 ...
##  $ V10: num  1450 1568 2631 554 2083 ...
##  $ V11: num  25 15 -33 -28 23 9 -44 -44 -53 -67 ...
##  $ V12: num  57 53.8 54.1 64.8 52.5 ...
##  $ V13: num  60 60 100 250 120 150 40 200 250 200 ...
##  - attr(*, "na.action")= 'omit' Named int [1:163] 1 2 3 4 10 11 17 18 20 24 ...
##   ..- attr(*, "names")= chr [1:163] "1" "2" "3" "4" ...

Выполним отбор признаков. В качестве целевой переменной используем V4:

set.seed(321)
boruta_result <- Boruta(V4 ~ ., data = ozone_data, doTrace = 0)
boruta_result
## Boruta performed 18 iterations in 0.3317089 secs.
##  9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
##  3 attributes confirmed unimportant: V2, V3, V6;

Построим график важности признаков:

plot(
  boruta_result,
  las = 2,
  cex.axis = 0.7,
  main = "Оценка важности признаков методом Boruta"
)

Посмотрим итоговое решение Boruta по признакам:

final_boruta <- attStats(boruta_result)
final_boruta
##         meanImp   medianImp      minImp     maxImp  normHits  decision
## V1   0.41096250  0.40960986  0.35760943 0.46785640 1.0000000 Confirmed
## V2   0.02918872  0.03406152 -0.03369836 0.10424128 0.0000000  Rejected
## V3  -0.05306116 -0.04258777 -0.11079874 0.01874702 0.0000000  Rejected
## V5   0.39323788  0.39677892  0.32238272 0.45811450 1.0000000 Confirmed
## V6   0.03167463  0.02244017 -0.03666906 0.15064050 0.1111111  Rejected
## V7   0.51623762  0.51936006  0.44466627 0.60793755 1.0000000 Confirmed
## V8   0.77232372  0.77006262  0.74191138 0.82035114 1.0000000 Confirmed
## V9   0.89607361  0.89698711  0.82591468 0.95376503 1.0000000 Confirmed
## V10  0.44715079  0.43596601  0.40288784 0.54094438 1.0000000 Confirmed
## V11  0.55336146  0.56235143  0.46693799 0.60844589 1.0000000 Confirmed
## V12  0.64190812  0.65311896  0.57822867 0.69316528 1.0000000 Confirmed
## V13  0.42188532  0.41730054  0.36515241 0.47906714 1.0000000 Confirmed

Вывод

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

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

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

График Boruta показывает распределение важности признаков. Чем выше значения на графике, тем больше вклад признака в объяснение целевой переменной.

Задание 5. Оформление отчета и публикация

Постановка задачи

Необходимо собрать все выполненные задания в единый файл RMarkdown и опубликовать результат на RPubs.

Выполнение

Данный отчет оформлен в формате RMarkdown. После нажатия кнопки Knit создается HTML-файл с результатами выполнения кода, графиками и текстовыми выводами.

Для публикации на RPubs необходимо:

  1. Выполнить Knit to HTML.
  2. В окне просмотра HTML нажать Publish.
  3. Выбрать сервис RPubs.
  4. Указать название публикации и описание.
  5. Опубликовать отчет и скопировать полученную ссылку.

Вывод

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

Общий вывод

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

С помощью пакета caret был выполнен графический разведочный анализ данных. Пакет FSelector позволил определить наиболее важные признаки в наборе данных iris. Пакет arules был использован для преобразования числовой переменной в категориальную. С помощью пакета Boruta был проведен отбор значимых признаков на наборе данных Ozone.

В результате работы были закреплены навыки использования RMarkdown, построения графиков, оценки важности признаков и подготовки отчета для публикации на RPubs. ```

Основные изменения:

  1. Убрано оглавлениеtoc: false в YAML-заголовке (вместо toc: true)
  2. Добавлена автоматическая проверка и установка пакетов — теперь код не упадет с ошибкой, если пакет не установлен
  3. Небольшие косметические улучшения — объединены некоторые строки кода для компактности

Если вам также нужно убрать нумерацию разделов или изменить оформление, дайте знать.