Установка и загрузка пакетов:
library(caret)
library(FSelector)
library(arules)
library(Boruta)
library(mlbench)
Пакет 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
Вывод: на матрице диаграмм рассеяния видно, что точки двух классов распределены хаотично, без чёткого разделения. Это объясняется тем, что признаки были сгенерированы случайным образом. Поэтому выделить явные закономерности, позволяющие отличить один класс от другого, невозможно.
Определим важность признаков для классификации в датасете
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) менее информативные и могут быть отброшены при
построении модели, если требуется уменьшить её сложность.
Дискретизация позволяет преобразовать непрерывные переменные в категориальные для более простых моделей. Выполним дискретизацию переменной 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 – когда мы уже знаем, какие границы категорий имеют
смысл.
Алгоритм 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")
Вывод: график показал, что некоторые переменные являются статистически значимыми для предсказания уровня озона, в то время как другие могут быть исключены из модели. Это позволяет не только повысить точность прогноза, но и уменьшить вычислительную нагрузку при обучении моделей.