library(readxl)
invisible(Sys.setlocale("LC_ALL", "Spanish_Spain.1252")) # Oculta el mensaje
aguacate <- read_excel("~/aguacate.xlsx")
library(knitr)
library(kableExtra)
temperatura=aguacate$Temperature
humedad=aguacate$`Relative Humidity`
viento=aguacate$`Wind Speed`
frutos_afectados=aguacate$Frutos_Afectados_Heilipus+aguacate$Frutos_Afectados_Stenoma
y=as.numeric(frutos_afectados>0)
df_frutos=data.frame(y,temperatura,humedad,viento)
df_frutos=na.omit(df_frutos)
modelo=glm(y~temperatura+humedad+viento,data=df_frutos,family = "binomial")
probas=modelo$fitted.values
presencia_plaga_modelo=probas>0.15
presencia_plaga_real=df_frutos$y
# Crear la tabla de confusión
tabla <- table(Real = presencia_plaga_real, Modelo = presencia_plaga_modelo)
# Mostrar con títulos bien formateados
kable(tabla, format = "html", caption = "Matriz de Confusión") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))| FALSE | TRUE | |
|---|---|---|
| 0 | 1796 | 355 |
| 1 | 105 | 107 |
calc_metricas=function(umbral){
presencia_plaga_modelo=probas>umbral
presencia_plaga_real=df_frutos$y
#matriz de confusión
tabla=table(presencia_plaga_real,presencia_plaga_modelo)
desempeño=(tabla[1,1]+tabla[2,2])/sum(tabla)
return(desempeño)
}
umbrales=seq(0.05,0.24,0.01)
desempeños=sapply(umbrales, calc_metricas)
# Calcular desempeño asegurando que el resultado tenga la misma longitud que umbrales
desempeños <- sapply(umbrales, function(u) calc_metricas(u)[1]) # Extrae solo el desempeño
# Graficar Desempeño vs Umbral
plot(umbrales, desempeños, type = "b", col = "blue", pch = 16, lty = 2,
xlab = "Umbral", ylab = "Desempeño", main = "Desempeño vs Umbral")# Definir función para calcular las métricas (desempeño, sensibilidad, especificidad)
calc_metricas <- function(umbral) {
presencia_plaga_modelo <- probas > umbral
presencia_plaga_real <- df_frutos$y
tabla <- table(presencia_plaga_real, presencia_plaga_modelo)
# Evitar errores si hay clases vacías en la tabla de contingencia
if (nrow(tabla) < 2 || ncol(tabla) < 2) {
return(c(NA, NA, NA)) # Retorna NA si hay problemas en la matriz
}
# Extraer valores de la matriz de confusión
VP <- tabla[2, 2] # Verdaderos positivos
FN <- tabla[2, 1] # Falsos negativos
TN <- tabla[1, 1] # Verdaderos negativos
FP <- tabla[1, 2] # Falsos positivos
# Calcular métricas
desempeño <- (VP + TN) / sum(tabla) # Exactitud global
sensibilidad <- VP / (VP + FN) # Tasa de verdaderos positivos
especificidad <- TN / (TN + FP) # Tasa de verdaderos negativos
return(c(desempeño, sensibilidad, especificidad))
}
# Definir umbrales
umbrales <- seq(0.05, 0.24, 0.01)
# Calcular todas las métricas para cada umbral
metricas <- sapply(umbrales, calc_metricas)
# Extraer sensibilidad
sensibilidades <- metricas[2, ] # Segunda fila contiene la sensibilidad
# Graficar Sensibilidad vs Umbral
plot(umbrales, sensibilidades, type = "b", col = "red", pch = 16, lty = 2,
xlab = "Umbral", ylab = "Sensibilidad", main = "Sensibilidad vs Umbral")# Extraer la especificidad desde la matriz de métricas
especificidades <- metricas[3, ] # Tercera fila contiene la especificidad
# Graficar Especificidad vs Umbral
plot(umbrales, especificidades, type = "b", col = "green", pch = 16, lty = 2,
xlab = "Umbral", ylab = "Especificidad", main = "Especificidad vs Umbral")# Calcular las tres métricas desde la matriz de resultados
desempeño <- metricas[1, ] # Primera fila: Desempeño
sensibilidad <- metricas[2, ] # Segunda fila: Sensibilidad
especificidad <- metricas[3, ] # Tercera fila: Especificidad
# Calcular la métrica promedio
metrica_promedio <- (desempeño + sensibilidad + especificidad) / 3
# Graficar Métrica Promedio vs Umbral
plot(umbrales, metrica_promedio, type = "b", col = "purple", pch = 16, lty = 2,
xlab = "Umbral", ylab = "Métrica Promedio", main = "Métrica Promedio vs Umbral")## Definir función para calcular métricas
calc_metricas = function(umbral) {
# Generar predicciones con el umbral dado
presencia_plaga_modelo = probas > umbral
presencia_plaga_real = df_frutos$y
# Crear la matriz de confusión
tabla = table(presencia_plaga_real, presencia_plaga_modelo)
# Extraer valores de la matriz de confusión
VP = ifelse("1" %in% rownames(tabla) & "TRUE" %in% colnames(tabla), tabla["1", "TRUE"], 0) # Verdaderos positivos
VN = ifelse("0" %in% rownames(tabla) & "FALSE" %in% colnames(tabla), tabla["0", "FALSE"], 0) # Verdaderos negativos
FP = ifelse("0" %in% rownames(tabla) & "TRUE" %in% colnames(tabla), tabla["0", "TRUE"], 0) # Falsos positivos
FN = ifelse("1" %in% rownames(tabla) & "FALSE" %in% colnames(tabla), tabla["1", "FALSE"], 0) # Falsos negativos
# Calcular métricas
desempeño = (VP + VN) / sum(tabla) # Precisión general
sensibilidad = ifelse((VP + FN) > 0, VP / (VP + FN), NA) # Tasa de verdaderos positivos
especificidad = ifelse((VN + FP) > 0, VN / (VN + FP), NA) # Tasa de verdaderos negativos
# Nueva métrica: promedio de las tres
metrica_promedio = mean(c(desempeño, sensibilidad, especificidad), na.rm = TRUE)
return(c(Desempeño = desempeño, Sensibilidad = sensibilidad, Especificidad = especificidad, Métrica_Promedio = metrica_promedio))
}
## Aplicar la función a distintos umbrales
umbrales = seq(0.05, 0.24, 0.01)
metricas = sapply(umbrales, calc_metricas)
## Graficar las métricas
plot(umbrales, metricas["Desempeño", ], type = "b", col = "blue", pch = 19, ylim = c(0, 1), ylab = "Métricas", xlab = "Umbral")
lines(umbrales, metricas["Sensibilidad", ], type = "b", col = "red", pch = 19)
lines(umbrales, metricas["Especificidad", ], type = "b", col = "green", pch = 19)
lines(umbrales, metricas["Métrica_Promedio", ], type = "b", col = "purple", pch = 19)
legend("bottomright", legend = c("Desempeño", "Sensibilidad", "Especificidad", "Métrica Promedio"), col = c("blue", "red", "green", "purple"), pch = 19, bty = "n")A umbrales bajos (~0.05), la sensibilidad es alta (~0.8-0.9), lo que significa que el modelo clasifica la mayoría de los casos positivos correctamente. Sin embargo, la especificidad es baja (~0.4), indicando que hay muchos falsos positivos.
A umbrales altos (~0.20), la sensibilidad cae (~0.2), lo que indica que el modelo detecta menos positivos, pero la especificidad aumenta (~0.9), lo que significa que hay menos falsos positivos.
La métrica promedio (morado) captura el balance entre sensibilidad, especificidad y desempeño.
Se mantiene relativamente estable, aunque tiene un leve decrecimiento a umbrales muy altos, lo que indica que un umbral excesivo sacrifica demasiado la detección de casos positivos.
Un umbral entre 0.08 y 0.12 parece ser el mejor compromiso entre sensibilidad y especificidad, ya que evita que una métrica sea demasiado baja sin afectar demasiado a la otra.
Si el objetivo es minimizar falsos negativos (detectar la mayor cantidad de casos de plaga posible), se debe usar un umbral bajo (~0.05-0.08).
Si el objetivo es minimizar falsos positivos (evitar alarmas innecesarias), se debe usar un umbral alto (~0.18-0.20).
El análisis indica que ajustar el umbral es clave para equilibrar la sensibilidad y la especificidad del modelo. Un umbral intermedio (~0.1) podría ser el más adecuado para mantener un buen balance entre detectar casos de plaga y evitar falsos positivos.