1. Графический анализ с CARET

Задание. Получить список методов names(getModelInfo()), исследовать данные из задания с помощью featurePlot() и сохранить графики в JPG.

library(caret)
models <- names(getModelInfo())
length(models)
## [1] 239
models
##   [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() и описываются функциями caretFuncs, rfFuncs, treebagFuncs, ldaFuncs и другими. Поэтому список getModelInfo() не следует трактовать как список только методов отбора признаков.

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

for (type in c("pairs", "box", "density", "strip")) {
  p <- featurePlot(x, y, plot = type)
  jpeg(file.path("jpg", paste0("caret_", type, ".jpg")),
       width = 1600, height = 1200, res = 150, quality = 95)
  print(p)
  dev.off()
}
Рисунок 1 — Попарные диаграммы признаков
Рисунок 1 — Попарные диаграммы признаков
Рисунок 2 — Диаграммы размаха по классам
Рисунок 2 — Диаграммы размаха по классам
Рисунок 3 — Плотности распределения признаков
Рисунок 3 — Плотности распределения признаков
Рисунок 4 — Наблюдения по классам
Рисунок 4 — Наблюдения по классам

Вывод. Классы A и B на попарных графиках перемешаны. Диаграммы размаха и плотности показывают значительное перекрытие распределений. Это ожидаемо: все пять признаков сгенерированы независимо от меток классов, поэтому устойчивого разделения по ним нет.

2. Оценка важности признаков набора iris

Задание. Оценить важность признаков для классификации видов ириса функциями пакета FSelector.

data(iris)
set.seed(123)
ig <- FSelector::information.gain(Species ~ ., iris)
gr <- FSelector::gain.ratio(Species ~ ., iris)
rel <- FSelector::relief(Species ~ ., iris,
                         neighbours.count = 5, sample.size = 150)
importance <- data.frame(
  Feature = rownames(ig),
  InformationGain = ig[, 1],
  GainRatio = gr[rownames(ig), 1],
  ReliefF = rel[rownames(ig), 1],
  row.names = NULL
)
knitr::kable(importance, digits = 4,
             caption = "Таблица 1 — Важность признаков iris")
Таблица 1 — Важность признаков iris
Feature InformationGain GainRatio ReliefF
Sepal.Length 0.4521 0.4196 0.1490
Sepal.Width 0.2673 0.2473 0.1486
Petal.Length 0.9403 0.8585 0.3469
Petal.Width 0.9554 0.8714 0.3462

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

3. Дискретизация длины лепестка

Задание. Преобразовать непрерывный признак Petal.Length в категории методами interval, frequency, cluster и fixed пакета arules.

z <- iris$Petal.Length
set.seed(123)
disc <- list(
  interval = arules::discretize(z, method = "interval", breaks = 3),
  frequency = arules::discretize(z, method = "frequency", breaks = 3),
  cluster = arules::discretize(z, method = "cluster", breaks = 3,
                              nstart = 25),
  fixed = arules::discretize(z, method = "fixed",
                             breaks = c(-Inf, 2.5, 5, Inf))
)
discretization_table <- do.call(rbind, lapply(names(disc), function(method) {
  counts <- table(disc[[method]])
  data.frame(Method = method, Interval = names(counts),
             Count = as.integer(counts))
}))
knitr::kable(discretization_table,
             caption = "Таблица 2 — Результаты дискретизации")
Таблица 2 — Результаты дискретизации
Method Interval Count
interval [1,2.97) 50
interval [2.97,4.93) 54
interval [4.93,6.9] 46
frequency [1,2.63) 50
frequency [2.63,4.9) 49
frequency [4.9,6.9] 51
cluster [1,2.88) 50
cluster [2.88,4.96) 54
cluster [4.96,6.9] 46
fixed [-Inf,2.5) 50
fixed [2.5,5) 54
fixed [5, Inf] 46

Вывод. interval задаёт три интервала равной ширины, а frequency стремится получить примерно равное число наблюдений в каждой категории. Совпадающие значения не разделяются между категориями, поэтому частоты могут немного различаться. cluster определяет границы по структуре данных; fixed использует заданные вручную границы 2,5 и 5. Разные способы задания границ могут дать одинаковую численность категорий.

4. Отбор признаков набора Ozone методом Boruta

Задание. Применить пакет Boruta к набору Ozone, построить boxplot важности признаков и сделать выводы.

data("Ozone", package = "mlbench")
ozone <- na.omit(Ozone)
cat("Наблюдений до удаления пропусков:", nrow(Ozone), "\n")
## Наблюдений до удаления пропусков: 366
cat("Наблюдений после удаления пропусков:", nrow(ozone), "\n")
## Наблюдений после удаления пропусков: 203
set.seed(123)
result <- Boruta::Boruta(V4 ~ ., data = ozone,
                         getImp = Boruta::getImpRfZ,
                         maxRuns = 300)
stats <- Boruta::attStats(result)
boruta_table <- data.frame(Feature = rownames(stats),
                           MedianImportance = stats$medianImp,
                           Decision = stats$decision,
                           row.names = NULL)
knitr::kable(boruta_table, digits = 3,
             caption = "Таблица 3 — Результаты Boruta")
Таблица 3 — Результаты Boruta
Feature MedianImportance Decision
V1 9.707 Confirmed
V2 1.158 Rejected
V3 -0.733 Rejected
V5 9.231 Confirmed
V6 1.362 Rejected
V7 11.517 Confirmed
V8 17.226 Confirmed
V9 19.063 Confirmed
V10 9.727 Confirmed
V11 11.848 Confirmed
V12 14.610 Confirmed
V13 9.549 Confirmed
cat("Выбранные признаки:",
    paste(Boruta::getSelectedAttributes(result), collapse = ", "), "\n")
## Выбранные признаки: V1, V5, V7, V8, V9, V10, V11, V12, V13
jpeg("jpg/boruta_ozone.jpg", width = 1800, height = 1300,
     res = 150, quality = 95)
par(mar = c(8, 4, 3, 1))
plot(result, las = 2, cex.axis = 0.8, xlab = "",
     ylab = "Importance")
dev.off()
## png 
##   2
Рисунок 5 — Boxplot важности признаков Ozone
Рисунок 5 — Boxplot важности признаков Ozone

Вывод. После удаления строк с пропущенными значениями анализируется 203 наблюдения из 366. Boruta подтвердил V1, V5, V7, V8, V9, V10, V11, V12 и V13; V2, V3 и V6 отклонены. Неопределённых решений не осталось. Наибольшая медианная важность у V9 (19,063), V8 (17,226) и V12 (14,610). Зелёные boxplot соответствуют подтверждённым признакам, красные — отклонённым, синие — искусственным теневым признакам для сравнения.