Los datos del fichero Wisconsin18.RData proceden de un estudio sobre diagnóstico del cáncer de mama por imagen. Mediante una punción con aguja fina se extrae una muestra del tejido sospechoso de la paciente. La muestra se tiñe para resaltar los núcleos de las células y se determinan los límites exactos de los núcleos. Las variables consideradas corresponden a los valores medios de distintos aspectos de la forma de los núcleos de cada muestra.
Tinción de los núcleos celulares
El fichero contiene un data.frame, llamado Wisconsin cuyas variables son:
tipo que contiene el tipo de tumor (benigno o maligno).Más información sobre los datos se puede encontrar en esta dirección.
Cargamos los paquetes que vamos a necesitar y los datos. Mediante el comando head vemos los datos de algunos de los primeros pacientes del fichero. El comando table permite saber cuántos tumores hay de cada tipo:
library(MASS)
library(class)
load(url('http://verso.mat.uam.es/~joser.berrendero/datos/Wisconsin18.RData'))
head(Wisconsin)## radius texture perimeter area smoothness compactness concavity
## 1 13.540 14.36 87.46 566.3 0.09779 0.08129 0.06664
## 2 13.080 15.71 85.63 520.0 0.10750 0.12700 0.04568
## 3 9.504 12.44 60.34 273.9 0.10240 0.06492 0.02956
## 4 13.030 18.42 82.61 523.8 0.08983 0.03766 0.02562
## 5 8.196 16.84 51.71 201.9 0.08600 0.05943 0.01588
## 6 12.050 14.63 78.04 449.3 0.10310 0.09092 0.06592
## concavepoints symmetry fractal tipo
## 1 0.047810 0.1885 0.05766 benigno
## 2 0.031100 0.1967 0.06811 benigno
## 3 0.020760 0.1815 0.06905 benigno
## 4 0.029230 0.1467 0.05863 benigno
## 5 0.005917 0.1769 0.06503 benigno
## 6 0.027490 0.1675 0.06043 benigno
table(Wisconsin$tipo)##
## benigno maligno
## 357 212
Representamos la matriz de diagramas de dispersión de los datos y los diagramas de estrella de las observaciones:
# Diagramas de dispersion
pairs(Wisconsin[,-11], col=Wisconsin$tipo)# Diagrama de estrellas
stars(Wisconsin[-11], col.stars=Wisconsin$tipo, labels=NULL)Se dispone en total de 569 imágenes, de las cuáles 357 corresponden a tumores benignos y 212 a malignos. Vemos que algunas variables están estrechamente relacionadas (por ejemplo, el radio, el perímetro y el área). También se observa que en términos generales, los tumores malignos corresponden a valores más altos de las variables.
El comando básico es lda del paquete MASS. Tiene tres argumentos principales:
resultado.lda <- lda(tipo ~ ., data=Wisconsin, prior = c(0.5, 0.5))El objeto resultado.lda es una lista con todos los resultados del análisis. Veamos sus elementos más importantes. El vector resultado.lda$means contiene los vectores de medias \(\bar{x}_0\) y \(\bar{x}_1\) correspondientes a cada grupo:
resultado.lda$means## radius texture perimeter area smoothness compactness
## benigno 12.14652 17.91476 78.07541 462.7902 0.09247765 0.08008462
## maligno 17.46283 21.60491 115.36538 978.3764 0.10289849 0.14518778
## concavity concavepoints symmetry fractal
## benigno 0.04605762 0.02571741 0.174186 0.06286739
## maligno 0.16077472 0.08799000 0.192909 0.06268009
Como habíamos observado en las representaciones gráficas, los valores medios del grupo de tumores malignos son superiores a los del grupo de benignos. El vector resultado.lda$scaling contiene los coeficientes de la función discriminante lineal de Fisher:
resultado.lda$scaling## LD1
## radius 2.173832578
## texture 0.097479319
## perimeter -0.243883158
## area -0.004235635
## smoothness 8.610211091
## compactness 0.431476344
## concavity 3.592356858
## concavepoints 28.529778564
## symmetry 4.489073661
## fractal -0.529214778
barplot(as.vector(resultado.lda$scaling), names.arg = names(Wisconsin[-11]))Si llamamos \(w\) al vector anterior, clasificamos una observación \(x\) en el grupo 1 siempre que \[
w^\top \left(x-\frac{\bar{x}_0 + \bar{x}_1}{2}\right) > 0,
\] y en el grupo 0 en caso contrario. El valor del término de la izquierda de la desigualdad es la puntuación discriminante de \(x\). Al aplicar plot a resultado.lda obtenemos histogramas de las puntuaciones discriminantes estandarizadas de las observaciones de cada grupo:
plot(resultado.lda)Vemos que, efectivamente, las puntuaciones en el grupo 0 tienden a ser negativas y en el grupo 1 tienden a ser positivas. Sin embargo, hay una zona de solapamiento de las puntuaciones discriminantes que llevará a un cierto porcentaje de errores de clasificación. Con el fin de calcular la tasa de error aparente, usamos el comando predict para clasificar los datos de la muestra y luego contamos la proporción de veces que nos hemos equivocado:
n <- resultado.lda$N
tipo.prediccion <- predict(resultado.lda)$class
1 - sum(tipo.prediccion == Wisconsin$tipo) / n## [1] 0.05975395
Esto significa que nos hemos equivocado para aproximadamente un 5.97 % de las observaciones. Para calcular la tasa de error por validación cruzada, seleccionamos CV=TRUE en el comando lda, de la siguiente forma:
tipo.prediccion.cv <- lda(tipo ~ ., data = Wisconsin, prior=c(0.5, 0.5), CV=TRUE)$class
1 - sum(tipo.prediccion.cv == Wisconsin$tipo) / n## [1] 0.06326889
Si queremos clasificar nuevos vectores de observaciones, tenemos que crear previamente un data frame que los contenga y luego usar de nuevo el comando predict. El siguiente código genera aleatoriamente dos vectores de observaciones y los clasifica. También permite obtener las dos puntuaciones discriminantes correspondientes:
# Predicciones
x1 <- rnorm(10)
x2 <- rnorm(10)
nuevas.obs <- data.frame(rbind(x1, x2))
names(nuevas.obs) <- names(Wisconsin[1:10])
nuevas.obs## radius texture perimeter area smoothness compactness
## x1 -1.862535 1.51021093 0.3085101 0.3750865 0.04350034 0.1270433
## x2 1.634009 0.09543244 -0.1420134 -0.6686547 0.27117558 -0.8899755
## concavity concavepoints symmetry fractal
## x1 -0.6977150 -0.7647932 0.8464859 -1.19973053
## x2 0.3748172 -0.8918474 -0.9102909 0.04594343
predict(resultado.lda, nuevas.obs)$class # resultado de la clasificación## [1] benigno benigno
## Levels: benigno maligno
predict(resultado.lda, nuevas.obs)$x # puntuaciones discriminantes## LD1
## x1 -34.58251
## x2 -33.80115
Las probabilidades a priori que lda utiliza por defecto son las proporciones muestrales de cada grupo, es decir, 357/569 y 212/569 en el ejemplo. Calcula las tasas de error (usando validación cruzada) que resultan cuando consideramos estas probabilidades a priori (esta es la opción por defecto).
Al mirar la función discriminante de Fisher, vemos que las variables smoothness y concavepoints son las que reciben una mayor ponderación. Repite los cálculos teniendo en cuenta únicamente estas dos variables. ¿Cuál es la función discriminante lineal de Fisher en este caso? ¿Cuáles es la nueva tasa de error usando validación cruzada?
El comando básico es qda del paquete MASS. Este comando se aplica de forma totalmente análoga a lda. Al igual que la versión lda con validación cruzada, al aplicar predict al resultado de este comando se obtiene una lista, cuyo elemento class nos da el grupo en el que clasificamos cada observación. Esto nos permite calcular la tasa de error aparente:
resultado.qda <- qda(Wisconsin$tipo ~ ., data = Wisconsin, prior=c(0.5, 0.5))
tipo.prediccion.qda <- predict(resultado.qda)$class
1-sum(tipo.prediccion.qda == Wisconsin$tipo) / n## [1] 0.05799649
Si queremos obtener la tasa de error por validación cruzada, usamos el argumento CV=TRUE del comando qda, de la siguiente forma:
clase.qdacv <- qda(Wisconsin$tipo ~ .,
data = Wisconsin,
prior=c(0.5,0.5),
CV=TRUE)
1 - sum(clase.qdacv$class == Wisconsin$tipo) / n## [1] 0.06678383
Los resultados no mejoran (de hecho empeoran ligeramente) al compararlos con los obtenidos mediante la función lineal de Fisher, que además es más fácil de interpretar.
Para clasificar nuevas observaciones, se procede de forma análoga a como se hacía en clasificación lineal:
predict(resultado.qda, nuevas.obs)$class## [1] benigno maligno
## Levels: benigno maligno
smoothness y concavepoints.En este caso, el comando principal es knn del paquete class. Tiene cuatro argumentos:
El resultado es un vector con los grupos en los que se clasifican los datos de test.
Para calcular la tasa de error aparente consideramos el mismo conjunto de entrenamiento y de test. Por ejemplo, si \(k=3\),
resultado.knn3 <- knn(Wisconsin[,-11], Wisconsin[,-11], Wisconsin[,11], 3)
error.knn3 <- 1 - sum(resultado.knn3 == Wisconsin$tipo)/ n
error.knn3## [1] 0.07029877
Para obtener la tasa de error por validación cruzada, usamos el comando knn.cv. Este comando tiene los mismos argumentos que knn excluyendo los datos de test (que no tienen sentido al aplicar validación cruzada):
resultado.knn3cv <- knn.cv(Wisconsin[,-11], Wisconsin[,11], 3)
error.knn3cv <- 1 - sum(resultado.knn3cv == Wisconsin$tipo)/ n
error.knn3cv## [1] 0.1230228
Para clasificar nuevas observaciones, basta escribir
knn(Wisconsin[,-11], nuevas.obs, Wisconsin[,11], 3)## [1] benigno benigno
## Levels: benigno maligno
Calcula la tasa de error aparente utilizando los 4 vecinos más próximos. Repítelo varias veces. ¿Qué se observa?
Estudia cómo van cambiando las tasas de error aparente y por validación cruzada a medida que aumenta el valor de \(k\).
Calcula las tasas de error aparente y por validación cruzada si \(k=3\) pero usando únicamente las variables smoothness y concavepoints.