В данной лабораторной работе рассматриваются: 1. Графический
разведочный анализ данных средствами пакета caret. 2.
Определение важности признаков для задачи классификации с использованием
пакета FSelector. 3. Дискретизация непрерывных признаков
различными методами с помощью пакета arules. 4. Выбор
признаков методом Boruta.
if (!require(caret)) install.packages("caret", dependencies = TRUE)
library(caret)
model_names <- names(getModelInfo())
length(model_names)
## [1] 239
head(model_names, 50)
## [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" "cforest" "chaid"
## [33] "CSimca" "ctree" "ctree2" "cubist"
## [37] "dda" "deepboost" "DENFIS" "dnn"
## [41] "dwdLinear" "dwdPoly" "dwdRadial" "earth"
## [45] "elm" "enet" "evtree" "extraTrees"
## [49] "fda" "FH.GBML"
set.seed(123)
x <- matrix(rnorm(50 * 5), ncol = 5)
colnames(x) <- paste0("X", 1:5)
x <- as.data.frame(x)
y <- factor(rep(c("A", "B"), 25))
str(x)
## 'data.frame': 50 obs. of 5 variables:
## $ X1: num -0.5605 -0.2302 1.5587 0.0705 0.1293 ...
## $ X2: num 0.2533 -0.0285 -0.0429 1.3686 -0.2258 ...
## $ X3: num -0.71 0.257 -0.247 -0.348 -0.952 ...
## $ X4: num 0.788 0.769 0.332 -1.008 -0.119 ...
## $ X5: num 2.199 1.312 -0.265 0.543 -0.414 ...
table(y)
## y
## A B
## 25 25
png("figures/featureplot_box.png", width = 1600, height = 1200, res = 200)
featurePlot(
x = x,
y = y,
plot = "box",
auto.key = list(columns = 2)
)
dev.off()
## png
## 2
png("figures/featureplot_density.png", width = 1600, height = 1200, res = 200)
featurePlot(
x = x,
y = y,
plot = "density",
auto.key = list(columns = 2)
)
dev.off()
## png
## 2
Команда names(getModelInfo()) позволяет получить список
моделей, доступных в пакете caret. Это показывает, что
пакет содержит большое количество алгоритмов для задач классификации и
регрессии.
С помощью функции featurePlot() были построены графики
для искусственно сгенерированных данных. Поскольку признаки создавались
случайным образом, выраженного разделения между классами A
и B не наблюдается. На boxplot-м и density-графиках
распределения признаков двух классов во многом пересекаются.
Следовательно, такие признаки не являются информативными для
качественной классификации.
if (!require(FSelector)) install.packages("FSelector", dependencies = TRUE)
library(FSelector)
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 ...
summary(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100
## 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
## Median :5.800 Median :3.000 Median :4.350 Median :1.300
## Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
## 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
## Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
## Species
## setosa :50
## versicolor:50
## virginica :50
##
##
##
importance_iris <- information.gain(Species ~ ., data = iris)
importance_iris <- importance_iris[order(-importance_iris$attr_importance), , drop = FALSE]
importance_iris
## attr_importance
## Petal.Width 0.9554360
## Petal.Length 0.9402853
## Sepal.Length 0.4521286
## Sepal.Width 0.2672750
vals <- importance_iris$attr_importance
names(vals) <- rownames(importance_iris)
png("figures/iris_importance.png", width = 1600, height = 1200, res = 200)
barplot(
vals,
main = "Важность признаков для набора iris",
ylab = "Information Gain",
las = 2
)
dev.off()
## png
## 2
Для набора данных iris была рассчитана важность
признаков с помощью функции information.gain() из пакета
FSelector. В результате признаки были ранжированы по
степени их влияния на классификацию вида ириса.
Наиболее важными являются признаки с наибольшим значением
attr_importance. Обычно наибольший вклад в задачу
классификации вносят признаки, связанные с размерами лепестков
(Petal.Length и Petal.Width), так как именно
они лучше всего разделяют виды ирисов. Менее значимыми оказываются
признаки чашелистиков. Следовательно, для построения модели
классификации в первую очередь следует использовать наиболее
информативные признаки.
if (!require(arules)) install.packages("arules", dependencies = TRUE)
library(arules)
x_cont <- iris$Sepal.Length
summary(x_cont)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 4.300 5.100 5.800 5.843 6.400 7.900
disc_interval <- discretize(x_cont, method = "interval", categories = 3)
table(disc_interval)
## disc_interval
## [4.3,5.5) [5.5,6.7) [6.7,7.9]
## 52 70 28
disc_frequency <- discretize(x_cont, method = "frequency", categories = 3)
table(disc_frequency)
## disc_frequency
## [4.3,5.4) [5.4,6.3) [6.3,7.9]
## 46 53 51
disc_cluster <- discretize(x_cont, method = "cluster", categories = 3)
table(disc_cluster)
## disc_cluster
## [4.3,5.42) [5.42,6.39) [6.39,7.9]
## 52 56 42
disc_fixed <- discretize(
x_cont,
method = "fixed",
breaks = c(-Inf, 5.5, 6.5, Inf),
labels = c("small", "medium", "large")
)
table(disc_fixed)
## disc_fixed
## small medium large
## 52 63 35
disc_result <- data.frame(
Original = x_cont,
Interval = disc_interval,
Frequency = disc_frequency,
Cluster = disc_cluster,
Fixed = disc_fixed
)
head(disc_result, 15)
## Original Interval Frequency Cluster Fixed
## 1 5.1 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 2 4.9 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 3 4.7 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 4 4.6 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 5 5.0 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 6 5.4 [4.3,5.5) [5.4,6.3) [4.3,5.42) small
## 7 4.6 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 8 5.0 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 9 4.4 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 10 4.9 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 11 5.4 [4.3,5.5) [5.4,6.3) [4.3,5.42) small
## 12 4.8 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 13 4.8 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 14 4.3 [4.3,5.5) [4.3,5.4) [4.3,5.42) small
## 15 5.8 [5.5,6.7) [5.4,6.3) [5.42,6.39) medium
Функция discretize() позволяет преобразовать непрерывную
переменную в категориальную несколькими способами.
Метод interval делит весь диапазон значений на интервалы
одинаковой ширины.
Метод frequency формирует интервалы так, чтобы в каждом
было примерно одинаковое число наблюдений.
Метод cluster выполняет группировку значений на основе их
близости.
Метод fixed использует заранее заданные пользователем
границы интервалов.
Таким образом, один и тот же числовой признак можно представить в категориальном виде разными способами. Выбор метода зависит от структуры данных и целей анализа.
if (!require(Boruta)) install.packages("Boruta", dependencies = TRUE)
if (!require(mlbench)) install.packages("mlbench", dependencies = TRUE)
library(Boruta)
library(mlbench)
data("Ozone", package = "mlbench")
str(Ozone)
## 'data.frame': 366 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",..: 1 2 3 4 5 6 7 8 9 10 ...
## $ V3 : Factor w/ 7 levels "1","2","3","4",..: 4 5 6 7 1 2 3 4 5 6 ...
## $ V4 : num 3 3 3 5 5 6 4 4 6 7 ...
## $ V5 : num 5480 5660 5710 5700 5760 5720 5790 5790 5700 5700 ...
## $ V6 : num 8 6 4 3 3 4 6 3 3 3 ...
## $ V7 : num 20 NA 28 37 51 69 19 25 73 59 ...
## $ V8 : num NA 38 40 45 54 35 45 55 41 44 ...
## $ V9 : num NA NA NA NA 45.3 ...
## $ V10: num 5000 NA 2693 590 1450 ...
## $ V11: num -15 -14 -25 -24 25 15 -33 -28 23 -2 ...
## $ V12: num 30.6 NA 47.7 55 57 ...
## $ V13: num 200 300 250 100 60 60 100 250 120 120 ...
summary(Ozone)
## V1 V2 V3 V4 V5
## 1 : 31 1 : 12 1:52 Min. : 1.00 Min. :5320
## 3 : 31 2 : 12 2:52 1st Qu.: 5.00 1st Qu.:5700
## 5 : 31 3 : 12 3:52 Median : 9.00 Median :5770
## 7 : 31 4 : 12 4:53 Mean :11.53 Mean :5753
## 8 : 31 5 : 12 5:53 3rd Qu.:16.00 3rd Qu.:5830
## 10 : 31 6 : 12 6:52 Max. :38.00 Max. :5950
## (Other):180 (Other):294 7:52 NA's :5 NA's :12
## V6 V7 V8 V9
## Min. : 0.000 Min. :19.00 Min. :25.00 Min. :27.68
## 1st Qu.: 3.000 1st Qu.:49.00 1st Qu.:51.00 1st Qu.:49.73
## Median : 5.000 Median :65.00 Median :62.00 Median :57.02
## Mean : 4.869 Mean :58.48 Mean :61.91 Mean :56.85
## 3rd Qu.: 6.000 3rd Qu.:73.00 3rd Qu.:72.00 3rd Qu.:66.11
## Max. :11.000 Max. :93.00 Max. :93.00 Max. :82.58
## NA's :15 NA's :2 NA's :139
## V10 V11 V12 V13
## Min. : 111 Min. :-69.0 Min. :27.50 Min. : 0.0
## 1st Qu.: 890 1st Qu.:-10.0 1st Qu.:51.26 1st Qu.: 70.0
## Median :2125 Median : 24.0 Median :62.24 Median :110.0
## Mean :2591 Mean : 17.8 Mean :60.93 Mean :123.3
## 3rd Qu.:5000 3rd Qu.: 45.0 3rd Qu.:70.52 3rd Qu.:150.0
## Max. :5000 Max. :107.0 Max. :91.76 Max. :500.0
## NA's :15 NA's :1 NA's :14
ozone_data <- na.omit(Ozone)
dim(ozone_data)
## [1] 203 13
set.seed(123)
boruta_model <- Boruta(V4 ~ ., data = ozone_data, doTrace = 0)
boruta_fixed <- TentativeRoughFix(boruta_model)
boruta_stats <- attStats(boruta_fixed)
boruta_stats
## meanImp medianImp minImp maxImp normHits decision
## V1 9.5563296 9.7071000 8.4255686 10.7247899 1.0000000 Confirmed
## V2 1.1557680 1.1576551 -0.2474598 2.7423660 0.1666667 Rejected
## V3 -0.9877372 -0.7333367 -3.4162909 0.3794342 0.0000000 Rejected
## V5 9.2426781 9.2313179 8.1108460 10.5140883 1.0000000 Confirmed
## V6 0.9886679 1.3615721 -1.1013954 1.9852132 0.0000000 Rejected
## V7 11.7026875 11.5169965 10.5127703 13.4896943 1.0000000 Confirmed
## V8 17.1647491 17.2255744 16.0336735 18.5525852 1.0000000 Confirmed
## V9 19.2281405 19.0627349 17.5889826 20.9190449 1.0000000 Confirmed
## V10 9.8662368 9.7266893 8.6477478 11.3131795 1.0000000 Confirmed
## V11 11.8977619 11.8484607 10.9347533 13.6520570 1.0000000 Confirmed
## V12 14.6326841 14.6095338 13.5595253 16.0775580 1.0000000 Confirmed
## V13 9.4438214 9.5489762 8.1005306 10.7881019 1.0000000 Confirmed
png("figures/boruta_ozone_boxplot.png", width = 1800, height = 1200, res = 200)
plot(boruta_fixed, las = 2, cex.axis = 0.8, main = "Boruta: важность признаков для Ozone")
dev.off()
## png
## 2
Метод Boruta позволяет определить, какие признаки
действительно важны для предсказания целевой переменной. Алгоритм
сравнивает реальные признаки с искусственно созданными теневыми
признаками и на этой основе принимает решение о значимости.
В результате анализа признаки делятся на подтвержденные значимые
(Confirmed), отклоненные (Rejected) и иногда
промежуточные (Tentative). Построенный boxplot наглядно
показывает распределение важности признаков. Те признаки, которые
стабильно превосходят теневые, считаются действительно полезными для
модели.
В ходе лабораторной работы были изучены основные методы анализа данных и отбора признаков в языке R.
С помощью пакета caret был выполнен графический
разведочный анализ данных, и показано, что случайно сгенерированные
признаки плохо разделяют классы.
С помощью пакета FSelector была определена важность
признаков для задачи классификации на наборе iris.
С помощью функции discretize() из пакета
arules были рассмотрены различные способы перевода
непрерывной переменной в категориальную.
С помощью алгоритма Boruta был выполнен отбор признаков для
набора Ozone.
Полученные результаты показывают, что выбор признаков и способ предварительной обработки данных существенно влияют на интерпретацию данных и качество последующего моделирования.
Все результаты лабораторной работы были собраны в единый файл формата R Markdown. Итоговый отчет может быть скомпилирован в HTML и опубликован на RPubs.