Изучение методов выбора и оценки важности признаков в задачах машинного обучения с использованием пакетов R: CARET, FSelector, arules и Boruta. В ходе работы осваиваются инструменты визуального разведочного анализа, дискретизация непрерывных переменных и автоматический отбор значимых признаков на реальных и синтетических наборах данных.
Пакет CARET предоставляет унифицированный интерфейс к более чем 230
методам машинного обучения. Функция featurePlot() позволяет
оценить разделяющую способность признаков через графики типа
pairs, density и box.
Для демонстрации сгенерирован синтетический датасет: матрица 50×5 из
нормально распределённых значений и бинарный отклик из 25 пар классов A
и B.
library(caret)
library(e1071)
set.seed(123)
x <- matrix(rnorm(50 * 5), ncol = 5)
y <- factor(rep(c("A", "B"), 25))
Список доступных методов пакета CARET (первые 20):
head(names(getModelInfo()), 20)
## [1] "ada" "AdaBag" "AdaBoost.M1" "adaboost" "amdai"
## [6] "ANFIS" "avNNet" "awnb" "awtan" "bag"
## [11] "bagEarth" "bagEarthGCV" "bagFDA" "bagFDAGCV" "bam"
## [16] "bartMachine" "bayesglm" "binda" "blackboost" "blasso"
featurePlot(x = x, y = y, plot = "pairs", auto.key = list(columns = 2))
featurePlot(x = x, y = y, plot = "density",
scales = list(x = list(relation = "free"),
y = list(relation = "free")),
auto.key = list(columns = 2))
featurePlot(x = x, y = y, plot = "box",
scales = list(x = list(relation = "free"),
y = list(relation = "free")),
auto.key = list(columns = 2))
Вывод: все три типа графиков подтвердили, что синтетические признаки не несут разделяющей информации — классы A и B перекрываются на всех визуализациях, что ожидаемо для случайно сгенерированных данных.
Пакет FSelector предоставляет фильтровые методы оценки важности
признаков на основе теории информации. Датасет iris
содержит 150 наблюдений трёх видов ириса с четырьмя морфологическими
признаками.
library(FSelector)
data(iris)
weights_ig <- information.gain(Species ~ ., data = iris)
print(weights_ig)
## attr_importance
## Sepal.Length 0.4521286
## Sepal.Width 0.2672750
## Petal.Length 0.9402853
## Petal.Width 0.9554360
weights_chi <- chi.squared(Species ~ ., data = iris)
print(weights_chi)
## attr_importance
## Sepal.Length 0.6288067
## Sepal.Width 0.4922162
## Petal.Length 0.9346311
## Petal.Width 0.9432359
weights_gr <- gain.ratio(Species ~ ., data = iris)
print(weights_gr)
## attr_importance
## Sepal.Length 0.4196464
## Sepal.Width 0.2472972
## Petal.Length 0.8584937
## Petal.Width 0.8713692
weights_su <- symmetrical.uncertainty(Species ~ ., data = iris)
print(weights_su)
## attr_importance
## Sepal.Length 0.4155563
## Sepal.Width 0.2452743
## Petal.Length 0.8571872
## Petal.Width 0.8705214
barplot(t(weights_ig), beside = TRUE, col = "steelblue",
main = "Feature Importance (Information Gain)",
ylab = "Importance", las = 2)
Вывод: все четыре метода единогласно показали, что Petal.Width и Petal.Length — наиболее информативные признаки (важность ≥ 0.85), тогда как Sepal.Width имеет наименьшую различающую способность (< 0.27 по Information Gain).
Функция discretize() из пакета arules преобразует
непрерывную переменную в категориальную четырьмя методами:
interval (равная ширина), frequency
(равная частота), cluster (k-means),
fixed (заданные границы). Используется признак
Sepal.Length датасета iris.
library(arules)
data(iris)
iris$SL_interval <- discretize(iris$Sepal.Length, method = "interval", breaks = 3)
iris$SL_frequency <- discretize(iris$Sepal.Length, method = "frequency", breaks = 3)
iris$SL_cluster <- discretize(iris$Sepal.Length, method = "cluster", breaks = 3)
iris$SL_fixed <- discretize(iris$Sepal.Length, method = "fixed",
breaks = c(4, 5, 6, 7, 8))
Первые строки с результатами дискретизации:
head(iris[, c("Sepal.Length", "SL_interval", "SL_frequency", "SL_cluster", "SL_fixed")])
## Sepal.Length SL_interval SL_frequency SL_cluster SL_fixed
## 1 5.1 [4.3,5.5) [4.3,5.4) [4.3,5.42) [5,6)
## 2 4.9 [4.3,5.5) [4.3,5.4) [4.3,5.42) [4,5)
## 3 4.7 [4.3,5.5) [4.3,5.4) [4.3,5.42) [4,5)
## 4 4.6 [4.3,5.5) [4.3,5.4) [4.3,5.42) [4,5)
## 5 5.0 [4.3,5.5) [4.3,5.4) [4.3,5.42) [5,6)
## 6 5.4 [4.3,5.5) [5.4,6.3) [4.3,5.42) [5,6)
Таблицы частот по методам:
table(iris$SL_interval)
##
## [4.3,5.5) [5.5,6.7) [6.7,7.9]
## 52 70 28
table(iris$SL_frequency)
##
## [4.3,5.4) [5.4,6.3) [6.3,7.9]
## 46 53 51
table(iris$SL_cluster)
##
## [4.3,5.42) [5.42,6.39) [6.39,7.9]
## 52 56 42
table(iris$SL_fixed)
##
## [4,5) [5,6) [6,7) [7,8]
## 22 61 54 13
par(mfrow = c(2, 2))
hist(iris$Sepal.Length, main = "Original", col = "lightblue", xlab = "Sepal.Length")
barplot(table(iris$SL_interval), main = "Interval (equal width)", col = "lightgreen")
barplot(table(iris$SL_frequency), main = "Frequency (equal frequency)", col = "lightcoral")
barplot(table(iris$SL_cluster), main = "Cluster (k-means)", col = "lightyellow")
Вывод: метод frequency обеспечивает наиболее сбалансированное распределение наблюдений (46/53/51), что предпочтительно при чувствительности к балансу классов. Метод interval создаёт неравномерное распределение (52/70/28), cluster находит естественные разрывы в данных, а fixed позволяет задать содержательно обоснованные границы.
Пакет Boruta реализует алгоритм выбора признаков на основе случайного
леса. Алгоритм создаёт «теневые» копии признаков и итеративно определяет
значимость каждого: Confirmed,
Rejected или Tentative. Используется
датасет airquality — ежедневные измерения качества воздуха
в Нью-Йорке (строки с пропусками удалены).
library(Boruta)
library(ggplot2)
data(airquality)
df <- na.omit(airquality)
df$Ozone <- as.numeric(df$Ozone)
set.seed(123)
boruta_output <- Boruta(Ozone ~ ., data = df, doTrace = 0)
Подтверждённые значимые признаки:
getSelectedAttributes(boruta_output, withTentative = FALSE)
## [1] "Solar.R" "Wind" "Temp" "Month" "Day"
plot(boruta_output, las = 2, cex.axis = 0.7,
main = "Boruta Feature Importance for Ozone Dataset")
boruta_importance <- attStats(boruta_output)
importance_df <- data.frame(
Feature = rownames(boruta_importance),
MeanImp = boruta_importance[, 1]
)
ggplot(importance_df, aes(x = reorder(Feature, MeanImp), y = MeanImp)) +
geom_col(fill = "steelblue", alpha = 0.8) +
coord_flip() +
labs(title = "Feature Importance (Boruta)",
x = "Features", y = "Mean Importance") +
theme_minimal()
Вывод: алгоритм Boruta подтвердил значимость всех пяти признаков датасета airquality. Наибольшую важность имеют Temp (температура) и Wind (скорость ветра), что согласуется с физическим смыслом: озон образуется при высоких температурах и накапливается при слабом ветре.
В ходе практической работы освоены четыре инструмента выбора и оценки
признаков в R. Пакет CARET продемонстрировал возможности визуального
разведочного анализа через featurePlot(). Пакет FSelector
показал, что фильтровые методы на основе теории информации дают
согласованные результаты: признаки лепестков ириса устойчиво оказываются
наиболее информативными вне зависимости от конкретного критерия. Пакет
arules позволил сравнить стратегии дискретизации и убедиться, что выбор
метода существенно влияет на распределение наблюдений по категориям.
Наконец, Boruta обеспечивает статистически обоснованный автоматический
отбор признаков без ручной настройки порогов.