1 Цель работы

Исследовать графические инструменты CARET, оценить важность признаков iris методами FSelector, сравнить четыре способа дискретизации arules и выполнить отбор признаков Ozone алгоритмом Boruta. Все результаты ниже получены выполнением кода R. Фиксированные начальные значения генератора обеспечивают повторяемость случайных этапов в указанной версии среды.

2 Разведочный анализ данных CARET

names(getModelInfo()) возвращает идентификаторы моделей, поддерживаемых CARET. Это каталог моделей обучения, а не отдельный список алгоритмов отбора признаков. Для отбора признаков CARET также предоставляет RFE и фильтрацию SBF.

model_names <- names(caret::getModelInfo())
length(model_names)
## [1] 239
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"
writeLines(model_names, "results/caret_models.txt")

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

set.seed(2026)
x <- matrix(rnorm(50*5), ncol=5)
y <- factor(rep(c("A", "B"), 25))
colnames(x) <- paste0("X", 1:5)
table(y)
## y
##  A  B 
## 25 25
summary(x)
##        X1                X2               X3               X4               X5         
##  Min.   :-2.5469   Min.   :-1.959   Min.   :-1.739   Min.   :-1.859   Min.   :-2.5352  
##  1st Qu.:-0.7145   1st Qu.:-0.973   1st Qu.:-0.669   1st Qu.:-0.581   1st Qu.:-0.5155  
##  Median : 0.0532   Median :-0.246   Median : 0.234   Median : 0.103   Median : 0.0904  
##  Mean   :-0.0163   Mean   :-0.180   Mean   : 0.126   Mean   : 0.117   Mean   : 0.1032  
##  3rd Qu.: 0.6107   3rd Qu.: 0.448   3rd Qu.: 0.830   3rd Qu.: 0.611   3rd Qu.: 0.7252  
##  Max.   : 1.9081   Max.   : 2.414   Max.   : 2.639   Max.   : 2.181   Max.   : 2.1616
write.csv(data.frame(x, Class=y), "results/caret_data.csv", row.names=FALSE)
p_pairs <- caret::featurePlot(x=x, y=y, plot="pairs", auto.key=TRUE)
print(p_pairs)
Матрица диаграмм рассеяния featurePlot

Матрица диаграмм рассеяния featurePlot

save_plot("results/figures/01_caret_pairs.jpg", p_pairs)
p_box <- caret::featurePlot(x=x, y=y, plot="box", auto.key=TRUE)
print(p_box)
Диаграммы размаха признаков по классам

Диаграммы размаха признаков по классам

save_plot("results/figures/02_caret_box.jpg", p_box)
p_density <- caret::featurePlot(x=x, y=y, plot="density", auto.key=TRUE)
print(p_density)
Оценки плотности признаков по классам

Оценки плотности признаков по классам

save_plot("results/figures/03_caret_density.jpg", p_density)

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

3 Оценка важности признаков FSelector

Набор iris содержит 150 наблюдений, четыре количественных признака и три вида растений по 50 наблюдений. Целевая переменная — Species. Сопоставлены прирост информации, отношение прироста и симметричная неопределённость. Перед энтропийной оценкой непрерывные признаки обрабатываются внутренней процедурой FSelector. Шкалы трёх критериев различаются; сравнивать следует прежде всего порядок признаков внутри каждого критерия.

data(iris)
ig <- FSelector::information.gain(Species ~ ., iris)
gr <- FSelector::gain.ratio(Species ~ ., iris)
su <- FSelector::symmetrical.uncertainty(Species ~ ., iris)
importance <- data.frame(
  Feature=rownames(ig), InformationGain=ig[[1]],
  GainRatio=gr[rownames(ig),1],
  SymmetricalUncertainty=su[rownames(ig),1])
