Autor/a

Diego Villalba

Fecha de publicación

16 de octubre de 2026

34.1 Introducción

A lo largo de los capítulos anteriores se desarrolló una ruta progresiva para el análisis exploratorio del conjunto de datos Big Mart Sales. La lógica seguida no fue arbitraria: se construyó una descripción estadística de las ventas a partir de seis dimensiones complementarias.

Primero se estudió la localización, para responder dónde se sitúan los valores típicos. Después se introdujo la variabilidad, para cuantificar la dispersión alrededor de esos centros. Más adelante se abordó la forma, con el fin de entender la asimetría, la curtosis y la estructura general de las distribuciones. Luego se examinó la heterogeneidad, es decir, las diferencias entre categorías, tipos de outlet y otros subgrupos. La concentración permitió analizar cómo se reparte el total entre las unidades del sistema y la correlación añadió la exploración de asociaciones entre variables numéricas.

El propósito de este capítulo es integrar todas esas perspectivas en un dashboard global, útil tanto para la enseñanza del EDA como para la comunicación de hallazgos en un entorno de análisis aplicado. Este dashboard no sustituye el análisis detallado de los capítulos previos; más bien, actúa como una síntesis estructurada que permite recorrer, de manera compacta, la construcción progresiva de la medida.

34.2 Objetivos del capítulo

Este capítulo busca que el lector sea capaz de:

  1. integrar múltiples dimensiones del EDA en una misma narrativa analítica;
  2. construir indicadores resumidos y visualizaciones consistentes entre sí;
  3. diseñar un dashboard reproducible en Quarto compatible con un libro;
  4. interpretar de forma conjunta las dimensiones de localización, variabilidad, forma, heterogeneidad, concentración y correlación.

34.3 Carga y preparación de datos

Mostrar código
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

DATA_PATH = "../../data/bigmart_sales.csv" # Ajustar según tu proyecto
df = pd.read_csv(DATA_PATH)

if "Item_Fat_Content" in df.columns:
    df["Item_Fat_Content"] = (
        df["Item_Fat_Content"]
        .replace({
            "LF": "Low Fat",
            "low fat": "Low Fat",
            "reg": "Regular"
        })
    )

df.head()
Item_Identifier Item_Weight Item_Fat_Content Item_Visibility Item_Type Item_MRP Outlet_Identifier Outlet_Establishment_Year Outlet_Size Outlet_Location_Type Outlet_Type Item_Outlet_Sales
0 FDA15 9.30 Low Fat 0.016047 Dairy 249.8092 OUT049 1999 Medium Tier 1 Supermarket Type1 3735.1380
1 DRC01 5.92 Regular 0.019278 Soft Drinks 48.2692 OUT018 2009 Medium Tier 3 Supermarket Type2 443.4228
2 FDN15 17.50 Low Fat 0.016760 Meat 141.6180 OUT049 1999 Medium Tier 1 Supermarket Type1 2097.2700
3 FDX07 19.20 Regular 0.000000 Fruits and Vegetables 182.0950 OUT010 1998 NaN Tier 3 Grocery Store 732.3800
4 NCD19 8.93 Low Fat 0.000000 Household 53.8614 OUT013 1987 High Tier 3 Supermarket Type1 994.7052

34.4 Arquitectura conceptual del dashboard

El dashboard global puede entenderse como una composición de seis capas analíticas:

34.4.1 Capa 1. Localización

Resume el centro de la distribución mediante indicadores como media, mediana, moda y cuantiles.

34.4.2 Capa 2. Variabilidad

Introduce la amplitud y dispersión mediante rango, desviación estándar, IQR y coeficiente de variación.

34.4.3 Capa 3. Forma

Permite describir asimetría, curtosis y presencia de colas pesadas o valores atípicos.

34.4.4 Capa 4. Heterogeneidad

Compara la estructura de ventas entre subgrupos relevantes, como tipos de producto o tipos de outlet.

34.4.5 Capa 5. Concentración

Evalúa qué tan equilibrada o dominada está la estructura total de ventas.

34.4.6 Capa 6. Correlación

Explora asociaciones lineales y monótonas entre variables numéricas sin atribuir causalidad.

