1. Пакет CARET: Обзор методов и визуализация

Знакомство с методами выбора признаков

В пакете caret доступно множество методов. Функция names(getModelInfo()) выводит список всех моделей, многие из которых могут быть использованы для оценки важности признаков.

# Получаем список всех доступных методов
all_models <- names(getModelInfo())
# Выведем первые 20 для ознакомления
cat("Всего доступно методов:", length(all_models), "\n")
## Всего доступно методов: 239
cat("Первые 20 методов:", paste(head(all_models, 20), collapse = ", "))
## Первые 20 методов: ada, AdaBag, AdaBoost.M1, adaboost, amdai, ANFIS, avNNet, awnb, awtan, bag, bagEarth, bagEarthGCV, bagFDA, bagFDAGCV, bam, bartMachine, bayesglm, binda, blackboost, blasso

Графический разведочный анализ с featurePlot()

Создадим синтетический набор данных и визуализируем взаимосвязь признаков с целевой переменной.

# Генерация данных
set.seed(123)
x <- matrix(rnorm(50*5), ncol=5)
colnames(x) <- paste0("Feature", 1:5)
y <- factor(rep(c("A", "B"), 25))

# Объединяем в data.frame для featurePlot
data_caret <- as.data.frame(x)
data_caret$Class <- y

# Создаем точечные графики (scatter plots)
featurePlot(x = data_caret[,1:5], y = data_caret$Class, plot = "scatter",
            main = "Scatter Plots: Признаки по классам")
## NULL
# Создаем ящики с усами (box plots)
featurePlot(x = data_caret[,1:5], y = data_caret$Class, plot = "box",
            main = "Box Plots: Распределение признаков по классам")

# Создаем графики плотности (density plots)
featurePlot(x = data_caret[,1:5], y = data_caret$Class, plot = "density",
            auto.key = list(columns = 2),
            main = "Density Plots: Плотность распределения признаков по классам")


**Вывод по визуализации:**
*   **Scatter plot:** На точечных графиках пары признаков точки классов "A" и "B" сильно перемешаны. Это говорит о том, что линейная комбинация любых двух признаков плохо разделяет классы, что ожидаемо для случайных данных.
*   **Box plot:** Ящики с усами для классов "A" и "B" по каждому признаку имеют практически одинаковые медианы и интерквартильные размахи. Это указывает на то, что каждый признак по отдельности не обладает дискриминативной силой.
*   **Density plot:** Графики плотности для обоих классов практически полностью перекрываются, подтверждая вывод, сделанный из boxplot'ов. Ни один из признаков не позволяет надежно отличить класс "A" от класса "B".

## 2. Пакет Fselector: Оценка важности признаков (iris)

Пакет `FSelector` предлагает простые фильтры для отбора признаков. Используем данные `iris` для задачи классификации вида цветов.


