Задание 1:

  1. Установить пакет CARET.
  2. Выполнить команду names(getModelInfo()).
  3. Ознакомиться со списком доступных методов выбора признаков.
  4. Выполнить графический разведочный анализ данных с использованием функции featurePlot().
library(caret)
## Загрузка требуемого пакета: ggplot2
## Загрузка требуемого пакета: lattice
models <- names(getModelInfo())
length(models)
## [1] 239
head(models)
## [1] "ada"         "AdaBag"      "AdaBoost.M1" "adaboost"    "amdai"      
## [6] "ANFIS"

Функция getModelInfo() возвращает список реализованных в пакете caret алгоритмов машинного обучения для задач классификации и регрессии.

set.seed(141)

x <- matrix(rnorm(50 * 5), ncol = 5)
colnames(x) <- paste0("X", 1:5)

y <- factor(rep(c("A", "B"), 25))

Попарные графики

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

Плотности распределения

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

Вывод:

Попарные диаграммы рассеяния признаков показали, что объекты классов A и B существенно перекрываются в большинстве двумерных проекций пространства признаков. Чёткой разделимости классов по отдельным парам признаков не наблюдается.

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

Задание 2:

  1. С использованием функций выбора признаков определить важность признаков для решения задачи классификации.
  2. Использовать набор данных iris.
  3. Сделать вывод.

Для выполнения задания используется пакет FSelectorRcpp, реализующий методы оценки информативности признаков.

library(FSelectorRcpp)

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

data(iris)
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 ...

Набор данных iris содержит четыре числовых признака и категориальную переменную Species, определяющую класс объекта.

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

weights <- information_gain(
  Species ~ .,
  data = iris
)

weights

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

weights_sorted <- weights[
  order(-weights$importance),
]

weights_sorted

Вывод:

В результате оценки признаков методом информационного выигрыша наиболее информативными для определения вида ириса являются характеристики лепестка — Petal.Width и Petal.Length.

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

Задание 3:

  1. С использованием функции discretize() из пакета arules выполнить преобразование непрерывной переменной в категориальную различными методами:
  1. Использовать набор данных iris.
  2. Сделать выводы.
library(arules)
## Загрузка требуемого пакета: Matrix
## 
## Присоединяю пакет: 'arules'
## Следующий объект скрыт от 'package:FSelectorRcpp':
## 
##     discretize
## Следующие объекты скрыты от 'package:base':
## 
##     abbreviate, write
data(iris)

x <- iris$Petal.Length

Метод «interval» — равная ширина интервалов

x_interval <- discretize(
  x,
  method = "interval",
  breaks = 3
)

table(x_interval)
## x_interval
##    [1,2.97) [2.97,4.93)  [4.93,6.9] 
##          50          54          46

Диапазон значений делится на три примерно одинаковых по ширине интервала.

Метод «frequency» — равная частота

x_frequency <- discretize(
  x,
  method = "frequency",
  breaks = 3
)

table(x_frequency)
## x_frequency
##   [1,2.63) [2.63,4.9)  [4.9,6.9] 
##         50         49         51

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

Метод «cluster» — кластеризация

x_cluster <- discretize(
  x,
  method = "cluster",
  breaks = 3
)

table(x_cluster)
## x_cluster
##    [1,2.95) [2.95,5.13)  [5.13,6.9] 
##          50          66          34

Границы интервалов определяются с учётом распределения и структуры исходных данных.

Метод «fixed» — фиксированные границы

x_fixed <- discretize(
  x,
  method = "fixed",
  breaks = c(1, 3, 5, 7)
)

table(x_fixed)
## x_fixed
## [1,3) [3,5) [5,7] 
##    50    54    46

При использовании метода fixed границы интервалов задаются вручную.

Сравним результаты различных методов:

head(
  data.frame(
    original = x,
    interval = x_interval,
    frequency = x_frequency,
    cluster = x_cluster,
    fixed = x_fixed
  )
)

Вывод:

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

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

Метод кластеризации формирует интервалы с учётом структуры исходных данных. Метод фиксированных границ позволяет самостоятельно задавать интервалы в зависимости от условий задачи.

Задание 4:

  1. Установить пакет Boruta.
  2. Провести выбор признаков для набора данных Ozone.
  3. Построить график boxplot.
  4. Сделать выводы.
library(mlbench)
library(Boruta)

Загрузка данных и просмотр структуры:

