alcohol y calorias muestran asimetría
positiva relevante, por lo que se prioriza la mediana y el rango
intercuartílico sobre la media y la desviación estándar para
resumirlas.Una empresa cervecera busca comprender las preferencias de sus consumidores y qué características del producto se asocian con mejores valoraciones, como insumo para decisiones de portafolio y comunicación. Este informe presenta un diagnóstico descriptivo de la base beer2, que reúne reseñas de cerveza con información de identificación, composición nutricional, procedencia y valoración.
dim(beer2)
sum(is.na(beer2))
length(unique(beer2$marca))
length(unique(beer2$nombre_cerveza))
La base contiene 1.586.614 filas (reseñas) y 15 columnas. Se verificó que no existen valores faltantes en ninguna variable. Cada reseña corresponde a una combinación marca–producto–usuario, por lo que una misma cerveza puede tener múltiples registros.
La tabla siguiente resume el tipo y contenido de cada variable:
diccionario <- data.frame(
Variable = c("id", "marca", "precio", "alcohol", "carbohidratos",
"calorias", "nacionalidad", "calificacion", "aroma",
"apariencia", "paladar", "sabor", "nombre_cerveza",
"tipo", "origen"),
Tipo = c("Identificador", "Cualitativa nominal", "Cuantitativa continua (simulada)",
"Cuantitativa continua", "Cuantitativa continua", "Cuantitativa discreta",
"Cualitativa nominal", "Cualitativa ordinal", "Cualitativa ordinal",
"Cualitativa ordinal", "Cualitativa ordinal", "Cualitativa ordinal",
"Cualitativa nominal", "Cualitativa nominal", "Cualitativa nominal dicotómica"),
Descripcion = c(
"Código del producto reseñado",
"Cervecería productora",
"Precio de referencia de una caja de 6 botellas de 355 ml",
"Gramos de alcohol puro en 355 ml",
"Gramos de carbohidratos en 355 ml",
"Calorías totales en 355 ml",
"País de la cervecería",
"Valoración general de la experiencia",
"Valoración del aroma",
"Valoración de la apariencia",
"Valoración en el paladar",
"Valoración del sabor",
"Nombre comercial del producto",
"Categoría del producto",
"Nacional o importada"
)
)
knitr::kable(diccionario, caption = "Tabla 1. Variables de la base beer2")
| Variable | Tipo | Descripcion |
|---|---|---|
| id | Identificador | Código del producto reseñado |
| marca | Cualitativa nominal | Cervecería productora |
| precio | Cuantitativa continua (simulada) | Precio de referencia de una caja de 6 botellas de 355 ml |
| alcohol | Cuantitativa continua | Gramos de alcohol puro en 355 ml |
| carbohidratos | Cuantitativa continua | Gramos de carbohidratos en 355 ml |
| calorias | Cuantitativa discreta | Calorías totales en 355 ml |
| nacionalidad | Cualitativa nominal | País de la cervecería |
| calificacion | Cualitativa ordinal | Valoración general de la experiencia |
| aroma | Cualitativa ordinal | Valoración del aroma |
| apariencia | Cualitativa ordinal | Valoración de la apariencia |
| paladar | Cualitativa ordinal | Valoración en el paladar |
| sabor | Cualitativa ordinal | Valoración del sabor |
| nombre_cerveza | Cualitativa nominal | Nombre comercial del producto |
| tipo | Cualitativa nominal | Categoría del producto |
| origen | Cualitativa nominal dicotómica | Nacional o importada |
Todos los cálculos se realizaron en R base
(funciones table, prop.table,
aggregate, quantile, sd,
hist, boxplot); el código completo se incluye
en el anexo al final del documento.
t_tipo <- sort(table(beer2$tipo), decreasing = TRUE)
t_origen <- sort(table(beer2$origen), decreasing = TRUE)
par(mfrow = c(1, 2), mar = c(8, 4, 3, 1))
barplot(as.vector(t_tipo), names.arg = names(t_tipo), las = 2,
col = "steelblue", main = "Reseñas por tipo de cerveza",
ylab = "Número de reseñas", cex.names = 0.8)
barplot(as.vector(t_origen), names.arg = names(t_origen), las = 1,
col = "steelblue", main = "Reseñas por origen",
ylab = "Número de reseñas")
par(mfrow = c(1, 1))
tabla_tipo <- data.frame(
Tipo = names(t_tipo),
Frecuencia = as.vector(t_tipo),
Porcentaje = round(prop.table(t_tipo) * 100, 2)
)
knitr::kable(tabla_tipo, row.names = FALSE,
caption = "Tabla 2. Frecuencia de reseñas por tipo de cerveza")
| Tipo | Frecuencia | Porcentaje.Var1 | Porcentaje.Freq |
|---|---|---|---|
| cerveza normal y helada | 839088 | cerveza normal y helada | 52.89 |
| clara artesanal | 483464 | clara artesanal | 30.47 |
| lager importada | 219194 | lager importada | 13.82 |
| lager artesanal | 22725 | lager artesanal | 1.43 |
| baja en calorías / sin alcohol | 22143 | baja en calorías / sin alcohol | 1.40 |
Más de la mitad de las reseñas corresponde a cerveza normal y helada (≈53%), seguida de clara artesanal (≈30%); entre ambas concentran más del 80% de los registros (moda: cerveza normal y helada). Las categorías lager artesanal y baja en calorías / sin alcohol son minoritarias (≈1,4% cada una). En cuanto al origen, el 89% de las reseñas son de cerveza importada frente a solo 11% de nacional (moda: importada): la base está claramente desbalanceada en esta variable.
t_nac <- sort(table(beer2$nacionalidad), decreasing = TRUE)
tabla_nac <- data.frame(
Pais = names(t_nac),
Frecuencia = as.vector(t_nac),
Porcentaje = round(prop.table(t_nac) * 100, 2)
)
knitr::kable(tabla_nac, row.names = FALSE,
caption = "Tabla 3. Reseñas por nacionalidad de la cervecería (10 países)")
| Pais | Frecuencia | Porcentaje.Var1 | Porcentaje.Freq |
|---|---|---|---|
| Austria | 201941 | Austria | 12.73 |
| Bélgica | 188677 | Bélgica | 11.89 |
| Francia | 181574 | Francia | 11.44 |
| Estados Unidos | 174165 | Estados Unidos | 10.98 |
| Inglaterra | 162644 | Inglaterra | 10.25 |
| Canadá | 159508 | Canadá | 10.05 |
| Japón | 155273 | Japón | 9.79 |
| Alemania | 121684 | Alemania | 7.67 |
| Irlanda | 120742 | Irlanda | 7.61 |
| República Checa | 120406 | República Checa | 7.59 |
t_marca <- sort(table(beer2$marca), decreasing = TRUE)
tabla_marca <- data.frame(
Marca = names(head(t_marca, 10)),
Frecuencia = as.vector(head(t_marca, 10)),
Porcentaje = round(prop.table(t_marca)[1:10] * 100, 2)
)
knitr::kable(tabla_marca, row.names = FALSE,
caption = "Tabla 4. Top 10 marcas con más reseñas (de 5.743 en total)")
| Marca | Frecuencia | Porcentaje.Var1 | Porcentaje.Freq |
|---|---|---|---|
| Boston Beer Company (Samuel Adams) | 39444 | Boston Beer Company (Samuel Adams) | 2.49 |
| Dogfish Head Brewery | 33839 | Dogfish Head Brewery | 2.13 |
| Stone Brewing Co. | 33066 | Stone Brewing Co. | 2.08 |
| Sierra Nevada Brewing Co. | 28751 | Sierra Nevada Brewing Co. | 1.81 |
| Bell’s Brewery, Inc. | 25191 | Bell’s Brewery, Inc. | 1.59 |
| Rogue Ales | 24083 | Rogue Ales | 1.52 |
| Founders Brewing Company | 20004 | Founders Brewing Company | 1.26 |
| Victory Brewing Company | 19479 | Victory Brewing Company | 1.23 |
| Lagunitas Brewing Company | 16837 | Lagunitas Brewing Company | 1.06 |
| Avery Brewing Company | 16107 | Avery Brewing Company | 1.02 |
La variable nacionalidad reúne solo 10 países con una
distribución relativamente equilibrada (entre ≈7,6% y ≈12,7% cada uno;
moda: Austria), sin un país claramente dominante. El mercado de
marcas, en cambio, está muy fragmentado: aun la más
reseñada (Boston Beer Company / Samuel Adams) no alcanza el 2,5% del
total, y existen 5.743 cervecerías distintas. La variable
nombre_cerveza (56.857 productos distintos) funciona como
identificador del producto —igual que id— más que como
variable de interés estadístico, por lo que no se calculan frecuencias
completas para ella.
Las cinco variables de valoración (calificacion,
aroma, apariencia, paladar,
sabor) se miden en una escala de 1 a 5, con incrementos de
0,5.
niveles <- sort(unique(unlist(lapply(beer2[ordinales], unique))))
tabla_ord <- sapply(ordinales, function(v) {
t <- table(factor(beer2[[v]], levels = niveles))
round(as.vector(prop.table(t)) * 100, 2)
})
rownames(tabla_ord) <- niveles
knitr::kable(tabla_ord,
caption = "Tabla 5. Distribución porcentual (%) de las variables de valoración")
| calificacion | aroma | apariencia | paladar | sabor | |
|---|---|---|---|---|---|
| 0 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 1 | 0.69 | 0.43 | 0.21 | 0.43 | 0.63 |
| 1.5 | 0.82 | 0.79 | 0.39 | 0.70 | 0.95 |
| 2 | 2.41 | 2.68 | 1.60 | 2.42 | 2.65 |
| 2.5 | 3.69 | 4.18 | 2.49 | 3.96 | 4.19 |
| 3 | 10.44 | 12.61 | 10.46 | 13.04 | 10.52 |
| 3.5 | 19.02 | 23.02 | 20.08 | 21.34 | 20.45 |
| 4 | 36.73 | 35.13 | 42.49 | 38.24 | 34.12 |
| 4.5 | 20.45 | 17.11 | 18.16 | 15.95 | 21.19 |
| 5 | 5.76 | 4.04 | 4.12 | 3.92 | 5.29 |
boxplot(beer2[ordinales], col = "lightgreen",
main = "Distribución de las variables de valoración")
Las cinco variables muestran un patrón muy similar: la moda
es 4 en todas ellas y la mayor concentración de respuestas está
entre 3,5 y 4,5. Las calificaciones bajas (1 a 2) son poco frecuentes,
lo que sugiere un sesgo hacia valoraciones positivas,
típico de plataformas de reseñas donde los usuarios tienden a comentar
productos que les gustaron. También se observan unos pocos registros
extremos (7 en calificacion y 7 en apariencia)
con valor 0; su frecuencia es tan baja (< 0,001%) que no afecta
materialmente los resultados, pero conviene señalarlos.
resumen <- data.frame(
Variable = cuant,
Media = sapply(cuant, function(v) mean(beer2[[v]])),
Mediana = sapply(cuant, function(v) median(beer2[[v]])),
Q1 = sapply(cuant, function(v) quantile(beer2[[v]], .25)),
Q3 = sapply(cuant, function(v) quantile(beer2[[v]], .75)),
IQR = sapply(cuant, function(v) IQR(beer2[[v]])),
DesvEst = sapply(cuant, function(v) sd(beer2[[v]])),
CoefVar_pct = sapply(cuant, function(v) sd(beer2[[v]]) / mean(beer2[[v]]) * 100),
Asimetria = sapply(cuant, function(v) asimetria(beer2[[v]]))
)
rownames(resumen) <- NULL
knitr::kable(resumen, digits = 2,
caption = "Tabla 6. Estadísticos descriptivos de las variables cuantitativas")
| Variable | Media | Mediana | Q1 | Q3 | IQR | DesvEst | CoefVar_pct | Asimetria |
|---|---|---|---|---|---|---|---|---|
| precio | 5.36 | 5.41 | 4.98 | 5.82 | 0.84 | 0.65 | 12.11 | -0.47 |
| alcohol | 18.71 | 17.67 | 14.28 | 22.63 | 8.35 | 5.46 | 29.16 | 0.96 |
| carbohidratos | 14.05 | 13.80 | 12.40 | 15.60 | 3.20 | 2.38 | 16.91 | 0.46 |
| calorias | 193.17 | 182.00 | 157.00 | 224.00 | 67.00 | 46.57 | 24.11 | 0.92 |
par(mfrow = c(2, 2))
for (v in cuant) {
hist(beer2[[v]], main = paste("Histograma de", v), xlab = v,
col = "steelblue", border = "white")
}
par(mfrow = c(1, 1))
El precio (simulado) y los carbohidratos presentan distribuciones casi simétricas (asimetría entre -0,47 y 0,46), con media y mediana muy cercanas; para ambas, la media y la desviación estándar son medidas resumen adecuadas. El alcohol y las calorías, en cambio, muestran asimetría positiva moderada (≈0,96 y ≈0,92, cola hacia la derecha) y mayor coeficiente de variación (≈29% y ≈24%): unas pocas cervezas artesanales con alto contenido alcohólico o calórico alargan la cola superior y elevan la media por encima de la mediana. Por ello, para estas dos variables se recomienda priorizar la mediana y el rango intercuartílico (IQR) frente a la media y la desviación estándar.
media_tipo <- aggregate(beer2[vars_comp], by = list(Tipo = beer2$tipo), FUN = mean)
mediana_tipo <- aggregate(beer2[vars_comp], by = list(Tipo = beer2$tipo), FUN = median)
names(mediana_tipo)[-1] <- paste0(names(mediana_tipo)[-1], "_mediana")
names(media_tipo)[-1] <- paste0(names(media_tipo)[-1], "_media")
tabla_tipo_comp <- merge(media_tipo, mediana_tipo, by = "Tipo")
knitr::kable(tabla_tipo_comp, digits = 2,
caption = "Tabla 7. Media y mediana de los indicadores según tipo de cerveza")
| Tipo | precio_media | alcohol_media | carbohidratos_media | calorias_media | calificacion_media | precio_mediana | alcohol_mediana | carbohidratos_mediana | calorias_mediana | calificacion_mediana |
|---|---|---|---|---|---|---|---|---|---|---|
| baja en calorías / sin alcohol | 4.28 | 9.84 | 8.66 | 109.5 | 2.95 | 4.25 | 11.07 | 8.7 | 116 | 3 |
| cerveza normal y helada | 5.27 | 20.10 | 14.29 | 203.8 | 3.86 | 5.34 | 18.94 | 14.1 | 198 | 4 |
| clara artesanal | 5.70 | 18.15 | 14.51 | 191.1 | 3.88 | 5.78 | 17.41 | 14.3 | 184 | 4 |
| lager artesanal | 4.55 | 15.67 | 13.54 | 169.9 | 3.64 | 4.54 | 13.75 | 13.1 | 158 | 4 |
| lager importada | 5.17 | 15.86 | 12.71 | 167.8 | 3.61 | 5.24 | 14.28 | 12.6 | 156 | 4 |
media_origen <- aggregate(beer2[vars_comp], by = list(Origen = beer2$origen), FUN = mean)
mediana_origen <- aggregate(beer2[vars_comp], by = list(Origen = beer2$origen), FUN = median)
names(mediana_origen)[-1] <- paste0(names(mediana_origen)[-1], "_mediana")
names(media_origen)[-1] <- paste0(names(media_origen)[-1], "_media")
tabla_origen_comp <- merge(media_origen, mediana_origen, by = "Origen")
knitr::kable(tabla_origen_comp, digits = 2,
caption = "Tabla 8. Media y mediana de los indicadores según origen")
| Origen | precio_media | alcohol_media | carbohidratos_media | calorias_media | calificacion_media | precio_mediana | alcohol_mediana | carbohidratos_mediana | calorias_mediana | calificacion_mediana |
|---|---|---|---|---|---|---|---|---|---|---|
| importada | 5.46 | 18.71 | 14.02 | 193.1 | 3.81 | 5.50 | 17.41 | 13.8 | 182 | 4 |
| nacional | 4.54 | 18.73 | 14.24 | 194.1 | 3.85 | 4.54 | 17.93 | 14.1 | 188 | 4 |
par(mfrow = c(1, 2), mar = c(8, 4, 3, 1))
boxplot(calificacion ~ tipo, data = beer2, las = 2, cex.axis = 0.7,
main = "Calificación según tipo", col = "lightgreen", xlab = "")
boxplot(alcohol ~ origen, data = beer2,
main = "Alcohol según origen", col = "lightgoldenrod")
par(mfrow = c(1, 1))
knitr::kable(table(beer2$tipo, beer2$origen),
caption = "Tabla 9. Número de reseñas por tipo y origen")
| importada | nacional | |
|---|---|---|
| baja en calorías / sin alcohol | 20432 | 1711 |
| cerveza normal y helada | 754796 | 84292 |
| clara artesanal | 418027 | 65437 |
| lager artesanal | 0 | 22725 |
| lager importada | 219194 | 0 |
El tipo baja en calorías / sin alcohol tiene en
promedio el menor contenido de alcohol, carbohidratos y calorías, y
también la calificación promedio más baja (≈2,95 sobre
5); en esta base, las cervezas más ligeras tienden a ser peor valoradas.
El tipo clara artesanal tiene el precio
simulado promedio más alto (≈5,71) y, junto con cerveza
normal y helada, las mejores calificaciones promedio (≈3,88 y
≈3,86). Al cruzar tipo con origen aparece una
relación estructural relevante (Tabla 9): toda la
categoría lager artesanal es de origen nacional y
toda la categoría lager importada es de origen
importado; para estos dos tipos, tipo determina por
definición el origen, por lo que las comparaciones entre
ambas variables no son totalmente independientes para esas categorías.
Comparando directamente por origen, las cervezas
nacionales obtienen una calificación levemente superior
(≈3,85 frente a ≈3,81) y un precio simulado menor (≈4,54 frente a ≈5,47)
que las importadas, aunque el alcohol y las calorías
son muy similares entre ambos grupos.
alcohol y calorias, cuya distribución
es asimétrica hacia la derecha, se recomienda reportar mediana e
IQR en lugar de media y desviación estándar.