importance <- importance[order(-importance$InformationGain),]
knitr::kable(importance, digits=6)
Feature InformationGain GainRatio SymmetricalUncertainty
4 Petal.Width 0.955436 0.871369 0.870521
3 Petal.Length 0.940285 0.858494 0.857187
1 Sepal.Length 0.452129 0.419646 0.415556
2 Sepal.Width 0.267275 0.247297 0.245274
selected_iris <- FSelector::cutoff.k(ig, 2)
selected_iris
## [1] "Petal.Width"  "Petal.Length"
write.csv(importance, "results/iris_importance.csv", row.names=FALSE)
jpeg("results/figures/04_fselector.jpg",1800,1100,res=150,quality=95)
par(mar=c(5,8,3,1))
barplot(rev(importance$InformationGain),
        names.arg=rev(importance$Feature), horiz=TRUE, las=1,
        col="steelblue", xlab="Information gain", main="Iris feature importance")
dev.off()
## png 
##   2
knitr::include_graphics("results/figures/04_fselector.jpg")
Информационная важность признаков iris
Информационная важность признаков iris

Вывод. По приросту информации выбраны Petal.Width и Petal.Length. Наиболее высокие оценки получают характеристики лепестка. Это соответствует различиям между видами iris. Отбор выполнен на всём наборе и служит исследовательскому анализу; он не является независимой оценкой точности будущей модели. Для такой оценки отбор нужно повторять внутри обучающих частей разбиений.

4 Дискретизация непрерывной переменной arules

Исследуется Sepal.Length. Для методов interval, frequency и cluster задано три категории. Для fixed границы выбраны явно: минус бесконечность, 5.5, 6.5 и плюс бесконечность. Интервалы замкнуты слева и открыты справа; крайнее наблюдаемое значение включается. Параметр breaks используется вместо устаревшего categories.

z <- iris$Sepal.Length
set.seed(2026)
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,5.5,6.5,Inf)))
disc_table <- do.call(rbind, lapply(names(disc), function(m) {
 tab <- table(disc[[m]])
 data.frame(Method=m, Interval=names(tab), Count=as.integer(tab))
}))
knitr::kable(disc_table)
Method Interval Count
interval [4.3,5.5) 52
interval [5.5,6.7) 70
interval [6.7,7.9] 28
frequency [4.3,5.4) 46
frequency [5.4,6.3) 53
frequency [6.3,7.9] 51
cluster [4.3,5.45) 52
cluster [5.45,6.46) 63
cluster [6.46,7.9] 35
fixed [-Inf,5.5) 52
fixed [5.5,6.5) 63
fixed [6.5, Inf] 35
disc_breaks <- lapply(disc, function(d) attr(d,"discretized:breaks"))
disc_breaks
## $interval
## [1] 4.3 5.5 6.7 7.9
## 
## $frequency
## [1] 4.3 5.4 6.3 7.9
## 
## $cluster
## [1] 4.30000 5.45232 6.46111 7.90000
## 
## $fixed
## [1] -Inf  5.5  6.5  Inf
stopifnot(all(vapply(disc, function(d) !anyNA(d) && length(d)==150, logical(1))))
write.csv(disc_table,"results/discretization_counts.csv",row.names=FALSE)
write.csv(data.frame(iris, disc),"results/iris_discretized.csv",row.names=FALSE)
jpeg("results/figures/05_discretization.jpg",1800,1200,res=150,quality=95)
par(mfrow=c(2,2),mar=c(4,4,3,1))
for (m in names(disc)) {
 hist(z,breaks=seq(4,8,by=0.2),col="grey85",border="white",
      main=m,xlab="Sepal length (cm)")
 cuts <- disc_breaks[[m]]
 abline(v=cuts[is.finite(cuts)],col="firebrick",lwd=2,lty=2)
}
dev.off()
## png 
##   2
knitr::include_graphics("results/figures/05_discretization.jpg")
Сравнение границ четырёх методов дискретизации
Сравнение границ четырёх методов дискретизации

Вывод. Interval делит диапазон на равные по ширине части, но не выравнивает численность категорий. Frequency использует квантили; одинаковые значения мешают получить строго одинаковые частоты. Cluster применяет k-means и адаптирует границы к расположению наблюдений. Fixed задаёт интерпретируемые пороговые значения, выбранные исследователем. Все четыре метода являются неконтролируемыми: Species при расчёте границ не используется. Дискретизация упрощает представление, но теряет информацию о различиях внутри интервала.

5 Отбор признаков Ozone алгоритмом Boruta

