library(readr)      # importación de archivos csv
library(dplyr)      # manipulación de datos
library(flextable)  # presentación de tablas

Asignatura: Teoría de Probabilidades
Docente: Daniel Enrique González Gómez
Institución: Pontificia Universidad Javeriana Cali
Base de datos: beer2.csv


1 Resumen

Este informe presenta el diagnóstico descriptivo inicial solicitado por la dirección de investigación de una empresa del sector cervecero. Se analizaron 1.586.614 reseñas de consumidores sobre 15 variables relativas a la identificación del producto, su composición nutricional, su procedencia, su precio de referencia y las valoraciones sensoriales otorgadas.

Para las variables cualitativas y ordinales se construyeron tablas de frecuencias absolutas, relativas y acumuladas, identificando la moda en cada caso. Para las variables cuantitativas se calcularon indicadores de tendencia central, posición, dispersión y forma, seleccionando en cada caso el indicador apropiado según la simetría observada en la distribución. Finalmente se compararon los indicadores entre tipos y orígenes de cerveza.

Los resultados muestran una base fuertemente concentrada en cerveza normal y helada de origen importado, con valoraciones sensoriales que se agrupan en los niveles altos de las escalas. Entre los atributos evaluados, el sabor y el paladar son los que presentan mayor asociación con la calificación general, mientras que el precio y la composición nutricional muestran asociaciones débiles. Todos los hallazgos son de naturaleza descriptiva y no permiten establecer relaciones causales.

2 Introducción

La presente investigación surge del interés de una empresa del sector cervecero por comprender las preferencias expresadas por los consumidores y reconocer qué características de las cervezas se relacionan con mejores valoraciones. Conocer las diferencias según el tipo, el origen, la composición nutricional, el precio y los atributos sensoriales puede aportar evidencia para orientar la selección del portafolio, la comunicación de los productos y futuras decisiones comerciales.

En esta primera aproximación se realiza un análisis descriptivo. Por tanto, los resultados permiten identificar patrones y formular nuevas hipótesis, pero no establecer relaciones causales.

3 Objetivos

3.1 Objetivo general

Elaborar un informe descriptivo que sirva como diagnóstico inicial de las preferencias expresadas por los consumidores de cerveza, a partir del procesamiento estadístico de la base de datos beer2.csv.

3.2 Objetivos específicos

  1. Clasificar las variables de la base de datos según su naturaleza y escala de medición, con el fin de seleccionar las técnicas de resumen apropiadas.
  2. Construir tablas de frecuencias absolutas, relativas y acumuladas para las variables cualitativas y ordinales, e identificar la moda en cada caso.
  3. Calcular indicadores de tendencia central, posición, dispersión y forma para las variables cuantitativas, justificando cuál resulta más representativo según la distribución observada.
  4. Comparar el comportamiento de los indicadores entre los distintos tipos y orígenes de cerveza.
  5. Interpretar los patrones encontrados en relación con el propósito de la investigación, señalando las limitaciones del análisis.

4 Metodología

4.1 Ficha técnica de la base de datos

cerveza <- read_csv("beer2.csv", show_col_types = FALSE)
ficha <- data.frame(
  Aspecto = c("Nombre del archivo", "Unidad de observación",
              "Número de registros", "Número de variables",
              "Datos faltantes", "Marcas o cervecerías distintas",
              "Cervezas distintas reseñadas", "Países de procedencia",
              "Variables simuladas"),
  Detalle = c("beer2.csv",
              "Una reseña de un consumidor sobre una cerveza",
              format(nrow(cerveza), big.mark = ".", decimal.mark = ","),
              as.character(ncol(cerveza)),
              format(sum(is.na(cerveza)), big.mark = ".", decimal.mark = ","),
              format(n_distinct(cerveza$marca), big.mark = ".", decimal.mark = ","),
              format(n_distinct(cerveza$nombre_cerveza), big.mark = ".", decimal.mark = ","),
              as.character(n_distinct(cerveza$nacionalidad)),
              "precio"),
  stringsAsFactors = FALSE
)

flextable(ficha) |>
  set_header_labels(Aspecto = "Aspecto", Detalle = "Detalle") |>
  theme_booktabs() |>
  align(j = 2, align = "left", part = "all") |>
  set_caption("Tabla 1. Ficha técnica de la base de datos") |>
  autofit()
Tabla 1. Ficha técnica de la base de datos

Aspecto

Detalle

Nombre del archivo

beer2.csv

Unidad de observación

Una reseña de un consumidor sobre una cerveza

Número de registros

1.586.614

Número de variables

15

Datos faltantes

0

Marcas o cervecerías distintas

5.743

Cervezas distintas reseñadas

56.857

Países de procedencia

10

Variables simuladas

precio

Cada fila corresponde a una reseña individual; por esta razón una misma cerveza puede aparecer en varios registros. Los indicadores calculados describen, en consecuencia, el comportamiento del conjunto de reseñas y no el de un catálogo de productos únicos.

4.2 Clasificación de las variables

La selección de las tablas e indicadores apropiados depende directamente del tipo de variable y de su escala de medición.

clasif <- data.frame(
  Variable = c("id", "marca", "nombre_cerveza", "nacionalidad", "tipo", "origen",
               "precio", "alcohol", "carbohidratos", "calorias",
               "calificacion", "aroma", "apariencia", "paladar", "sabor"),
  Naturaleza = c("Identificador", rep("Cualitativa", 5),
                 rep("Cuantitativa", 4), rep("Cualitativa", 5)),
  Escala = c("No aplica", rep("Nominal", 4), "Nominal dicotómica",
             "Continua (de razón)", "Continua (de razón)",
             "Continua (de razón)", "Discreta (de razón)",
             rep("Ordinal", 5)),
  Resumen_apropiado = c("No se resume",
                        rep("Tabla de frecuencias y moda", 5),
                        rep("Indicadores de tendencia, posición, dispersión y forma", 4),
                        rep("Tabla de frecuencias, moda y mediana", 5)),
  stringsAsFactors = FALSE
)

flextable(clasif) |>
  set_header_labels(Variable = "Variable", Naturaleza = "Naturaleza",
                    Escala = "Escala de medición",
                    Resumen_apropiado = "Forma de resumen apropiada") |>
  theme_booktabs() |>
  align(align = "left", part = "all") |>
  set_caption("Tabla 2. Clasificación de las variables según naturaleza y escala") |>
  autofit()
Tabla 2. Clasificación de las variables según naturaleza y escala

Variable

Naturaleza

Escala de medición

Forma de resumen apropiada

id

Identificador

No aplica

No se resume

marca

Cualitativa

Nominal

Tabla de frecuencias y moda

nombre_cerveza

Cualitativa

Nominal

Tabla de frecuencias y moda

nacionalidad

Cualitativa

Nominal

Tabla de frecuencias y moda

tipo

