Установка необходимых пакетов (выполнить в обычной среде R, для FSelector из-за зависимостей от RWeka надо ставить Java 8 или выше): install.packages(c(“caret”, “FSelector”, “arules”, “Boruta”, “mlbench”, “ellipse”, “rmarkdown”, “knitr”))

Проверка установленных библиотек:

library(caret)
library(FSelector)
library(arules)
library(Boruta)
library(mlbench)
  1. caret и featurePlot

Список методов:

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"
length(names(getModelInfo()))
## [1] 239

Данные:

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

Графики:

  1. Создание графиков:
p_pairs <- featurePlot(x, y, plot = "pairs",
                       auto.key = list(columns = 2))

p_ellipse <- featurePlot(x, y, plot = "ellipse",
                         auto.key = list(columns = 2))

p_density <- featurePlot(x, y, plot = "density",
                         scales = list(x = list(relation = "free"),
                                       y = list(relation = "free")),
                         auto.key = list(columns = 2))

p_box <- featurePlot(x, y, plot = "box",
                     scales = list(y = list(relation = "free")),
                     auto.key = list(columns = 2))
  1. Вывод
print(p_pairs)

print(p_ellipse)

print(p_density)

print(p_box)

  1. Сохранение в jpg:
plots <- list(pairs = p_pairs, ellipse = p_ellipse,
              density = p_density, box = p_box)

for (name in names(plots)) {
  jpeg(paste0(name, ".jpg"), width = 900, height = 700)
  print(plots[[name]])
  dev.off()
}

list.files(pattern = "\\.jpg$")
## [1] "box.jpg"     "density.jpg" "ellipse.jpg" "pairs.jpg"

Признаки сгенерированы независимо из нормального распределения, а классы назначены без связи с ними, поэтому признаки не содержат систематической информации о классе. Графики в целом согласуются с этим: точки классов A и B перемешаны, эллипсы и кривые плотности частично перекрываются, а межквартильные интервалы на диаграммах размаха в большинстве случаев пересекаются. Небольшие различия в распределениях, например в медианах V1 и V4, скорее всего, объясняются случайными колебаниями в выборках по 25 наблюдений на класс.

  1. FSelector на iris
data(iris)
set.seed(123)

ig  <- information.gain(Species ~ ., iris)
gr  <- gain.ratio(Species ~ ., iris)
su  <- symmetrical.uncertainty(Species ~ ., iris)
chi <- chi.squared(Species ~ ., iris)
rf  <- random.forest.importance(Species ~ ., iris, importance.type = 1)
one <- oneR(Species ~ ., iris)
rel <- relief(Species ~ ., iris, neighbours.count = 5, sample.size = 20)

res <- data.frame(IG = ig[,1], GR = gr[,1], SU = su[,1], Chi = chi[,1],
                  RF = rf[,1], OneR = one[,1], Relief = rel[,1],
                  row.names = rownames(ig))
res
##                     IG        GR        SU       Chi        RF      OneR
## Sepal.Length 0.4521286 0.4196464 0.4155563 0.6288067 15.232793 0.1733333
## Sepal.Width  0.2672750 0.2472972 0.2452743 0.4922162  6.771835 0.0400000
## Petal.Length 0.9402853 0.8584937 0.8571872 0.9346311 47.478185 0.4000000
## Petal.Width  0.9554360 0.8713692 0.8705214 0.9432359 46.266486 0.4066667
##                 Relief
## Sepal.Length 0.1715278
## Sepal.Width  0.1335417
## Petal.Length 0.3528814
## Petal.Width  0.3597917
cutoff.k(ig, 2)
## [1] "Petal.Width"  "Petal.Length"
as.simple.formula(cutoff.k(ig, 2), "Species")
## Species ~ Petal.Width + Petal.Length
## <environment: 0x0000017aaaa59510>
barplot(ig[,1], names.arg = rownames(ig), main = "Information gain")

Все семь методов дают согласованный результат: наиболее важны признаки лепестка Petal.Width и Petal.Length, их веса примерно в 2–4 раза выше, чем у признаков чашелистика. Petal.Width лидирует в шести методах из семи, и только по случайному лесу (RF) Petal.Length чуть опережает его (47,5 против 46,3). Разница между этими двумя признаками везде невелика. Третье место у Sepal.Length, наименее информативен Sepal.Width, который оказывается последним во всех методах. Значит, для классификации ирисов достаточно двух признаков лепестка, что подтверждает и cutoff.k(ig, 2).

  1. Дискретизация (arules)
v <- iris$Petal.Length

