1 Цель работы

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

В работе используются пакеты caret, FSelector, arules и Boruta.

2 1. Пакет CARET и функция featurePlot()

2.1 1.1. Доступные методы

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

Выполним команду names(getModelInfo()).

model_names <- names(getModelInfo())

cat("Количество доступных моделей:", length(model_names), "\n\n")
## Количество доступных моделей: 239
print(model_names)
##   [1] "ada"                 "AdaBag"              "AdaBoost.M1"        
##   [4] "adaboost"            "amdai"               "ANFIS"              
##   [7] "avNNet"              "awnb"                "awtan"              
##  [10] "bag"                 "bagEarth"            "bagEarthGCV"        
##  [13] "bagFDA"              "bagFDAGCV"           "bam"                
##  [16] "bartMachine"         "bayesglm"            "binda"              
##  [19] "blackboost"          "blasso"              "blassoAveraged"     
##  [22] "bridge"              "brnn"                "BstLm"              
##  [25] "bstSm"               "bstTree"             "C5.0"               
##  [28] "C5.0Cost"            "C5.0Rules"           "C5.0Tree"           
##  [31] "cforest"             "chaid"               "CSimca"             
##  [34] "ctree"               "ctree2"              "cubist"             
##  [37] "dda"                 "deepboost"           "DENFIS"             
##  [40] "dnn"                 "dwdLinear"           "dwdPoly"            
##  [43] "dwdRadial"           "earth"               "elm"                
##  [46] "enet"                "evtree"              "extraTrees"         
##  [49] "fda"                 "FH.GBML"             "FIR.DM"             
##  [52] "foba"                "FRBCS.CHI"           "FRBCS.W"            
##  [55] "FS.HGD"              "gam"                 "gamboost"           
##  [58] "gamLoess"            "gamSpline"           "gaussprLinear"      
##  [61] "gaussprPoly"         "gaussprRadial"       "gbm_h2o"            
##  [64] "gbm"                 "gcvEarth"            "GFS.FR.MOGUL"       
##  [67] "GFS.LT.RS"           "GFS.THRIFT"          "glm.nb"             
##  [70] "glm"                 "glmboost"            "glmnet_h2o"         
##  [73] "glmnet"              "glmStepAIC"          "gpls"               
##  [76] "hda"                 "hdda"                "hdrda"              
##  [79] "HYFIS"               "icr"                 "J48"                
##  [82] "JRip"                "kernelpls"           "kknn"               
##  [85] "knn"                 "krlsPoly"            "krlsRadial"         
##  [88] "lars"                "lars2"               "lasso"              
##  [91] "lda"                 "lda2"                "leapBackward"       
##  [94] "leapForward"         "leapSeq"             "Linda"              
##  [97] "lm"                  "lmStepAIC"           "LMT"                
## [100] "loclda"              "logicBag"            "LogitBoost"         
## [103] "logreg"              "lssvmLinear"         "lssvmPoly"          
## [106] "lssvmRadial"         "lvq"                 "M5"                 
## [109] "M5Rules"             "manb"                "mda"                
## [112] "Mlda"                "mlp"                 "mlpKerasDecay"      
## [115] "mlpKerasDecayCost"   "mlpKerasDropout"     "mlpKerasDropoutCost"
## [118] "mlpML"               "mlpSGD"              "mlpWeightDecay"     
## [121] "mlpWeightDecayML"    "monmlp"              "msaenet"            
## [124] "multinom"            "mxnet"               "mxnetAdam"          
## [127] "naive_bayes"         "nb"                  "nbDiscrete"         
## [130] "nbSearch"            "neuralnet"           "nnet"               
## [133] "nnls"                "nodeHarvest"         "null"               
## [136] "OneR"                "ordinalNet"          "ordinalRF"          
## [139] "ORFlog"              "ORFpls"              "ORFridge"           
## [142] "ORFsvm"              "ownn"                "pam"                
## [145] "parRF"               "PART"                "partDSA"            
## [148] "pcaNNet"             "pcr"                 "pda"                
## [151] "pda2"                "penalized"           "PenalizedLDA"       
## [154] "plr"                 "pls"                 "plsRglm"            
## [157] "polr"                "ppr"                 "pre"                
## [160] "PRIM"                "protoclass"          "qda"                
## [163] "QdaCov"              "qrf"                 "qrnn"               
## [166] "randomGLM"           "ranger"              "rbf"                
## [169] "rbfDDA"              "Rborist"             "rda"                
## [172] "regLogistic"         "relaxo"              "rf"                 
## [175] "rFerns"              "RFlda"               "rfRules"            
## [178] "ridge"               "rlda"                "rlm"                
## [181] "rmda"                "rocc"                "rotationForest"     
## [184] "rotationForestCp"    "rpart"               "rpart1SE"           
## [187] "rpart2"              "rpartCost"           "rpartScore"         
## [190] "rqlasso"             "rqnc"                "RRF"                
## [193] "RRFglobal"           "rrlda"               "RSimca"             
## [196] "rvmLinear"           "rvmPoly"             "rvmRadial"          
## [199] "SBC"                 "sda"                 "sdwd"               
## [202] "simpls"              "SLAVE"               "slda"               
## [205] "smda"                "snn"                 "sparseLDA"          
## [208] "spikeslab"           "spls"                "stepLDA"            
## [211] "stepQDA"             "superpc"             "svmBoundrangeString"
## [214] "svmExpoString"       "svmLinear"           "svmLinear2"         
## [217] "svmLinear3"          "svmLinearWeights"    "svmLinearWeights2"  
## [220] "svmPoly"             "svmRadial"           "svmRadialCost"      
## [223] "svmRadialSigma"      "svmRadialWeights"    "svmSpectrumString"  
## [226] "tan"                 "tanSearch"           "treebag"            
## [229] "vbmpRadial"          "vglmAdjCat"          "vglmContRatio"      
## [232] "vglmCumulative"      "widekernelpls"       "WM"                 
## [235] "wsrf"                "xgbDART"             "xgbLinear"          
## [238] "xgbTree"             "xyf"