Cualitativa

Nominal

Tabla de frecuencias y moda

origen

Cualitativa

Nominal dicotómica

Tabla de frecuencias y moda

precio

Cuantitativa

Continua (de razón)

Indicadores de tendencia, posición, dispersión y forma

alcohol

Cuantitativa

Continua (de razón)

Indicadores de tendencia, posición, dispersión y forma

carbohidratos

Cuantitativa

Continua (de razón)

Indicadores de tendencia, posición, dispersión y forma

calorias

Cuantitativa

Discreta (de razón)

Indicadores de tendencia, posición, dispersión y forma

calificacion

Cualitativa

Ordinal

Tabla de frecuencias, moda y mediana

aroma

Cualitativa

Ordinal

Tabla de frecuencias, moda y mediana

apariencia

Cualitativa

Ordinal

Tabla de frecuencias, moda y mediana

paladar

Cualitativa

Ordinal

Tabla de frecuencias, moda y mediana

sabor

Cualitativa

Ordinal

Tabla de frecuencias, moda y mediana

La variable id identifica el producto reseñado pero no constituye una variable estadística de interés, por lo que se excluye del análisis. Las cinco escalas sensoriales están codificadas con números, pero representan niveles de valoración y no cantidades medibles; por ello se tratan como ordinales.

4.3 Criterio de selección de indicadores

Para las variables cuantitativas, la elección entre media y mediana se fundamenta en el coeficiente de asimetría:

\[g_1 = \frac{\frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^3}{s^3}\]

El criterio aplicado en este informe es el siguiente:

  • Si \(|g_1| < 0{,}5\) la distribución se considera aproximadamente simétrica y la media junto con la desviación estándar son representativas.
  • Si \(|g_1| \geq 0{,}5\) la distribución presenta sesgo apreciable; en ese caso la mediana y el rango intercuartílico describen mejor el centro y la dispersión, porque no se ven arrastrados por los valores extremos.
asimetria <- function(x) {
  n <- length(x)
  (sum((x - mean(x))^3) / n) / (sd(x)^3)
}

curtosis <- function(x) {
  n <- length(x)
  (sum((x - mean(x))^4) / n) / (sd(x)^4) - 3
}

# Tabla de frecuencias: absoluta, relativa, porcentual y acumuladas
tabla_frecuencia <- function(x, ordenar = "frecuencia") {
  fa <- table(x)
  df <- data.frame(Categoria = names(fa),
                   fa = as.numeric(fa),
                   stringsAsFactors = FALSE)
  if (ordenar == "frecuencia") {
    df <- df[order(-df$fa), ]
  } else {
    df <- df[order(as.numeric(df$Categoria)), ]
  }
  df$fr       <- df$fa / sum(df$fa)
  df$pct      <- df$fr * 100
  df$fa_acum  <- cumsum(df$fa)
  df$pct_acum <- cumsum(df$pct)
  rownames(df) <- NULL
  df
}

moda <- function(x) {
  fa <- table(x)
  names(fa)[which.max(fa)]
}

# Presentación de la tabla de frecuencias
ft_frecuencia <- function(df, titulo) {
  flextable(df) |>
    set_header_labels(Categoria = "Categoría", fa = "Frec. absoluta",
                      fr = "Frec. relativa", pct = "Porcentaje (%)",
                      fa_acum = "Frec. acumulada", pct_acum = "% acumulado") |>
    colformat_double(j = "fr", digits = 4) |>
    colformat_double(j = c("pct", "pct_acum"), digits = 2) |>
    colformat_double(j = c("fa", "fa_acum"), digits = 0, big.mark = ".", decimal.mark = ",") |>
    theme_booktabs() |>
    align(align = "center", part = "all") |>
    align(j = 1, align = "left", part = "body") |>
    set_caption(titulo) |>
    autofit()
}

# Resumen completo de una variable cuantitativa
resumen_cuantitativa <- function(x) {
  data.frame(
    Indicador = c("Media", "Mediana", "Desviación estándar",
                  "Coeficiente de variación", "Mínimo", "Percentil 10",
                  "Cuartil 1 (Q1)", "Cuartil 3 (Q3)", "Percentil 90", "Máximo",
                  "Rango", "Rango intercuartílico (IQR)",
                  "Asimetría", "Curtosis"),
    Valor = c(mean(x), median(x), sd(x), sd(x) / mean(x), min(x),
              unname(quantile(x, 0.10)), unname(quantile(x, 0.25)),
              unname(quantile(x, 0.75)), unname(quantile(x, 0.90)), max(x),
              max(x) - min(x), IQR(x), asimetria(x), curtosis(x)),
    stringsAsFactors = FALSE
  )
}

ft_cuantitativa <- function(x, titulo) {
  flextable(resumen_cuantitativa(x)) |>
    set_header_labels(Indicador = "Indicador", Valor = "Valor") |>
    colformat_double(j = "Valor", digits = 3, big.mark = ".", decimal.mark = ",") |>
    theme_booktabs() |>
    align(j = 1, align = "left", part = "all") |>
    align(j = 2, align = "right", part = "all") |>
    set_caption(titulo) |>
    autofit()
}

# Comparación de una variable cuantitativa entre grupos
comparar_grupos <- function(variable, grupo) {
  data.frame(
    Grupo     = names(tapply(variable, grupo, mean)),
    n         = as.numeric(tapply(variable, grupo, length)),
    Media     = as.numeric(tapply(variable, grupo, mean)),
    Mediana   = as.numeric(tapply(variable, grupo, median)),
    Desv      = as.numeric(tapply(variable, grupo, sd)),
    CV        = as.numeric(tapply(variable, grupo, function(z) sd(z) / mean(z))),
    Q1        = as.numeric(tapply(variable, grupo, function(z) quantile(z, 0.25))),
    Q3        = as.numeric(tapply(variable, grupo, function(z) quantile(z, 0.75))),
    Asimetria = as.numeric(tapply(variable, grupo, asimetria)),
    stringsAsFactors = FALSE
  )
}

ft_comparacion <- function(df, titulo) {
  flextable(df) |>
    set_header_labels(Grupo = "Grupo", n = "n", Media = "Media",
                      Mediana = "Mediana", Desv = "Desv. estándar",
                      CV = "CV", Q1 = "Q1", Q3 = "Q3",
                      Asimetria = "Asimetría") |>
    colformat_double(j = "n", digits = 0, big.mark = ".", decimal.mark = ",") |>
    colformat_double(j = c("Media", "Mediana", "Desv", "Q1", "Q3"), digits = 2) |>
    colformat_double(j = c("CV", "Asimetria"), digits = 3) |>
    theme_booktabs() |>
    align(align = "center", part = "all") |>
    align(j = 1, align = "left", part = "body") |>
    set_caption(titulo) |>
    autofit()
}

5 Resultados: variables cualitativas

