Цветы Ирисы трех сортов.
df <- iris
df[,5] <- as.factor(df[,5]) #Изменяем тип 5-ого признака с численного на факторный, чтобы было легче дальше с ним работать.
#df <- df[,-1] #Удаляем первый столбец отвечающий за номер индивида.
head(df)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
library(matrixStats) #Позволяет делать разные прикольные штуки с матрицами - например, считать максимум по столбцам.
df.active <- df[, 1:4]
apply(df.active, 2, function(x) sum(x > 0))
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 150 150 150 150
colMaxs(as.matrix(df.active))
## [1] 7.9 4.4 6.9 2.5
library("caTools") #Библиотека, позволяющая разбивать данные на тренировочные и тестовые случайным образом.
set.seed(13)
#dfo <- df[-c(10,77,132),] #Заранее убираем аутлаеры.
dfo <- df
sample <- sample.split(dfo, SplitRatio = 0.75)
train <- subset(dfo, sample == TRUE)
test <- subset(dfo, sample == FALSE)
#Разбиваем данные на тренировочную и тестовую часть в отношении 3 к 1.
panel.hist <- function(x, ...)
{
usr <- par("usr"); on.exit(par(usr))
par(usr = c(usr[1:2], 0, 1.5) )
h <- hist(x, plot = FALSE)
breaks <- h$breaks; nB <- length(breaks)
y <- h$counts; y <- y/max(y)
rect(breaks[-nB], 0, breaks[-1], y, col = "cyan", ...)
}
pairs(train[,c(1:5)], diag.panel = panel.hist)
#Хотим посмотреть на данные
train.active <- train[,1:4]
library("FactoMineR")
library("factoextra")
## Loading required package: ggplot2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
res2 <- PCA(train.active, scale.unit = TRUE, graph = FALSE)
fviz_pca_var(res2)
fviz_pca_biplot(res2,
col.ind = train$Species,
gradient.cols = c("red", "blue"),
label = "var",
col.var = "black",
legend.title = "Results")
train.manova <- manova(cbind(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width) ~ Species, data = train) #Смотрим на значимость дискриминации, то есть на то, отличаются ли средние по признакам между разными группами.
summary(train.manova, 'Wilks')
## Df Wilks approx F num Df den Df Pr(>F)
## Species 2 0.023954 114.69 8 168 < 2.2e-16 ***
## Residuals 87
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(train.manova, 'Roy')
## Df Roy approx F num Df den Df Pr(>F)
## Species 2 30.062 638.83 4 85 < 2.2e-16 ***
## Residuals 87
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#Выше показано, как получать результаты по разным критериям. В данном случае, значения p-value будут одинаковы для всех трех, так как данные критерии эквивалентны при сравнении двух групп.
library("MASS")
Проводим классификацию по всем данным.
full.lda <- lda(dfo[,1:4], dfo[,5]) #строим LDA
full.ldap <- predict(full.lda, dfo[,1:4]) #предсказываем по полученной классификации
#Далее строим таблицу ошибок классификации
full.ldapc <- full.ldap$class
ct <- table(full.ldapc, dfo[,5])
ct
##
## full.ldapc setosa versicolor virginica
## setosa 50 0 0
## versicolor 0 48 1
## virginica 0 2 49
diag(prop.table(ct, 2)) #Столбцы - настоящие классы, строки - предсказанные классы.
## setosa versicolor virginica
## 1.00 0.96 0.98
#Все аналогично, но теперь смотрим на таблицу ошибок классификации по train data на train и test отдельно.
train.lda <- lda(train[,1:4], train[,5])
train.ldap <- predict(train.lda, test[,1:4])
train.ldapc <- train.ldap$class
ct <- table(train.ldapc, test[,5])
ct
##
## train.ldapc setosa versicolor virginica
## setosa 20 0 0
## versicolor 0 19 1
## virginica 0 1 19
diag(prop.table(ct, 2))
## setosa versicolor virginica
## 1.00 0.95 0.95
train.ldap <- predict(train.lda, train[,1:4])
train.ldapc <- train.ldap$class
ct <- table(train.ldapc, train[,5])
ct
##
## train.ldapc setosa versicolor virginica
## setosa 30 0 0
## versicolor 0 29 1
## virginica 0 1 29
diag(prop.table(ct, 2))
## setosa versicolor virginica
## 1.0000000 0.9666667 0.9666667
#Так как данных мало, то лучше не делить на части, а использовать кросс-валидацию.
cv.lda <- lda(dfo[,1:4], dfo[,5], CV = T)
cv.ldac <- cv.lda$class
ct <- table(cv.ldac, dfo[,5])
ct
##
## cv.ldac setosa versicolor virginica
## setosa 50 0 0
## versicolor 0 48 1
## virginica 0 2 49
diag(prop.table(ct, 2))
## setosa versicolor virginica
## 1.00 0.96 0.98
#Этот блок будет работать, если число групп больше 2 (надо бы стрелки надписать именами признаков, иначе непонятно, какой от них толк)
#Это та плоскость, на которой различие максимально видно.
library(candisc)
## Loading required package: car
## Warning: package 'car' was built under R version 4.0.3
## Loading required package: carData
## Loading required package: heplots
##
## Attaching package: 'candisc'
## The following object is masked from 'package:stats':
##
## cancor
result.candisc <- candisc(train.manova)
ggplot(result.candisc$scores) +
geom_point(aes(x = Can1, y = Can2, color = Species)) +
stat_ellipse(aes(x = Can1, y = Can2, color = Species)) +
geom_segment(as.data.frame(result.candisc$structure), mapping = aes(x = 0, y = 0, xend = 6.5 * Can1, yend = 6.5 * Can2), arrow = arrow(angle = 10, type = "closed"), size = 0.5)
#Две следующие функции должны строить двумерные графики классификации, но у меня библиотека почему-то не запустилась. NE: Это из-за специфики данных.
#library(klaR)
#plot(partimat(Species ~ ., dfo, method="lda"))
#Следующий код имеет смысл использовать, только если всего две группы и предыдущий скаттерплот не построить.
ldahist(train.ldap$x[,1], g = test[,5]) #Показывает гистограмму значений индивидов по первой канонической переменной. Здесь как раз видно, почему четыре индивида были неправильно распределены классификацией (в группе 1 - два левых столбца = 3 индивида, в группе ноль самые правый столбец)
ldahist(full.ldap$x[,1], g = full.ldapc) #Аналогично для классификации по всем данным.
#Это работает только для двух классов. Поэтому быстро повторим анализ, убрав вид цветка SETOSA
library(FSA)
## Warning: package 'FSA' was built under R version 4.0.3
## ## FSA v0.8.31. See citation('FSA') if used in publication.
## ## Run fishR() for related website and fishR('IFAR') for related book.
##
## Attaching package: 'FSA'
## The following object is masked from 'package:car':
##
## bootCase
two.dfo <- Subset(dfo,Species!="setosa")
two.lda <- lda(two.dfo[,1:4], two.dfo[,5])
two.ldap <- predict(two.lda, two.dfo[,1:4])
two.cv.lda <- lda(two.dfo[,1:4], two.dfo[,5], CV = T)
library(ROCR) #Библиотека позволяющая строить ROC-кривые и считать AUC
pred <- prediction(two.ldap$posterior[,2], two.dfo[,5]) #Переводит данные в стандартизированный формат. Первый параметр - предсказания, второй - настоящая классификация.
perf <- performance(pred, "tpr", "fpr") #Строит ROC-кривую
plot(perf, colorize = TRUE) #рисует ее
AUC.ROCR <- performance(pred, "auc") #Считает AUC
abline(a = 0, b = 1)
text(x = .25, y = .65, paste("AUC = ", round(AUC.ROCR@y.values[[1]],5))) #Добавляет значение AUC на график
#Аналогично делаем для кросс-валидации
predcv <- prediction(two.cv.lda$posterior[,2], two.dfo[,5])
perfcv <- performance(predcv, "tpr", "fpr")
plot(perfcv, colorize = TRUE)
AUCcv <- performance(predcv, "auc")
abline(a = 0, b = 1)
text(x = .25, y = .65, paste("AUC = ", round(AUCcv@y.values[[1]],5)))
Рисуем ROC-кривые на одной картинке. Сверху находится ROC-кривая классификации на полных данных, что проверяется по AUC
plot(perfcv, colorize = TRUE)
par(new = T)
plot(perf, colorize = TRUE)
text(x = .25, y = .55, paste("AUC_cv = ", round(AUCcv@y.values[[1]],5)))
text(x = .25, y = .65, paste("AUC_full = ", round(AUC.ROCR@y.values[[1]],4)))
Аналогично, ROC можно построить на тестовых данных, но из-за малого количества наблюдений, на нее можно не смотреть.
#Все делается аналогичным образом, только буква l в lda заменяется на q.
full.qda <- qda(dfo[,1:4], dfo[,5])
full.qdap <- predict(full.qda, dfo[,1:4])
full.qdapc <- full.qdap$class
ct <- table(full.qdapc, dfo[,5])
ct
##
## full.qdapc setosa versicolor virginica
## setosa 50 0 0
## versicolor 0 48 1
## virginica 0 2 49
diag(prop.table(ct, 2))
## setosa versicolor virginica
## 1.00 0.96 0.98
Далее мы не будем повторять код, но нужно посмотреть на результат, построить ROC-кривую, сравнить результаты LDA и QDA