En un dashboard pedagógico, estas seis capas deben coexistir de forma coherente, de manera que el lector pueda pasar del nivel global al comparativo sin perder el hilo interpretativo.

34.5 Selección de variable principal

Para articular el dashboard usaremos como variable principal:

[ , ]

ya que representa el fenómeno central del conjunto de datos: las ventas observadas por producto y outlet.

Mostrar código
sales = df["Item_Outlet_Sales"].dropna()
sales.describe()
count     8523.000000
mean      2181.288914
std       1706.499616
min         33.290000
25%        834.247400
50%       1794.331000
75%       3101.296400
max      13086.964800
Name: Item_Outlet_Sales, dtype: float64

34.6 Panel de indicadores globales

El primer componente del dashboard debe consistir en un bloque de KPIs generales que resuman el estado del conjunto de datos.

Mostrar código
def safe_mode(series):
    m = series.mode(dropna=True)
    if len(m) == 0:
        return np.nan
    return m.iloc[0]

mean_sales = sales.mean()
median_sales = sales.median()
mode_sales = safe_mode(sales)
std_sales = sales.std(ddof=1)
iqr_sales = sales.quantile(0.75) - sales.quantile(0.25)
cv_sales = std_sales / mean_sales if mean_sales != 0 else np.nan
skew_sales = sales.skew()
kurt_sales = sales.kurt()

global_kpis = pd.DataFrame({
    "Indicador": [
        "Número de registros",
        "Ventas totales",
        "Media",
        "Mediana",
        "Moda",
        "Desviación estándar",
        "IQR",
        "Coeficiente de variación",
        "Asimetría",
        "Curtosis"
    ],
    "Valor": [
        len(df),
        round(sales.sum(), 2),
        round(mean_sales, 2),
        round(median_sales, 2),
        round(mode_sales, 2) if pd.notnull(mode_sales) else np.nan,
        round(std_sales, 2),
        round(iqr_sales, 2),
        round(cv_sales, 4),
        round(skew_sales, 4),
        round(kurt_sales, 4)
    ]
})

global_kpis
Indicador Valor
0 Número de registros 8.523000e+03
1 Ventas totales 1.859113e+07
2 Media 2.181290e+03
3 Mediana 1.794330e+03
4 Moda 9.587500e+02
5 Desviación estándar 1.706500e+03
6 IQR 2.267050e+03
7 Coeficiente de variación 7.823000e-01
8 Asimetría 1.177500e+00
9 Curtosis 1.615900e+00

Este bloque de KPIs cumple una función importante: reúne en una sola tabla las medidas fundamentales de centro, dispersión y forma que fueron desarrolladas por separado en capítulos previos.

34.7 Panel de localización y forma

Una forma natural de combinar localización y forma es mediante un histograma enriquecido con líneas de referencia para la media y la mediana.

Mostrar código
fig = px.histogram(
    df,
    x="Item_Outlet_Sales",
    nbins=50,
    marginal="box",
    title="Distribución global de Item_Outlet_Sales"
)

fig.add_vline(x=mean_sales, line_dash="dash", annotation_text="Media")
fig.add_vline(x=median_sales, line_dash="dot", annotation_text="Mediana")

fig.update_layout(
    xaxis_title="Ventas",
    yaxis_title="Frecuencia"
)
fig.show()

34.7.1 Interpretación

Este panel sintetiza dos preguntas esenciales:

  • ¿Dónde se localizan los valores típicos?
  • ¿La distribución es aproximadamente simétrica o presenta sesgo?

Cuando la media y la mediana difieren de manera visible, y cuando la cola derecha es más larga, suele haber evidencia de asimetría positiva. En un entorno como Big Mart Sales, esto es compatible con la existencia de observaciones con ventas muy elevadas respecto del grueso de la distribución.

34.8 Panel de variabilidad

La variabilidad puede representarse con un boxplot acompañado de una tabla resumida.