5.1 Tipo de cerveza

ft_frecuencia(tabla_frecuencia(cerveza$tipo),
              "Tabla 3. Distribución de las reseñas según el tipo de cerveza")
Tabla 3. Distribución de las reseñas según el tipo de cerveza

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

cerveza normal y helada

839.088

0.5289

52.89

839.088

52.89

clara artesanal

483.464

0.3047

30.47

1.322.552

83.36

lager importada

219.194

0.1382

13.82

1.541.746

97.17

lager artesanal

22.725

0.0143

1.43

1.564.471

98.60

baja en calorías / sin alcohol

22.143

0.0140

1.40

1.586.614

100.00

par(mar = c(5, 12, 4, 2))
barplot(sort(table(cerveza$tipo)), horiz = TRUE, las = 1,
        col = "#1F4E79", border = NA, cex.names = 0.85,
        main = "Figura 1. Reseñas por tipo de cerveza",
        xlab = "Número de reseñas")

La moda corresponde a cerveza normal y helada, con 839.088 reseñas (52.9 % del total). Los dos tipos más frecuentes concentran más del 80 % de los registros, mientras que las categorías lager artesanal y baja en calorías / sin alcohol apenas superan el 1 % cada una. Esta concentración es relevante para la lectura del resto del informe: los indicadores globales están dominados por el comportamiento de los tipos mayoritarios.

5.2 Origen

ft_frecuencia(tabla_frecuencia(cerveza$origen),
              "Tabla 4. Distribución de las reseñas según el origen")
Tabla 4. Distribución de las reseñas según el origen

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

importada

1.412.449

0.8902

89.02

1.412.449

89.02

nacional

174.165

0.1098

10.98

1.586.614

100.00

La moda es importada (89 % de las reseñas). La base está claramente desbalanceada hacia el producto importado.

5.3 Relación entre tipo y origen

cruce <- as.data.frame.matrix(table(cerveza$tipo, cerveza$origen))
cruce <- data.frame(Tipo = rownames(cruce), cruce, row.names = NULL,
                    check.names = FALSE)

flextable(cruce) |>
  set_header_labels(Tipo = "Tipo de cerveza") |>
  colformat_double(j = c("importada", "nacional"), digits = 0, big.mark = ".", decimal.mark = ",") |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = 1, align = "left", part = "body") |>
  set_caption("Tabla 5. Tabla de doble entrada: tipo de cerveza según origen") |>
  autofit()
Tabla 5. Tabla de doble entrada: tipo de cerveza según origen

Tipo de cerveza

importada

nacional

baja en calorías / sin alcohol

20,432

1,711

cerveza normal y helada

754,796

84,292

clara artesanal

418,027

65,437

lager artesanal

0

22,725

lager importada

219,194

0

Esta tabla revela una dependencia estructural entre ambas variables: las categorías lager importada y lager artesanal aparecen exclusivamente en un solo origen. En consecuencia, cualquier diferencia observada entre orígenes está parcialmente condicionada por la composición de tipos que cada uno contiene, y no debe atribuirse únicamente a la procedencia del producto.

5.4 Nacionalidad

ft_frecuencia(tabla_frecuencia(cerveza$nacionalidad),
              "Tabla 6. Distribución de las reseñas según la nacionalidad")
Tabla 6. Distribución de las reseñas según la nacionalidad

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

Austria

201.941

0.1273

12.73

201.941

12.73

Bélgica

188.677

0.1189

11.89

390.618

24.62

Francia

181.574

0.1144

11.44

572.192

36.06

Estados Unidos

174.165

0.1098

10.98

746.357

47.04

Inglaterra

162.644

0.1025

10.25

909.001

57.29

Canadá

159.508

0.1005

10.05

1.068.509

67.35

Japón

155.273

0.0979

9.79

1.223.782

77.13

Alemania

121.684

0.0767

7.67

1.345.466

84.80

Irlanda

120.742

0.0761

7.61

1.466.208

92.41

República Checa

120.406

0.0759

7.59

1.586.614

100.00

La moda es Austria. A diferencia de las variables anteriores, la nacionalidad se distribuye de forma relativamente homogénea entre los diez países registrados, con porcentajes que oscilan aproximadamente entre el 7 % y el 13 %.

5.5 Marca y nombre comercial

Estas dos variables presentan un número muy elevado de categorías (5.743 marcas y 56.857 nombres comerciales), por lo que una tabla completa no resulta interpretable. Se presentan las diez categorías de mayor frecuencia.

top_marca <- cerveza |>
  count(marca, sort = TRUE, name = "resenas") |>
  slice_head(n = 10) |>
  mutate(porcentaje = resenas / nrow(cerveza) * 100)

flextable(top_marca) |>
  set_header_labels(marca = "Marca o cervecería", resenas = "Reseñas",
                    porcentaje = "Porcentaje (%)") |>
  colformat_double(j = "resenas", digits = 0, big.mark = ".", decimal.mark = ",") |>
  colformat_double(j = "porcentaje", digits = 2) |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = 1, align = "left", part = "body") |>
  set_caption("Tabla 7. Diez marcas con mayor número de reseñas") |>
  autofit()
Tabla 7. Diez marcas con mayor número de reseñas

Marca o cervecería

Reseñas

Porcentaje (%)

Boston Beer Company (Samuel Adams)

39,444

2.49

Dogfish Head Brewery

33,839

2.13

Stone Brewing Co.

33,066

2.08

Sierra Nevada Brewing Co.

28,751

1.81

Bell's Brewery, Inc.

25,191

1.59

Rogue Ales

24,083

1.52

Founders Brewing Company

20,004

1.26

Victory Brewing Company

19,479

1.23

Lagunitas Brewing Company

16,837

1.06

Avery Brewing Company

16,107

1.02

top_nombre <- cerveza |>
  count(nombre_cerveza, sort = TRUE, name = "resenas") |>
  slice_head(n = 10) |>
  mutate(porcentaje = resenas / nrow(cerveza) * 100)

flextable(top_nombre) |>
  set_header_labels(nombre_cerveza = "Cerveza", resenas = "Reseñas",
                    porcentaje = "Porcentaje (%)") |>
  colformat_double(j = "resenas", digits = 0, big.mark = ".", decimal.mark = ",") |>
  colformat_double(j = "porcentaje", digits = 2) |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = 1, align = "left", part = "body") |>
  set_caption("Tabla 8. Diez cervezas con mayor número de reseñas") |>
  autofit()
Tabla 8. Diez cervezas con mayor número de reseñas

Cerveza

Reseñas

Porcentaje (%)

90 Minute IPA

3,290

0.21

India Pale Ale

3,130

0.20

Old Rasputin Russian Imperial Stout

3,111

0.20

Sierra Nevada Celebration Ale

3,000

0.19

Two Hearted Ale

2,728

0.17

Arrogant Bastard Ale

2,704

