knitr::opts_chunk$set(echo = TRUE)
En este trabajo se realizará un análisis exploratorio de una base de datos sobre vinos tintos. La información contiene diferentes características del vino, como acidez, azúcar, alcohol y otras propiedades, además de una variable que indica la calidad del vino.
El objetivo es conocer mejor la información disponible, revisar si existen datos faltantes o repetidos y obtener algunos estadísticos que permitan describir las variables.
También se analizará la distribución de la calidad y la relación entre las características del vino y su calidad.
Primero se importa la base de datos utilizando la función
read.csv().
BD <- read.csv(
"winequality-red.csv",
header = TRUE,
sep = ","
)
str(BD)
## '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 ...
Para conocer el número de filas y columnas de la base utilizamos
dim().
dim(BD)
## [1] 1599 12
La base contiene 1599 observaciones y 12 variables.
También podemos observar las primeras filas:
head(BD)
## 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
Es importante verificar si existen datos faltantes, ya que estos pueden afectar los análisis posteriores.
faltantes <- colSums(is.na(BD))
faltantes
## fixed.acidity volatile.acidity citric.acid
## 0 0 0
## residual.sugar chlorides free.sulfur.dioxide
## 0 0 0
## total.sulfur.dioxide density pH
## 0 0 0
## sulphates alcohol quality
## 0 0 0
El número total de valores faltantes es:
sum(is.na(BD))
## [1] 0
La base presenta un total de 0 valores faltantes.
También se revisará si existen filas repetidas.
duplicados <- sum(duplicated(BD))
duplicados
## [1] 240
Se encontraron 240 registros duplicados.
Los registros duplicados no serán eliminados en esta etapa, ya que primero es necesario determinar si realmente representan información repetida o si pueden ser observaciones válidas.
Para obtener una primera descripción de las variables utilizamos la
función summary().
summary(BD)
## fixed.acidity volatile.acidity citric.acid residual.sugar
## Min. : 4.60 Min. :0.1200 Min. :0.000 Min. : 0.900
## 1st Qu.: 7.10 1st Qu.:0.3900 1st Qu.:0.090 1st Qu.: 1.900
## Median : 7.90 Median :0.5200 Median :0.260 Median : 2.200
## Mean : 8.32 Mean :0.5278 Mean :0.271 Mean : 2.539
## 3rd Qu.: 9.20 3rd Qu.:0.6400 3rd Qu.:0.420 3rd Qu.: 2.600
## Max. :15.90 Max. :1.5800 Max. :1.000 Max. :15.500
## chlorides free.sulfur.dioxide total.sulfur.dioxide density
## Min. :0.01200 Min. : 1.00 Min. : 6.00 Min. :0.9901
## 1st Qu.:0.07000 1st Qu.: 7.00 1st Qu.: 22.00 1st Qu.:0.9956
## Median :0.07900 Median :14.00 Median : 38.00 Median :0.9968
## Mean :0.08747 Mean :15.87 Mean : 46.47 Mean :0.9967
## 3rd Qu.:0.09000 3rd Qu.:21.00 3rd Qu.: 62.00 3rd Qu.:0.9978
## Max. :0.61100 Max. :72.00 Max. :289.00 Max. :1.0037
## pH sulphates alcohol quality
## Min. :2.740 Min. :0.3300 Min. : 8.40 Min. :3.000
## 1st Qu.:3.210 1st Qu.:0.5500 1st Qu.: 9.50 1st Qu.:5.000
## Median :3.310 Median :0.6200 Median :10.20 Median :6.000
## Mean :3.311 Mean :0.6581 Mean :10.42 Mean :5.636
## 3rd Qu.:3.400 3rd Qu.:0.7300 3rd Qu.:11.10 3rd Qu.:6.000
## Max. :4.010 Max. :2.0000 Max. :14.90 Max. :8.000
Esta función permite observar valores como el mínimo, máximo, mediana y los cuartiles de las variables numéricas.
Para complementar el análisis se calcularán algunos estadísticos básicos de las variables numéricas.
variables_numericas <- names(BD)[sapply(BD, is.numeric)]
tabla_descriptiva <- data.frame(
Variable = variables_numericas,
Minimo = sapply(BD[variables_numericas], min),
Mediana = sapply(BD[variables_numericas], median),
Media = sapply(BD[variables_numericas], mean),
Maximo = sapply(BD[variables_numericas], max)
)
tabla_descriptiva[, -1] <- round(
tabla_descriptiva[, -1],
2
)
knitr::kable(
tabla_descriptiva,
caption = "Estadísticos descriptivos"
)
| Variable | Minimo | Mediana | Media | Maximo | |
|---|---|---|---|---|---|
| fixed.acidity | fixed.acidity | 4.60 | 7.90 | 8.32 | 15.90 |
| volatile.acidity | volatile.acidity | 0.12 | 0.52 | 0.53 | 1.58 |
| citric.acid | citric.acid | 0.00 | 0.26 | 0.27 | 1.00 |
| residual.sugar | residual.sugar | 0.90 | 2.20 | 2.54 | 15.50 |
| chlorides | chlorides | 0.01 | 0.08 | 0.09 | 0.61 |
| free.sulfur.dioxide | free.sulfur.dioxide | 1.00 | 14.00 | 15.87 | 72.00 |
| total.sulfur.dioxide | total.sulfur.dioxide | 6.00 | 38.00 | 46.47 | 289.00 |
| density | density | 0.99 | 1.00 | 1.00 | 1.00 |
| pH | pH | 2.74 | 3.31 | 3.31 | 4.01 |
| sulphates | sulphates | 0.33 | 0.62 | 0.66 | 2.00 |
| alcohol | alcohol | 8.40 | 10.20 | 10.42 | 14.90 |
| quality | quality | 3.00 | 6.00 | 5.64 | 8.00 |
Para evitar repetir el mismo código para cada variable, se creó una función sencilla que permite obtener información básica de una variable.
analizar_variable <- function(datos, variable) {
x <- datos[[variable]]
cat("Variable:", variable, "\n")
cat("Tipo:", class(x), "\n")
cat("Valores faltantes:", sum(is.na(x)), "\n")
cat("Valores diferentes:", length(unique(x)), "\n")
if (is.numeric(x)) {
cat("Media:", round(mean(x, na.rm = TRUE), 2), "\n")
cat("Mediana:", round(median(x, na.rm = TRUE), 2), "\n")
cat("Mínimo:", min(x, na.rm = TRUE), "\n")
cat("Máximo:", max(x, na.rm = TRUE), "\n")
hist(
x,
main = paste("Distribución de", variable),
xlab = variable,
ylab = "Frecuencia"
)
boxplot(
x,
main = paste("Diagrama de caja de", variable),
ylab = variable
)
} else {
print(table(x))
}
}
Por ejemplo, podemos utilizar la función con la variable
alcohol:
analizar_variable(BD, "alcohol")
## Variable: alcohol
## Tipo: numeric
## Valores faltantes: 0
## Valores diferentes: 65
## Media: 10.42
## Mediana: 10.2
## Mínimo: 8.4
## Máximo: 14.9
También podemos utilizarla con la variable quality:
analizar_variable(BD, "quality")
## Variable: quality
## Tipo: integer
## Valores faltantes: 0
## Valores diferentes: 6
## Media: 5.64
## Mediana: 6
## Mínimo: 3
## Máximo: 8
Los diagramas de caja permiten observar posibles valores atípicos en las variables numéricas.
Para realizar una revisión sencilla, se utilizará el rango intercuartílico.
contar_atipicos <- function(x) {
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
RIQ <- Q3 - Q1
limite_inferior <- Q1 - 1.5 * RIQ
limite_superior <- Q3 + 1.5 * RIQ
sum(
x < limite_inferior | x > limite_superior,
na.rm = TRUE
)
}
Ahora se aplica la función a las variables numéricas:
atipicos <- sapply(
BD[variables_numericas],
contar_atipicos
)
atipicos
## fixed.acidity volatile.acidity citric.acid
## 49 19 1
## residual.sugar chlorides free.sulfur.dioxide
## 155 112 30
## total.sulfur.dioxide density pH
## 55 45 35
## sulphates alcohol quality
## 59 13 28
Estos valores se consideran posibles valores atípicos, por lo que no necesariamente deben eliminarse. Es necesario revisar cada caso antes de tomar una decisión.
La variable quality representa la calificación del vino.
Primero observamos cuántos vinos existen para cada nivel de calidad.
frecuencia_calidad <- table(BD$quality)
frecuencia_calidad
##
## 3 4 5 6 7 8
## 10 53 681 638 199 18
Podemos representar esta información mediante un gráfico de barras.
barplot(
frecuencia_calidad,
main = "Calidad de los vinos",
xlab = "Calidad",
ylab = "Cantidad",
col = "darkred"
)
También calculamos el porcentaje de cada nivel de calidad:
porcentaje_calidad <- round(
prop.table(frecuencia_calidad) * 100,
2
)
porcentaje_calidad
##
## 3 4 5 6 7 8
## 0.63 3.31 42.59 39.90 12.45 1.13
La calidad que aparece con mayor frecuencia es:
calidad_mas_frecuente <- names(
which.max(frecuencia_calidad)
)
calidad_mas_frecuente
## [1] "5"
Para conocer qué variables presentan una mayor relación con la calidad del vino, se calcularán las correlaciones.
correlaciones <- cor(
BD[variables_numericas],
use = "complete.obs"
)
cor_quality <- correlaciones[, "quality"]
cor_quality <- sort(
cor_quality,
decreasing = TRUE
)
cor_quality
## quality alcohol sulphates
## 1.00000000 0.47616632 0.25139708
## citric.acid fixed.acidity residual.sugar
## 0.22637251 0.12405165 0.01373164
## free.sulfur.dioxide pH chlorides
## -0.05065606 -0.05773139 -0.12890656
## density total.sulfur.dioxide volatile.acidity
## -0.17491923 -0.18510029 -0.39055778
Podemos representar las correlaciones mediante un gráfico:
barplot(
cor_quality,
las = 2,
main = "Correlación con la calidad",
ylab = "Correlación"
)
abline(h = 0, lty = 2)
Una correlación positiva indica que, cuando una variable aumenta, la calidad tiende a aumentar. Por otro lado, una correlación negativa indica una relación en sentido contrario.
La variable que presenta la mayor correlación en valor absoluto con la calidad es:
cor_sin_quality <- cor_quality[
names(cor_quality) != "quality"
]
variable_mayor <- names(
sort(abs(cor_sin_quality), decreasing = TRUE)
)[1]
valor_correlacion <- cor_sin_quality[
variable_mayor
]
variable_mayor
## [1] "alcohol"
valor_correlacion
## alcohol
## 0.4761663