Примечание: пакет FSelector требует установленной Java (JRE) и пакета rJava. Если при
library(FSelector)возникает ошибка про Java — установите Java с https://www.java.com, затем в консоли R выполнитеinstall.packages("rJava")иlibrary(rJava)передlibrary(FSelector).
model_names <- names(getModelInfo())
length(model_names)
## [1] 239
head(model_names, 30)
## [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"
Функция getModelInfo() возвращает полный список моделей
(методов), которые поддерживает caret для обучения (train). Отдельно в
caret есть функции именно для отбора признаков:
rfe() (Recursive Feature Elimination), sbf()
(Selection By Filtering), а также varImp() для оценки
важности признаков уже обученной модели.
set.seed(1)
x <- matrix(rnorm(50 * 5), ncol = 5)
colnames(x) <- paste0("V", 1:5)
y <- factor(rep(c("A", "B"), 25))
# 1. Boxplot по каждому признаку в разрезе класса
jpeg("featurePlot_box.jpg", width = 1000, height = 700, quality = 100)
featurePlot(x = x, y = y, plot = "box",
scales = list(y = list(relation = "free")),
main = "Boxplot признаков по классам")
dev.off()
## quartz_off_screen
## 2
# 2. Плотности распределения признаков
jpeg("featurePlot_density.jpg", width = 1000, height = 700, quality = 100)
featurePlot(x = x, y = y, plot = "density",
scales = list(x = list(relation = "free"), y = list(relation = "free")),
auto.key = list(columns = 2))
dev.off()
## quartz_off_screen
## 2
# 3. Матрица рассеяния (pairs)
jpeg("featurePlot_pairs.jpg", width = 1000, height = 1000, quality = 100)
featurePlot(x = x, y = y, plot = "pairs", auto.key = list(columns = 2))
dev.off()
## quartz_off_screen
## 2
# Показать графики и в самом отчёте
featurePlot(x = x, y = y, plot = "box", scales = list(y = list(relation = "free")))
featurePlot(x = x, y = y, plot = "density", scales = list(x = list(relation = "free"), y = list(relation = "free")))
featurePlot(x = x, y = y, plot = "pairs")
Вывод: данные x сгенерированы функцией
rnorm(), то есть представляют собой случайный шум, никак не
связанный с меткой класса y (метки чередуются регулярно и
не зависят от значений x). Поэтому на всех трёх графиках
(boxplot, density, pairs) распределения признаков для классов A и B
практически совпадают — визуально разделить классы по этим пяти
признакам невозможно. Это ожидаемый результат для случайно
сгенерированных данных и иллюстрирует, что featurePlot() полезен именно
для того, чтобы на глаз отличить информативные признаки (с
разделяющимися распределениями по классам) от неинформативных (с
совпадающими распределениями), как в этом примере.
data(iris)
# Оценка важности признаков разными методами
ig <- information.gain(Species ~ ., data = iris)
gr <- gain.ratio(Species ~ ., data = iris)
chi <- chi.squared(Species ~ ., data = iris)
print(ig)
## attr_importance
## Sepal.Length 0.4521286
## Sepal.Width 0.2672750
## Petal.Length 0.9402853
## Petal.Width 0.9554360
print(gr)
## attr_importance
## Sepal.Length 0.4196464
## Sepal.Width 0.2472972
## Petal.Length 0.8584937
## Petal.Width 0.8713692
print(chi)
## attr_importance
## Sepal.Length 0.6288067
## Sepal.Width 0.4922162
## Petal.Length 0.9346311
## Petal.Width 0.9432359
# Отбор двух самых важных признаков по information gain
best2 <- cutoff.k(ig, 2)
best2
## [1] "Petal.Width" "Petal.Length"
Вывод: все три метода (information gain, gain ratio,
chi-squared) согласованно показывают, что наиболее информативными
признаками для классификации вида ириса (Species) являются
Petal.Length и Petal.Width — у них наибольшие
значения важности. Признаки Sepal.Length и особенно
Sepal.Width вносят заметно меньший вклад в разделение
классов. Это согласуется с общеизвестным фактом об этом наборе данных:
длина и ширина лепестка почти линейно разделяют виды ирисов, тогда как
параметры чашелистика (sepal) более “перемешаны” между видами.
library(arules)
data(iris)
x_cont <- iris$Sepal.Length
d_interval <- discretize(x_cont, method = "interval", breaks = 3)
d_frequency <- discretize(x_cont, method = "frequency", breaks = 3)
d_cluster <- discretize(x_cont, method = "cluster", breaks = 3)
d_fixed <- discretize(x_cont, method = "fixed",
breaks = c(-Inf, 5.5, 6.5, Inf),
labels = c("low", "medium", "high"))
table(d_interval)
## d_interval
## [4.3,5.5) [5.5,6.7) [6.7,7.9]
## 52 70 28
table(d_frequency)
## d_frequency
## [4.3,5.4) [5.4,6.3) [6.3,7.9]
## 46 53 51
table(d_cluster)
## d_cluster
## [4.3,5.53) [5.53,6.55) [6.55,7.9]
## 59 61 30
table(d_fixed)
## d_fixed
## low medium high
## 52 63 35
par(mfrow = c(2, 2))
plot(table(d_interval), main = "interval", col = "steelblue")
plot(table(d_frequency), main = "frequency", col = "darkorange")
plot(table(d_cluster), main = "cluster", col = "forestgreen")
plot(table(d_fixed), main = "fixed", col = "tomato")
par(mfrow = c(1, 1))
Вывод: метод interval делит диапазон
значений Sepal.Length на интервалы одинаковой
ширины, из-за чего группы получаются неравномерными по числу
наблюдений. Метод frequency строит интервалы так, чтобы в
каждой группе было примерно одинаковое число
наблюдений, но ширина интервалов при этом разная. Метод
cluster использует кластеризацию (k-means) для поиска
естественных группировок значений — границы интервалов подстраиваются
под плотность данных. Метод fixed использует
заранее заданные вручную границы, что удобно, когда
пороги имеют содержательный смысл (например, агрономические нормы), но
требует экспертного знания предметной области для выбора этих границ.
Выбор метода дискретизации существенно влияет на дальнейший результат
анализа (например, для алгоритмов ассоциативных правил).
library(Boruta)
library(mlbench)
data("Ozone", package = "mlbench")
Ozone <- na.omit(Ozone) # Boruta не работает с пропусками
colnames(Ozone) <- paste0("V", 1:ncol(Ozone)) # у Ozone нет "говорящих" имён столбцов
set.seed(1)
boruta_result <- Boruta(V4 ~ ., data = Ozone, doTrace = 0)
print(boruta_result)
## Boruta performed 24 iterations in 0.463366 secs.
## 9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
## 3 attributes confirmed unimportant: V2, V3, V6;
# Итоговое решение по каждому признаку
final_decision <- TentativeRoughFix(boruta_result)
print(final_decision)
## Boruta performed 24 iterations in 0.463366 secs.
## 9 attributes confirmed important: V1, V10, V11, V12, V13 and 4 more;
## 3 attributes confirmed unimportant: V2, V3, V6;
attStats(boruta_result)
## meanImp medianImp minImp maxImp normHits decision
## V1 0.41193902 0.40874574 0.34596313 0.47748700 1.0000000 Confirmed
## V2 0.04400055 0.03685704 -0.04866888 0.13111930 0.1666667 Rejected
## V3 -0.07047045 -0.07645198 -0.13945444 -0.02468737 0.0000000 Rejected
## V5 0.39047635 0.38898201 0.31980785 0.45176242 1.0000000 Confirmed
## V6 0.01227390 0.01008201 -0.05960559 0.08967574 0.0000000 Rejected
## V7 0.52451495 0.52579776 0.45444201 0.57880602 1.0000000 Confirmed
## V8 0.77608962 0.78131286 0.71917639 0.83519238 1.0000000 Confirmed
## V9 0.87502711 0.86707260 0.83054602 0.96504062 1.0000000 Confirmed
## V10 0.43160816 0.42959427 0.37462118 0.50877226 1.0000000 Confirmed
## V11 0.54131356 0.54091540 0.48309934 0.60971909 1.0000000 Confirmed
## V12 0.65892496 0.65796083 0.58947681 0.71773702 1.0000000 Confirmed
## V13 0.40542650 0.39918574 0.35147274 0.45302205 1.0000000 Confirmed
jpeg("boruta_boxplot.jpg", width = 1200, height = 700, quality = 100)
plot(boruta_result, las = 2, cex.axis = 0.7,
main = "Важность признаков по Boruta (Ozone)")
dev.off()
## quartz_off_screen
## 2
plot(boruta_result, las = 2, cex.axis = 0.7,
main = "Важность признаков по Boruta (Ozone)")
Вывод: Boruta сравнивает важность каждого реального
признака с важностью его “теневых” случайных копий (shadow features) и
по результатам множества итераций случайного леса помечает признаки как
Confirmed (значимые), Rejected (незначимые)
или Tentative (неопределённые). На boxplot-графике зелёным
обычно выделяются подтверждённые признаки, красным — отклонённые, жёлтым
— неопределённые, синим — статистика теневых признаков (min/mean/max),
которая служит порогом сравнения. В наборе Ozone (целевая
переменная — среднесуточная концентрация озона, V4)
наиболее значимыми, как правило, оказываются признаки, связанные с
температурой и давлением (например, V8, V9,
V10 — температура на разных станциях), тогда как признаки
вроде дня недели/месяца часто попадают в Rejected или
Tentative, поскольку не несут прямой физической связи с
уровнем озона.
В ходе работы были опробованы четыре разных подхода к разведочному анализу и отбору признаков:
Совместное использование этих методов позволяет как визуально, так и статистически обоснованно определить, какие признаки действительно важны для решаемой задачи, а какие можно исключить без потери качества модели.