Mostrar código
variability_kpis = pd.DataFrame({
    "Medida": ["Mínimo", "Q1", "Mediana", "Q3", "Máximo", "Rango", "IQR", "Desv. estándar", "CV"],
    "Valor": [
        round(sales.min(), 2),
        round(sales.quantile(0.25), 2),
        round(sales.median(), 2),
        round(sales.quantile(0.75), 2),
        round(sales.max(), 2),
        round(sales.max() - sales.min(), 2),
        round(iqr_sales, 2),
        round(std_sales, 2),
        round(cv_sales, 4)
    ]
})
variability_kpis
Medida Valor
0 Mínimo 33.2900
1 Q1 834.2500
2 Mediana 1794.3300
3 Q3 3101.3000
4 Máximo 13086.9600
5 Rango 13053.6700
6 IQR 2267.0500
7 Desv. estándar 1706.5000
8 CV 0.7823
Mostrar código
fig = px.box(
    df,
    y="Item_Outlet_Sales",
    points="suspectedoutliers",
    title="Variabilidad global de Item_Outlet_Sales"
)
fig.update_layout(yaxis_title="Ventas")
fig.show()

Este panel muestra de forma visual la amplitud de la distribución, la caja intercuartílica y la presencia de observaciones extremas.

34.9 Panel de heterogeneidad

La heterogeneidad requiere comparar subgrupos. Una opción muy expresiva es contrastar las ventas por tipo de outlet y por tipo de producto.

34.9.1 Ventas por tipo de outlet

Mostrar código
fig = px.box(
    df,
    x="Outlet_Type",
    y="Item_Outlet_Sales",
    color="Outlet_Type",
    title="Heterogeneidad de ventas por tipo de outlet"
)
fig.update_layout(
    xaxis_title="Tipo de outlet",
    yaxis_title="Ventas",
    xaxis_tickangle=-20,
    showlegend=False
)
fig.show()

34.9.2 Ventas por tipo de producto

Para evitar saturación visual, puede ser útil mostrar solo las categorías con mayor venta total.

Mostrar código
top_item_types = (
    df.groupby("Item_Type")["Item_Outlet_Sales"]
      .sum()
      .sort_values(ascending=False)
      .head(8)
      .index
)

df_top_items = df[df["Item_Type"].isin(top_item_types)].copy()

fig = px.box(
    df_top_items,
    x="Item_Type",
    y="Item_Outlet_Sales",
    color="Item_Type",
    title="Heterogeneidad de ventas en las principales categorías de producto"
)
fig.update_layout(
    xaxis_title="Tipo de producto",
    yaxis_title="Ventas",
    xaxis_tickangle=-45,
    showlegend=False
)
fig.show()

34.9.3 Tabla comparativa por grupo

Mostrar código
heterogeneity_summary = (
    df.groupby("Outlet_Type")["Item_Outlet_Sales"]
      .agg(["count", "mean", "median", "std"])
      .reset_index()
      .rename(columns={
          "count": "n",
          "mean": "media",
          "median": "mediana",
          "std": "desv_std"
      })
)

heterogeneity_summary
Outlet_Type n media mediana desv_std
0 Grocery Store 1083 339.828500 256.9988 260.851582
1 Supermarket Type1 5577 2316.181148 1990.7420 1515.965558
2 Supermarket Type2 928 1995.498739 1655.1788 1375.932889
3 Supermarket Type3 935 3694.038558 3364.9532 2127.760054

Este bloque permite observar que la heterogeneidad no se limita a diferencias de media: también pueden cambiar la mediana, la dispersión y la presencia de valores extremos.

34.10 Panel de concentración

Para el dashboard global conviene incorporar una visualización compacta de concentración. El análisis por tipo de producto suele ser especialmente ilustrativo.

Mostrar código
sales_by_item_type = (
    df.groupby("Item_Type")["Item_Outlet_Sales"]
      .sum()
      .sort_values(ascending=False)
      .reset_index()
)

sales_by_item_type["share"] = (
    sales_by_item_type["Item_Outlet_Sales"] /
    sales_by_item_type["Item_Outlet_Sales"].sum()
)
sales_by_item_type["cum_share"] = sales_by_item_type["share"].cumsum()
sales_by_item_type["share_pct"] = 100 * sales_by_item_type["share"]
sales_by_item_type["cum_share_pct"] = 100 * sales_by_item_type["cum_share"]