d_int  <- discretize(v, method = "interval",  breaks = 3)
d_freq <- discretize(v, method = "frequency", breaks = 3)
d_clu  <- discretize(v, method = "cluster",   breaks = 3)
d_fix  <- discretize(v, method = "fixed", breaks = c(-Inf, 2.5, 5, Inf),
                     labels = c("small", "medium", "large"))

table(d_int); table(d_freq); table(d_clu); table(d_fix)
## d_int
##    [1,2.97) [2.97,4.93)  [4.93,6.9] 
##          50          54          46
## d_freq
##   [1,2.63) [2.63,4.9)  [4.9,6.9] 
##         50         49         51
## d_clu
##    [1,2.95) [2.95,5.13)  [5.13,6.9] 
##          50          66          34
## d_fix
##  small medium  large 
##     50     54     46
table(d_clu, iris$Species)
##              
## d_clu         setosa versicolor virginica
##   [1,2.95)        50          0         0
##   [2.95,5.13)      0         50        16
##   [5.13,6.9]       0          0        34

Гистограммы с границами интервалов:

par(mfrow = c(2, 2))
for (m in c("interval", "frequency", "cluster")) {
  hist(v, breaks = 20, main = m, xlab = "Petal.Length")
  abline(v = discretize(v, method = m, breaks = 3, onlycuts = TRUE),
         col = "red", lwd = 2)
}
hist(v, breaks = 20, main = "fixed", xlab = "Petal.Length")
abline(v = c(2.5, 5), col = "red", lwd = 2)

par(mfrow = c(1, 1))

Метод interval дал интервалы одинаковой ширины с разной численностью (50/54/46), frequency - почти равные группы (50/49/51) за счёт разной ширины интервалов, cluster - границы по структуре данных и неравные группы (50/66/34). У fixed с границами 2,5 и 5 распределение совпало с interval, так как в этих областях наблюдений почти нет. Первая граница у всех методов попадает в разрыв на гистограмме и отделяет ровно 50 setosa. Кластеры хорошо соответствуют видам: setosa и versicolor попали каждая в свой кластер целиком, а 16 virginica оказались вместе с versicolor, то есть совпадение составляет 134 из 150 (≈89%).

  1. Boruta на Ozone
data("Ozone")
Ozone <- na.omit(Ozone)

set.seed(42)
bor <- Boruta(V4 ~ ., data = Ozone, doTrace = 0, maxRuns = 100)
print(bor)
## Boruta performed 21 iterations in 0.4417119 secs.
##  9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
##  3 attributes confirmed unimportant: V2, V3, V6;
plot(bor, las = 2, cex.axis = 0.8, xlab = "")

plotImpHistory(bor)

getSelectedAttributes(bor, withTentative = FALSE)
## [1] "V1"  "V5"  "V7"  "V8"  "V9"  "V10" "V11" "V12" "V13"
attStats(bor)
##         meanImp   medianImp      minImp     maxImp  normHits  decision
## V1   0.40832700  0.41286596  0.32226876 0.47443026 1.0000000 Confirmed
## V2   0.03630079  0.04288927 -0.03301760 0.10983565 0.0000000  Rejected
## V3  -0.04668555 -0.05121816 -0.12973322 0.05836646 0.0000000  Rejected
## V5   0.39237797  0.39139823  0.31671328 0.44197223 1.0000000 Confirmed
## V6   0.05371338  0.06176711 -0.04041718 0.12062160 0.1428571  Rejected
## V7   0.52271613  0.51879561  0.47630171 0.56514800 1.0000000 Confirmed
## V8   0.76671886  0.76754267  0.70941659 0.83272127 1.0000000 Confirmed
## V9   0.88789089  0.88686438  0.82668819 0.99602188 1.0000000 Confirmed
## V10  0.43906302  0.43962045  0.36477347 0.51909248 1.0000000 Confirmed
## V11  0.54787436  0.54192563  0.50000214 0.62146819 1.0000000 Confirmed
## V12  0.65481180  0.66243636  0.58700258 0.69820162 1.0000000 Confirmed
## V13  0.41297531  0.41782410  0.35739084 0.45129002 1.0000000 Confirmed

Алгоритм Boruta за 21 итерацию подтвердил важность 9 признаков из 12, неопределённых не осталось. Наиболее значимы температуры в Эль-Монте и Сандбурге (V9 и V8), затем температура основания инверсии (V12), градиент давления (V11) и влажность (V7). Месяц (V1), высота изобарической поверхности (V5), видимость (V13) и высота основания инверсии (V10) тоже подтверждены, но с меньшей важностью. Отвергнуты день месяца (V2), день недели (V3) и скорость ветра (V6), так как их важность не превышает shadowMax. Это согласуется со смыслом переменных: уровень озона определяется температурой, метеоусловиями и сезоном, а не днём месяца или недели.