El AnĂ¡lisis Discriminante Lineal o Linear Discrimiant Analysis (LDA) es un mĂ©todo de clasificaciĂ³n supervisado de variables cualitativas en el que dos o mĂ¡s grupos son conocidos a priori y nuevas observaciones se clasifican en uno de ellos en funciĂ³n de sus caracterĂsticas. Haciendo uso del teorema de Bayes, LDA estima la probabilidad de que una observaciĂ³n, dado un determinado valor de los predictores, pertenezca a cada una de las clases de la variable cualitativa, \(P(Y=k|X=x)\). Finalmente se asigna la observaciĂ³n a la clase \(k\) para la que la probabilidad posterior es mayor.
Es una alternativa a la regresiĂ³n logĂstica cuando la variable cualitativa tiene mĂ¡s de dos niveles. Si bien existen extensiones de la regresiĂ³n logĂstica para mĂºltiples clases, LDA presenta una serie de ventajas:
Cuando se trata de un problema de clasificaciĂ³n con solo dos niveles, ambos mĂ©todos suelen llegar a resultados similares.
El proceso de un anĂ¡lisis discriminante puede resumirse en 6 pasos:
Disponer de un conjunto de datos de entrenamiento (training data) en el que se conoce a que grupo pertenece cada observaciĂ³n.
Calcular las probabilidades a priori (prior probabilities): la proporciĂ³n esperada de observaciones que pertenecen a cada grupo.
Determinar si la varianza o matriz de covarianzas es homogĂ©nea en todos los grupos. De esto dependerĂ¡ que se emplee LDA o QDA.
Estimar los parĂ¡metros necesarios para las funciones de probabilidad condicional, verificando que se cumplen las condiciones para hacerlo.
Calcular el resultado de la funciĂ³n discriminante. El resultado de esta determina a quĂ© grupo se asigna cada observaciĂ³n.
Utilizar validaciĂ³n cruzada (cross-validation) para estimar las probabilidades de clasificaciones errĂ³neas.
ConsidĂ©rense dos eventos \(A\) y \(B\), sabemos que el teorema de Bayes establece que: \[ P(B|A)=\dfrac{P(AB)}{P(A)}\] SupĂ³ngase que se desea clasificar una nueva observaciĂ³n en una de las \(K\) clases de una variable cualitativa \(Y\), siendo \(K≥2\), a partir de un solo predictor \(X\). Se dispone de las siguientes definiciones:
Se define como probabilidad a priori \((Ï€_k)\) la probabilidad de que una observaciĂ³n aleatoria pertenezca a la clase \(k\).
Se define \(f_k(X)≡P(X=x|Y=k)\) como la funciĂ³n de densidad de probabilidad condicional de \(X\) para una observaciĂ³n que pertenece a la clase \(k\). Cuanto mayor sea \(f_k(X)\) mayor la probabilidad de que una observaciĂ³n de la clase \(k\) adquiera una valor de \(X≈x\).
Se define como probabilidad posterior \(P(Y=k|X=x)\) la probabilidad de que una observaciĂ³n pertenezca a la clase \(k\) siendo \(x\) el valor del predictor.
Aplicando del teorema de Bayes se pueden conocer la probabilidad posterior para cada clase:
\[P(Y=k|X=x)=\dfrac{Ï€_k \, f_k(x)}{\displaystyle\sum_{j=1}^k Ï€_jf_j(x)}\] Para que la clasificaciĂ³n basada en Bayes sea posible, se necesita conocer la probabilidad poblacional de que una observaciĂ³n cualquiera pertenezca a cada clase (\(Ï€_k\)) y la probabilidad poblacional de que una observaciĂ³n que pertenece a la clase \(k\) adquiera el valor \(x\) en el predictor (\(f_k(X)≡P(X=x|Y=k)\)). En la prĂ¡ctica, raramente se dispone de esta informaciĂ³n, por lo que los parĂ¡metros tienen que ser estimados a partir de la muestra. Como consecuencia, el clasificador LDA obtenido se aproxima al clasificador de Bayes.
La capacidad del LDA para clasificar correctamente las observaciones depende de cĂ³mo de buenas sean las estimaciones de \(Ï€_k\) y \(f_k(X)\). Cuanto mĂ¡s cercanas al valor real, mĂ¡s se aproximarĂ¡ el clasificador LDA al clasificador de Bayes. En el caso de la prior probability (\(Ï€_k\)) la estimaciĂ³n suele ser sencilla, la probabilidad de que una observaciĂ³n cualquiera pertenezca a la clase \(k\) es igual al nĂºmero de observaciones de esa clase entre el nĂºmero total de observaciones \(\hat{Ï€}_k=\frac{n_k}{N}\).
La estimaciĂ³n de \(f_k(X)\) no es tan directa y para conseguirla se requiere de ciertas asunciones. Si se considera que \(f_k(X)\) se distribuye de forma normal en las \(K\) clases, entonces se puede estimar su valor a partir de la densidad normal.
Si ademĂ¡s se asume que la varianza es constante en todos los grupos \(σ^2_1=σ_2^2=\dots=σ^2_k=σ^2\), entonces, el sumatorio \(∑^K_{j=1} Ï€_jf_j(x)\) se simplifica en gran medida permitiendo calcular la probabilidad posterior segĂºn la ecuaciĂ³n:
\[P(Y=k|X=x)=\dfrac{Ï€_k\frac{1}{2Ï€}\exp\left(−\frac{1}{2σ^2}(x−μ_k)^2\right)}{\displaystyle\sum_{j=1}^k Ï€_j\frac{1}{2πσ}\exp \left(−\frac{1}{2σ^2}(x−μ_j)^2\right)}\] Esta ecuaciĂ³n se simplifica aun mĂ¡s mediante una transformaciĂ³n logarĂtmica de sus dos tĂ©rminos:
\[\log(P(Y=k|X=x))=x \frac{μ_k}{σ^2}−\frac{μ^2_k}{σ^2}+\log(Ï€_k)\] El tĂ©rmino lineal en el nombre AnĂ¡lisis discriminante lineal se debe al hecho de que la funciĂ³n discriminatoria es lineal respecto de \(X\).
En la prĂ¡ctica, a pesar de tener una certeza considerable de que \(X\) se distribuye de forma normal dentro de cada clase, los valores \(μ_1,\dots,μ_k\), \(Ï€_1,\dots,Ï€_k\) y \(σ^2\) se desconocen, por lo que tienen que ser estimados a partir de las observaciones. Las estimaciones empleadas en LDA son:
\[\begin{align*} \hat{μ}_k = & \frac{1}{n_k} \displaystyle\sum_{i:y_1} x_i \\ \hat{σ}_k = & \frac{1}{N−K} \displaystyle\sum_{k=1}^K \displaystyle\sum_{i:y_1} (x_i−\hat{μ}_k)^2 \\ \hat{\pi}_k = & \frac{n_k}{N} \end{align*}\]
\(\hat{μ}_k\) es la media de las observaciones del grupo \(k\), \(\hat{\sigma}_k\) es la media ponderada de las varianzas muestrales de las \(K\) clases y \(\hat{\pi}_k\) la proporciĂ³n de observaciones de la clase \(k\) respecto al tamaño total de la muestra.
La clasificaciĂ³n de Bayes consiste en asignar cada observaciĂ³n \(X = x\) a aquella clase para la que \(P(Y=k|X=x)\) sea mayor. En el caso particular de una variable cualitativa Y con solo dos niveles, se puede expresar la regla de clasificaciĂ³n como un cociente entre las dos probabilidades posteriores. Se asignarĂ¡ la observaciĂ³n a la clase 1 si \(\frac{P(Y=1|X=x)}{P(Y=2|X=x)}>1\), y a la clase 2 si es menor. En este caso particular el lĂmite de decisiĂ³n de Bayes viene dado por \(x=\frac{μ1+μ2}{2}\).
La siguiente imagen muestra dos grupos distribuidos de forma normal con medias \(μ_1= -1.25\), \(μ_2= 1.25\) y varianzas \(σ^2_1=σ^2_2= 1\). Dado que se conoce el valor real de las medias y varianzas poblacionales (esto en la realidad no suele ocurrir), se puede calcular el lĂmite de decisiĂ³n de Bayes $x== 0 (linea discontinua).
library(ggplot2)
## Warning in as.POSIXlt.POSIXct(Sys.time()): unable to identify current timezone 'H':
## please set environment variable 'TZ'
ggplot(data.frame(x = c(-4, 4)), aes(x)) +
stat_function(fun = dnorm, args = list(mean = -1.25, sd = 1),
color = "firebrick") +
stat_function(fun = dnorm, args = list(mean = 1.25, sd = 1), color = "green3") +
geom_vline(xintercept = 0, linetype = "longdash") +
theme_bw()
Si en lugar de conocer la verdadera distribuciĂ³n poblacional de cada grupo solo se dispone de muestras, escenario que suele ocurrir en los casos reales, el lĂmite de decisiĂ³n LDA se aproxima al verdadero lĂmite de decisiĂ³n de Bayes pero no es exacto. Cuanto mĂ¡s representativas sean las muestras mejor la aproximaciĂ³n.
set.seed(1981)
library(ggplot2)
n <-30
grupo_a <- rnorm(n, mean = -1.25, sd = 1)
grupo_b <- rnorm(n, mean = 1.25, sd = 1)
datos <- data.frame(valor = c(grupo_a, grupo_b),
grupo = rep(c("A","B"), each = n))
ggplot(data = datos, aes(x = valor, fill = grupo)) +
geom_histogram(alpha = 0.5, position = "identity") +
geom_vline(xintercept = 0, linetype = "longdash") +
geom_vline(xintercept = (mean(grupo_a) + mean(grupo_b))/2) +
annotate(geom = "text", x = 1.5, y = 9, label = "LĂmite decisiĂ³n LDA") +
annotate(geom = "text", x = -1.5, y = 10, label = "LĂmite decisiĂ³n Bayes") +
theme_bw() +
theme(legend.position = "top")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Los conceptos anteriormente descritos empleando un Ăºnico predictor pueden generalizarse para introducir mĂºltiples predictores en el modelo. La diferencia reside en que \(X\), en lugar de ser un Ăºnico valor, es un vector formado por el valor de \(p\) predictores \(X=(X_1,X_2,\dots,X_n)\) y que, en lugar de proceder de una distribuciĂ³n normal, procede de una distribuciĂ³n normal multivariante.
Un vector sigue una distribuciĂ³n \(k\)-normal multivariante si cada uno de los elementos individuales que lo forman sigue una distribuciĂ³n normal y lo mismo para toda combinaciĂ³n lineal de sus \(k\) elementos. Las siguientes imĂ¡genes muestran representaciones grĂ¡ficas de distribuciones normales multivariante de 2 elementos (distribuciĂ³n normal bivariante).
mu1 <- 0 # set mean x1
mu2 <- 0 # set mean x2
s11 <- 10 # set variance x1
s22 <- 10 # set variance x2
s12 <- 15 # set covariance x1 and x2
rho <- 0.5 # set correlation coefficient x1 and x2
x1 <- seq(-10,10,length = 41) # generate vector x1
x2 <- x1 # copy x1 to x2
f <- function(x1,x2) # multivariate function
{
term1 <- 1/(2 * pi * sqrt(s11*s22*(1 - rho^2)))
term2 <- -1/(2 * (1 - rho^2))
term3 <- (x1 - mu1)^2/s11
term4 <- (x2 - mu2)^2/s22
term5 <- -2*rho*((x1 - mu1)*(x2 - mu2))/(sqrt(s11)*sqrt(s22))
term1*exp(term2*(term3 + term4 - term5))
}
z <- outer(x1,x2,f) # calculate density values
persp(x1, x2, z, # 3-D plot
main = "DistribuciĂ³n multivariante con dos predictores",
col = "lightgreen",
theta = 30, phi = 20,
r = 50,
d = 0.1,
expand = 0.5,
ltheta = 90, lphi = 180,
shade = 0.75,
ticktype = "simple",
nticks = 5)
# Otra forma de representar una distribuciĂ³n bivariante
library(mvtnorm)
library(scatterplot3d)
ss <- matrix(c(1,0,0,1), ncol = 2)
x1000 <- rmvnorm(n = 1000, mean = c(0,0), sigma = ss)
scatterplot3d(x1000[,1], x1000[,2],
dmvnorm(x1000, mean = c(0,0), sigma = ss),
highlight = TRUE, xlab = "x", ylab = "y", zlab = "z")
Las condiciones que se deben cumplir para que un AnĂ¡lisis Discriminante Lineal sea vĂ¡lido son:
Cada predictor que forma parte del modelo se distribuye de forma normal en cada una de las clases de la variable respuesta. En el caso de mĂºltiples predictores, las observaciones siguen una distribuciĂ³n normal multivariante en todas las clases.
La varianza del predictor es igual en todas las clases de la variable respuesta. En el caso de mĂºltiples predictores, la matriz de covarianza es igual en todas las clases. Si esto no se cumple se recurre a AnĂ¡lisis Discriminante CuadrĂ¡tico (QDA).
Cuando la condiciĂ³n de normalidad no se cumple, LDA pierde precisiĂ³n pero aun asĂ puede llegar a clasificaciones relativamente buenas.
Un equipo de biĂ³logos quiere generar un modelo estadĂstico que permita identificar a que especie \((a \text{ o } b)\) pertenece un determinado insecto. Para ello se han medido tres variables (longitud de las patas, diĂ¡metro del abdomen y diĂ¡metro del Ă³rgano sexual) en 10 individuos de cada una de las dos especies.
input <- ("
especie pata abdomen organo_sexual
a 191 131 53
a 185 134 50
a 200 137 52
a 173 127 50
a 171 128 49
a 160 118 47
a 188 134 54
a 186 129 51
a 174 131 52
a 163 115 47
b 186 107 49
b 211 122 49
b 201 144 47
b 242 131 54
b 184 108 43
b 211 118 51
b 217 122 49
b 223 127 51
b 208 125 50
b 199 124 46
")
datos <- read.table(textConnection(input), header = TRUE)
library(ggplot2)
library(ggpubr)
## Loading required package: magrittr
p1 <- ggplot(data = datos, aes(x = pata, fill = especie)) +
geom_histogram(position = "identity", alpha = 0.5)
p2 <- ggplot(data = datos, aes(x = abdomen, fill = especie)) +
geom_histogram(position = "identity", alpha = 0.5)
p3 <- ggplot(data = datos, aes(x = organo_sexual, fill = especie)) +
geom_histogram(position = "identity", alpha = 0.5)
ggarrange(p1, p2, p3, nrow = 3, common.legend = TRUE)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
A nivel individual, la longitud de la pata parece ser la variable que mĂ¡s se diferencia entre especies (menor solapamiento entre poblaciones).
pairs(x = datos[, c("pata","abdomen","organo_sexual")],
col = c("blue", "green4")[datos$especie], pch = 19)
El par de variables abdomen-pata
y el par pata-organo_sexual
parecen separar bien las dos especies.
library(scatterplot3d)
scatterplot3d(datos$pata, datos$abdomen, datos$organo_sexual,
color = c("firebrick", "green3")[datos$especie], pch = 19,
grid = TRUE, xlab = "pata", ylab = "abdomen",
zlab = "organo sexual", angle = 65, cex.axis = 0.6)
legend("topleft",
bty = "n", cex = 0.8,
title = "Especie",
c("a", "b"), fill = c("firebrick", "green3"))
La representaciĂ³n de las tres variables de forma simultĂ¡nea parece indicar que las dos especies sĂ estĂ¡n bastante separadas en el espacio 3D generado.
Como no se dispone de informaciĂ³n sobre la abundancia relativa de las especies a nivel poblacional, se considera como probabilidad a pirori de cada especie el nĂºmero de observaciones de la especie entre el nĂºmero de observaciones totales.
\[\hat{Ï€}_a=\hat{Ï€}_b=\dfrac{10}{20}=0.5\]
De entre los diferentes test que contrastan la homogeneidad de varianza, el mĂ¡s recomendable cuando solo hay un predictor, dado que se asume que se distribuye de forma normal, es el test de Barttlet. Cuando se emplean mĂºltiples predictores, se tiene que contrastar que la matriz de covarianzas \(∑\) es constante en todos los grupos, siendo recomendable comprobar tambiĂ©n la homogeneidad de varianza para cada predictor a nivel individual.
El test Box fue desarrollado por el matemĂ¡tico Box (1949) como una extensiĂ³n del test de Barttlet para escenarios multivariante y permite contrastar la igualdad de matrices entre grupos. El test Box es muy sensible a violaciones de la normalidad multivariante, por lo que esta debe ser contrastada con anterioridad.
# RepresentaciĂ³n mediante Histograma de cada variable para cada especie
par(mfcol = c(2, 3))
for (k in 2:4) {
j0 <- names(datos)[k]
#br0 <- seq(min(datos[, k]), max(datos[, k]), le = 11)
x0 <- seq(min(datos[, k]), max(datos[, k]), le = 50)
for (i in 1:2) {
i0 <- levels(datos$especie)[i]
x <- datos[datos$especie == i0, j0]
hist(x, proba = T, col = grey(0.8), main = paste("especie", i0),
xlab = j0)
lines(x0, dnorm(x0, mean(x), sd(x)), col = "red", lwd = 2)
}
}
# RepresentaciĂ³n de cuantiles normales de cada variable para cada especie
par(mfcol = c(2, 3))
for (k in 2:4) {
j0 <- names(datos)[k]
x0 <- seq(min(datos[, k]), max(datos[, k]), le = 50)
for (i in 1:2) {
i0 <- levels(datos$especie)[i]
x <- datos[datos$especie == i0, j0]
qqnorm(x, main = paste("especie", i0, j0), pch = 19, col = i + 1)
qqline(x)
}
}
# Contraste de normalidad Shapiro-Wilk para cada variable en cada especie
library(reshape2)
library(knitr)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
datos_tidy <- melt(datos, value.name = "valor")
## Using especie as id variables
kable(datos_tidy %>% group_by(especie, variable) %>% summarise(p_value_Shapiro.test = shapiro.test(valor)$p.value))
especie | variable | p_value_Shapiro.test |
---|---|---|
a | pata | 0.7763034 |
a | abdomen | 0.1845349 |
a | organo_sexual | 0.6430844 |
b | pata | 0.7985711 |
b | abdomen | 0.5538213 |
b | organo_sexual | 0.8217855 |
# Misma operaciĂ³n con aggregate
aggregate(formula = valor ~ especie + variable, data = datos_tidy,
FUN = function(x){shapiro.test(x)$p.value})
## especie variable valor
## 1 a pata 0.7763034
## 2 b pata 0.7985711
## 3 a abdomen 0.1845349
## 4 b abdomen 0.5538213
## 5 a organo_sexual 0.6430844
## 6 b organo_sexual 0.8217855
No hay evidencias de falta de normalidad univariante en ninguna de las variables empleadas como predictores en ninguno de los grupos.
El paquete MVN
contiene funciones que permiten realizar los tres test de hipĂ³tesis comĂºnmente empleados para evaluar la normalidad multivariante (Mardia, Henze-Zirkler y Royston) y tambiĂ©n funciones para identificar outliers que puedan influenciar en el contraste. Para informaciĂ³n detallada de cada uno consultar https://cran.r-project.org/web/packages/MVN/vignettes/MVN.pdf.
library(MVN)
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
## sROC 0.1-2 loaded
outliers <- mvn(data = datos[,-1], mvnTest = "hz", multivariateOutlierMethod = "quan")
royston_test <- mvn(data = datos[,-1], mvnTest = "royston", multivariatePlot = "qq")
royston_test$multivariateNormality
## Test H p value MVN
## 1 Royston 0.4636176 0.9299447 YES
hz_test <- mvn(data = datos[,-1], mvnTest = "hz")
hz_test$multivariateNormality
## Test HZ p value MVN
## 1 Henze-Zirkler 0.7870498 0.07666139 YES
A pesar de los 5 outliers detectados, ninguno de los dos test encuentran evidencias significativas \((α=0.05)\) de falta de normalidad multivariante.
Finalmente, mediante la funciĂ³n boxM()
del paquete biotools se realiza el contraste de matrices de covarianza.
library(biotools)
## Loading required package: rpanel
## Loading required package: tcltk
## Package `rpanel', version 1.1-4: type help(rpanel) for summary information
## Loading required package: tkrplot
## Loading required package: MASS
##
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
##
## select
## Loading required package: lattice
## Loading required package: SpatialEpi
## Loading required package: sp
## ---
## biotools version 3.1
##
boxM(data = datos[, 2:4], grouping = datos[, 1])
##
## Box's M-test for Homogeneity of Covariance Matrices
##
## data: datos[, 2:4]
## Chi-Sq (approx.) = 9.831, df = 6, p-value = 0.132
Se puede aceptar que la matriz de covarianza es igual en todos los grupos.
Estos dos pasos se realizan mediante la funciĂ³n lda
del paquete MASS
. lda
realiza la clasificaciĂ³n mediante la aproximaciĂ³n de Fisher.
modelo_lda <- lda(formula = especie ~ pata + abdomen + organo_sexual,
data = datos)
Una vez obtenidas las funciones discriminantes, se puede clasificar un nuevo insecto en funciĂ³n de sus medidas. Por ejemplo, un nuevo espĂ©cimen cuyas medidas sean: pata = 194, abdomen = 124, organo_sexual = 49.
nuevas_observaciones <- data.frame(pata = 194, abdomen = 124,
organo_sexual = 49)
predict(object = modelo_lda, newdata = nuevas_observaciones)
## $class
## [1] b
## Levels: a b
##
## $posterior
## a b
## 1 0.05823333 0.9417667
##
## $x
## LD1
## 1 0.5419421
El resultado muestra que, segĂºn la funciĂ³n discriminante, la probabilidad posterior de que el espĂ©cimen pertenezca a la especie \(b\) es del \(94.2\%\) frente al \(5.8\%\) de que pertenezca a la especie \(a\).
predicciones <- predict(object = modelo_lda, newdata = datos[, -1],
method = "predictive")
table(datos$especie, predicciones$class,
dnn = c("Clase real", "Clase predicha"))
## Clase predicha
## Clase real a b
## a 10 0
## b 0 10
trainig_error <- mean(datos$especie != predicciones$class) * 100
paste("trainig_error=", trainig_error, "%")
## [1] "trainig_error= 0 %"
Empleando las mismas observaciones con las que se ha generado el modelo discriminante (trainig data), la precisiĂ³n de clasificaciĂ³n es del \(100\%\). Evaluar un modelo con los mismos datos con los que se ha creado suele resultar en estimaciones de la precisiĂ³n demasiado optimistas (error muy bajo). Por lo tanto, la estimaciĂ³n del test error mediante validaciĂ³n cruzada es mĂ¡s adecuada para obtener una evaluaciĂ³n realista del modelo.
La siguiente imagen muestra la representaciĂ³n de las observaciones, coloreadas por la verdadera especie a la que pertenecen y acompañadas por una etiqueta con la especie que ha predicho el LDA.
with(datos, {
s3d <- scatterplot3d(pata, abdomen, organo_sexual,
color = c("firebrick", "green3")[datos$especie],
pch = 19, grid = TRUE, xlab = "pata", ylab = "abdomen",
zlab = "organo sexual", angle = 65, cex.axis = 0.6)
s3d.coords <- s3d$xyz.convert(pata, abdomen, organo_sexual)
# convierte coordenadas 3D en proyecciones 2D
text(s3d.coords$x, s3d.coords$y, # cordenadas x, y
labels = datos$especie, # texto
cex = .8, pos = 4)
legend("topleft",
bty = "n", cex = 0.8,
title = "Especie",
c("a", "b"), fill = c("firebrick", "green3"))
})
data("iris")
kable(head(iris, n = 10))
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | Species |
---|---|---|---|---|
5.1 | 3.5 | 1.4 | 0.2 | setosa |
4.9 | 3.0 | 1.4 | 0.2 | setosa |
4.7 | 3.2 | 1.3 | 0.2 | setosa |
4.6 | 3.1 | 1.5 | 0.2 | setosa |
5.0 | 3.6 | 1.4 | 0.2 | setosa |
5.4 | 3.9 | 1.7 | 0.4 | setosa |
4.6 | 3.4 | 1.4 | 0.3 | setosa |
5.0 | 3.4 | 1.5 | 0.2 | setosa |
4.4 | 2.9 | 1.4 | 0.2 | setosa |
4.9 | 3.1 | 1.5 | 0.1 | setosa |
Como no se dispone de informaciĂ³n sobre la abundancia relativa de las especies a nivel poblacional, se considera como probabilidad previa de cada especie el nĂºmero de observaciones de la especie entre el nĂºmero de observaciones totales.
\[\hat{Ï€}_{\text{setosa}}=\hat{Ï€}_{\text{versicolor}}=\hat{Ï€}_{\text{virginica}}=\dfrac{50}{150}=\dfrac{1}{3}\]
#representaciĂ³n mediante histograma de cada variable para cada especie
par(mfcol = c(3, 4))
for (k in 1:4) {
j0 <- names(iris)[k]
x0 <- seq(min(iris[, k]), max(iris[, k]), le = 50)
for (i in 1:3) {
i0 <- levels(iris$Species)[i]
x <- iris[iris$Species == i0, j0]
hist(x, proba = T, col = grey(0.8), main = paste("especie", i0),
xlab = j0)
lines(x0, dnorm(x0, mean(x), sd(x)), col = "red", lwd = 2)
}
}
#representaciĂ³n de cuantiles normales de cada variable para cada especie
for (k in 1:4) {
j0 <- names(iris)[k]
x0 <- seq(min(iris[, k]), max(iris[, k]), le = 50)
for (i in 1:3) {
i0 <- levels(iris$Species)[i]
x <- iris[iris$Species == i0, j0]
qqnorm(x, main = paste(i0, j0), pch = 19, col = i + 1)
qqline(x)
}
}
#Contraste de normalidad Shapiro-Wilk para cada variable en cada especie
library(reshape2)
library(knitr)
library(dplyr)
datos_tidy <- melt(iris, value.name = "valor")
## Using Species as id variables
kable(datos_tidy %>% group_by(Species, variable) %>% summarise(p_value_Shapiro.test = round(shapiro.test(valor)$p.value,5)))
Species | variable | p_value_Shapiro.test |
---|---|---|
setosa | Sepal.Length | 0.45951 |
setosa | Sepal.Width | 0.27153 |
setosa | Petal.Length | 0.05481 |
setosa | Petal.Width | 0.00000 |
versicolor | Sepal.Length | 0.46474 |
versicolor | Sepal.Width | 0.33800 |
versicolor | Petal.Length | 0.15848 |
versicolor | Petal.Width | 0.02728 |
virginica | Sepal.Length | 0.25831 |
virginica | Sepal.Width | 0.18090 |
virginica | Petal.Length | 0.10978 |
virginica | Petal.Width | 0.08695 |
Observamos que la variable petal.width
no parece tener una distribuciĂ³n normal en los grupos setosa y versicolor.
library(MVN)
outliers <- mvn(data = iris[,-5], mvnTest = "hz", multivariateOutlierMethod = "quan")
royston_test <- mvn(data = iris[,-5], mvnTest = "royston", multivariatePlot = "qq")
royston_test$multivariateNormality
## Test H p value MVN
## 1 Royston 50.39667 3.098229e-11 NO
hz_test <- mvn(data = iris[,-5], mvnTest = "hz")
hz_test$multivariateNormality
## Test HZ p value MVN
## 1 Henze-Zirkler 2.336394 0 NO
Ambos test muestran evidencias significativas de falta de normalidad multivariante. LDA tiene cierta robustez frente a la falta de normalidad multivariante, pero es importante tenerlo en cuenta en la conclusiĂ³n del anĂ¡lisis.
library(biotools)
boxM(data = iris[, -5], grouping = iris[, 5])
##
## Box's M-test for Homogeneity of Covariance Matrices
##
## data: iris[, -5]
## Chi-Sq (approx.) = 140.94, df = 20, p-value < 2.2e-16
El test Box muestra evidencias de que la matriz de covarianza no es constante en todos los grupos, lo que a priori descartarĂa el mĂ©todo LDA en favor del QDA. Sin embargo, como el test Box’s M es muy sensible a la falta de normalidad multivariante, con frecuencia resulta significativo no porque la matriz de covarianza no sea constante sino por la falta de normalidad, cosa que ocurre para los datos de Iris. Por esta razĂ³n se va a asumir que la matriz de covarianza sĂ es constante y que LDA puede alcanzar una buena precisiĂ³n en la clasificaciĂ³n. En la evaluaciĂ³n del modelo se verĂ¡ como de buena es esta aproximaciĂ³n. AdemĂ¡s, en las conclusiones se debe explicar la asunciĂ³n hecha.
library(MASS)
modelo_lda <- lda(Species ~ Sepal.Width + Sepal.Length + Petal.Length +
Petal.Width, data = iris)
modelo_lda
## Call:
## lda(Species ~ Sepal.Width + Sepal.Length + Petal.Length + Petal.Width,
## data = iris)
##
## Prior probabilities of groups:
## setosa versicolor virginica
## 0.3333333 0.3333333 0.3333333
##
## Group means:
## Sepal.Width Sepal.Length Petal.Length Petal.Width
## setosa 3.428 5.006 1.462 0.246
## versicolor 2.770 5.936 4.260 1.326
## virginica 2.974 6.588 5.552 2.026
##
## Coefficients of linear discriminants:
## LD1 LD2
## Sepal.Width 1.5344731 2.16452123
## Sepal.Length 0.8293776 0.02410215
## Petal.Length -2.2012117 -0.93192121
## Petal.Width -2.8104603 2.83918785
##
## Proportion of trace:
## LD1 LD2
## 0.9912 0.0088
predicciones <- predict(object = modelo_lda, newdata = iris[, -5])
table(iris$Species, predicciones$class, dnn = c("Clase real", "Clase predicha"))
## Clase predicha
## Clase real setosa versicolor virginica
## setosa 50 0 0
## versicolor 0 48 2
## virginica 0 1 49
trainig_error <- mean(iris$Species != predicciones$class) * 100
paste("trainig_error =", trainig_error, "%")
## [1] "trainig_error = 2 %"
Solo 3 de las 150 predicciones que ha realizado el modelo han sido errĂ³neas. El error es muy bajo (\(2\%\)), lo que apunta a que el modelo es bueno. Sin embargo, para validarlo es necesario un nuevo set de datos con el que calcular el test error o recurrir a validaciĂ³n cruzada.
La funciĂ³n partimat()
del paquete klar
permite representar los lĂmites de clasificaciĂ³n de un modelo discriminante lineal o cuadrĂ¡tico para cada par de predictores. Cada color representa una regiĂ³n de clasificaciĂ³n acorde al modelo, se muestra el centroide de cada regiĂ³n y el valor real de las observaciones.
library(klaR)
partimat(Species ~ Sepal.Width + Sepal.Length + Petal.Length + Petal.Width,
data = iris, method = "lda", prec = 200,
image.colors = c("darkgoldenrod1", "snow2", "skyblue2"),
col.mean = "firebrick")
El clasificador cuadrĂ¡tico o Quadratic Discriminat Analysis (QDA) se asemeja en gran medida al LDA, con la Ăºnica diferencia de que el QDA considera que cada clase \(k\) tiene su propia matriz de covarianza (\(∑_k\)) y, como consecuencia, la funciĂ³n discriminante toma forma cuadrĂ¡tica:
\[\log(P(Y=k|X=x))=−\dfrac{1}{2}\log|Σ_k|−\dfrac{1}{2}(x−μ_k)^T Σ^{−1}_k(x−μ_k)+\log(π_k)\]
Para poder calcular la probabilidad posteriro a partir de esta ecuaciĂ³n discriminante es necesario estimar, para cada clase, \(∑_k\), \(μ_k\) y \(Ï€_k\) a partir de la muestra.
QDA genera lĂmites de decisiĂ³n curvos por lo que puede aplicarse a situaciones en las que la separaciĂ³n entre grupos no es lineal.
Se dispone de los siguientes datos simulados.
set.seed(8558)
grupoA_x <- seq(from = -3, to = 4, length.out = 100)
grupoA_y <- 6 + 0.15 * grupoA_x - 0.3 * grupoA_x^2 + rnorm(100, sd = 1)
grupoA <- data.frame(variable_z = grupoA_x, variable_w = grupoA_y, grupo = "A")
grupoB_x <- rnorm(n = 100, mean = 0.5, sd = 0.8)
grupoB_y <- rnorm(n = 100, mean = 2, sd = 0.8)
grupoB <- data.frame(variable_z = grupoB_x, variable_w = grupoB_y, grupo = "B")
datos <- rbind(grupoA, grupoB)
plot(datos[, 1:2], col = datos$grupo, pch = 19)
La separaciĂ³n entre los grupos no es de tipo lineal, sino que muestra cierta curvatura. En este tipo de escenarios el mĂ©todo QDA es mĂ¡s adecuado que el LDA.
library(ggplot2)
library(ggpubr)
p1 <- ggplot(data = datos, aes(x = variable_z, fill = grupo)) +
geom_histogram(position = "identity", alpha = 0.5)
p2 <- ggplot(data = datos, aes(x = variable_w, fill = grupo)) +
geom_histogram(position = "identity", alpha = 0.5)
ggarrange(p1, p2, nrow = 2, common.legend = TRUE, legend = "bottom")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
La variable \(W\) permite discriminar entre grupos mejor que la variable \(z\).
DistribuciĂ³n de los predictores de forma individual:
# RepresentaciĂ³n mediante histograma de cada variable para cada grupo
par(mfcol = c(2, 2))
for (k in 1:2) {
j0 <- names(datos)[k]
x0 <- seq(min(datos[, k]), max(datos[, k]), le = 50)
for (i in 1:2) {
i0 <- levels(datos$grupo)[i]
x <- datos[datos$grupo == i0, j0]
hist(x, proba = T, col = grey(0.8), main = paste("grupo", i0),
xlab = j0)
lines(x0, dnorm(x0, mean(x), sd(x)), col = "red", lwd = 2)
}
}
#representaciĂ³n de cuantiles normales de cada variable para cada grupo
for (k in 1:2) {
j0 <- names(datos)[k]
x0 <- seq(min(datos[, k]), max(datos[, k]), le = 50)
for (i in 1:2) {
i0 <- levels(datos$grupo)[i]
x <- datos[datos$grupo == i0, j0]
qqnorm(x, main = paste(i0, j0), pch = 19, col = i + 1)
qqline(x)
}
}
#Contraste de normalidad Shapiro-Wilk para cada variable en cada grupo
library(reshape2)
datos_tidy <- melt(datos, value.name = "valor")
## Using grupo as id variables
library(dplyr)
datos_tidy %>%
group_by(grupo, variable) %>%
summarise(p_value_Shapiro.test = round(shapiro.test(valor)$p.value,5))
## # A tibble: 4 x 3
## # Groups: grupo [2]
## grupo variable p_value_Shapiro.test
## <fct> <fct> <dbl>
## 1 A variable_z 0.00172
## 2 A variable_w 0.0932
## 3 B variable_z 0.625
## 4 B variable_w 0.810
La variable \(Z\) no se distribuye de forma normal en el grupo \(A\).
library(MVN)
outliers <- mvn(data = datos[,-3], mvnTest = "hz", multivariateOutlierMethod = "quan")
royston_test <- mvn(data = datos[,-3], mvnTest = "royston", multivariatePlot = "qq")
royston_test$multivariateNormality
## Test H p value MVN
## 1 Royston 29.11024 4.77274e-07 NO
hz_test <- mvn(data = datos[,-3], mvnTest = "hz")
hz_test$multivariateNormality
## Test HZ p value MVN
## 1 Henze-Zirkler 6.739874 5.317968e-14 NO
Ambos test muestran evidencias significativas de falta de normalidad multivariante. QDA tiene cierta robustez frente a la falta de normalidad multivariante, pero es importante tenerlo en cuenta en la conclusiĂ³n del anĂ¡lisis.
library(MASS)
modelo_qda <- qda(grupo ~ variable_z + variable_w, data = datos)
modelo_qda
## Call:
## qda(grupo ~ variable_z + variable_w, data = datos)
##
## Prior probabilities of groups:
## A B
## 0.5 0.5
##
## Group means:
## variable_z variable_w
## A 0.5000000 4.615307
## B 0.4864889 1.992911
predicciones <- predict(object = modelo_qda, newdata = datos)
table(datos$grupo, predicciones$class,
dnn = c("Clase real", "Clase predicha"))
## Clase predicha
## Clase real A B
## A 97 3
## B 7 93
trainig_error <- mean(datos$grupo != predicciones$class) * 100
paste("trainig_error=",trainig_error,"%")
## [1] "trainig_error= 5 %"
library(klaR)
partimat(formula = grupo ~ variable_z + variable_w, data = datos,
method = "qda", prec = 400,
image.colors = c("darkgoldenrod1", "skyblue2"),
col.mean = "firebrick")