sales_by_item_type.head()
Item_Type Item_Outlet_Sales share cum_share share_pct cum_share_pct
0 Fruits and Vegetables 2.820060e+06 0.151688 0.151688 15.168849 15.168849
1 Snack Foods 2.732786e+06 0.146994 0.298683 14.699412 29.868261
2 Household 2.055494e+06 0.110563 0.409246 11.056317 40.924578
3 Frozen Foods 1.825735e+06 0.098205 0.507450 9.820464 50.745042
4 Dairy 1.522594e+06 0.081899 0.589349 8.189897 58.934939
Mostrar código
fig = go.Figure()
fig.add_bar(
    x=sales_by_item_type["Item_Type"],
    y=sales_by_item_type["share_pct"],
    name="Participación (%)"
)
fig.add_scatter(
    x=sales_by_item_type["Item_Type"],
    y=sales_by_item_type["cum_share_pct"],
    mode="lines+markers",
    name="Acumulado (%)",
    yaxis="y2"
)
fig.update_layout(
    title="Concentración de ventas por tipo de producto",
    yaxis=dict(title="Participación (%)"),
    yaxis2=dict(
        title="Acumulado (%)",
        overlaying="y",
        side="right",
        range=[0, 105]
    ),
    xaxis=dict(title="Tipo de producto", tickangle=-45)
)
fig.show()

34.10.1 Indicadores breves de concentración

Mostrar código
def categories_to_reach_threshold(table, threshold=0.8):
    idx = np.argmax(table["cum_share"].to_numpy() >= threshold)
    return idx + 1

n80 = categories_to_reach_threshold(sales_by_item_type, 0.80)
top3_item_share = sales_by_item_type["share"].head(3).sum() * 100

concentration_kpis = pd.DataFrame({
    "Indicador": [
        "Top 3 categorías (%)",
        "Categorías para 80% de ventas"
    ],
    "Valor": [
        round(top3_item_share, 2),
        n80
    ]
})

concentration_kpis
Indicador Valor
0 Top 3 categorías (%) 40.92
1 Categorías para 80% de ventas 9.00

34.11 Panel de correlación

La síntesis se completa con Pearson y Spearman. La comparación ayuda a detectar asociaciones monotónicas que no son estrictamente lineales.

Mostrar código
num_cols = df.select_dtypes(include=np.number).columns
pearson = df[num_cols].corr(method="pearson")
spearman = df[num_cols].corr(method="spearman")

fig = px.imshow(
    pearson,
    text_auto=".2f",
    color_continuous_scale="RdBu_r",
    zmin=-1,
    zmax=1,
    title="Matriz de correlación de Pearson"
)
fig.update_layout(height=650)
fig.show()
Mostrar código
corr_target = pd.DataFrame({
    "Variable": [c for c in num_cols if c != "Item_Outlet_Sales"],
    "Pearson": [pearson.loc[c, "Item_Outlet_Sales"] for c in num_cols if c != "Item_Outlet_Sales"],
    "Spearman": [spearman.loc[c, "Item_Outlet_Sales"] for c in num_cols if c != "Item_Outlet_Sales"]
}).melt(id_vars="Variable", var_name="Coeficiente", value_name="Correlación")

fig = px.bar(
    corr_target,
    x="Variable",
    y="Correlación",
    color="Coeficiente",
    barmode="group",
    range_y=[-1, 1],
    title="Asociaciones con Item_Outlet_Sales"
)
fig.add_hline(y=0, line_dash="dot")
fig.show()

El panel de correlación sirve para generar hipótesis, no para afirmar causalidad. Cada coeficiente relevante debe contrastarse con un diagrama de dispersión y con el patrón de faltantes.

34.12 Ensamblaje del dashboard global

En un libro de Quarto, el dashboard puede presentarse como una secuencia ordenada de paneles. Una estructura práctica es la siguiente:

  1. Bloque inicial de KPIs: resumen numérico de alto nivel.
  2. Distribución global: histograma y boxplot para localización, forma y variabilidad.
  3. Comparación entre grupos: heterogeneidad por outlet y por producto.
  4. Concentración: Pareto y tabla de acumulación.
  5. Correlación: heatmap y comparación Pearson–Spearman.
  6. Texto interpretativo: síntesis narrativa de hallazgos.

