Se clasificarán variables cuantitativas para predecir variables categóricas, cubriendo los conceptos de preprocesamiento de datos para su uso adecuado al hacer predicciones. Los pasos a seguir son:
-Reconocer situaciones en las que un clasificador sería apropiado para hacer predicciones.
-Describir qué es un conjunto de datos de entrenamiento y cómo se usa en la clasificación.
-Interpretar la salida de un clasificador por regresión logística
-Realizar la clasificación logística usando tidymodels
-Explicar por qué se debe centrar, escalar y equilibrar la información en la modelación predictiva.
-Realizar pre-procesamiento para centrar, escalar y equilibrar un conjunto de datos mediante recipes
-Combinar la modelación en un flujo de trabajo de tidymodels
En muchas situaciones, se hacen predicciones basadas en la situación actual así como en experiencias pasadas. Por ejemplo, en medicina se puede querer diagnosticar a pacientes en enfermedad o salud según sus síntomas y la experiencia previa de la especialidad médica con cada paciente. Otros ejemplos clásicos incluyen querer etiquetar un correo electrónico dado como spam o no spam dependiendo de los datos de texto del correo electrónico anterior, o predecir si un pedido es fraudulento o no.
Estas tareas ejemplifican la clasificación, es decir, predecir una clase categórica (a veces llamada etiqueta) para una observación dadas sus otras variables cuantitativas o cualitativas (a veces llamadas características). Generalmente, un clasificador asigna una observación (nuevas muestras de pacientes) a una clase (enfermedad o salud) en función de su similitud con otras observaciones de las que se conoce la clase (pacientes anteriores con síntomas y enfermedades conocidas). Estas observaciones con clases conocidas usadas como base para la predicción se denominan conjunto de entrenamiento, porque se usan para entrenar o enseñar a la clasificación, de modo que se puedan hacer predicciones sobre nueva infortmación no vista anteriormente.
Hay mucha algorítmica de clasificación posible para predecir una clase o etiqueta categórica para una observación. Además, existen muchas variaciones en el problema de clasificación básico, v. gr., clasificación binaria donde solo están involucradas dos clases, o clasificación multiclase, que implica asignar un objeto a una de varias clases. Se aplicará la regresión logística, ampliamente utilizada para el problema de clasificación binaria. Otras formas de modelación incluyen árboles de decisión, máquinas de vectores de soporte (SVM), vecinos más cercanos (KNN) y redes neuronales.
Se estudiará un conjunto de datos de imágenes de cáncer de mama digitalizadas. Cada fila de la base representa una imagen de una muestra de tumor, incluido el diagnóstico (benigno o maligno) y varias otras medidas (por ejemplo, textura del núcleo, perímetro, área, etc.).
Aquí, la pregunta es predictiva es: ¿se pueden usar las medidas de la imagen del tumor disponibles para predecir si una imagen de tumor en el futuro (con diagnóstico desconocido) muestra un tumor benigno o maligno?
Responder a esta pregunta es importante porque los métodos tradicionales, no basados en datos, para el diagnóstico de tumores son bastante subjetivos y dependen de cuán hábil y experimentada sea la especialidad médica que realiza el diagnóstico. Además, los tumores benignos normalmente no son peligrosos; las células permanecen en el mismo lugar y el tumor deja de crecer antes de que crezca mucho.
Por el contrario, en los tumores malignos, las células invaden el tejido circundante y se diseminan a los órganos cercanos donde pueden causar daños graves. Por tanto, es importante diagnosticar de forma rápida y precisa el tipo de tumor para guiar el tratamiento de cada paciente.
Las librerías necesarias para este análisis son:
library(tidyverse)
library(tidymodels)
library(knitr)
library(caret)
library(ranger)
library(data.table)
library(themis)
Y la base de datos se encuentra en: https://www.kaggle.com/uciml/breast-cancer-wisconsin-data?select=data.csv
El archivo que contiene el conjunto de datos de cáncer de mama es un archivo .csv simple con encabezados, por lo que la función read_csv sin argumentos adicionales permite su lectura e inspección.
cancer <- read_csv("wdbc.csv")
cancer
## # A tibble: 569 x 32
## id diagnosis radius_mean texture_mean perimeter_mean area_mean
## <dbl> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 842302 M 18.0 10.4 123. 1001
## 2 842517 M 20.6 17.8 133. 1326
## 3 84300903 M 19.7 21.2 130 1203
## 4 84348301 M 11.4 20.4 77.6 386.
## 5 84358402 M 20.3 14.3 135. 1297
## 6 843786 M 12.4 15.7 82.6 477.
## 7 844359 M 18.2 20.0 120. 1040
## 8 84458202 M 13.7 20.8 90.2 578.
## 9 844981 M 13 21.8 87.5 520.
## 10 84501001 M 12.5 24.0 84.0 476.
## # … with 559 more rows, and 26 more variables: smoothness_mean <dbl>,
## # compactness_mean <dbl>, concavity_mean <dbl>, concave points_mean <dbl>,
## # symmetry_mean <dbl>, fractal_dimension_mean <dbl>, radius_se <dbl>,
## # texture_se <dbl>, perimeter_se <dbl>, area_se <dbl>, smoothness_se <dbl>,
## # compactness_se <dbl>, concavity_se <dbl>, concave points_se <dbl>,
## # symmetry_se <dbl>, fractal_dimension_se <dbl>, radius_worst <dbl>,
## # texture_worst <dbl>, perimeter_worst <dbl>, area_worst <dbl>,
## # smoothness_worst <dbl>, compactness_worst <dbl>, concavity_worst <dbl>,
## # concave points_worst <dbl>, symmetry_worst <dbl>,
## # fractal_dimension_worst <dbl>
Los tumores de mama se pueden diagnosticar mediante una biopsia, un proceso en el que se extrae tejido del cuerpo y se examina para detectar la presencia de una enfermedad.
Tradicionalmente, estos procedimientos eran bastante invasivos; los métodos modernos, como la aspiración con aguja fina, que se utilizaron para recopilar este conjunto de datos, extraen solo una pequeña cantidad de tejido y son menos invasivos.
Con base en una imagen digital de cada muestra de tejido mamario recolectada para este conjunto de datos, se midieron \(10\) variables diferentes para cada núcleo celular. La media, el error estándar y la peor o mayor (media de los tres valores más grandes) de estas características se calcularon para cada imagen, resultando en \(30\) funciones.
Como parte de la preparación de datos, se escalan los valroes, además, cada imagen recibió una identificación única y un diagnóstico de malignidad o benignidad.
El conjunto total de variables por imagen en este conjunto de datos es:
-Número de identificación.
-Clase, el diagnóstico de malignidad o benignidad.
-Radio, la media de las distancias desde el centro hasta los puntos del perímetro.
-Textura, la desviación estándar de los valores de escala de grises.
-Perímetro, la longitud del contorno circundante.
-Área, el área dentro del contorno.
-Suavidad, la variación local en las longitudes de los radios.
-Compacidad, la relación entre el perímetro cuadrado y el área.
-Concavidad, severidad de las porciones cóncavas del contorno.
-Puntos de concavidad, el número de porciones de concavidad del contorno
-Simetría.
-Dimensión fractal.
Se obtiene una vista previa del conjunto de datos:
glimpse(cancer[,1:12])
## Rows: 569
## Columns: 12
## $ id <dbl> 842302, 842517, 84300903, 84348301, 84358402, 8…
## $ diagnosis <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M", "M…
## $ radius_mean <dbl> 17.990, 20.570, 19.690, 11.420, 20.290, 12.450,…
## $ texture_mean <dbl> 10.38, 17.77, 21.25, 20.38, 14.34, 15.70, 19.98…
## $ perimeter_mean <dbl> 122.80, 132.90, 130.00, 77.58, 135.10, 82.57, 1…
## $ area_mean <dbl> 1001.0, 1326.0, 1203.0, 386.1, 1297.0, 477.1, 1…
## $ smoothness_mean <dbl> 0.11840, 0.08474, 0.10960, 0.14250, 0.10030, 0.…
## $ compactness_mean <dbl> 0.27760, 0.07864, 0.15990, 0.28390, 0.13280, 0.…
## $ concavity_mean <dbl> 0.30010, 0.08690, 0.19740, 0.24140, 0.19800, 0.…
## $ `concave points_mean` <dbl> 0.14710, 0.07017, 0.12790, 0.10520, 0.10430, 0.…
## $ symmetry_mean <dbl> 0.2419, 0.1812, 0.2069, 0.2597, 0.1809, 0.2087,…
## $ fractal_dimension_mean <dbl> 0.07871, 0.05667, 0.05999, 0.09744, 0.05883, 0.…
Del resumen, se observa la variable objetivo diagnosis es de tipo carácter (<chr>). Para darle un tratamiento estadístico, se convierte a factor usando la función as_factor.
cancer <- cancer %>%
mutate(diagnosis = as_factor(diagnosis))
glimpse(cancer[,1:12])
## Rows: 569
## Columns: 12
## $ id <dbl> 842302, 842517, 84300903, 84348301, 84358402, 8…
## $ diagnosis <fct> M, M, M, M, M, M, M, M, M, M, M, M, M, M, M, M,…
## $ radius_mean <dbl> 17.990, 20.570, 19.690, 11.420, 20.290, 12.450,…
## $ texture_mean <dbl> 10.38, 17.77, 21.25, 20.38, 14.34, 15.70, 19.98…
## $ perimeter_mean <dbl> 122.80, 132.90, 130.00, 77.58, 135.10, 82.57, 1…
## $ area_mean <dbl> 1001.0, 1326.0, 1203.0, 386.1, 1297.0, 477.1, 1…
## $ smoothness_mean <dbl> 0.11840, 0.08474, 0.10960, 0.14250, 0.10030, 0.…
## $ compactness_mean <dbl> 0.27760, 0.07864, 0.15990, 0.28390, 0.13280, 0.…
## $ concavity_mean <dbl> 0.30010, 0.08690, 0.19740, 0.24140, 0.19800, 0.…
## $ `concave points_mean` <dbl> 0.14710, 0.07017, 0.12790, 0.10520, 0.10430, 0.…
## $ symmetry_mean <dbl> 0.2419, 0.1812, 0.2069, 0.2597, 0.1809, 0.2087,…
## $ fractal_dimension_mean <dbl> 0.07871, 0.05667, 0.05999, 0.09744, 0.05883, 0.…
La función levels nos da las categorías, pero requiere un argumento de vector, mientras que la función select genera un data frame; por lo que se usa la función pull, que convierte una sola columna de un data frame en un vector.
cancer %>%
dplyr::select(diagnosis) %>%
pull() %>% # convierte data frame a vector
levels()
## [1] "M" "B"
A continuación, observamos que tenemos \(357\) observaciones de tumores benignos y \(212\) de tumores malignos.
num_obs <- nrow(cancer)
cancer %>%
group_by(diagnosis) %>%
summarise(n = n(),percentage = n() / num_obs * 100)
## # A tibble: 2 x 3
## diagnosis n percentage
## <fct> <int> <dbl>
## 1 M 212 37.3
## 2 B 357 62.7
Ahora, se gráfica un diagrama de dispersión para visualizar la relación entre el perímetro y las variables de concavidad.
cbPalette <- c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7", "#999999")
perim_concav <- cancer %>%
ggplot(aes(x = perimeter_mean, y = concavity_mean, color = diagnosis)) +
geom_point(alpha = 0.5) +
labs(color = "diagnosis") +
scale_color_manual(labels = c("Malignant", "Benign"), values = cbPalette)
perim_concav
Se distingue que las observaciones malignas normalmente se encuentran en la esquina superior derecha del área de la gráfica. Por el contrario, las observaciones benignas suelen ubicarse en la esquina inferior izquierda de la gráfica.
Entonces, suponiendo que obtenemos una nueva observación que no está en el conjunto de datos actual y que tiene todas las variables medidas excepto la etiqueta (es decir, una imagen sin diagnóstico para la clase de tumor), se pueden calcular los valores de perímetro y concavidad, dando como resultado valores de, por ejemplo, \(120\) y \(0.2\).
¿Se puede usar esta información para clasificar esa observación como benigna o maligna?
¿Qué pasa con una nueva observación con un valor de perímetro de \(80\) y un valor de concavidad de \(0.1\)? ¿Qué pasa si las mediciones son \(160\) y \(0.1\)?
Parece que la predicción de una etiqueta no observada podría ser posible, según la visualización. Para poder hacer esto computacionalmente en la práctica se necesita del algoritmo de clasificación, en este caso, la regresión logística.
El algoritmo para la regresión logística se implementa en el paquete parsnip incluido en la colección de paquetes tidymodels, junto con muchos otros modelos. tidymodels proporciona herramientas para ayudar a crear y usar modelos, como clasificadores. El uso de los paquetes de esta colección ayudan a mantener código simple, legible y preciso; cuanto menos codificación, es probable que se cometan menos errores.
Sea una nueva observación con perímetro \(120\) y concavidad \(0.4\), pero con diagnóstico desconocido. Se usarán las variables explicativas de perímetro y concavidad para predecir la clase de diagnóstico de esta observación. Seleccionando las \(2\) variables predictoras deseadas y la etiqueta de clase, se almacenan como un nuevo conjunto de datos llamado cancer_train.
cancer_train <- cancer %>%
dplyr::select(diagnosis, perimeter_mean, concavity_mean)
cancer_train
## # A tibble: 569 x 3
## diagnosis perimeter_mean concavity_mean
## <fct> <dbl> <dbl>
## 1 M 123. 0.300
## 2 M 133. 0.0869
## 3 M 130 0.197
## 4 M 77.6 0.241
## 5 M 135. 0.198
## 6 M 82.6 0.158
## 7 M 120. 0.113
## 8 M 90.2 0.0937
## 9 M 87.5 0.186
## 10 M 84.0 0.227
## # … with 559 more rows
A continuación, se crea una especificación de modelo para la clasificación llamando a la función logistic_reg. También se especifica el motor computacional particular (en este caso, glm) para entrenar el modelo con la función set_engine. Finalmente se especifica que este es un problema de clasificación con la función set_mode.
log_reg <- logistic_reg() %>%
set_engine("glm") %>%
set_mode("classification")
log_reg
## Logistic Regression Model Specification (classification)
##
## Computational engine: glm
Para ajustar el modelo a los datos del cáncer de mama, se pasa la especificación del modelo y el conjunto de datos a la función fit.
log_fit <- log_reg %>%
fit(diagnosis ~ ., data = cancer_train)
log_fit
## parsnip model object
##
## Fit time: 20ms
##
## Call: stats::glm(formula = diagnosis ~ ., family = stats::binomial,
## data = data)
##
## Coefficients:
## (Intercept) perimeter_mean concavity_mean
## 15.8589 -0.1426 -24.5954
##
## Degrees of Freedom: 568 Total (i.e. Null); 566 Residual
## Null Deviance: 751.4
## Residual Deviance: 229.8 AIC: 235.8
El resumen final del modelo entrenado confirma que el motor computacional utilizado para entrenar el modelo fue stats::glm. También muestra las estimaciones y las métricas de rendimiento.
Con un objeto de ajuste, se procede a la predicción sobre la nueva observación llamando a la función predict, pasándole este objeto.
new_obs <- tibble(perimeter_mean = 120, concavity_mean = 0.4)
predict(log_fit, new_obs)
## # A tibble: 1 x 1
## .pred_class
## <fct>
## 1 M
El objeto log_fit clasifica la nueva observación como maligna (“M”) y la función predict genera un data frame con una única variable denominada .pred_class.
Cuando se usa clasificación, la escala de cada variable (es decir, su tamaño y rango de valores) es importante.
Por ejemplo, si se tiene un conjunto de datos con dos atributos, salario y años de educación, y se desea predecir el tipo de trabajo correspondiente, una diferencia de \(1000\) en salario es enorme en comparación con una diferencia de \(10\) años de educación.
En otros modelos predictivos, el centro de cada variable (por ejemplo, su media) también es importante.
Por ejemplo, si se tuviera un conjunto de datos con una variable de temperatura medida en grados Kelvin, y el mismo conjunto de datos con la temperatura medida en grados Celsius, las dos variables diferirían por un cambio constante de \(273\) (aunque contienen exactamente la misma información).
Del mismo modo, para el ejemplo de clasificación de puestos de trabajo, es probable que el centro de la variable de salario está en las decenas de miles, mientras que el centro de la variable de años de educación está en un solo dígito. Aunque esto no afecta al algoritmo de clasificación de regresión logística, este gran cambio puede cambiar el resultado de usar muchos otros modelos predictivos.
Estandarización: cuando todas las variables en un conjunto de datos tienen una media (centro) de \(0\) y una desviación estándar (escala) de \(1\), se dice que los datos se han estandarizado.
Para ilustrar el efecto que la estandarización puede tener en este modelo lineal generalizado, se utilizará recipes, una componente más en tidymodels. Se usarán las variables area_mean, smoothness_mean y diagnosis.
unscaled_cancer <- cancer %>%
mutate(diagnosis = as_factor(diagnosis)) %>%
select(diagnosis, area_mean, smoothness_mean)
unscaled_cancer
## # A tibble: 569 x 3
## diagnosis area_mean smoothness_mean
## <fct> <dbl> <dbl>
## 1 M 1001 0.118
## 2 M 1326 0.0847
## 3 M 1203 0.110
## 4 M 386. 0.142
## 5 M 1297 0.100
## 6 M 477. 0.128
## 7 M 1040 0.0946
## 8 M 578. 0.119
## 9 M 520. 0.127
## 10 M 476. 0.119
## # … with 559 more rows
Al observar los datos sin escala / no centrados anteriores, se ve que la diferencia entre los valores para las mediciones de area_mean son mucho mayores que los de smoothness_mean, y la media parece ser mucho mayor también.
¿Afectará esto a las predicciones? Para averiguarlo, se crea un diagrama de dispersión de estos dos predictores (coloreados por diagnóstico) tanto para los datos no estandarizados como para la versión estandarizada de esos mismos datos.
En el marco de referencia tidymodels, todo el preprocesamiento de datos se realiza mediante recipe. Aquí se inicializa una recipe para los datos unscaled_cancer anteriores, especificando que la variable diagnosis es la objetivo y todas las demás variables son predictoras.
uc_recipe <- recipe(diagnosis ~ ., data = unscaled_cancer)
print(uc_recipe)
## Data Recipe
##
## Inputs:
##
## role #variables
## outcome 1
## predictor 2
Hasta ahora, no hay mucho en esta recipe; solo una declaración sobre el número de objetivos y predictores. Después, se agregan pasos de escala (step_scale) y centrado (step_center) para todos los predictores para que cada uno tenga una media de \(0\) y una desviación estándar de \(1\). La función prep finaliza la recipe usando los datos unscaled_cancer para calcular cualquier cosa necesaria para ejecutar la recipe (en este caso, las medias de la columna y las desviaciones estándar).
uc_recipe <- uc_recipe %>%
step_scale(all_predictors()) %>%
step_center(all_predictors()) %>%
prep()
uc_recipe
## Data Recipe
##
## Inputs:
##
## role #variables
## outcome 1
## predictor 2
##
## Training data contained 569 data points and no missing data.
##
## Operations:
##
## Scaling for area_mean, smoothness_mean [trained]
## Centering for area_mean, smoothness_mean [trained]
Ahora se puede ver que la recipe incluye un step_scale y un step_center para todas las variables predictoras. Se debe tener en cuenta que cuando se agrega un paso a una recipe, se debe especificar a qué columnas aplicar la estandarización. La función all_predictors () se usa para especificar que la estandarización debe aplicarse a todas las variables predictoras. Sin embargo, hay varios argumentos diferentes que se pueden usar aquí, además de nombrar columnas particulares con la misma sintaxis que la función select. Por ejemplo:
-all_nominal () y all_numeric (): especifican todas las variables categóricas o numéricas
-all_predictors () y all_outcomes (): especifican todos los predictores o todas las variables objetivo
-area_mean, smoothness_mean: especifican las variable area_mean y smoothness_mean
--diagnosis: especifica todo excepto la variable diagnosis
Se pueden encontrar todas las funciones dentro de recipe en: https://recipes.tidymodels.org/reference/index.html. Finalmente se usa la función bake para aplicar la recipe.
scaled_cancer <- bake(uc_recipe, unscaled_cancer)
scaled_cancer
## # A tibble: 569 x 3
## area_mean smoothness_mean diagnosis
## <dbl> <dbl> <fct>
## 1 0.984 1.57 M
## 2 1.91 -0.826 M
## 3 1.56 0.941 M
## 4 -0.764 3.28 M
## 5 1.82 0.280 M
## 6 -0.505 2.24 M
## 7 1.09 -0.123 M
## 8 -0.219 1.60 M
## 9 -0.384 2.20 M
## 10 -0.509 1.58 M
## # … with 559 more rows
Ahora se generan los dos diagramas de dispersión, uno para unscaled_cancer y otro para scaled_cancer.
Aunque estás gráficas son similares en su distribución, como regla general, la estandarización de datos debe de se parte del procesamiento previo que se realiza antes de cualquier modeladación predictiva.
Otro problema potencial en un conjunto de datos para un clasificador es el desequilibrio de clases, es decir, cuando una etiqueta es mucho más común que otra. Dado que algunos clasificadores usan etiquetas para predecir la etiqueta de un nuevo punto, si hay muchos más puntos de datos con una etiqueta en general, es más probable que el algoritmo elija esa etiqueta en general (incluso si el patrón de datos sugiere lo contrario).
El desequilibrio de clases es en realidad un problema bastante común e importante: desde el diagnóstico de enfermedades raras hasta la detección de correo electrónico malicioso, hay muchos casos en los que la clase importante para identificar (presencia de enfermedad, correo electrónico malicioso) es mucho más rara que la clase no importante (sin enfermedad, correo electrónico normal).
Para ilustrar mejor el problema, se revisan los datos del cáncer de mama; excepto que ahora se eliminaN muchas de las observaciones de tumores malignos, simulando cómo se verían los datos si el cáncer fuera raro. Esto se hará seleccionando solo \(3\) observaciones al azar del grupo maligno y manteniendo todas las observaciones benignas.
set.seed(3)
rare_cancer <- bind_rows(
filter(cancer, diagnosis == "B"),
cancer %>% filter(diagnosis == "M") %>% sample_n(3)
) %>%
select(diagnosis, perimeter_mean, concavity_mean)
rare_plot <- rare_cancer %>%
ggplot(aes(x = perimeter_mean, y = concavity_mean, color = diagnosis)) +
geom_point(alpha = 0.5) +
labs(color = "diagnosis") +
scale_color_manual(labels = c("Malignant", "Benign"), values = cbPalette)
rare_plot
Con solo \(3\) observaciones de tumores malignos, puede que el clasificador siempre preddiga que el tumor es benigno, sin importar cuál sea su concavidad y perímetro. Esto se debe a que en un voto mayoritario de \(7\) observaciones, como máximo \(3\) serán malignas (solo tenemos \(3\) observaciones malignas en total), por lo que al menos \(4\) deben ser benignas, y el voto benigno siempre ganará.
A pesar de la simplicidad del problema, resolverlo de una manera estadísticamente sólida es en realidad bastante matizado, y un tratamiento cuidadoso requeriría muchos más detalles.
Para este clasificador, será suficiente reequilibrar los datos sobremuestreando la clase rara. En otras palabras, se replican observaciones raras varias veces en nuestro conjunto de datos para darles más poder de voto en el algoritmo de clasificación.
Para hacer esto, se agrega un paso de sobremuestreo a la recipe anterior uc_recipe con la función step_upsample.
ups_recipe <- recipe(diagnosis ~ ., data = rare_cancer) %>%
themis::step_upsample(diagnosis, over_ratio = 1, skip = FALSE) %>%
prep()
ups_recipe
## Data Recipe
##
## Inputs:
##
## role #variables
## outcome 1
## predictor 2
##
## Training data contained 360 data points and no missing data.
##
## Operations:
##
## Up-sampling based on diagnosis [trained]
upsampled_cancer <- bake(ups_recipe, rare_cancer)
upsampled_cancer %>%
group_by(diagnosis) %>%
summarize(n = n())
## # A tibble: 2 x 2
## diagnosis n
## <fct> <int>
## 1 M 357
## 2 B 357
Al entrenar en estos datos, la decisión será más razonable; cuando los puntos están cerca de los etiquetados como malignos, se predice un tumor maligno y viceversa.
tidymodels también proporciona workflow, una forma sencilla de encadenar varios pasos de análisis de datos sin mucho código que de otro modo sería necesario para los pasos intermedios. Para ilustrar todo el proceso, se comienza desde cero con los datos wdbc.csv. Primero se cargan los datos, se crea un modelo y se especifica una recipe sobre cómo se deben preprocesar los datos.
# carga de data
unscaled_cancer <- read_csv("wdbc.csv") %>%
mutate(diagnosis = as_factor(diagnosis))
# modelo
log_reg <- logistic_reg() %>%
set_engine("glm") %>%
set_mode("classification")
# centrado y escala
uc_recipe3 <- recipe(diagnosis ~ area_mean + smoothness_mean, data = unscaled_cancer) %>%
step_scale(all_predictors()) %>%
step_center(all_predictors())
La única diferencia importante es que no se usa la función select para extraer las variables relevantes del data frame, sino que simplemente se especifican las variables relevantes para usarlas a través de la fórmula diagnosis ~ area_mean + smoothness_mean (en lugar de diagnosis ~ .) en la recipe. También se nota que no se llama a prep () en la recipe; esto es innecesario cuando se coloca en un workflow.
Finalmente se colocan estos pasos en un workflow usando las funciones add_recipe y add_model, y finalmente se usa la función de fit para ejecutar todo el workflow en los datos unscaled_cancer. Otra diferencia con respecto a lo anterior es que no se incluye una fórmula en la función de fit. Esto se debe nuevamente a que ya va incluida en la fórmula de la recipe.
log_fit2 <- workflow() %>%
add_recipe(uc_recipe3) %>%
add_model(log_reg) %>%
fit(data = unscaled_cancer)
log_fit2
## ══ Workflow [trained] ══════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
##
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 2 Recipe Steps
##
## • step_scale()
## • step_center()
##
## ── Model ───────────────────────────────────────────────────────────────────────
##
## Call: stats::glm(formula = ..y ~ ., family = stats::binomial, data = data)
##
## Coefficients:
## (Intercept) area_mean smoothness_mean
## 0.3398 -4.9495 -1.4544
##
## Degrees of Freedom: 568 Total (i.e. Null); 566 Residual
## Null Deviance: 751.4
## Residual Deviance: 244.9 AIC: 250.9
Como antes, el objeto fit enumera la función que entrena el modelo, así como las estimaciones. Pero ahora, el objeto fit también incluye información sobre el workflow general, incluidos los pasos de preprocesamiento de centrado y escala.
Se visualizan las predicciones que hará este modelo entrenado en nuevas observaciones creando una cuadrícula de nuevas observaciones sintéticas con la función expand.grid.
# crear cuadrícula de valores area/smoothness en un data frame
are_grid <- seq(min(unscaled_cancer$area_mean), max(unscaled_cancer$area_mean), length.out = 100)
smo_grid <- seq(min(unscaled_cancer$smoothness_mean), max(unscaled_cancer$smoothness_mean), length.out = 100)
asgrid <- as_tibble(expand.grid(area_mean = are_grid, smoothness_mean = smo_grid))
# usar el workflow ajustado para predecir en la cuadrícula
logPredGrid <- predict(log_fit2, asgrid)
# unir las predicciones como una nueva columna con los puntos de la cuadrícula
prediction_table <- bind_cols(logPredGrid, asgrid) %>% rename(diagnosis = .pred_class)
# workflow
wkflw_plot <-
ggplot() +
geom_point(data = unscaled_cancer, mapping = aes(x = area_mean, y = smoothness_mean, color = diagnosis), alpha = 0.75) +
geom_point(data = prediction_table, mapping = aes(x = area_mean, y = smoothness_mean, color = diagnosis), alpha = 0.02, size = 5.) +
labs(color = "diagnosis") +
scale_color_manual(labels = c("Malignant", "Benign"), values = cbPalette)
wkflw_plot
Para evaluar la precisión de la predicción se dividen los datos para realizar pruebas y se eligen los parámetros del modelo para maximizar el rendimiento. Los pasos a seguir serán:
-Describir qué son los conjuntos de datos de entrenamiento, validación y prueba y cómo se utilizan en la clasificación.
-Dividir los datos en conjuntos de datos de entrenamiento, validación y prueba.
-Evaluar la precisión de la clasificación en R utilizando un conjunto de datos de validación y métricas adecuadas.
-Ejecutar la validación cruzada para evaluar los resultados de un análisis estadístico y garantizar que son independientes de la partición entre datos de entrenamiento y prueba.
-Describir las ventajas y desventajas de la regresión logística.
A veces, la clasificación puede hacer una predicción incorrecta. No es necesario que un clasificador sea correcto en su totalidad para ser útil, sin embargo se busca que el clasificador no haga demasiadas predicciones incorrectas.
¿Cómo medimos qué tan buena es una clasificación?
Al pensar en cómo se utilizará la clasificación de imágenes cancerígenas en la práctica, se empieza a esclarecer la pregunta anterior.
Se realizará una biopsia del tumor de una nueva observación, se analizará la imagen resultante y se le pedirá al clasificador que decida si el tumor es benigno o maligno.
La palabra clave aquí es nueva: la clasificación es buena si proporciona predicciones precisas sobre datos que no se ven durante el entrenamiento.
¿cómo se puede evaluar la clasificación sin tener que visitar el hospital para recolectar más imágenes de tumores?
El truco consiste en dividir el conjunto de datos en un conjunto de entrenamiento y un conjunto de prueba, y sólo mostrar a la clasificación el conjunto de entrenamiento al construir el clasificador. Luego, para evaluar la precisión del clasificador, se puede usar para la predicción las etiquetas (conocidas) en el conjunto de prueba.
Si las predicciones coinciden muy bien con las etiquetas verdaderas para las observaciones en el conjunto de prueba, entonces se tiene cierta confianza en que la clasificación también podría hacer un buen trabajo al predecir las etiquetas de clase para nuevas observaciones para las que no se cuenta con un diagnóstico.
¿Cómo se evalua exactamente qué tan bien las predicciones coinciden con las etiquetas verdaderas para las observaciones en el conjunto de prueba?
Una forma de hacer esto es calcular la precisión de la predicción. Esta es la fracción de ejemplos para los que el clasificador hizo la predicción correcta.
Para calcular esto, se divide el número de predicciones correctas por el número de predicciones realizadas.
| Y_estimada | Y_0_negativa | Y_1_positiva |
|---|---|---|
| Y_0_negativa | Decisión correcta | Error Tipo II |
| Y_1_positiva | Error Tipo I | Decisión correcta |
En tidymodels, también se puede evaluar qué tan bien funcionó una modelación.
K. P. Bennett, “Decision Tree Construction Via Linear Programming.” Proceedings of the 4th Midwest Artificial Intelligence and Cognitive Science Society, pp. 97-101, 1992.
Timbers, T.-A., et al. Data Science: A First Introduction. 2021-01-12: https://ubc-dsci.github.io/introduction-to-datascience/