0.17

Stone Ruination IPA

2,704

0.17

Sierra Nevada Pale Ale

2,587

0.16

Stone IPA (India Pale Ale)

2,575

0.16

Pliny The Elder

2,527

0.16

6 Resultados: variables ordinales

Las cinco escalas sensoriales (calificacion, aroma, apariencia, paladar y sabor) se miden en niveles que van de 0 a 5 con incrementos de 0,5. Al tratarse de escalas ordinales, se resumen mediante tablas de frecuencia, moda y mediana; no se calculan medias aritméticas como indicador principal, ya que la distancia entre niveles consecutivos no es necesariamente constante.

6.1 Calificación general

ft_frecuencia(tabla_frecuencia(cerveza$calificacion, ordenar = "categoria"),
              "Tabla 9. Distribución de la calificación general")
Tabla 9. Distribución de la calificación general

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

0

7

0.0000

0.00

7

0.00

1

10.954

0.0069

0.69

10.961

0.69

1.5

12.975

0.0082

0.82

23.936

1.51

2

38.225

0.0241

2.41

62.161

3.92

2.5

58.523

0.0369

3.69

120.684

7.61

3

165.644

0.1044

10.44

286.328

18.05

3.5

301.817

0.1902

19.02

588.145

37.07

4

582.764

0.3673

36.73

1.170.909

73.80

4.5

324.385

0.2045

20.45

1.495.294

94.24

5

91.320

0.0576

5.76

1.586.614

100.00

6.2 Aroma, apariencia, paladar y sabor

ft_frecuencia(tabla_frecuencia(cerveza$aroma, ordenar = "categoria"),
              "Tabla 10. Distribución de la valoración del aroma")
Tabla 10. Distribución de la valoración del aroma

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

1

6.873

0.0043

0.43

6.873

0.43

1.5

12.524

0.0079

0.79

19.397

1.22

2

42.566

0.0268

2.68

61.963

3.91

2.5

66.359

0.0418

4.18

128.322

8.09

3

200.030

0.1261

12.61

328.352

20.70

3.5

365.312

0.2302

23.02

693.664

43.72

4

557.383

0.3513

35.13

1.251.047

78.85

4.5

271.450

0.1711

17.11

1.522.497

95.96

5

64.117

0.0404

4.04

1.586.614

100.00

ft_frecuencia(tabla_frecuencia(cerveza$apariencia, ordenar = "categoria"),
              "Tabla 11. Distribución de la valoración de la apariencia")
Tabla 11. Distribución de la valoración de la apariencia

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

0

7

0.0000

0.00

7

0.00

1

3.323

0.0021

0.21

3.330

0.21

1.5

6.147

0.0039

0.39

9.477

0.60

2

25.414

0.0160

1.60

34.891

2.20

2.5

39.493

0.0249

2.49

74.384

4.69

3

166.009

0.1046

10.46

240.393

15.15

3.5

318.529

0.2008

20.08

558.922

35.23

4

674.186

0.4249

42.49

1.233.108

77.72

4.5

288.108

0.1816

18.16

1.521.216

95.88

5

65.398

0.0412

4.12

1.586.614

100.00

ft_frecuencia(tabla_frecuencia(cerveza$paladar, ordenar = "categoria"),
              "Tabla 12. Distribución de la valoración del paladar")
Tabla 12. Distribución de la valoración del paladar

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

1

6.874

0.0043

0.43

6.874

0.43

1.5

11.045

0.0070

0.70

17.919

1.13

2

38.333

0.0242

2.42

56.252

3.55

2.5

62.842

0.0396

3.96

119.094

7.51

3

206.932

0.1304

13.04

326.026

20.55

3.5

338.585

0.2134

21.34

664.611

41.89

4

606.711

0.3824

38.24

1.271.322

80.13

4.5

253.102

0.1595

15.95

1.524.424

96.08

5

62.190

0.0392

3.92

1.586.614

100.00

ft_frecuencia(tabla_frecuencia(cerveza$sabor, ordenar = "categoria"),
              "Tabla 13. Distribución de la valoración del sabor")
Tabla 13. Distribución de la valoración del sabor

Categoría

Frec. absoluta

Frec. relativa

Porcentaje (%)

Frec. acumulada

% acumulado

1

9.991

0.0063

0.63

9.991

0.63

1.5

15.128

0.0095

0.95

25.119

1.58

2

41.992

0.0265

2.65

67.111

4.23

2.5

66.534

0.0419

4.19

133.645

8.42

3

166.860

0.1052

10.52

300.505

18.94

3.5

324.541

0.2045

20.45

625.046

39.39

4

541.429

0.3412

34.12

1.166.475

73.52

4.5

336.162

0.2119

21.19

1.502.637

94.71

5

83.977

0.0529

5.29

1.586.614

100.00

6.3 Síntesis de las escalas sensoriales

ordinales <- c("calificacion", "aroma", "apariencia", "paladar", "sabor")

sintesis <- data.frame(
  Escala  = c("Calificación general", "Aroma", "Apariencia", "Paladar", "Sabor"),
  Moda    = sapply(ordinales, function(v) moda(cerveza[[v]])),
  Mediana = sapply(ordinales, function(v) median(cerveza[[v]])),
  Q1      = sapply(ordinales, function(v) unname(quantile(cerveza[[v]], 0.25))),
  Q3      = sapply(ordinales, function(v) unname(quantile(cerveza[[v]], 0.75))),
  Pct_4_o_mas = sapply(ordinales,
                       function(v) mean(cerveza[[v]] >= 4) * 100),
  stringsAsFactors = FALSE, row.names = NULL
)

flextable(sintesis) |>
  set_header_labels(Escala = "Escala", Moda = "Moda", Mediana = "Mediana",
                    Q1 = "Q1", Q3 = "Q3",
                    Pct_4_o_mas = "% con valoración ≥ 4") |>
  colformat_double(j = c("Mediana", "Q1", "Q3"), digits = 1) |>
  colformat_double(j = "Pct_4_o_mas", digits = 1) |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = 1, align = "left", part = "body") |>
  set_caption("Tabla 14. Síntesis de las cinco escalas sensoriales") |>
  autofit()
Tabla 14. Síntesis de las cinco escalas sensoriales

Escala

Moda

Mediana

Q1

Q3

% con valoración ≥ 4

Calificación general

4

4.0

3.5

4.5

62.9

Aroma

4

4.0

3.5

4.0

56.3

Apariencia

4

4.0

3.5

4.0

64.8

Paladar

4

4.0

3.5

4.0

58.1

Sabor

4

4.0

3.5

4.5

60.6

par(mfrow = c(2, 3), mar = c(4, 4, 3, 1))
titulos <- c("Calificación general", "Aroma", "Apariencia", "Paladar", "Sabor")
for (i in seq_along(ordinales)) {
  barplot(table(cerveza[[ordinales[i]]]),
          col = "#2E75B6", border = NA,
          main = titulos[i], xlab = "Nivel", ylab = "Frecuencia",
          cex.main = 1)
}
par(mfrow = c(1, 1))