34.12.1 Ejemplo de lectura integrada

Desde una perspectiva estadística, el dashboard global permite articular una lectura de este tipo:

  • La distribución global de ventas presenta un centro identificable, pero también una dispersión importante.
  • La separación entre media y mediana, junto con la cola derecha, sugiere asimetría positiva.
  • Los boxplots muestran variabilidad considerable y presencia de valores atípicos.
  • Las comparaciones por tipo de outlet y tipo de producto revelan heterogeneidad entre subgrupos.
  • El análisis de concentración muestra que una fracción limitada de categorías contribuye de forma desproporcionada al total.

Esta lectura resume precisamente la lógica del EDA progresivo: la medida no se agota en un único número, sino que se construye mediante varias capas complementarias.

34.13 Propuesta de cierre visual para el book

En un proyecto de Quarto Book, este capítulo puede funcionar como capítulo de cierre analítico. Una buena práctica consiste en terminar con una tabla compacta de hallazgos.

Mostrar código
final_findings = pd.DataFrame({
    "Dimensión": [
        "Localización",
        "Variabilidad",
        "Forma",
        "Heterogeneidad",
        "Concentración"
    ],
    "Pregunta guía": [
        "¿Dónde se ubican los valores típicos?",
        "¿Qué tan dispersas son las ventas?",
        "¿La distribución es simétrica o sesgada?",
        "¿Cambian las ventas entre grupos?",
        "¿Se reparte el total de manera equilibrada?"
    ],
    "Indicador / Visual principal": [
        "Media, mediana, histograma",
        "IQR, desviación estándar, boxplot",
        "Asimetría, curtosis, histograma",
        "Boxplots por grupo, tablas comparativas",
        "Pareto, shares acumulados"
    ]
})

final_findings
Dimensión Pregunta guía Indicador / Visual principal
0 Localización ¿Dónde se ubican los valores típicos? Media, mediana, histograma
1 Variabilidad ¿Qué tan dispersas son las ventas? IQR, desviación estándar, boxplot
2 Forma ¿La distribución es simétrica o sesgada? Asimetría, curtosis, histograma
3 Heterogeneidad ¿Cambian las ventas entre grupos? Boxplots por grupo, tablas comparativas
4 Concentración ¿Se reparte el total de manera equilibrada? Pareto, shares acumulados

34.14 Conclusiones

El dashboard global no debe entenderse como una simple colección de gráficas, sino como una síntesis argumentada del EDA. Su valor reside en que integra distintos niveles de descripción en una sola narrativa:

  • el centro de la distribución,
  • su amplitud,
  • su geometría,
  • sus diferencias entre subgrupos,
  • y su estructura de reparto.

En el caso de Big Mart Sales, esta integración permite comunicar de forma compacta cómo se comportan las ventas desde varios ángulos complementarios. Para fines docentes, el dashboard muestra que la estadística descriptiva es una construcción progresiva de la medida; para fines aplicados, ofrece un instrumento claro de exploración y comunicación.

34.15 Sugerencias de extensión

  1. Añadir filtros interactivos por Outlet_Type, Outlet_Size o Item_Fat_Content.
  2. Integrar una sección de correlación entre variables numéricas.
  3. Incorporar mapas si se dispone de georreferenciación de outlets.
  4. Conectar el dashboard con la fase posterior de modelado predictivo.

34.16 Integración al libro de Quarto

Si tu libro ya incluye los capítulos anteriores, puedes agregar este cierre junto con el capítulo de concentración en el archivo _quarto.yml con una estructura como la siguiente:

book:
  chapters:
    - index.qmd
    - 01_localizacion_bigmart.qmd
    - 02_variabilidad_bigmart.qmd
    - 03_forma_bigmart.qmd
    - 04_heterogeneidad_bigmart.qmd
    - 05_concentracion_bigmart.qmd
    - 06_dashboard_global_bigmart.qmd

Con ello quedará completa la secuencia del EDA progresivo en formato de book.