Установка и загрузка пакетов:

library(caret)
library(FSelector)
library(arules)
library(Boruta)
library(mlbench)

Задание 1: Пакет caret и графический анализ данных

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

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

names(getModelInfo())
##   [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"

Создадим матрицу случайных данных и вектор классов, затем проведем разведочный анализ данных с помощью featurePlot():

set.seed(123)
x <- matrix(rnorm(50 * 5), ncol = 5)
y <- factor(rep(c("A", "B"), 25))

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

Сохраним графики в .jpg:

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

Вывод: на матрице диаграмм рассеяния видно, что точки двух классов распределены хаотично, без чёткого разделения. Это объясняется тем, что признаки были сгенерированы случайным образом. Поэтому выделить явные закономерности, позволяющие отличить один класс от другого, невозможно.

Задание 2: Определение важности признаков с FSelector

Определим важность признаков для классификации в датасете iris с использованием метода information.gain, который вычисляет, насколько каждый признак уменьшает неопределённость при классификации.

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 ...
importance <- information.gain(Species ~ ., iris)
print(importance)
##              attr_importance
## Sepal.Length       0.4521286
## Sepal.Width        0.2672750
## Petal.Length       0.9402853
## Petal.Width        0.9554360

Визуализируем важность признаков:

barplot(importance$attr_importance, 
        names.arg = rownames(importance),
        main = "Важность признаков",
        col = "lightblue",
        ylab = "Information Gain")

Вывод: результаты анализа показывают, что наиболее значимыми характеристиками для классификации видов ирисов являются размеры лепестка (Petal.Length и Petal.Width). Они дают наибольший прирост информации, то есть лучше всего помогают различать виды. Признаки чашелистика (Sepal.Length и Sepal.Width) менее информативные и могут быть отброшены при построении модели, если требуется уменьшить её сложность.

Задание 3: Дискретизация данных с arules

Дискретизация позволяет преобразовать непрерывные переменные в категориальные для более простых моделей. Выполним дискретизацию переменной Sepal.Length в iris разными методами: «interval» (равная ширина интервала), «frequency» (равная частота), «cluster» (кластеризация) и «fixed» (заранее заданные границы).

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

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

set.seed(42)
iris$Sepal.Length.cluster <- as.factor(kmeans(iris$Sepal.Length, centers = 3)$cluster)

iris$Sepal.Length.fixed <- discretize(iris$Sepal.Length, 
                                      method = "fixed", breaks = c(4, 5.5, 6.5, 8))

# Построим гистограммы распределения категорий
par(mfrow = c(2, 2))
barplot(table(iris$Sepal.Length.interval), main = "Interval", col = "lightblue")
barplot(table(iris$Sepal.Length.frequency), main = "Frequency", col = "lightgreen")
barplot(table(iris$Sepal.Length.cluster), main = "Cluster", col = "lightcoral")
barplot(table(iris$Sepal.Length.fixed), main = "Fixed", col = "lightgray")

par(mfrow = c(1, 1))

Вывод: метод interval полезен, когда нужно равномерное разбиение на интервалы. Frequency помогает, если важно, чтобы группы содержали одинаковое количество наблюдений. Cluster полезен для обнаружения естественных групп, а fixed – когда мы уже знаем, какие границы категорий имеют смысл.

Задание 4: Выбор признаков с Boruta

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

Проведем выбор наиболее значимых признаков на наборе данных Ozone. Целевой переменной (target) будет уровень загрязнения озоном (V4), а остальные переменные будут рассматриваться как потенциальные факторы, влияющие на него:

data("Ozone")
# Удаляем пропущенные значения
Ozone <- na.omit(Ozone)
str(Ozone)
## '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" ...
set.seed(42)
boruta_result <- Boruta(V4 ~ ., data = Ozone, doTrace = 2)
print(boruta_result)
## Boruta performed 21 iterations in 11.03696 secs.
##  9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
##  3 attributes confirmed unimportant: V2, V3, V6;
# Построение boxplot по важности признаков
plot(boruta_result, las = 2, cex.axis = 0.7, 
     main = "Feature Importance by Boruta")

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