Использован именно Ozone из пакета mlbench, а не airquality из datasets. Цель V4 — максимальная за день средняя часовая концентрация озона. Она числовая, поэтому выполняется регрессионный отбор признаков. Остальные 12 столбцов включены как потенциальные предикторы. Для воспроизводимой обработки пропусков исключены строки с хотя бы одним NA.

data("Ozone", package="mlbench")
original_n <- nrow(Ozone)
missing_by_variable <- colSums(is.na(Ozone))
ozone <- na.omit(Ozone)
data.frame(Original=original_n, Complete=nrow(ozone),
           Removed=original_n-nrow(ozone), Predictors=ncol(ozone)-1)
missing_by_variable
##  V1  V2  V3  V4  V5  V6  V7  V8  V9 V10 V11 V12 V13 
##   0   0   0   5  12   0  15   2 139  15   1  14   0
write.csv(Ozone,"results/ozone_original.csv",row.names=FALSE)
write.csv(ozone,"results/ozone_complete.csv",row.names=FALSE)
set.seed(2026)
boruta <- Boruta::Boruta(V4 ~ ., data=ozone, maxRuns=500,
                         pValue=0.01, mcAdj=TRUE, doTrace=0,
                         holdHistory=TRUE)
boruta
## Boruta performed 18 iterations in 0.213927 secs.
##  9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
##  3 attributes confirmed unimportant: V2, V3, V6;
boruta_stats <- Boruta::attStats(boruta)
boruta_stats$Feature <- rownames(boruta_stats)
boruta_stats <- boruta_stats[order(-boruta_stats$medianImp),]
knitr::kable(boruta_stats[,c("Feature","medianImp","meanImp","decision")],digits=4)
Feature medianImp meanImp decision
V9 V9 0.8781 0.8728 Confirmed
V8 V8 0.7853 0.7795 Confirmed
V12 V12 0.6577 0.6554 Confirmed
V11 V11 0.5555 0.5448 Confirmed
V7 V7 0.5303 0.5220 Confirmed
V10 V10 0.4355 0.4408 Confirmed
V1 V1 0.4160 0.4134 Confirmed
V13 V13 0.3991 0.4027 Confirmed
V5 V5 0.3829 0.3860 Confirmed
V2 V2 0.0408 0.0322 Rejected
V6 V6 0.0087 0.0206 Rejected
V3 V3 -0.0617 -0.0467 Rejected
selected_ozone <- Boruta::getSelectedAttributes(boruta, withTentative=FALSE)
selected_ozone
## [1] "V1"  "V5"  "V7"  "V8"  "V9"  "V10" "V11" "V12" "V13"
write.csv(boruta_stats,"results/boruta_importance.csv",row.names=FALSE)
saveRDS(boruta,"results/boruta_model.rds")
jpeg("results/figures/06_boruta.jpg",2100,1400,res=170,quality=95)
plot(boruta,las=2,cex.axis=0.8,xlab="",main="Boruta feature selection for Ozone")
dev.off()
## png 
##   2
knitr::include_graphics("results/figures/06_boruta.jpg")
Boxplot важности признаков Ozone и теневых признаков Boruta
Boxplot важности признаков Ozone и теневых признаков Boruta

После удаления пропусков осталось 203 наблюдений из 366, исключено 163 строк. Подтверждённые признаки: V1, V5, V7, V8, V9, V10, V11, V12, V13. Решения алгоритма: Tentative 0; Confirmed 9; Rejected 3.

Вывод. Boruta сравнивает важность исходных переменных с важностью теневых признаков, полученных перестановкой значений. Зелёные коробки соответствуют подтверждённым переменным, красные — отклонённым, жёлтые — неопределённым, синие — теневым. Подтверждённость означает значимость относительно такого контрольного уровня, а не причинное влияние на озон. Boruta ищет все релевантные признаки и не обязана выдавать минимальный набор. Удаление пропущенных строк может сместить выборку; вывод относится к полным наблюдениям.

6 Общие выводы