data(Ozone)
str(Ozone)
## 'data.frame':    366 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",..: 1 2 3 4 5 6 7 8 9 10 ...
##  $ V3 : Factor w/ 7 levels "1","2","3","4",..: 4 5 6 7 1 2 3 4 5 6 ...
##  $ V4 : num  3 3 3 5 5 6 4 4 6 7 ...
##  $ V5 : num  5480 5660 5710 5700 5760 5720 5790 5790 5700 5700 ...
##  $ V6 : num  8 6 4 3 3 4 6 3 3 3 ...
##  $ V7 : num  20 NA 28 37 51 69 19 25 73 59 ...
##  $ V8 : num  NA 38 40 45 54 35 45 55 41 44 ...
##  $ V9 : num  NA NA NA NA 45.3 ...
##  $ V10: num  5000 NA 2693 590 1450 ...
##  $ V11: num  -15 -14 -25 -24 25 15 -33 -28 23 -2 ...
##  $ V12: num  30.6 NA 47.7 55 57 ...
##  $ V13: num  200 300 250 100 60 60 100 250 120 120 ...

Получим краткую статистическую информацию о наборе данных:

summary(Ozone)
##        V1            V2      V3           V4              V5      
##  1      : 31   1      : 12   1:52   Min.   : 1.00   Min.   :5320  
##  3      : 31   2      : 12   2:52   1st Qu.: 5.00   1st Qu.:5700  
##  5      : 31   3      : 12   3:52   Median : 9.00   Median :5770  
##  7      : 31   4      : 12   4:53   Mean   :11.53   Mean   :5753  
##  8      : 31   5      : 12   5:53   3rd Qu.:16.00   3rd Qu.:5830  
##  10     : 31   6      : 12   6:52   Max.   :38.00   Max.   :5950  
##  (Other):180   (Other):294   7:52   NAs    :5       NAs    :12    
##        V6               V7              V8              V9       
##  Min.   : 0.000   Min.   :19.00   Min.   :25.00   Min.   :27.68  
##  1st Qu.: 3.000   1st Qu.:49.00   1st Qu.:51.00   1st Qu.:49.73  
##  Median : 5.000   Median :65.00   Median :62.00   Median :57.02  
##  Mean   : 4.869   Mean   :58.48   Mean   :61.91   Mean   :56.85  
##  3rd Qu.: 6.000   3rd Qu.:73.00   3rd Qu.:72.00   3rd Qu.:66.11  
##  Max.   :11.000   Max.   :93.00   Max.   :93.00   Max.   :82.58  
##                   NAs    :15      NAs    :2       NAs    :139    
##       V10            V11             V12             V13       
##  Min.   : 111   Min.   :-69.0   Min.   :27.50   Min.   :  0.0  
##  1st Qu.: 890   1st Qu.:-10.0   1st Qu.:51.26   1st Qu.: 70.0  
##  Median :2125   Median : 24.0   Median :62.24   Median :110.0  
##  Mean   :2591   Mean   : 17.8   Mean   :60.93   Mean   :123.3  
##  3rd Qu.:5000   3rd Qu.: 45.0   3rd Qu.:70.52   3rd Qu.:150.0  
##  Max.   :5000   Max.   :107.0   Max.   :91.76   Max.   :500.0  
##  NAs    :15     NAs    :1       NAs    :14

Удалим строки, содержащие пропущенные значения:

oz <- na.omit(Ozone)

Запустим алгоритм Boruta:

set.seed(123)

bor <- Boruta(
  V4 ~ .,
  data = oz,
  doTrace = 1
)
## After 11 iterations, +0.36 secs:
##  confirmed 9 attributes: V1, V10, V11, V12, V13 and 4 more;
##  rejected 1 attribute: V3;
##  still have 2 attributes left.
## After 15 iterations, +0.48 secs:
##  rejected 1 attribute: V6;
##  still have 1 attribute left.
## After 18 iterations, +0.56 secs:
##  rejected 1 attribute: V2;
##  no more attributes left.

Посмотрим полученный результат:

bor
## Boruta performed 18 iterations in 0.5635951 secs.
##  9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
##  3 attributes confirmed unimportant: V2, V3, V6;

Извлечём признаки, которые алгоритм определил как значимые:

getSelectedAttributes(
  bor,
  withTentative = TRUE
)
## [1] "V1"  "V5"  "V7"  "V8"  "V9"  "V10" "V11" "V12" "V13"

График важности признаков

plot(
  bor,
  las = 2,
  cex.axis = 0.7
)

Вывод:

С помощью алгоритма Boruta был выполнен отбор признаков для набора данных Ozone. Алгоритм сравнивает реальные признаки с искусственно созданными теневыми признаками и позволяет определить переменные, обладающие статистически значимой важностью.

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