Figura 2. Distribución de las cinco escalas sensoriales

En las cinco escalas la moda y la mediana coinciden en el nivel 4, y las distribuciones presentan un marcado sesgo hacia los valores altos: los niveles más bajos (0, 1 y 1,5) representan en conjunto menos del 2 % de los registros en todos los casos. Este patrón sugiere que los consumidores que dejan una reseña tienden a evaluar favorablemente el producto, lo que constituye una característica de la base que debe tenerse en cuenta al interpretar cualquier comparación entre grupos.

7 Resultados: variables cuantitativas

7.1 Precio

ft_cuantitativa(cerveza$precio,
                "Tabla 15. Indicadores descriptivos del precio (COP, caja de 6 × 355 ml)")
Tabla 15. Indicadores descriptivos del precio (COP, caja de 6 × 355 ml)

Indicador

Valor

Media

5,363

Mediana

5,410

Desviación estándar

0,649

Coeficiente de variación

0,121

Mínimo

2,200

Percentil 10

4,500

Cuartil 1 (Q1)

4,980

Cuartil 3 (Q3)

5,820

Percentil 90

6,140

Máximo

8,030

Rango

5,830

Rango intercuartílico (IQR)

0,840

Asimetría

-0,468

Curtosis

0,365

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
hist(cerveza$precio, breaks = 40, col = "#9DC3E6", border = "white",
     main = "Histograma", xlab = "Precio")
abline(v = mean(cerveza$precio), col = "#C00000", lwd = 2)
abline(v = median(cerveza$precio), col = "#1F4E79", lwd = 2, lty = 2)
legend("topleft", legend = c("Media", "Mediana"),
       col = c("#C00000", "#1F4E79"), lwd = 2, lty = c(1, 2),
       bty = "n", cex = 0.85)
boxplot(cerveza$precio, horizontal = TRUE, col = "#9DC3E6",
        main = "Diagrama de caja", xlab = "Precio")

par(mfrow = c(1, 1))

Figura 3. Distribución del precio

Indicador seleccionado: con una asimetría de -0.468, en valor absoluto inferior a 0,5, la distribución se considera aproximadamente simétrica. La media (5.36) y la mediana (5.41) prácticamente coinciden, por lo que ambas son representativas y se reporta la media acompañada de la desviación estándar. El coeficiente de variación (0.121) indica una dispersión relativa baja: los precios se concentran en un rango estrecho alrededor del centro.

7.2 Alcohol

ft_cuantitativa(cerveza$alcohol,
                "Tabla 16. Indicadores descriptivos del contenido de alcohol (g / 355 ml)")
Tabla 16. Indicadores descriptivos del contenido de alcohol (g / 355 ml)

Indicador

Valor

Media

18,712

Mediana

17,670

Desviación estándar

5,456

Coeficiente de variación

0,292

Mínimo

0,030

Percentil 10

13,220

Cuartil 1 (Q1)

14,280

Cuartil 3 (Q3)

22,630

Percentil 90

26,140

Máximo

59,450

Rango

59,420

Rango intercuartílico (IQR)

8,350

Asimetría

0,959

Curtosis

1,310

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
hist(cerveza$alcohol, breaks = 40, col = "#F4B183", border = "white",
     main = "Histograma", xlab = "Alcohol (g)")
abline(v = mean(cerveza$alcohol), col = "#C00000", lwd = 2)
abline(v = median(cerveza$alcohol), col = "#1F4E79", lwd = 2, lty = 2)
legend("topright", legend = c("Media", "Mediana"),
       col = c("#C00000", "#1F4E79"), lwd = 2, lty = c(1, 2),
       bty = "n", cex = 0.85)
boxplot(cerveza$alcohol, horizontal = TRUE, col = "#F4B183",
        main = "Diagrama de caja", xlab = "Alcohol (g)")

par(mfrow = c(1, 1))

Figura 4. Distribución del contenido de alcohol

Indicador seleccionado: la asimetría es 0.959, superior a 0,5, lo que confirma un sesgo positivo visible en el histograma. La media (18.71) supera a la mediana (17.67) porque un grupo reducido de cervezas de alta graduación desplaza el promedio hacia arriba. Por ello se reporta como indicadores principales la mediana y el rango intercuartílico (8.35).

7.3 Carbohidratos

ft_cuantitativa(cerveza$carbohidratos,
                "Tabla 17. Indicadores descriptivos de los carbohidratos (g / 355 ml)")
Tabla 17. Indicadores descriptivos de los carbohidratos (g / 355 ml)

Indicador

Valor

Media

14,048

Mediana

13,800

Desviación estándar

2,376

Coeficiente de variación

0,169

Mínimo

3,500

Percentil 10

11,200

Cuartil 1 (Q1)

12,400

Cuartil 3 (Q3)

15,600

Percentil 90

17,200

Máximo

30,200

Rango

26,700

Rango intercuartílico (IQR)

3,200

Asimetría

0,461

Curtosis

0,869

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
hist(cerveza$carbohidratos, breaks = 40, col = "#FFE699", border = "white",
     main = "Histograma", xlab = "Carbohidratos (g)")
abline(v = mean(cerveza$carbohidratos), col = "#C00000", lwd = 2)
abline(v = median(cerveza$carbohidratos), col = "#1F4E79", lwd = 2, lty = 2)
legend("topright", legend = c("Media", "Mediana"),
       col = c("#C00000", "#1F4E79"), lwd = 2, lty = c(1, 2),
       bty = "n", cex = 0.85)
boxplot(cerveza$carbohidratos, horizontal = TRUE, col = "#FFE699",
        main = "Diagrama de caja", xlab = "Carbohidratos (g)")

par(mfrow = c(1, 1))

Figura 5. Distribución de los carbohidratos

Indicador seleccionado: la asimetría es 0.461, por debajo del umbral de 0,5, por lo que la distribución se considera aproximadamente simétrica y se reportan la media (14.05) y la desviación estándar (2.38).

7.4 Calorías

ft_cuantitativa(cerveza$calorias,
                "Tabla 18. Indicadores descriptivos de las calorías (kcal / 355 ml)")
Tabla 18. Indicadores descriptivos de las calorías (kcal / 355 ml)

Indicador

Valor

Media

193,173

Mediana

182,000

Desviación estándar

46,569

Coeficiente de variación

0,241

Mínimo

21,000

Percentil 10

145,000

Cuartil 1 (Q1)

157,000

Cuartil 3 (Q3)

224,000

Percentil 90

258,000

Máximo

541,000

Rango

520,000

Rango intercuartílico (IQR)

67,000

Asimetría

0,915

Curtosis

