El objetivo de esta sección es desarrollar un análisis exploratorio
de la base de datos wine.csv, con el fin de revisar la
estructura de los datos, los estadísticos descriptivos y el
comportamiento gráfico de cada variable.
# El archivo wine.csv debe estar en la misma carpeta que este RMarkdown.
# Si el archivo mantiene el nombre original descargado, también se reconoce automáticamente.
if(file.exists("wine.csv")){
wine <- read.csv("wine.csv")
} else if(file.exists("winequality-red (2).csv")){
wine <- read.csv("winequality-red (2).csv")
} else if(file.exists("winequality-red.csv")){
wine <- read.csv("winequality-red.csv")
} else {
stop("No se encontró el archivo wine.csv. Coloque el CSV en la misma carpeta que este RMarkdown.")
}
str(wine)
## 'data.frame': 1599 obs. of 12 variables:
## $ fixed.acidity : num 7.4 7.8 7.8 11.2 7.4 7.4 7.9 7.3 7.8 7.5 ...
## $ volatile.acidity : num 0.7 0.88 0.76 0.28 0.7 0.66 0.6 0.65 0.58 0.5 ...
## $ citric.acid : num 0 0 0.04 0.56 0 0 0.06 0 0.02 0.36 ...
## $ residual.sugar : num 1.9 2.6 2.3 1.9 1.9 1.8 1.6 1.2 2 6.1 ...
## $ chlorides : num 0.076 0.098 0.092 0.075 0.076 0.075 0.069 0.065 0.073 0.071 ...
## $ free.sulfur.dioxide : num 11 25 15 17 11 13 15 15 9 17 ...
## $ total.sulfur.dioxide: num 34 67 54 60 34 40 59 21 18 102 ...
## $ density : num 0.998 0.997 0.997 0.998 0.998 ...
## $ pH : num 3.51 3.2 3.26 3.16 3.51 3.51 3.3 3.39 3.36 3.35 ...
## $ sulphates : num 0.56 0.68 0.65 0.58 0.56 0.56 0.46 0.47 0.57 0.8 ...
## $ alcohol : num 9.4 9.8 9.8 9.8 9.4 9.4 9.4 10 9.5 10.5 ...
## $ quality : int 5 5 5 6 5 5 5 7 7 5 ...
dim(wine)
## [1] 1599 12
head(wine)
## fixed.acidity volatile.acidity citric.acid residual.sugar chlorides
## 1 7.4 0.70 0.00 1.9 0.076
## 2 7.8 0.88 0.00 2.6 0.098
## 3 7.8 0.76 0.04 2.3 0.092
## 4 11.2 0.28 0.56 1.9 0.075
## 5 7.4 0.70 0.00 1.9 0.076
## 6 7.4 0.66 0.00 1.8 0.075
## free.sulfur.dioxide total.sulfur.dioxide density pH sulphates alcohol
## 1 11 34 0.9978 3.51 0.56 9.4
## 2 25 67 0.9968 3.20 0.68 9.8
## 3 15 54 0.9970 3.26 0.65 9.8
## 4 17 60 0.9980 3.16 0.58 9.8
## 5 11 34 0.9978 3.51 0.56 9.4
## 6 13 40 0.9978 3.51 0.56 9.4
## quality
## 1 5
## 2 5
## 3 5
## 4 6
## 5 5
## 6 5
valores_perdidos <- data.frame(
Variable = names(wine),
Valores_Perdidos = colSums(is.na(wine))
)
kable(valores_perdidos, caption = "Cantidad de valores perdidos por variable")
| Variable | Valores_Perdidos | |
|---|---|---|
| fixed.acidity | fixed.acidity | 0 |
| volatile.acidity | volatile.acidity | 0 |
| citric.acid | citric.acid | 0 |
| residual.sugar | residual.sugar | 0 |
| chlorides | chlorides | 0 |
| free.sulfur.dioxide | free.sulfur.dioxide | 0 |
| total.sulfur.dioxide | total.sulfur.dioxide | 0 |
| density | density | 0 |
| pH | pH | 0 |
| sulphates | sulphates | 0 |
| alcohol | alcohol | 0 |
| quality | quality | 0 |
resumen <- data.frame(
Variable = names(wine),
Media = sapply(wine, mean, na.rm = TRUE),
Mediana = sapply(wine, median, na.rm = TRUE),
Desviacion_Estandar = sapply(wine, sd, na.rm = TRUE),
Minimo = sapply(wine, min, na.rm = TRUE),
Maximo = sapply(wine, max, na.rm = TRUE)
)
# Se redondean solo las columnas numéricas para evitar errores con la columna de texto "Variable".
resumen <- resumen %>%
mutate(across(where(is.numeric), ~ round(.x, 4)))
kable(resumen, caption = "Resumen descriptivo de las variables")
| Variable | Media | Mediana | Desviacion_Estandar | Minimo | Maximo | |
|---|---|---|---|---|---|---|
| fixed.acidity | fixed.acidity | 8.3196 | 7.9000 | 1.7411 | 4.6000 | 15.9000 |
| volatile.acidity | volatile.acidity | 0.5278 | 0.5200 | 0.1791 | 0.1200 | 1.5800 |
| citric.acid | citric.acid | 0.2710 | 0.2600 | 0.1948 | 0.0000 | 1.0000 |
| residual.sugar | residual.sugar | 2.5388 | 2.2000 | 1.4099 | 0.9000 | 15.5000 |
| chlorides | chlorides | 0.0875 | 0.0790 | 0.0471 | 0.0120 | 0.6110 |
| free.sulfur.dioxide | free.sulfur.dioxide | 15.8749 | 14.0000 | 10.4602 | 1.0000 | 72.0000 |
| total.sulfur.dioxide | total.sulfur.dioxide | 46.4678 | 38.0000 | 32.8953 | 6.0000 | 289.0000 |
| density | density | 0.9967 | 0.9968 | 0.0019 | 0.9901 | 1.0037 |
| pH | pH | 3.3111 | 3.3100 | 0.1544 | 2.7400 | 4.0100 |
| sulphates | sulphates | 0.6581 | 0.6200 | 0.1695 | 0.3300 | 2.0000 |
| alcohol | alcohol | 10.4230 | 10.2000 | 1.0657 | 8.4000 | 14.9000 |
| quality | quality | 5.6360 | 6.0000 | 0.8076 | 3.0000 | 8.0000 |
analizar_variable <- function(data, variable){
x <- data[[variable]]
cat("\nVariable:", variable, "\n")
cat("Media:", round(mean(x, na.rm = TRUE), 4), "\n")
cat("Mediana:", round(median(x, na.rm = TRUE), 4), "\n")
cat("Desviación estándar:", round(sd(x, na.rm = TRUE), 4), "\n")
cat("Mínimo:", round(min(x, na.rm = TRUE), 4), "\n")
cat("Máximo:", round(max(x, na.rm = TRUE), 4), "\n")
p1 <- ggplot(data, aes(x = .data[[variable]])) +
geom_histogram(bins = 30, fill = "deeppink", color = "white") +
labs(
title = paste("Histograma de", variable),
x = variable,
y = "Frecuencia"
) +
theme_minimal()
p2 <- ggplot(data, aes(y = .data[[variable]])) +
geom_boxplot(fill = "orange") +
labs(
title = paste("Boxplot de", variable),
y = variable
) +
theme_minimal()
print(p1)
print(p2)
}
for(v in names(wine)){
analizar_variable(wine, v)
}
##
## Variable: fixed.acidity
## Media: 8.3196
## Mediana: 7.9
## Desviación estándar: 1.7411
## Mínimo: 4.6
## Máximo: 15.9
##
## Variable: volatile.acidity
## Media: 0.5278
## Mediana: 0.52
## Desviación estándar: 0.1791
## Mínimo: 0.12
## Máximo: 1.58
##
## Variable: citric.acid
## Media: 0.271
## Mediana: 0.26
## Desviación estándar: 0.1948
## Mínimo: 0
## Máximo: 1
##
## Variable: residual.sugar
## Media: 2.5388
## Mediana: 2.2
## Desviación estándar: 1.4099
## Mínimo: 0.9
## Máximo: 15.5
##
## Variable: chlorides
## Media: 0.0875
## Mediana: 0.079
## Desviación estándar: 0.0471
## Mínimo: 0.012
## Máximo: 0.611
##
## Variable: free.sulfur.dioxide
## Media: 15.8749
## Mediana: 14
## Desviación estándar: 10.4602
## Mínimo: 1
## Máximo: 72
##
## Variable: total.sulfur.dioxide
## Media: 46.4678
## Mediana: 38
## Desviación estándar: 32.8953
## Mínimo: 6
## Máximo: 289
##
## Variable: density
## Media: 0.9967
## Mediana: 0.9968
## Desviación estándar: 0.0019
## Mínimo: 0.9901
## Máximo: 1.0037
##
## Variable: pH
## Media: 3.3111
## Mediana: 3.31
## Desviación estándar: 0.1544
## Mínimo: 2.74
## Máximo: 4.01
##
## Variable: sulphates
## Media: 0.6581
## Mediana: 0.62
## Desviación estándar: 0.1695
## Mínimo: 0.33
## Máximo: 2
##
## Variable: alcohol
## Media: 10.423
## Mediana: 10.2
## Desviación estándar: 1.0657
## Mínimo: 8.4
## Máximo: 14.9
##
## Variable: quality
## Media: 5.636
## Mediana: 6
## Desviación estándar: 0.8076
## Mínimo: 3
## Máximo: 8
tabla_quality <- data.frame(table(wine$quality))
names(tabla_quality) <- c("Calidad", "Frecuencia")
kable(tabla_quality, caption = "Distribución de la calidad del vino")
| Calidad | Frecuencia |
|---|---|
| 3 | 10 |
| 4 | 53 |
| 5 | 681 |
| 6 | 638 |
| 7 | 199 |
| 8 | 18 |
ggplot(wine, aes(x = factor(quality))) +
geom_bar(fill = "darkgreen") +
labs(
title = "Distribución de la calidad del vino",
x = "Calidad",
y = "Frecuencia"
) +
theme_minimal()
correlaciones <- cor(wine)
cor_quality <- data.frame(
Variable = names(correlaciones[, "quality"]),
Correlacion_con_quality = as.numeric(correlaciones[, "quality"])
) %>%
arrange(desc(Correlacion_con_quality)) %>%
mutate(across(where(is.numeric), ~ round(.x, 4)))
kable(cor_quality, caption = "Correlación de cada variable con quality")
| Variable | Correlacion_con_quality |
|---|---|
| quality | 1.0000 |
| alcohol | 0.4762 |
| sulphates | 0.2514 |
| citric.acid | 0.2264 |
| fixed.acidity | 0.1241 |
| residual.sugar | 0.0137 |
| free.sulfur.dioxide | -0.0507 |
| pH | -0.0577 |
| chlorides | -0.1289 |
| density | -0.1749 |
| total.sulfur.dioxide | -0.1851 |
| volatile.acidity | -0.3906 |
wine %>%
pivot_longer(cols = -quality, names_to = "Variable", values_to = "Valor") %>%
ggplot(aes(x = Valor, y = quality)) +
geom_point(alpha = 0.3) +
facet_wrap(~ Variable, scales = "free") +
labs(
title = "Relación de las variables explicativas con la calidad del vino",
x = "Valor de la variable",
y = "Calidad"
) +
theme_minimal()
El conjunto de datos contiene 1599 observaciones y 12 variables
numéricas. No se identifican valores perdidos en la base de datos. La
variable quality se concentra principalmente en valores
medios, especialmente en las calificaciones 5 y 6.
A partir del análisis de correlaciones, se observa que la variable
con mayor relación positiva con la calidad del vino es
alcohol, mientras que volatile acidity
presenta una relación negativa importante. También se identifican
variables con posible presencia de valores atípicos, como
residual sugar, chlorides,
sulphates y total sulfur dioxide, lo cual
puede observarse en los boxplots generados por la función de análisis
exploratorio.
El objetivo de esta sección es comparar el modelo de mantenimiento actual con tres propuestas de mejora: árbol de clasificación, redes neuronales y métodos de regresión. Como el objetivo es reducir el tiempo de fallas, el método con menor promedio y diferencia estadísticamente significativa será considerado como la mejor alternativa.
arbol <- c(23.81, 22.13, 22.64, 21.69, 23.58, 22.14, 18.73, 21.59,
20.36, 20.53, 20.11, 20.34, 19.19, 22.92, 18.65, 20.60,
19.83, 20.09, 19.43, 22.06, 21.15, 19.26, 18.08, 20.24,
18.75, 20.69, 21.62, 23.69, 23.93, 23.19)
redn <- c(23.24, 20.08, 18.01, 23.28, 19.23, 21.22, 21.47, 20.60,
21.11, 21.27, 21.03, 17.34, 22.80, 21.85, 17.85, 23.15,
19.57, 19.56, 20.79, 18.04, 20.95, 21.83, 18.17, 22.66,
18.29, 18.89, 19.49, 19.19, 26.47, 25.25)
regresion <- c(16.13, 17.84, 18.28, 15.61, 17.62, 16.12, 17.29, 16.13,
16.64, 15.03, 18.16, 16.82, 17.44, 16.76, 17.26, 15.55,
17.49, 18.42, 17.54, 17.13, 15.50, 16.80, 18.47, 18.42,
18.43, 15.56, 16.03, 15.39, 15.12, 17.77)
actual <- c(17.09, 15.77, 18.45, 16.55, 22.23, 22.11, 18.26, 18.04,
19.66, 19.76, 18.74, 19.02, 18.54, 16.70, 17.57, 19.89,
19.06, 18.70, 19.39, 19.68, 19.20, 16.85, 19.91, 19.82,
18.08, 19.38, 20.30, 21.60, 23.39, 19.33)
data2 <- data.frame(
Replica = factor(1:30),
Arbol_Clasificacion = arbol,
Redes_Neuronales = redn,
Metodos_Regresion = regresion,
Sistema_Actual = actual
)
head(data2)
## Replica Arbol_Clasificacion Redes_Neuronales Metodos_Regresion Sistema_Actual
## 1 1 23.81 23.24 16.13 17.09
## 2 2 22.13 20.08 17.84 15.77
## 3 3 22.64 18.01 18.28 18.45
## 4 4 21.69 23.28 15.61 16.55
## 5 5 23.58 19.23 17.62 22.23
## 6 6 22.14 21.22 16.12 22.11
datos_long <- data2 %>%
pivot_longer(
cols = c(Arbol_Clasificacion, Redes_Neuronales, Metodos_Regresion, Sistema_Actual),
names_to = "Metodo",
values_to = "Tiempo"
)
head(datos_long)
## # A tibble: 6 × 3
## Replica Metodo Tiempo
## <fct> <chr> <dbl>
## 1 1 Arbol_Clasificacion 23.8
## 2 1 Redes_Neuronales 23.2
## 3 1 Metodos_Regresion 16.1
## 4 1 Sistema_Actual 17.1
## 5 2 Arbol_Clasificacion 22.1
## 6 2 Redes_Neuronales 20.1
resumen_metodos <- datos_long %>%
group_by(Metodo) %>%
summarise(
Promedio = mean(Tiempo),
Mediana = median(Tiempo),
Desviacion = sd(Tiempo),
Minimo = min(Tiempo),
Maximo = max(Tiempo),
.groups = "drop"
) %>%
arrange(Promedio) %>%
mutate(across(where(is.numeric), ~ round(.x, 4)))
kable(resumen_metodos, caption = "Estadísticos descriptivos por método")
| Metodo | Promedio | Mediana | Desviacion | Minimo | Maximo |
|---|---|---|---|---|---|
| Metodos_Regresion | 16.8917 | 16.975 | 1.1033 | 15.03 | 18.47 |
| Sistema_Actual | 19.1023 | 19.130 | 1.7296 | 15.77 | 23.39 |
| Redes_Neuronales | 20.7560 | 20.870 | 2.2243 | 17.34 | 26.47 |
| Arbol_Clasificacion | 21.0340 | 20.645 | 1.7056 | 18.08 | 23.93 |
ggplot(datos_long, aes(x = Metodo, y = Tiempo)) +
geom_boxplot(fill = "lightblue") +
labs(
title = "Comparación de tiempos de falla por método",
x = "Método",
y = "Tiempo de fallas"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 25, hjust = 1))
Hipótesis nula:
H0: No existen diferencias significativas entre los tiempos promedio de fallas de los métodos evaluados.
Hipótesis alternativa:
H1: Al menos uno de los métodos presenta un tiempo promedio de fallas diferente.
Nivel de significancia:
α = 0.05
Como se tienen 30 réplicas para cada método, se considera la réplica como un bloque. El análisis permite comparar los métodos controlando el efecto de cada réplica.
modelo_anova <- aov(Tiempo ~ Metodo + Replica, data = datos_long)
summary(modelo_anova)
## Df Sum Sq Mean Sq F value Pr(>F)
## Metodo 3 326.4 108.81 36.867 1.8e-15 ***
## Replica 29 93.1 3.21 1.088 0.371
## Residuals 87 256.8 2.95
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
comparaciones <- pairwise.t.test(
datos_long$Tiempo,
datos_long$Metodo,
paired = TRUE,
p.adjust.method = "bonferroni"
)
comparaciones
##
## Pairwise comparisons using paired t tests
##
## data: datos_long$Tiempo and datos_long$Metodo
##
## Arbol_Clasificacion Metodos_Regresion Redes_Neuronales
## Metodos_Regresion 1.0e-09 - -
## Redes_Neuronales 1.0000 1.3e-07 -
## Sistema_Actual 0.0001 6.4e-05 0.0130
##
## P value adjustment method: bonferroni
La prueba de Friedman se utiliza como complemento cuando se comparan varios tratamientos relacionados o bloqueados.
friedman.test(as.matrix(data2[, -1]))
##
## Friedman rank sum test
##
## data: as.matrix(data2[, -1])
## Friedman chi-squared = 53.28, df = 3, p-value = 1.598e-11
t.test(regresion, actual, paired = TRUE, alternative = "less")
##
## Paired t-test
##
## data: regresion and actual
## t = -5.3138, df = 29, p-value = 5.303e-06
## alternative hypothesis: true mean difference is less than 0
## 95 percent confidence interval:
## -Inf -1.503788
## sample estimates:
## mean difference
## -2.210667
A partir de los estadísticos descriptivos, el método con menor tiempo
promedio es Metodos_Regresion, con una media aproximada de
16.89. Este promedio es menor que el del sistema actual, cuya media es
aproximadamente 19.10. Por otro lado, los métodos de árbol de
clasificación y redes neuronales presentan promedios superiores al
sistema actual, por lo que no representan una mejora en términos de
reducción del tiempo de fallas.
El ANOVA con bloques permite evaluar si existen diferencias significativas entre los métodos, asi que si el valor p asociado al método es menor que 0.05, se rechaza la hipótesis nula y se concluye que existen diferencias significativas entre los métodos evaluados. Además, la comparación directa entre métodos de regresión y el sistema actual permite sustentar que la propuesta basada en métodos de regresión reduce significativamente el tiempo de fallas.
Se concluye que con un nivel de significancia del 5%, la propuesta
que debería implementarse es Métodos de Regresión, debido a
que presenta el menor tiempo promedio de fallas y mejora el desempeño
respecto al sistema actual.