Для случайных данных CARET не выявлено устойчивого визуального разделения классов. FSelector выделил измерения лепестка iris как наиболее информативные. Четыре метода arules дали разные границы категорий и разную численность интервалов. Boruta выполнила отбор для регрессионной задачи Ozone с явной обработкой пропусков и сравнением с теневыми переменными. Все графики сохранены как JPG, числовые результаты — как CSV, объект Boruta — как RDS.

7 Источники

  1. CARET — Visualizations.
  2. FSelector — Entropy-based filters.
  3. arules — discretize.
  4. mlbench — Ozone.
  5. Boruta — пакет CRAN.

8 Среда выполнения

versions <- vapply(c("caret","FSelector","arules","Boruta","mlbench"),
                   function(p) as.character(packageVersion(p)), character(1))
versions
##     caret FSelector    arules    Boruta   mlbench 
##   "7.0.1"    "0.34"  "1.7.15"  "10.0.0"  "2.1.11"
summary_data <- list(model_count=length(model_names), importance=importance,
 selected_iris=selected_iris, discretization=disc_table, cuts=disc_breaks,
 ozone_original=original_n, ozone_complete=nrow(ozone),
 selected_ozone=selected_ozone, boruta_stats=boruta_stats,
 decisions=as.list(table(boruta_stats$decision)), versions=as.list(versions))
jsonlite::write_json(summary_data,"results/summary.json",pretty=TRUE,auto_unbox=TRUE)
writeLines(capture.output(sessionInfo()),"results/session_info.txt")
sessionInfo()
## R version 4.6.1 (2026-06-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=English_United States.utf8  LC_CTYPE=English_United States.utf8   
## [3] LC_MONETARY=English_United States.utf8 LC_NUMERIC=C                          
## [5] LC_TIME=English_United States.utf8    
## 
## time zone: Europe/Moscow
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] mlbench_2.1-11 Boruta_10.0.0  arules_1.7.15  Matrix_1.7-5   FSelector_0.34
## [6] caret_7.0-1    lattice_0.22-9 ggplot2_4.0.3 
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6         xfun_0.62            bslib_0.12.0         recipes_1.4.0       
##  [5] rJava_1.0-18         vctrs_0.7.3          tools_4.6.1          generics_0.1.4      
##  [9] stats4_4.6.1         parallel_4.6.1       fru_1.0.0            tibble_3.3.1        
## [13] pkgconfig_2.0.3      ModelMetrics_1.2.2.2 data.table_1.18.6.1  RColorBrewer_1.1-3  
## [17] S7_0.2.2             lifecycle_1.0.5      compiler_4.6.1       farver_2.1.2        
## [21] stringr_1.6.0        codetools_0.2-20     htmltools_0.5.9      class_7.3-23        
## [25] sass_0.4.10          yaml_2.3.12          prodlim_2026.03.11   pillar_1.11.1       
## [29] jquerylib_0.1.4      MASS_7.3-65          entropy_1.3.2        cachem_1.1.0        
## [33] gower_1.0.2          iterators_1.0.14     rpart_4.1.27         foreach_1.5.2       
## [37] nlme_3.1-169         parallelly_1.48.0    lava_1.9.3           tidyselect_1.2.1    
## [41] digest_0.6.39        stringi_1.8.9        future_1.80.0        dplyr_1.2.1         
## [45] reshape2_1.4.5       purrr_1.2.2          listenv_1.1.0        RWekajars_3.9.3-2   
## [49] splines_4.6.1        fastmap_1.2.0        grid_4.6.1           cli_3.6.6           
## [53] magrittr_2.0.5       randomForest_4.7-1.2 survival_3.8-6       future.apply_1.21.0 
## [57] withr_3.0.3          scales_1.4.0         lubridate_1.9.5      timechange_0.4.0    
## [61] rmarkdown_2.32       globals_0.20.0       otel_0.2.0           nnet_7.3-20         
## [65] RWeka_0.4-50         timeDate_4052.112    evaluate_1.0.5       knitr_1.52          
## [69] hardhat_1.4.3        rlang_1.3.0          Rcpp_1.1.2           glue_1.8.1          
## [73] pROC_1.19.1          ipred_0.9-16         jsonlite_2.0.0       R6_2.6.1            
## [77] plyr_1.8.9