1,318

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
hist(cerveza$calorias, breaks = 40, col = "#C5A5CF", border = "white",
     main = "Histograma", xlab = "Calorías")
abline(v = mean(cerveza$calorias), col = "#C00000", lwd = 2)
abline(v = median(cerveza$calorias), col = "#1F4E79", lwd = 2, lty = 2)
legend("topright", legend = c("Media", "Mediana"),
       col = c("#C00000", "#1F4E79"), lwd = 2, lty = c(1, 2),
       bty = "n", cex = 0.85)
boxplot(cerveza$calorias, horizontal = TRUE, col = "#C5A5CF",
        main = "Diagrama de caja", xlab = "Calorías")

par(mfrow = c(1, 1))

Figura 6. Distribución de las calorías

Indicador seleccionado: con una asimetría de 0.915, la distribución presenta sesgo positivo. Se reportan como indicadores principales la mediana (182) y el rango intercuartílico (67).

7.5 Resumen comparado de las cuatro variables cuantitativas

cuantitativas <- c("precio", "alcohol", "carbohidratos", "calorias")

global <- data.frame(
  Variable  = c("Precio", "Alcohol (g)", "Carbohidratos (g)", "Calorías"),
  Media     = sapply(cuantitativas, function(v) mean(cerveza[[v]])),
  Mediana   = sapply(cuantitativas, function(v) median(cerveza[[v]])),
  Desv      = sapply(cuantitativas, function(v) sd(cerveza[[v]])),
  CV        = sapply(cuantitativas, function(v) sd(cerveza[[v]]) / mean(cerveza[[v]])),
  IQR       = sapply(cuantitativas, function(v) IQR(cerveza[[v]])),
  Asimetria = sapply(cuantitativas, function(v) asimetria(cerveza[[v]])),
  Indicador = c("Media y desviación estándar", "Mediana e IQR",
                "Media y desviación estándar", "Mediana e IQR"),
  stringsAsFactors = FALSE, row.names = NULL
)

flextable(global) |>
  set_header_labels(Variable = "Variable", Media = "Media", Mediana = "Mediana",
                    Desv = "Desv. estándar", CV = "CV", IQR = "IQR",
                    Asimetria = "Asimetría",
                    Indicador = "Indicador seleccionado") |>
  colformat_double(j = c("Media", "Mediana", "Desv", "IQR"), digits = 2) |>
  colformat_double(j = c("CV", "Asimetria"), digits = 3) |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = c(1, 8), align = "left", part = "body") |>
  set_caption("Tabla 19. Resumen de indicadores y criterio de selección aplicado") |>
  autofit()
Tabla 19. Resumen de indicadores y criterio de selección aplicado

Variable

Media

Mediana

Desv. estándar

CV

IQR

Asimetría

Indicador seleccionado

Precio

5.36

5.41

0.65

0.121

0.84

-0.468

Media y desviación estándar

Alcohol (g)

18.71

17.67

5.46

0.292

8.35

0.959

Mediana e IQR

Carbohidratos (g)

14.05

13.80

2.38

0.169

3.20

0.461

Media y desviación estándar

Calorías

193.17

182.00

46.57

0.241

67.00

0.915

Mediana e IQR

8 Comparación entre tipos y orígenes de cerveza

8.1 Comparación según el tipo de cerveza

ft_comparacion(comparar_grupos(cerveza$precio, cerveza$tipo),
               "Tabla 20. Precio según el tipo de cerveza")
Tabla 20. Precio según el tipo de cerveza

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

baja en calorías / sin alcohol

22.143

4.28

4.25

0.71

0.165

3.73

4.67

0.328

cerveza normal y helada

839.088

5.27

5.34

0.60

0.114

4.93

5.68

-0.604

clara artesanal

483.464

5.70

5.78

0.59

0.103

5.33

6.13

-0.508

lager artesanal

22.725

4.55

4.54

0.54

0.119

4.21

4.93

-0.210

lager importada

219.194

5.17

5.24

0.56

0.107

4.83

5.56

-0.415

ft_comparacion(comparar_grupos(cerveza$alcohol, cerveza$tipo),
               "Tabla 21. Contenido de alcohol según el tipo de cerveza")
Tabla 21. Contenido de alcohol según el tipo de cerveza

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

baja en calorías / sin alcohol

22.143

9.84

11.07

2.66

0.270

9.17

11.61

-1.876

cerveza normal y helada

839.088

20.10

18.94

5.77

0.287

15.34

24.06

0.770

clara artesanal

483.464

18.15

17.41

4.58

0.252

14.55

20.93

1.146

lager artesanal

22.725

15.67

13.75

3.78

0.241

13.22

17.41

1.416

lager importada

219.194

15.86

14.28

3.84

0.242

13.49

17.03

1.950

ft_comparacion(comparar_grupos(cerveza$carbohidratos, cerveza$tipo),
               "Tabla 22. Carbohidratos según el tipo de cerveza")
Tabla 22. Carbohidratos según el tipo de cerveza

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

baja en calorías / sin alcohol

22.143

8.66

8.70

1.52

0.176

7.60

10.10

-0.614

cerveza normal y helada

839.088

14.29

14.10

2.40

0.168

12.50

15.80

0.527

clara artesanal

483.464

14.51

14.30

2.07

0.143

13.00

15.80

0.805

lager artesanal

22.725

13.54

13.10

1.86

0.137

12.20

14.40

0.844

lager importada

219.194

12.71

12.60

1.79

0.141

11.30

13.50

1.160

ft_comparacion(comparar_grupos(cerveza$calorias, cerveza$tipo),
               "Tabla 23. Calorías según el tipo de cerveza")
Tabla 23. Calorías según el tipo de cerveza

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

baja en calorías / sin alcohol

22.143

109.52

116.00

23.04

0.210

100.00

125.00

-1.755

cerveza normal y helada

839.088

203.82

198.00

49.05

0.241

163.00

240.00

0.763

clara artesanal

483.464

191.10

184.00

39.24

0.205

160.00

215.00

1.156

lager artesanal

22.725

169.88

158.00

32.87

0.193

148.00

185.00

1.404

lager importada

219.194

167.84

156.00

32.83

0.196

147.00

179.00

1.957

par(mfrow = c(2, 2), mar = c(9, 4, 3, 1))
for (v in cuantitativas) {
  boxplot(cerveza[[v]] ~ cerveza$tipo, las = 2, col = "#9DC3E6",
          main = v, xlab = "", ylab = v, cex.axis = 0.7)
}

par(mfrow = c(1, 1))

Figura 7. Variables cuantitativas según el tipo de cerveza