Для выбора признаков в пакете caret также используются функции:

  • rfe() — Recursive Feature Elimination;
  • sbf() — Selection By Filtering;
  • gafs() — выбор признаков генетическим алгоритмом;
  • safs() — выбор признаков методом имитации отжига.

2.2 1.2. Создание исходных данных

В соответствии с условием лабораторной работы создадим матрицу из 50 объектов и 5 признаков.

set.seed(123)

x <- matrix(
  rnorm(50 * 5),
  ncol = 5
)

colnames(x) <- paste0("X", 1:5)

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

cat("Размер матрицы x:\n")
## Размер матрицы x:
print(dim(x))
## [1] 50  5
cat("\nПервые строки:\n")
## 
## Первые строки:
print(head(x))
##               X1          X2          X3         X4         X5
## [1,] -0.56047565  0.25331851 -0.71040656  0.7877388  2.1988103
## [2,] -0.23017749 -0.02854676  0.25688371  0.7690422  1.3124130
## [3,]  1.55870831 -0.04287046 -0.24669188  0.3322026 -0.2651451
## [4,]  0.07050839  1.36860228 -0.34754260 -1.0083766  0.5431941
## [5,]  0.12928774 -0.22577099 -0.95161857 -0.1194526 -0.4143399
## [6,]  1.71506499  1.51647060 -0.04502772 -0.2803953 -0.4762469
cat("\nКоличество объектов каждого класса:\n")
## 
## Количество объектов каждого класса:
print(table(y))
## y
##  A  B 
## 25 25

2.3 1.3. Ellipse plot

featurePlot(
  x = x,
  y = y,
  plot = "ellipse",
  auto.key = TRUE
)

2.4 1.4. Strip plot

featurePlot(
  x = x,
  y = y,
  plot = "strip",
  jitter = TRUE,
  auto.key = TRUE
)

2.5 1.5. Boxplot

featurePlot(
  x = x,
  y = y,
  plot = "box",
  auto.key = TRUE
)

