Цель работы — выполнить графический разведочный анализ, сравнить
оценки важности признаков для классификации, изучить способы
дискретизации и применить алгоритм Boruta для поиска значимых признаков.
Все таблицы и рисунки ниже получены выполнением кода R. Начальное
состояние генератора случайных чисел задано через
set.seed.
В работе используются три набора данных: случайные признаки двух
классов, iris и Ozone из пакета
mlbench. Для iris решается задача
классификации вида растения. В Ozone целевая переменная
V4 числовая, поэтому Boruta применяется к задаче
регрессии.
Для повторного выполнения необходимы R и следующие пакеты. Пакет
FSelector использует RWeka и
rJava; требуется 64-разрядная Java, совместимая с
установленной R. Для сборки HTML также требуется Pandoc (он поставляется
вместе с RStudio).
Выполнена команда, указанная в задании.
## [1] 239
## [1] "ada" "AdaBag" "AdaBoost.M1" "adaboost"
## [5] "amdai" "ANFIS" "avNNet" "awnb"
## [9] "awtan" "bag" "bagEarth" "bagEarthGCV"
## [13] "bagFDA" "bagFDAGCV" "bam" "bartMachine"
## [17] "bayesglm" "binda" "blackboost" "blasso"
## [21] "blassoAveraged" "bridge" "brnn" "BstLm"
## [25] "bstSm" "bstTree" "C5.0" "C5.0Cost"
## [29] "C5.0Rules" "C5.0Tree" "cforest" "chaid"
## [33] "CSimca" "ctree" "ctree2" "cubist"
## [37] "dda" "deepboost" "DENFIS" "dnn"
## [41] "dwdLinear" "dwdPoly" "dwdRadial" "earth"
## [45] "elm" "enet" "evtree" "extraTrees"
## [49] "fda" "FH.GBML" "FIR.DM" "foba"
## [53] "FRBCS.CHI" "FRBCS.W" "FS.HGD" "gam"
## [57] "gamboost" "gamLoess" "gamSpline" "gaussprLinear"
## [61] "gaussprPoly" "gaussprRadial" "gbm_h2o" "gbm"
## [65] "gcvEarth" "GFS.FR.MOGUL" "GFS.LT.RS" "GFS.THRIFT"
## [69] "glm.nb" "glm" "glmboost" "glmnet_h2o"
## [73] "glmnet" "glmStepAIC" "gpls" "hda"
## [77] "hdda" "hdrda" "HYFIS" "icr"
## [81] "J48" "JRip" "kernelpls" "kknn"
## [85] "knn" "krlsPoly" "krlsRadial" "lars"
## [89] "lars2" "lasso" "lda" "lda2"
## [93] "leapBackward" "leapForward" "leapSeq" "Linda"
## [97] "lm" "lmStepAIC" "LMT" "loclda"
## [101] "logicBag" "LogitBoost" "logreg" "lssvmLinear"
## [105] "lssvmPoly" "lssvmRadial" "lvq" "M5"
## [109] "M5Rules" "manb" "mda" "Mlda"
## [113] "mlp" "mlpKerasDecay" "mlpKerasDecayCost" "mlpKerasDropout"
## [117] "mlpKerasDropoutCost" "mlpML" "mlpSGD" "mlpWeightDecay"
## [121] "mlpWeightDecayML" "monmlp" "msaenet" "multinom"
## [125] "mxnet" "mxnetAdam" "naive_bayes" "nb"
## [129] "nbDiscrete" "nbSearch" "neuralnet" "nnet"
## [133] "nnls" "nodeHarvest" "null" "OneR"
## [137] "ordinalNet" "ordinalRF" "ORFlog" "ORFpls"
## [141] "ORFridge" "ORFsvm" "ownn" "pam"
## [145] "parRF" "PART" "partDSA" "pcaNNet"
## [149] "pcr" "pda" "pda2" "penalized"
## [153] "PenalizedLDA" "plr" "pls" "plsRglm"
## [157] "polr" "ppr" "pre" "PRIM"
## [161] "protoclass" "qda" "QdaCov" "qrf"
## [165] "qrnn" "randomGLM" "ranger" "rbf"
## [169] "rbfDDA" "Rborist" "rda" "regLogistic"
## [173] "relaxo" "rf" "rFerns" "RFlda"
## [177] "rfRules" "ridge" "rlda" "rlm"
## [181] "rmda" "rocc" "rotationForest" "rotationForestCp"
## [185] "rpart" "rpart1SE" "rpart2" "rpartCost"
## [189] "rpartScore" "rqlasso" "rqnc" "RRF"
## [193] "RRFglobal" "rrlda" "RSimca" "rvmLinear"
## [197] "rvmPoly" "rvmRadial" "SBC" "sda"
## [201] "sdwd" "simpls" "SLAVE" "slda"
## [205] "smda" "snn" "sparseLDA" "spikeslab"
## [209] "spls" "stepLDA" "stepQDA" "superpc"
## [213] "svmBoundrangeString" "svmExpoString" "svmLinear" "svmLinear2"
## [217] "svmLinear3" "svmLinearWeights" "svmLinearWeights2" "svmPoly"
## [221] "svmRadial" "svmRadialCost" "svmRadialSigma" "svmRadialWeights"
## [225] "svmSpectrumString" "tan" "tanSearch" "treebag"
## [229] "vbmpRadial" "vglmAdjCat" "vglmContRatio" "vglmCumulative"
## [233] "widekernelpls" "WM" "wsrf" "xgbDART"
## [237] "xgbLinear" "xgbTree" "xyf"
В данной версии CARET доступно 239 описаний моделей.
getModelInfo() возвращает сведения о методах обучения
моделей, а не отдельный перечень алгоритмов отбора признаков. Для отбора
признаков CARET также предоставляет rfe, sbf и
модельные оценки varImp. Примеры в списке: rf
— случайный лес, rpart — дерево решений,
svmRadial — метод опорных векторов и glm —
обобщённая линейная модель.
set.seed(20261014)
x <- matrix(rnorm(50 * 5), ncol=5)
colnames(x) <- paste0("X", 1:5)
y <- factor(rep(c("A", "B"), 25))
dim(x)## [1] 50 5
## y
## A B
## 25 25
## X1 X2 X3 X4 X5
## Min. :-1.6478 Min. :-1.691880 Min. :-3.30960 Min. :-2.3553 Min. :-3.02618
## 1st Qu.:-0.7297 1st Qu.:-0.887315 1st Qu.:-0.47271 1st Qu.:-0.8309 1st Qu.:-0.78187
## Median :-0.1968 Median : 0.001592 Median : 0.22525 Median :-0.3323 Median : 0.07242
## Mean :-0.1221 Mean :-0.125107 Mean : 0.04861 Mean :-0.2615 Mean :-0.11343
## 3rd Qu.: 0.5399 3rd Qu.: 0.613635 3rd Qu.: 0.73305 3rd Qu.: 0.4550 3rd Qu.: 0.51617
## Max. : 1.8898 Max. : 1.786271 Max. : 2.24731 Max. : 1.3352 Max. : 2.23997
Создано 50 наблюдений и пять непрерывных признаков. В каждом классе по 25 наблюдений. Метки классов заданы независимо от генерации признаков; систематическая связь между ними не заложена.
save_feature_plot <- function(kind, file, ...) {
jpeg(file, width=1800, height=1400, res=180, quality=95)
on.exit(dev.off())
result <- featurePlot(x=x, y=y, plot=kind, ...)
if (inherits(result, "trellis") || inherits(result, "ggplot")) print(result)
invisible(file)
}
save_feature_plot("box", "figures/caret_box.jpg")
save_feature_plot("density", "figures/caret_density.jpg",
auto.key=list(space="bottom", columns=2),
scales=list(x=list(relation="free"),
y=list(relation="free")))
save_feature_plot("pairs", "figures/caret_pairs.jpg",
auto.key=list(space="bottom", columns=2))
save_feature_plot("ellipse", "figures/caret_ellipse.jpg",
auto.key=list(space="bottom", columns=2))Диаграммы размаха признаков по классам
Плотности распределений по классам
Попарные диаграммы рассеяния
Попарные диаграммы с эллипсами
Диаграммы размаха и плотности позволяют сравнить положение и разброс распределений классов; матрицы рассеяния — проверить попарные связи и возможное разделение классов. На рисунках распределения A и B существенно перекрываются. Отдельные отличия средних или медиан в малой случайной выборке не означают, что признак имеет устойчивую предсказательную ценность. Для проверки качества классификации потребовалась бы независимая проверочная выборка или перекрёстная проверка. Все четыре рисунка сохранены в файлы JPG.
## '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 ...
##
## setosa versicolor virginica
## 50 50 50
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 0 0 0 0 0
Набор содержит 150 наблюдений: по 50 растений каждого из трёх видов.
Используются четыре числовых признака; пропущенных значений нет. Целевая
переменная Species — фактор.
Сравниваются четыре фильтра из FSelector: прирост
информации, отношение прироста информации, симметричная неопределённость
и важность случайного леса. Первые три оценивают связь признака с
классом на основе энтропии; последний использует модельный подход. Для
непрерывных признаков энтропийные фильтры применяют внутреннюю
дискретизацию, предусмотренную пакетом.
set.seed(20261014)
ig <- information.gain(Species ~ ., iris)
gr <- gain.ratio(Species ~ ., iris)
su <- symmetrical.uncertainty(Species ~ ., iris)
set.seed(20261014)
rf <- random.forest.importance(Species ~ ., iris)
importance <- data.frame(
Feature=rownames(ig),
InformationGain=ig[, 1],
GainRatio=gr[rownames(ig), 1],
SymmetricalUncertainty=su[rownames(ig), 1],
RandomForest=rf[rownames(ig), 1], row.names=NULL
)
importance <- importance[order(-importance$InformationGain), ]
rownames(importance) <- NULL
knitr::kable(importance, digits=5,
caption="Оценки важности четырьмя методами FSelector")| Feature | InformationGain | GainRatio | SymmetricalUncertainty | RandomForest |
|---|---|---|---|---|
| Petal.Width | 0.95544 | 0.87137 | 0.87052 | 47.53636 |
| Petal.Length | 0.94029 | 0.85849 | 0.85719 | 48.18096 |
| Sepal.Length | 0.45213 | 0.41965 | 0.41556 | 15.04162 |
| Sepal.Width | 0.26728 | 0.24730 | 0.24527 | 7.31904 |
ranks <- importance
ranks[-1] <- lapply(ranks[-1], function(z) rank(-z, ties.method="min"))
knitr::kable(ranks, caption="Ранги признаков внутри каждого метода 1 соответствует лучшему")| Feature | InformationGain | GainRatio | SymmetricalUncertainty | RandomForest |
|---|---|---|---|---|
| Petal.Width | 1 | 1 | 1 | 2 |
| Petal.Length | 2 | 2 | 2 | 1 |
| Sepal.Length | 3 | 3 | 3 | 3 |
| Sepal.Width | 4 | 4 | 4 | 4 |
## [1] "Petal.Width" "Petal.Length"
## [1] "Petal.Length" "Petal.Width"
par(mar=c(5, 10, 3, 1))
barplot(setNames(importance$InformationGain, importance$Feature),
horiz=TRUE, las=1, col="#4477AA", xlab="Information gain",
main="Iris: information gain")Важность признаков iris по приросту информации
Два лучших признака по приросту информации: Petal.Width, Petal.Length; по случайному лесу: Petal.Length, Petal.Width. Размеры лепестка обычно лучше различают виды iris, чем размеры чашелистика; это отражено в полученных оценках и рангах. Числа разных методов имеют разные шкалы, поэтому сравнивать их абсолютные значения между столбцами нельзя. Отбор по важности не заменяет проверку точности модели. Если отбор используется в оценке качества классификатора, его необходимо выполнять внутри обучающих частей перекрёстной проверки, чтобы избежать утечки информации.
Преобразованы все четыре непрерывных признака iris. Для методов
interval, frequency и cluster
задано по три категории. Метод fixed использует заранее
указанные границы. Species не участвует в определении
границ.
features <- names(iris)[1:4]
fixed_breaks <- list(
Sepal.Length=c(-Inf, 5.5, 6.5, Inf),
Sepal.Width=c(-Inf, 2.8, 3.5, Inf),
Petal.Length=c(-Inf, 2, 5, Inf),
Petal.Width=c(-Inf, 0.6, 1.8, Inf)
)
methods <- c("interval", "frequency", "cluster", "fixed")
disc <- setNames(vector("list", length(methods)), methods)
for (method in methods) {
set.seed(20261014)
disc[[method]] <- setNames(lapply(features, function(feature) {
if (method == "fixed") {
discretize(iris[[feature]], method="fixed",
breaks=fixed_breaks[[feature]])
} else {
discretize(iris[[feature]], method=method, breaks=3)
}
}), features)
}
disc_tables <- do.call(rbind, lapply(methods, function(method) {
do.call(rbind, lapply(features, function(feature) {
freq <- table(disc[[method]][[feature]])
data.frame(Method=method, Feature=feature,
Interval=names(freq), Count=as.integer(freq))
}))
}))
stopifnot(!anyNA(disc_tables),
all(aggregate(Count ~ Method + Feature, disc_tables, sum)$Count == 150))
knitr::kable(disc_tables, caption="Границы категорий и число наблюдений")| Method | Feature | Interval | Count |
|---|---|---|---|
| interval | Sepal.Length | [4.3,5.5) | 52 |
| interval | Sepal.Length | [5.5,6.7) | 70 |
| interval | Sepal.Length | [6.7,7.9] | 28 |
| interval | Sepal.Width | [2,2.8) | 47 |
| interval | Sepal.Width | [2.8,3.6) | 88 |
| interval | Sepal.Width | [3.6,4.4] | 15 |
| interval | Petal.Length | [1,2.97) | 50 |
| interval | Petal.Length | [2.97,4.93) | 54 |
| interval | Petal.Length | [4.93,6.9] | 46 |
| interval | Petal.Width | [0.1,0.9) | 50 |
| interval | Petal.Width | [0.9,1.7) | 52 |
| interval | Petal.Width | [1.7,2.5] | 48 |
| frequency | Sepal.Length | [4.3,5.4) | 46 |
| frequency | Sepal.Length | [5.4,6.3) | 53 |
| frequency | Sepal.Length | [6.3,7.9] | 51 |
| frequency | Sepal.Width | [2,2.9) | 47 |
| frequency | Sepal.Width | [2.9,3.2) | 47 |
| frequency | Sepal.Width | [3.2,4.4] | 56 |
| frequency | Petal.Length | [1,2.63) | 50 |
| frequency | Petal.Length | [2.63,4.9) | 49 |
| frequency | Petal.Length | [4.9,6.9] | 51 |
| frequency | Petal.Width | [0.1,0.867) | 50 |
| frequency | Petal.Width | [0.867,1.6) | 48 |
| frequency | Petal.Width | [1.6,2.5] | 52 |
| cluster | Sepal.Length | [4.3,5.33) | 46 |
| cluster | Sepal.Length | [5.33,6.27) | 53 |
| cluster | Sepal.Length | [6.27,7.9] | 51 |
| cluster | Sepal.Width | [2,2.75) | 33 |
| cluster | Sepal.Width | [2.75,3.29) | 74 |
| cluster | Sepal.Width | [3.29,4.4] | 43 |
| cluster | Petal.Length | [1,2.88) | 50 |
| cluster | Petal.Length | [2.88,4.96) | 54 |
| cluster | Petal.Length | [4.96,6.9] | 46 |
| cluster | Petal.Width | [0.1,0.785) | 50 |
| cluster | Petal.Width | [0.785,1.69) | 52 |
| cluster | Petal.Width | [1.69,2.5] | 48 |
| fixed | Sepal.Length | [-Inf,5.5) | 52 |
| fixed | Sepal.Length | [5.5,6.5) | 63 |
| fixed | Sepal.Length | [6.5, Inf] | 35 |
| fixed | Sepal.Width | [-Inf,2.8) | 33 |
| fixed | Sepal.Width | [2.8,3.5) | 92 |
| fixed | Sepal.Width | [3.5, Inf] | 25 |
| fixed | Petal.Length | [-Inf,2) | 50 |
| fixed | Petal.Length | [2,5) | 54 |
| fixed | Petal.Length | [5, Inf] | 46 |
| fixed | Petal.Width | [-Inf,0.6) | 49 |
| fixed | Petal.Width | [0.6,1.8) | 55 |
| fixed | Petal.Width | [1.8, Inf] | 46 |
example <- data.frame(Petal.Length=iris$Petal.Length)
for (method in methods) example[[method]] <- disc[[method]]$Petal.Length
knitr::kable(head(example, 12), caption="Пример преобразования длины лепестка")| Petal.Length | interval | frequency | cluster | fixed |
|---|---|---|---|---|
| 1.4 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.4 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.3 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.5 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.4 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.7 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.4 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.5 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.4 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.5 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.5 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
| 1.6 | [1,2.97) | [1,2.63) | [1,2.88) | [-Inf,2) |
for (method in methods) {
cat("\nMethod:", method, "\n")
print(table(Category=disc[[method]]$Petal.Length, Species=iris$Species))
}##
## Method: interval
## Species
## Category setosa versicolor virginica
## [1,2.97) 50 0 0
## [2.97,4.93) 0 48 6
## [4.93,6.9] 0 2 44
##
## Method: frequency
## Species
## Category setosa versicolor virginica
## [1,2.63) 50 0 0
## [2.63,4.9) 0 46 3
## [4.9,6.9] 0 4 47
##
## Method: cluster
## Species
## Category setosa versicolor virginica
## [1,2.88) 50 0 0
## [2.88,4.96) 0 48 6
## [4.96,6.9] 0 2 44
##
## Method: fixed
## Species
## Category setosa versicolor virginica
## [-Inf,2) 50 0 0
## [2,5) 0 48 6
## [5, Inf] 0 2 44
par(mfrow=c(2, 2), mar=c(5, 4, 3, 1))
for (method in methods) {
barplot(table(disc[[method]]$Petal.Length), col="#66AABB",
main=method, ylab="Number of observations", las=1, cex.names=0.85)
}Численность категорий длины лепестка для четырёх способов дискретизации
interval делит диапазон на интервалы одинаковой ширины.
Количество наблюдений в них зависит от формы распределения и может
различаться.frequency определяет границы по квантилям и стремится
получить группы одинаковой численности. Из-за совпадающих значений
границы не обязаны обеспечивать ровно по 50 наблюдений.cluster использует кластеризацию k-means. Границы
отражают структуру числовых значений, но ширина и численность групп не
фиксированы.fixed позволяет задать содержательные пороги. Значения
-Inf и Inf обеспечивают покрытие всей числовой
шкалы; внутренние границы нужно выбирать исходя из задачи.Таблицы показывают, что методы могут присваивать одному значению разные категории. Дискретизация облегчает использование категориальных методов, но теряет информацию о различиях внутри интервала. При обучении модели границы следует определять на обучающих данных и затем применять без пересчёта к проверочным наблюдениям.
## [1] 366 13
## '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 ...
missing_by_column <- colSums(is.na(Ozone))
knitr::kable(data.frame(Feature=names(missing_by_column),
Missing=as.integer(missing_by_column)),
caption="Пропуски в исходном Ozone")| Feature | Missing |
|---|---|
| V1 | 0 |
| V2 | 0 |
| V3 | 0 |
| V4 | 5 |
| V5 | 12 |
| V6 | 0 |
| V7 | 15 |
| V8 | 2 |
| V9 | 139 |
| V10 | 15 |
| V11 | 1 |
| V12 | 14 |
| V13 | 0 |
ozo <- na.omit(Ozone)
data.frame(OriginalRows=nrow(Ozone), CompleteRows=nrow(ozo),
RemovedRows=nrow(Ozone)-nrow(ozo), Predictors=ncol(ozo)-1)## OriginalRows CompleteRows RemovedRows Predictors
## 1 366 203 163 12
V4 — максимальное за день среднее часовое значение
озона. Другие столбцы — календарные и метеорологические признаки. Их
смысл приведён по справке mlbench::Ozone.
| Столбец | Переменная |
|---|---|
| V1 | Месяц |
| V2 | День месяца |
| V3 | День недели |
| V5 | Высота поверхности давления 500 мбар, Vandenberg, м |
| V6 | Скорость ветра, LAX, мили в час |
| V7 | Влажность, LAX, проценты |
| V8 | Температура, Sandburg, градусы F |
| V9 | Температура, El Monte, градусы F |
| V10 | Высота основания инверсии, LAX, футы |
| V11 | Градиент давления между LAX и Daggett, мм ртутного столба |
| V12 | Температура основания инверсии, LAX, градусы F |
| V13 | Видимость, LAX, мили |
После исключения строк с пропусками остаётся 203 наблюдений из 366,
удалено 163 строк. Используются 12 предикторов; V4
исключена из их состава с помощью формулы V4 ~ .. Удаление
неполных строк сокращает выборку и может влиять на представительность
результата.
Boruta сравнивает важности исходных признаков с важностями их
случайно перемешанных копий — теневых признаков. Он ищет все релевантные
признаки, а не обязательно минимальный набор. Confirmed
означает подтверждённую важность, Rejected — отклонение,
Tentative — неопределённый результат. Использован адаптер
getImpLegacyRfZ на основе randomForest: 500
деревьев на итерацию, не более 300 итераций, уровень 0,01 с поправкой на
множественные сравнения. История важностей сохранена для построения
boxplot.
set.seed(20261014)
bor <- Boruta(V4 ~ ., data=ozo, maxRuns=300, pValue=0.01,
mcAdj=TRUE, doTrace=0, holdHistory=TRUE,
getImp=getImpLegacyRfZ, ntree=500)
print(bor)## Boruta performed 18 iterations in 5.248606 secs.
## 9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
## 3 attributes confirmed unimportant: V2, V3, V6;
##
## Tentative Confirmed Rejected
## 0 9 3
# Если остались Tentative, применяется отдельный приближённый шаг.
bor_final <- if (any(bor$finalDecision == "Tentative")) {
TentativeRoughFix(bor)
} else bor
stats <- attStats(bor_final)
stats <- data.frame(Feature=rownames(stats), stats, row.names=NULL)
stats <- stats[order(-stats$meanImp), ]
rownames(stats) <- NULL
knitr::kable(stats, digits=4, caption="Статистика важности и решения Boruta")| Feature | meanImp | medianImp | minImp | maxImp | normHits | decision |
|---|---|---|---|---|---|---|
| V9 | 19.8807 | 19.9803 | 18.3050 | 21.3997 | 1.0000 | Confirmed |
| V8 | 17.7926 | 17.7609 | 16.8414 | 19.6099 | 1.0000 | Confirmed |
| V12 | 13.7231 | 13.4673 | 12.5726 | 16.1614 | 1.0000 | Confirmed |
| V1 | 12.3035 | 11.8965 | 10.3993 | 15.3417 | 1.0000 | Confirmed |
| V11 | 9.3628 | 8.9674 | 6.5233 | 12.3176 | 1.0000 | Confirmed |
| V10 | 8.5730 | 8.7061 | 5.9855 | 10.1287 | 1.0000 | Confirmed |
| V7 | 8.4847 | 8.2226 | 6.4653 | 11.1574 | 1.0000 | Confirmed |
| V5 | 7.0131 | 6.9439 | 5.4010 | 9.4069 | 1.0000 | Confirmed |
| V13 | 6.6610 | 6.7154 | 5.0898 | 8.1693 | 1.0000 | Confirmed |
| V6 | 0.1705 | -0.0408 | -1.5071 | 2.4544 | 0.1111 | Rejected |
| V3 | -0.9016 | -0.8440 | -2.2833 | 0.0810 | 0.0000 | Rejected |
| V2 | -2.3523 | -2.3670 | -3.2686 | -0.8004 | 0.0000 | Rejected |
## [1] "V1" "V5" "V7" "V8" "V9" "V10" "V11" "V12" "V13"
par(mar=c(8, 4, 3, 1))
plot(bor, las=2, cex.axis=0.85, xlab="", ylab="Importance Z score",
main="Boruta: Ozone feature importance")Boxplot распределений важности Boruta и теневых признаков
На рисунке зелёным отмечены подтверждённые признаки, красным —
отклонённые, жёлтым — неопределённые; синим показаны теневые признаки.
Рисунок показывает исходное решение Boruta до возможного
TentativeRoughFix. Приближённое разрешение неопределённости
не тождественно полноценному статистическому подтверждению.
Подтверждённые признаки после окончательной обработки: V1, V5, V7,
V8, V9, V10, V11, V12, V13. Число подтверждённых признаков — 9. Исходное
число неопределённых признаков — 0. Дополнительный TentativeRoughFix не
потребовался. Самая высокая средняя важность получена у V9. В данном
запуске это температура в El Monte. Отклонены V2,
V3 и V6: день месяца, день недели и скорость
ветра. Это решение относится к данной выборке и настройкам алгоритма, а
не означает, что ветер вообще не связан с загрязнением воздуха.
Релевантность в Boruta означает, что признак даёт информацию сверх
случайного теневого контроля в выбранной модели. Она не доказывает
причинного влияния на уровень озона и не гарантирует качество прогноза
на будущих датах. Для такого прогноза нужна отдельная проверка с учётом
временного порядка наблюдений.
Выполнены все четыре вычислительных раздела работы. CARET использован для сравнения графических представлений признаков; четыре рисунка сохранены в JPG. Для iris получены оценки и ранги признаков четырьмя функциями FSelector. Применены четыре способа дискретизации ко всем непрерывным признакам iris и проверено сохранение числа наблюдений. Для Ozone выполнен отбор Boruta с учётом пропусков, построен boxplot и приведён список выбранных признаков.
Фильтры, дискретизация и Boruta решают разные задачи. Фильтры ранжируют связь признаков с целевой переменной, дискретизация меняет представление данных, Boruta проверяет релевантность относительно случайных признаков. Ни один из этих шагов сам по себе не является независимой оценкой качества модели.
## R version 4.6.1 (2026-06-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Russian_Russia.utf8 LC_CTYPE=Russian_Russia.utf8
## [3] LC_MONETARY=Russian_Russia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Russian_Russia.utf8
##
## time zone: Europe/Moscow
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] knitr_1.52 mlbench_2.1-11 Boruta_10.0.0 arules_1.7.15 Matrix_1.7-5 FSelector_0.34
## [7] caret_7.0-1 lattice_0.23-1 ggplot2_4.0.3
##
## loaded via a namespace (and not attached):
## [1] ellipse_0.5.0 gtable_0.3.6 xfun_0.61 bslib_0.12.0
## [5] recipes_1.4.0 rJava_1.0-18 vctrs_0.7.3 tools_4.6.1
## [9] generics_0.1.4 stats4_4.6.1 parallel_4.6.1 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.76.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.20.2
## [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 hardhat_1.4.3
## [69] rlang_1.3.0 Rcpp_1.1.2 glue_1.8.1 pROC_1.19.1
## [73] ipred_0.9-16 rstudioapi_0.19.0 jsonlite_2.0.0 R6_2.6.1
## [77] plyr_1.8.9