calif_tipo <- data.frame(
  Tipo = names(tapply(cerveza$calificacion, cerveza$tipo, median)),
  n = as.numeric(tapply(cerveza$calificacion, cerveza$tipo, length)),
  Mediana = as.numeric(tapply(cerveza$calificacion, cerveza$tipo, median)),
  Moda = sapply(split(cerveza$calificacion, cerveza$tipo), moda),
  Pct_4_o_mas = as.numeric(tapply(cerveza$calificacion, cerveza$tipo,
                                  function(z) mean(z >= 4) * 100)),
  stringsAsFactors = FALSE, row.names = NULL
)

flextable(calif_tipo) |>
  set_header_labels(Tipo = "Tipo de cerveza", n = "n", Mediana = "Mediana",
                    Moda = "Moda", Pct_4_o_mas = "% con calificación ≥ 4") |>
  colformat_double(j = "n", digits = 0, big.mark = ".", decimal.mark = ",") |>
  colformat_double(j = "Mediana", digits = 1) |>
  colformat_double(j = "Pct_4_o_mas", digits = 1) |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = 1, align = "left", part = "body") |>
  set_caption("Tabla 24. Calificación general según el tipo de cerveza") |>
  autofit()
Tabla 24. Calificación general según el tipo de cerveza

Tipo de cerveza

n

Mediana

Moda

% con calificación ≥ 4

baja en calorías / sin alcohol

22.143

3.0

3

27.2

cerveza normal y helada

839.088

4.0

4

65.7

clara artesanal

483.464

4.0

4

65.7

lager artesanal

22.725

4.0

4

51.1

lager importada

219.194

4.0

4

51.0

Lectura de los resultados. El tipo baja en calorías / sin alcohol se separa nítidamente del resto en todas las variables nutricionales: presenta el menor contenido de alcohol, de carbohidratos y de calorías, junto con el precio promedio más bajo. Este comportamiento es consistente con la naturaleza del producto. Es además el tipo con la menor proporción de calificaciones altas, lo que sugiere que en esta base los productos de perfil reducido reciben valoraciones menos favorables que los tipos convencionales y artesanales.

Los tipos cerveza normal y helada y clara artesanal, que concentran la mayor parte de las reseñas, muestran los contenidos nutricionales más altos y también la mayor proporción de calificaciones favorables. Las dos categorías lager ocupan posiciones intermedias.

8.2 Comparación según el origen

ft_comparacion(comparar_grupos(cerveza$precio, cerveza$origen),
               "Tabla 25. Precio según el origen")
Tabla 25. Precio según el origen

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

importada

1.412.449

5.46

5.50

0.58

0.106

5.11

5.86

-0.370

nacional

174.165

4.54

4.54

0.59

0.131

4.12

4.94

0.024

ft_comparacion(comparar_grupos(cerveza$alcohol, cerveza$origen),
               "Tabla 26. Contenido de alcohol según el origen")
Tabla 26. Contenido de alcohol según el origen

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

importada

1.412.449

18.71

17.41

5.47

0.292

14.28

22.63

0.961

nacional

174.165

18.73

17.93

5.35

0.286

14.55

22.15

0.944

ft_comparacion(comparar_grupos(cerveza$carbohidratos, cerveza$origen),
               "Tabla 27. Carbohidratos según el origen")
Tabla 27. Carbohidratos según el origen

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

importada

1.412.449

14.02

13.80

2.39

0.170

12.30

15.50

0.488

nacional

174.165

14.24

14.10

2.29

0.160

12.70

15.90

0.237

ft_comparacion(comparar_grupos(cerveza$calorias, cerveza$origen),
               "Tabla 28. Calorías según el origen")
Tabla 28. Calorías según el origen

Grupo

n

Media

Mediana

Desv. estándar

CV

Q1

Q3

Asimetría

importada

1.412.449

193.06

182.00

46.72

0.242

157.00

224.00

0.923

nacional

174.165

194.06

188.00

45.31

0.233

158.00

223.00

0.847

par(mfrow = c(2, 2), mar = c(4, 4, 3, 1))
for (v in cuantitativas) {
  boxplot(cerveza[[v]] ~ cerveza$origen, col = c("#9DC3E6", "#F4B183"),
          main = v, xlab = "", ylab = v)
}

par(mfrow = c(1, 1))

Figura 8. Variables cuantitativas según el origen

calif_origen <- data.frame(
  Origen = names(tapply(cerveza$calificacion, cerveza$origen, median)),
  n = as.numeric(tapply(cerveza$calificacion, cerveza$origen, length)),
  Mediana = as.numeric(tapply(cerveza$calificacion, cerveza$origen, median)),
  Moda = sapply(split(cerveza$calificacion, cerveza$origen), moda),
  Pct_4_o_mas = as.numeric(tapply(cerveza$calificacion, cerveza$origen,
                                  function(z) mean(z >= 4) * 100)),
  stringsAsFactors = FALSE, row.names = NULL
)

flextable(calif_origen) |>
  set_header_labels(Origen = "Origen", n = "n", Mediana = "Mediana",
                    Moda = "Moda", Pct_4_o_mas = "% con calificación ≥ 4") |>
  colformat_double(j = "n", digits = 0, big.mark = ".", decimal.mark = ",") |>
  colformat_double(j = "Mediana", digits = 1) |>
  colformat_double(j = "Pct_4_o_mas", digits = 1) |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = 1, align = "left", part = "body") |>
  set_caption("Tabla 29. Calificación general según el origen") |>
  autofit()
Tabla 29. Calificación general según el origen

Origen

n

Mediana

Moda

% con calificación ≥ 4

importada

1.412.449

4.0

4

62.7

nacional

174.165

4.0

4

64.8

Lectura de los resultados. La diferencia más notoria entre orígenes se presenta en el precio: la cerveza importada registra una media de 5.46 frente a 4.54 de la nacional. En contraste, los perfiles nutricionales son muy similares entre ambos grupos: las medianas de alcohol, carbohidratos y calorías difieren de forma marginal. Las calificaciones también son prácticamente equivalentes.

Esto sugiere que, dentro de esta base, la brecha de precio entre producto importado y nacional no se explica por diferencias en la composición nutricional ni por una mejor valoración del consumidor. Debe recordarse, sin embargo, que el precio es una variable simulada y que ambas variables presentan la dependencia estructural señalada en la Tabla 5.

9 Relación entre los atributos y la valoración general

El propósito declarado de la investigación incluye reconocer qué características se relacionan con mejores valoraciones. Para explorar esta pregunta de forma descriptiva se calcula la matriz de correlaciones de Spearman, apropiada cuando intervienen variables ordinales, ya que se basa en rangos y no exige distribuciones simétricas.

vars_cor <- c("precio", "alcohol", "carbohidratos", "calorias",
              "calificacion", "aroma", "apariencia", "paladar", "sabor")

mat <- cor(cerveza[, vars_cor], method = "spearman")
cor_df <- data.frame(Variable = rownames(mat), round(mat, 2),
                     row.names = NULL, check.names = FALSE)