2.6 1.6. Pair plot

featurePlot(
  x = x,
  y = y,
  plot = "pairs",
  auto.key = TRUE
)

2.7 1.7. Density plot

featurePlot(
  x = x,
  y = y,
  plot = "density",
  scales = list(
    x = list(relation = "free"),
    y = list(relation = "free")
  ),
  auto.key = TRUE
)

2.8 Вывод по заданию 1

В результате графического разведочного анализа наблюдается значительное перекрытие объектов классов A и B.

Ни один из пяти признаков не обеспечивает явного разделения объектов на два класса.

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

Следовательно, между сгенерированными признаками и принадлежностью объекта к классу A или B отсутствует выраженная зависимость.

Все построенные графики автоматически сохраняются в формате JPG в папке graphs.

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

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

data(iris)

cat("Первые строки набора iris:\n")
## Первые строки набора iris:
print(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
cat("\nСтруктура данных:\n")
## 
## Структура данных:
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 ...
cat("\nСводная статистика:\n")
## 
## Сводная статистика:
print(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  
##                 
##                 
## 

Целевой переменной является Species, которая характеризует вид ириса.

Будем оценивать важность четырех признаков:

  • Sepal.Length;
  • Sepal.Width;
  • Petal.Length;
  • Petal.Width.

3.1 2.1. Information Gain

ig <- information.gain(
  Species ~ .,
  data = iris
)

ig <- ig[
  order(ig[, 1], decreasing = TRUE),
  ,
  drop = FALSE
]

print(ig)
##              attr_importance
## Petal.Width        0.9554360
## Petal.Length       0.9402853
## Sepal.Length       0.4521286
## Sepal.Width        0.2672750

3.2 2.2. Gain Ratio

gr <- gain.ratio(
  Species ~ .,
  data = iris
)

gr <- gr[
  order(gr[, 1], decreasing = TRUE),
  ,
  drop = FALSE
]

print(gr)
##              attr_importance
## Petal.Width        0.8713692
## Petal.Length       0.8584937
## Sepal.Length       0.4196464
## Sepal.Width        0.2472972

3.3 2.3. Symmetrical Uncertainty

su <- symmetrical.uncertainty(
  Species ~ .,
  data = iris
)

su <- su[
  order(su[, 1], decreasing = TRUE),
  ,
  drop = FALSE
]

print(su)
##              attr_importance
## Petal.Width        0.8705214
## Petal.Length       0.8571872
## Sepal.Length       0.4155563
## Sepal.Width        0.2452743

3.4 2.4. Relief

set.seed(123)

rel <- relief(
  Species ~ .,
  data = iris,
  neighbours.count = 5,
  sample.size = 100
)

rel <- rel[
  order(rel[, 1], decreasing = TRUE),
  ,
  drop = FALSE
]

print(rel)
##              attr_importance
## Petal.Width        0.3452500
## Petal.Length       0.3423729
## Sepal.Length       0.1472778
## Sepal.Width        0.1449583

3.5 2.5. Random Forest Importance

set.seed(123)

rf_imp <- random.forest.importance(
  Species ~ .,
  data = iris,
  importance.type = 1
)

rf_imp <- rf_imp[
  order(rf_imp[, 1], decreasing = TRUE),
  ,
  drop = FALSE
]

print(rf_imp)
##              attr_importance
## Petal.Length       47.478185
## Petal.Width        46.266486
## Sepal.Length       15.232793
## Sepal.Width         6.771835

3.6 2.6. Общая таблица важности

features <- rownames(ig)

importance_table <- data.frame(
  Feature = features,
  Information_Gain = ig[features, 1],
  Gain_Ratio = gr[features, 1],
  Symmetrical_Uncertainty = su[features, 1],
  Relief = rel[features, 1],
  Random_Forest = rf_imp[features, 1],
  row.names = NULL
)

print(importance_table)
##        Feature Information_Gain Gain_Ratio Symmetrical_Uncertainty    Relief
## 1  Petal.Width        0.9554360  0.8713692               0.8705214 0.3452500
## 2 Petal.Length        0.9402853  0.8584937               0.8571872 0.3423729
## 3 Sepal.Length        0.4521286  0.4196464               0.4155563 0.1472778
## 4  Sepal.Width        0.2672750  0.2472972               0.2452743 0.1449583
##   Random_Forest
## 1     46.266486
## 2     47.478185
## 3     15.232793
## 4      6.771835

3.7 2.7. График Information Gain

barplot(
  ig[, 1],
  names.arg = rownames(ig),
  las = 2,
  main = "Важность признаков iris: Information Gain",
  ylab = "Важность"
)

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

Методы оценки важности признаков показывают, что наиболее информативными переменными для определения вида ириса являются характеристики лепестков:

Petal.Length и Petal.Width.

Признаки Sepal.Length и Sepal.Width имеют меньшую информативность.

Таким образом, при построении классификатора для определения вида ириса наиболее полезными являются длина и ширина лепестка.

4 3. Дискретизация непрерывных признаков с помощью arules

Для дискретизации используется функция discretize() пакета arules.

Исследуем непрерывную переменную Sepal.Length из набора данных iris.

z <- iris$Sepal.Length

cat("Сводная статистика Sepal.Length:\n")
## Сводная статистика Sepal.Length:
print(summary(z))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   4.300   5.100   5.800   5.843   6.400   7.900

Посмотрим исходное распределение признака.

hist(
  z,
  breaks = 15,
  main = "Исходное распределение Sepal.Length",
  xlab = "Sepal.Length"
)

4.1 3.1. Метод interval

Метод interval делит диапазон значений на интервалы одинаковой ширины.

d_interval <- discretize(
  z,
  method = "interval",
  breaks = 3
)

cat("Количество объектов в категориях:\n")
## Количество объектов в категориях:
print(table(d_interval))
## d_interval
## [4.3,5.5) [5.5,6.7) [6.7,7.9] 
##        52        70        28
cat("\nГраницы интервалов:\n")
## 
## Границы интервалов:
print(
  discretize(
    z,
    method = "interval",
    breaks = 3,
    onlycuts = TRUE
  )
)
## [1] 4.3 5.5 6.7 7.9

4.2 3.2. Метод frequency

Метод frequency стремится получить группы с примерно одинаковым количеством наблюдений.

d_frequency <- discretize(
  z,
  method = "frequency",
  breaks = 3
)

cat("Количество объектов в категориях:\n")
## Количество объектов в категориях:
print(table(d_frequency))
## d_frequency
## [4.3,5.4) [5.4,6.3) [6.3,7.9] 
##        46        53        51
cat("\nГраницы интервалов:\n")
## 
## Границы интервалов:
print(
  discretize(
    z,
    method = "frequency",
    breaks = 3,
    onlycuts = TRUE
  )
)
## [1] 4.3 5.4 6.3 7.9

4.3 3.3. Метод cluster

Метод cluster использует кластеризацию k-means для определения границ.

set.seed(123)

d_cluster <- discretize(
  z,
  method = "cluster",
  breaks = 3
)

cat("Количество объектов в категориях:\n")
## Количество объектов в категориях:
print(table(d_cluster))
## d_cluster
##  [4.3,5.37) [5.37,6.36)  [6.36,7.9] 
##          46          62          42
cat("\nГраницы интервалов:\n")
## 
## Границы интервалов:
set.seed(123)

print(
  discretize(
    z,
    method = "cluster",
    breaks = 3,
    onlycuts = TRUE
  )
)
## [1] 4.300000 5.371213 6.361482 7.900000

4.4 3.4. Метод fixed

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

Используем следующие интервалы:

  • меньше 5;
  • от 5 до 6;
  • больше либо равно 6.
d_fixed <- discretize(
  z,
  method = "fixed",
  breaks = c(-Inf, 5, 6, Inf),
  labels = c(
    "Низкое",
    "Среднее",
    "Высокое"
  )
)

cat("Количество объектов в категориях:\n")
## Количество объектов в категориях:
print(table(d_fixed))
## d_fixed
##  Низкое Среднее Высокое 
##      22      61      67

4.5 3.5. Сравнение результатов

discretization_result <- data.frame(
  Original = z,
  Interval = d_interval,
  Frequency = d_frequency,
  Cluster = d_cluster,
  Fixed = d_fixed
)

print(head(discretization_result, 20))
##    Original  Interval Frequency     Cluster   Fixed
## 1       5.1 [4.3,5.5) [4.3,5.4)  [4.3,5.37) Среднее
## 2       4.9 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 3       4.7 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 4       4.6 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 5       5.0 [4.3,5.5) [4.3,5.4)  [4.3,5.37) Среднее
## 6       5.4 [4.3,5.5) [5.4,6.3) [5.37,6.36) Среднее
## 7       4.6 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 8       5.0 [4.3,5.5) [4.3,5.4)  [4.3,5.37) Среднее
## 9       4.4 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 10      4.9 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 11      5.4 [4.3,5.5) [5.4,6.3) [5.37,6.36) Среднее
## 12      4.8 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 13      4.8 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 14      4.3 [4.3,5.5) [4.3,5.4)  [4.3,5.37)  Низкое
## 15      5.8 [5.5,6.7) [5.4,6.3) [5.37,6.36) Среднее
## 16      5.7 [5.5,6.7) [5.4,6.3) [5.37,6.36) Среднее
## 17      5.4 [4.3,5.5) [5.4,6.3) [5.37,6.36) Среднее
## 18      5.1 [4.3,5.5) [4.3,5.4)  [4.3,5.37) Среднее
## 19      5.7 [5.5,6.7) [5.4,6.3) [5.37,6.36) Среднее
## 20      5.1 [4.3,5.5) [4.3,5.4)  [4.3,5.37) Среднее

Количество объектов в категориях:

cat("INTERVAL:\n")
## INTERVAL:
print(table(d_interval))
## d_interval
## [4.3,5.5) [5.5,6.7) [6.7,7.9] 
##        52        70        28
cat("\nFREQUENCY:\n")
## 
## FREQUENCY:
print(table(d_frequency))
## d_frequency
## [4.3,5.4) [5.4,6.3) [6.3,7.9] 
##        46        53        51
cat("\nCLUSTER:\n")
## 
## CLUSTER:
print(table(d_cluster))
## d_cluster
##  [4.3,5.37) [5.37,6.36)  [6.36,7.9] 
##          46          62          42
cat("\nFIXED:\n")
## 
## FIXED:
print(table(d_fixed))
## d_fixed
##  Низкое Среднее Высокое 
##      22      61      67

4.6 3.6. Визуальное сравнение границ

4.6.1 Interval

hist(
  z,
  breaks = 15,
  main = "Дискретизация: interval",
  xlab = "Sepal.Length"
)

abline(
  v = discretize(
    z,
    method = "interval",
    breaks = 3,
    onlycuts = TRUE
  ),
  lty = 2,
  lwd = 2
)

4.6.2 Frequency

hist(
  z,
  breaks = 15,
  main = "Дискретизация: frequency",
  xlab = "Sepal.Length"
)

abline(
  v = discretize(
    z,
    method = "frequency",
    breaks = 3,
    onlycuts = TRUE
  ),
  lty = 2,
  lwd = 2
)

4.6.3 Cluster

hist(
  z,
  breaks = 15,
  main = "Дискретизация: cluster",
  xlab = "Sepal.Length"
)

set.seed(123)

cluster_cuts <- discretize(
  z,
  method = "cluster",
  breaks = 3,
  onlycuts = TRUE
)

abline(
  v = cluster_cuts,
  lty = 2,
  lwd = 2
)

4.6.4 Fixed

hist(
  z,
  breaks = 15,
  main = "Дискретизация: fixed",
  xlab = "Sepal.Length"
)

abline(
  v = c(5, 6),
  lty = 2,
  lwd = 2
)

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

В работе были рассмотрены четыре метода дискретизации.

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

Метод frequency формирует интервалы таким образом, чтобы число наблюдений в них было примерно одинаковым.

Метод cluster использует кластеризацию k-means и определяет границы исходя из структуры имеющихся данных.

Метод fixed позволяет пользователю самостоятельно задавать необходимые границы интервалов.

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

5 4. Выбор признаков методом Boruta

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

data(Ozone)

cat("Размер исходного набора Ozone:\n")
## Размер исходного набора Ozone:
print(dim(Ozone))
## [1] 366  13
cat("\nПервые строки:\n")
## 
## Первые строки:
print(head(Ozone))
##   V1 V2 V3 V4   V5 V6 V7 V8    V9  V10 V11   V12 V13
## 1  1  1  4  3 5480  8 20 NA    NA 5000 -15 30.56 200
## 2  1  2  5  3 5660  6 NA 38    NA   NA -14    NA 300
## 3  1  3  6  3 5710  4 28 40    NA 2693 -25 47.66 250
## 4  1  4  7  5 5700  3 37 45    NA  590 -24 55.04 100
## 5  1  5  1  5 5760  3 51 54 45.32 1450  25 57.02  60
## 6  1  6  2  6 5720  4 69 35 49.64 1568  15 53.78  60

В наборе имеются пропущенные значения. Удалим строки, содержащие NA.

ozone_clean <- na.omit(Ozone)

cat("Размер набора после удаления NA:\n")
## Размер набора после удаления NA:
print(dim(ozone_clean))
## [1] 203  13
cat("\nКоличество удалённых строк:\n")
## 
## Количество удалённых строк:
print(nrow(Ozone) - nrow(ozone_clean))
## [1] 163

В соответствии с примером применения Boruta к набору Ozone, целевой переменной является V4.

5.1 4.1. Запуск алгоритма Boruta

set.seed(123)

boruta_result <- Boruta(
  V4 ~ .,
  data = ozone_clean,
  doTrace = 0,
  maxRuns = 100
)

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

5.2 4.2. Статистика важности

boruta_stats <- attStats(boruta_result)

boruta_stats <- boruta_stats[
  order(
    boruta_stats$meanImp,
    decreasing = TRUE
  ),
  ,
  drop = FALSE
]

print(boruta_stats)
##         meanImp   medianImp      minImp     maxImp   normHits  decision
## V9   0.86915268  0.86432645  0.84273725 0.92413075 1.00000000 Confirmed
## V8   0.77086858  0.76889989  0.73251688 0.81327051 1.00000000 Confirmed
## V12  0.66051279  0.65832222  0.60234621 0.73958611 1.00000000 Confirmed
## V11  0.55143690  0.54721914  0.49482770 0.61161304 1.00000000 Confirmed
## V7   0.51883205  0.50588169  0.46725744 0.59114026 1.00000000 Confirmed
## V13  0.42761454  0.42057856  0.38618408 0.46920282 1.00000000 Confirmed
## V10  0.42389058  0.41739463  0.38164901 0.49245967 1.00000000 Confirmed
## V1   0.40660229  0.39874710  0.37208192 0.50361585 1.00000000 Confirmed
## V5   0.37961385  0.37663333  0.33809868 0.42147363 1.00000000 Confirmed
## V6   0.04740489  0.05979747 -0.01572443 0.12718160 0.05555556  Rejected
## V2   0.04391890  0.03169822 -0.02604472 0.13137821 0.11111111  Rejected
## V3  -0.03235907 -0.04528594 -0.08039941 0.03178173 0.00000000  Rejected

5.3 4.3. Confirmed, Rejected и Tentative

stats_original <- attStats(boruta_result)

confirmed <- rownames(
  stats_original[
    stats_original$decision == "Confirmed",
    ,
    drop = FALSE
  ]
)

rejected <- rownames(
  stats_original[
    stats_original$decision == "Rejected",
    ,
    drop = FALSE
  ]
)

tentative <- rownames(
  stats_original[
    stats_original$decision == "Tentative",
    ,
    drop = FALSE
  ]
)

cat("CONFIRMED:\n")
## CONFIRMED:
print(confirmed)
## [1] "V1"  "V5"  "V7"  "V8"  "V9"  "V10" "V11" "V12" "V13"
cat("\nREJECTED:\n")
## 
## REJECTED:
print(rejected)
## [1] "V2" "V3" "V6"
cat("\nTENTATIVE:\n")
## 
## TENTATIVE:
print(tentative)
## character(0)

Если после выполнения алгоритма некоторые признаки остались неопределёнными (Tentative), применим TentativeRoughFix().

if (length(tentative) > 0) {
  
  boruta_final <- TentativeRoughFix(
    boruta_result
  )
  
} else {
  
  boruta_final <- boruta_result
  
}

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

5.4 4.4. Окончательно выбранные признаки

final_selected <- getSelectedAttributes(
  boruta_final,
  withTentative = FALSE
)

cat("Окончательно выбранные признаки:\n")
## Окончательно выбранные признаки:
print(final_selected)
## [1] "V1"  "V5"  "V7"  "V8"  "V9"  "V10" "V11" "V12" "V13"

5.5 4.5. Boxplot Boruta

plot(
  boruta_result,
  las = 2,
  cex.axis = 0.65,
  sort = TRUE,
  main = "Важность признаков Boruta для Ozone",
  xlab = "",
  ylab = "Importance"
)

5.6 4.6. Итоговая таблица

final_stats <- attStats(
  boruta_final
)

final_stats <- final_stats[
  order(
    final_stats$meanImp,
    decreasing = TRUE
  ),
  ,
  drop = FALSE
]

print(final_stats)
##         meanImp   medianImp      minImp     maxImp   normHits  decision
## V9   0.86915268  0.86432645  0.84273725 0.92413075 1.00000000 Confirmed
## V8   0.77086858  0.76889989  0.73251688 0.81327051 1.00000000 Confirmed
## V12  0.66051279  0.65832222  0.60234621 0.73958611 1.00000000 Confirmed
## V11  0.55143690  0.54721914  0.49482770 0.61161304 1.00000000 Confirmed
## V7   0.51883205  0.50588169  0.46725744 0.59114026 1.00000000 Confirmed
## V13  0.42761454  0.42057856  0.38618408 0.46920282 1.00000000 Confirmed
## V10  0.42389058  0.41739463  0.38164901 0.49245967 1.00000000 Confirmed
## V1   0.40660229  0.39874710  0.37208192 0.50361585 1.00000000 Confirmed
## V5   0.37961385  0.37663333  0.33809868 0.42147363 1.00000000 Confirmed
## V6   0.04740489  0.05979747 -0.01572443 0.12718160 0.05555556  Rejected
## V2   0.04391890  0.03169822 -0.02604472 0.13137821 0.11111111  Rejected
## V3  -0.03235907 -0.04528594 -0.08039941 0.03178173 0.00000000  Rejected

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

Алгоритм Boruta выполняет отбор информативных признаков посредством сравнения их важности с важностью случайных теневых признаков (Shadow).

Реальные признаки, важность которых достоверно превосходит важность теневых признаков, получают статус Confirmed.

Незначимые признаки получают статус Rejected, а признаки, для которых нельзя сразу принять однозначное решение, получают статус Tentative.

В результате работы Boruta подтверждены следующие признаки:

V1, V5, V7, V8, V9, V10, V11, V12, V13

Следовательно, подтверждённые алгоритмом Boruta признаки являются наиболее целесообразными для дальнейшего построения модели, прогнозирующей переменную V4.

6 Общий вывод

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

С помощью пакета caret и функции featurePlot() был выполнен графический разведочный анализ искусственно созданного набора данных. Поскольку признаки были сгенерированы независимо от классов A и B, явного разделения классов обнаружено не было.

При использовании пакета FSelector для набора iris установлено, что наиболее информативными для определения вида ириса являются Petal.Length и Petal.Width.

С помощью функции discretize() пакета arules были рассмотрены методы interval, frequency, cluster и fixed. Эти методы используют разные способы определения границ категорий.

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

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