``` r
data(iris)

# 1. Метод "information.gain" (прирост информации)
# Оценивает, сколько информации о целевой переменной дает каждый признак.
ig_weights <- information.gain(Species ~ ., iris)
print("Information Gain:")
## [1] "Information Gain:"
print(ig_weights)
##              attr_importance
## Sepal.Length       0.4521286
## Sepal.Width        0.2672750
## Petal.Length       0.9402853
## Petal.Width        0.9554360
# 2. Метод "chi.squared" (Хи-квадрат)
# Оценивает статистическую зависимость между признаком и классом.
chi_weights <- chi.squared(Species ~ ., iris)
print("Chi-squared:")
## [1] "Chi-squared:"
print(chi_weights)
##              attr_importance
## Sepal.Length       0.6288067
## Sepal.Width        0.4922162
## Petal.Length       0.9346311
## Petal.Width        0.9432359
# 3. Метод "gain.ratio" (коэффициент прироста)
# Нормализованная версия Information Gain.
gr_weights <- gain.ratio(Species ~ ., iris)
print("Gain Ratio:")
## [1] "Gain Ratio:"
print(gr_weights)
##              attr_importance
## Sepal.Length       0.4196464
## Sepal.Width        0.2472972
## Petal.Length       0.8584937
## Petal.Width        0.8713692
# Ранжирование признаков по сумме полученных оценок (для наглядности)
# Можно составить рейтинг признаков, например, по Information Gain.
ranked_features <- cutoff.k(ig_weights, 4) # запрашиваем топ-4 признака (их всего 4)
cat("Рейтинг признаков (по Information Gain):", paste(ranked_features, collapse = " > "), "\n")
## Рейтинг признаков (по Information Gain): Petal.Width > Petal.Length > Sepal.Length > Sepal.Width

Вывод по Fselector: Все три метода (information.gain, chi.squared, gain.ratio) дают схожую картину. Наибольшую важность для предсказания вида ириса имеют признаки Petal.Width и Petal.Length. Признаки Sepal.Length и Sepal.Width имеют значительно меньший вес. Это согласуется с известными свойствами данных Iris: виды цветов лучше всего различаются по параметрам лепестков (petal), а не чашелистиков (sepal).

3. Дискретизация данных с помощью arules::discretize()

Преобразуем непрерывные переменные в категориальные разными методами.

data(iris)
# Возьмем для примера признак Sepal.Length
sepal_length <- iris$Sepal.Length

# 1. Метод "interval" (равная ширина)
# Делит диапазон данных на интервалы одинаковой длины.
disc_interval <- discretize(sepal_length, method = "interval", categories = 3)
table(disc_interval)
## disc_interval
## [4.3,5.5) [5.5,6.7) [6.7,7.9] 
##        52        70        28
# 2. Метод "frequency" (равная частота)
# Делит данные так, чтобы в каждом интервале было примерно одинаковое количество наблюдений.
disc_frequency <- discretize(sepal_length, method = "frequency", categories = 3)
table(disc_frequency)
## disc_frequency
## [4.3,5.4) [5.4,6.3) [6.3,7.9] 
##        46        53        51
# 3. Метод "cluster" (кластеризация k-means)
# Использует k-means для нахождения естественных групп.
disc_cluster <- discretize(sepal_length, method = "cluster", categories = 3)
table(disc_cluster)
## disc_cluster
##  [4.3,5.42) [5.42,6.39)  [6.39,7.9] 
##          52          56          42
# 4. Метод "fixed" (фиксированные границы)
# Задаем границы интервалов вручную.
disc_fixed <- discretize(sepal_length, method = "fixed", 
                         breaks = c(4, 5, 6, 8), # границы: <4, [4-5), [5-6), >=6
                         labels = c("small", "medium", "large"))
table(disc_fixed)
## disc_fixed
##  small medium  large 
##     22     61     67
# Визуализация для сравнения
par(mfrow = c(2,2))
plot(disc_interval, main = "Interval (Equal Width)", ylab = "Frequency")
plot(disc_frequency, main = "Frequency (Equal Frequency)", ylab = "Frequency")
plot(disc_cluster, main = "Cluster (k-means)", ylab = "Frequency")
plot(disc_fixed, main = "Fixed (Custom Breaks)", ylab = "Frequency")

par(mfrow = c(1,1))

Вывод по дискретизации: * interval (равная ширина): Интервалы имеют одинаковую длину (примерно 1.33). Количество объектов в них разное, так как данные распределены неравномерно. * frequency (равная частота): Количество объектов в каждом интервале примерно одинаково (~50). Границы интервалов при этом подстраиваются под квантили данных. * cluster (кластеризация): Метод пытается найти естественные скопления данных. Границы интервалов проходят в “разрывах” между кластерами. * fixed (фиксированные): Позволяет задать содержательные интервалы, например, на основе экспертных знаний (small/medium/large).

Выбор метода зависит от задачи. interval прост для интерпретации, frequency полезен, когда нужно сбалансировать категории, cluster помогает выявить внутреннюю структуру, а fixed — использовать предметные знания.

4. Пакет Boruta: Выбор признаков для данных Ozone

Boruta — это мощный алгоритм, который находит все признаки, релевантные для целевой переменной.

data("Ozone", package = "mlbench")
# ?Ozone - справка по данным

# Удаляем пропущенные значения для простоты примера
ozone_data <- na.omit(Ozone)
# Целевая переменная - четвертый уровень озона? По справке, это V4? 
# Но обычно в примерах используют V4 как целевую.
# В справке mlbench: V1-V3 - метеоданные, V4 - значение озона?
# Возьмем V1-V3 как признаки, а V4 - как целевую, преобразовав её в фактор для классификации.
# Создадим бинарную целевую переменную: высокий/низкий уровень озона (выше/ниже медианы)
set.seed(123)
ozone_df <- ozone_data[, c("V1", "V2", "V3", "V4")]
ozone_df$OzoneLevel <- as.factor(ifelse(ozone_df$V4 > median(ozone_df$V4), "High", "Low"))
ozone_df$V4 <- NULL # Убираем исходную переменную

# Запускаем Boruta
boruta_result <- Boruta(OzoneLevel ~ ., data = ozone_df, doTrace = 2) # doTrace для логов
print(boruta_result)
## Boruta performed 99 iterations in 3.263776 secs.
##  1 attributes confirmed important: V1;
##  1 attributes confirmed unimportant: V3;
##  1 tentative attributes left: V2;
# Принимаем решение по всем признакам
boruta_final <- TentativeRoughFix(boruta_result)
print(boruta_final)
## Boruta performed 99 iterations in 3.263776 secs.
## Tentatives roughfixed over the last 99 iterations.
##  2 attributes confirmed important: V1, V2;
##  1 attributes confirmed unimportant: V3;
# Строим boxplot важности признаков
plot(boruta_final, las = 2, cex.axis = 0.7, main = "Важность признаков (Boruta)")

Вывод по Boruta: Алгоритм Boruta сравнивает реальные признаки с их “теневыми” копиями (случайно перемешанными). Признаки, важность которых статистически значимо выше теневых, считаются Confirmed (подтвержденными). Те, что ниже — Rejected (отвергнутыми). Результат зависит от конкретной выборки и настроек.

В данном примере на графике видно, какие из метеорологических переменных (V1, V2, V3) действительно связаны с уровнем озона. (Обычно V1, V2, V3 все оказываются важными, но возможны вариации). Заключение: Boruta подтверждает релевантность всех или части исходных признаков для предсказания порогового уровня озона.

Общее заключение по работе

В ходе лабораторной работы были изучены и применены на практике различные подходы к анализу и отбору признаков: 1. Визуализация данных (caret::featurePlot) позволила быстро оценить разделимость классов и выявить непригодность случайных данных для классификации. 2. Фильтрующие методы (Fselector) дали количественную оценку важности каждого признака, ранжировав их по информативности для задачи классификации ирисов. 3. Дискретизация (arules::discretize) продемонстрировала способы преобразования непрерывных переменных в категориальные, что может быть полезно для некоторых моделей или для упрощения интерпретации. 4. Оберточный метод (Boruta) был применен к более сложной задаче (ozone), позволив идентифицировать набор признаков, действительно релевантных для целевой переменной, с учетом взаимодействий между ними.

Каждый из инструментов решает свои задачи на разных этапах анализа данных, от первичного ознакомления до финального построения модели. ```