flextable(cor_df) |>
  theme_booktabs() |>
  align(align = "center", part = "all") |>
  align(j = 1, align = "left", part = "body") |>
  fontsize(size = 8, part = "all") |>
  set_caption("Tabla 30. Matriz de correlaciones de Spearman") |>
  autofit()
Tabla 30. Matriz de correlaciones de Spearman

Variable

precio

alcohol

carbohidratos

calorias

calificacion

aroma

apariencia

paladar

sabor

precio

1.00

0.14

0.17

0.15

0.24

0.25

0.21

0.24

0.25

alcohol

0.14

1.00

0.82

0.99

0.18

0.38

0.31

0.33

0.35

carbohidratos

0.17

0.82

1.00

0.89

0.15

0.33

0.25

0.28

0.29

calorias

0.15

0.99

0.89

1.00

0.18

0.38

0.31

0.33

0.35

calificacion

0.24

0.18

0.15

0.18

1.00

0.56

0.46

0.65

0.73

aroma

0.25

0.38

0.33

0.38

0.56

1.00

0.50

0.56

0.67

apariencia

0.21

0.31

0.25

0.31

0.46

0.50

1.00

0.51

0.49

paladar

0.24

0.33

0.28

0.33

0.65

0.56

0.51

1.00

0.69

sabor

0.25

0.35

0.29

0.35

0.73

0.67

0.49

0.69

1.00

cor_calif <- data.frame(
  Atributo = c("Sabor", "Paladar", "Aroma", "Apariencia", "Precio",
               "Alcohol", "Calorías", "Carbohidratos"),
  Correlacion = c(mat["calificacion", "sabor"], mat["calificacion", "paladar"],
                  mat["calificacion", "aroma"], mat["calificacion", "apariencia"],
                  mat["calificacion", "precio"], mat["calificacion", "alcohol"],
                  mat["calificacion", "calorias"],
                  mat["calificacion", "carbohidratos"]),
  stringsAsFactors = FALSE
)
cor_calif <- cor_calif[order(-cor_calif$Correlacion), ]

par(mar = c(4, 8, 3, 2))
barplot(rev(cor_calif$Correlacion), horiz = TRUE, las = 1,
        names.arg = rev(cor_calif$Atributo), col = "#1F4E79", border = NA,
        xlim = c(0, 0.8), xlab = "Correlación de Spearman con la calificación",
        main = "Figura 9. Asociación de cada atributo con la calificación general")

Los atributos sensoriales presentan las asociaciones más fuertes con la calificación general, encabezados por el sabor (0.73) y el paladar (0.65). La apariencia muestra la asociación más débil dentro del bloque sensorial (0.46).

Las variables de composición y precio presentan asociaciones claramente menores: el precio alcanza 0.24 y el contenido de alcohol 0.18.

Se observa además una correlación casi perfecta entre alcohol y calorías (0.99), lo que indica que ambas variables aportan prácticamente la misma información y resultan redundantes entre sí.

10 Discusión

Los resultados permiten formular varias observaciones de interés para el propósito de la investigación:

  1. La base no es un catálogo balanceado del mercado. Más de la mitad de las reseñas corresponde a un solo tipo de cerveza y cerca del 89 % a producto importado. Cualquier indicador global refleja principalmente el comportamiento de esos segmentos mayoritarios.

  2. Las valoraciones están concentradas en la parte alta de la escala. La moda y la mediana coinciden en el nivel 4 en las cinco escalas sensoriales. Esto limita la capacidad de las escalas para discriminar entre productos y es coherente con el hecho de que quienes reseñan voluntariamente tienden a hacerlo sobre productos que ya conocen o prefieren.

  3. La experiencia gustativa concentra la asociación con la valoración general. El sabor y el paladar son, con diferencia, los atributos más asociados a la calificación, mientras que la apariencia lo está en menor medida. Esto sugiere una hipótesis para etapas posteriores: la comunicación y el desarrollo de producto podrían priorizar los atributos gustativos.

  4. El precio no acompaña a la valoración. Su asociación con la calificación es débil, y la diferencia de precio entre importado y nacional no se corresponde con diferencias nutricionales ni de valoración. Dado que se trata de una variable simulada, esta observación describe únicamente la base académica.

  5. Existe redundancia entre variables nutricionales. Alcohol y calorías están correlacionadas casi perfectamente, por lo que en análisis posteriores bastaría con incluir una de las dos.

11 Conclusiones

  • La base analizada reúne 1.586.614 reseñas sin datos faltantes. La moda del tipo de cerveza es cerveza normal y helada y la del origen es importada.

  • Las cinco escalas sensoriales presentan moda y mediana en el nivel 4, con distribuciones sesgadas hacia las valoraciones altas.

  • En las variables cuantitativas, el criterio de asimetría condujo a reportar media y desviación estándar para el precio y los carbohidratos, y mediana y rango intercuartílico para el alcohol y las calorías, dado su sesgo positivo.

  • La comparación por tipo muestra que la categoría baja en calorías / sin alcohol se diferencia del resto en todas las variables nutricionales y registra la menor proporción de calificaciones altas.

  • La comparación por origen muestra una diferencia apreciable en el precio, pero perfiles nutricionales y valoraciones muy similares entre producto importado y nacional.

  • Entre los atributos evaluados, el sabor y el paladar son los que presentan mayor asociación con la calificación general.

12 Limitaciones

Advertencias sobre el alcance de este informe.

  • El análisis es exclusivamente descriptivo. Las asociaciones reportadas identifican patrones y permiten formular hipótesis, pero no establecen relaciones causales de ningún tipo.
  • La variable precio es simulada. Sus valores describen únicamente esta base académica y no deben interpretarse como precios reales de mercado ni extrapolarse a decisiones comerciales.
  • La unidad de observación es la reseña y no el producto, por lo que las cervezas con más reseñas tienen mayor peso en todos los indicadores.
  • Las reseñas provienen de consumidores que decidieron evaluar voluntariamente, lo que introduce un sesgo de autoselección: la muestra no es representativa del conjunto de consumidores de cerveza.
  • La dependencia estructural entre tipo y origen (Tabla 5) impide atribuir las diferencias observadas entre orígenes exclusivamente a la procedencia.

13 Referencias

  • González Gómez, D. E. (2026). Teoría de Probabilidades — Guía 1.2 y Código 1.2: Tablas de frecuencia e indicadores descriptivos. Pontificia Universidad Javeriana Cali. https://dgonxalex80.github.io/curso013/
  • R Core Team (2026). R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing, Viena, Austria.
  • Gohel, D. (2026). flextable: Functions for Tabular Reporting. Paquete de R.
  • Wickham, H. et al. (2026). dplyr: A Grammar of Data Manipulation. Paquete de R.

Informe elaborado por Manuel Alejandro Vargas Imuez — Actividad 121, Caso 1
Teoría de Probabilidades, Pontificia Universidad Javeriana Cali