import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (8, 4.5)
plt.rcParams["axes.grid"] = True
DATA_PATH = "../../data/bigmart_sales.csv"
df = pd.read_csv(DATA_PATH)
vars_variabilidad = [
"Item_Weight",
"Item_Visibility",
"Item_MRP",
"Item_Outlet_Sales",
]Medidas de variabilidad en Big Mart Sales
Una vez identificada la ubicación o región central de las principales variables del conjunto de datos, el siguiente paso natural es estudiar su variabilidad. Mientras las medidas de localización responden a la pregunta “¿dónde se ubican los valores típicos?”, las medidas de dispersión responden a “¿qué tan separados o concentrados están los datos alrededor de ese centro?”. Esta distinción es crucial porque dos distribuciones pueden tener la misma media o la misma mediana y, sin embargo, exhibir comportamientos muy distintos en términos de estabilidad, riesgo o heterogeneidad interna.
En el contexto de Big Mart Sales, la variabilidad tiene una lectura operativa inmediata. Una variable con baja dispersión sugiere un comportamiento relativamente estable entre productos u outlets, mientras que una alta dispersión puede indicar mezcla de segmentos, presencia de valores extremos o una dinámica comercial desigual. Para variables como Item_Outlet_Sales, esto es especialmente relevante porque no basta con conocer el nivel promedio de ventas: también interesa saber si las ventas se mantienen relativamente homogéneas o si existen diferencias muy amplias entre observaciones.
Preparación del entorno
Trabajaremos con el siguiente dataset BigMart, para esta sección usaremos Python y las bibliotecas pandas, numpy, plotly y matplotlib.
De la localización a la dispersión
Si en el capítulo anterior se observó que la media y la mediana resumen el centro, ahora interesa medir qué tanto se alejan los datos de esos valores representativos. Las medidas más comunes son:
- Rango: \[ R = x_{\max} - x_{\min} \]
- Varianza muestral: \[ s^2 = \frac{1}{n-1}\sum_{i=1}^{n}(x_i-\bar{x})^2 \]
- Desviación estándar muestral: \[ s = \sqrt{s^2} \]
- Recorrido intercuartílico: \[ IQR = Q_3 - Q_1 \]
- Coeficiente de variación: \[ CV = \frac{s}{\bar{x}} \] siempre que la media sea positiva y tenga interpretación de escala.
Estas medidas no son intercambiables. El rango depende exclusivamente de los valores extremos, la desviación estándar resume el alejamiento promedio respecto a la media y el IQR se concentra en el 50% central de la distribución, por lo que resulta más robusto ante observaciones atípicas.
Cálculo de medidas de variabilidad
Para el calculo de las mediads de interes podemos utilizar los metodos de pandas .mean(), .var(), .std(), .min(), .quantile(), .median() y .max().
summary_var = pd.DataFrame({
"media": df[vars_variabilidad].mean(),
"varianza": df[vars_variabilidad].var(),
"desv_std": df[vars_variabilidad].std(),
"min": df[vars_variabilidad].min(),
"q1": df[vars_variabilidad].quantile(0.25),
"mediana": df[vars_variabilidad].median(),
"q3": df[vars_variabilidad].quantile(0.75),
"max": df[vars_variabilidad].max(),
})
summary_var["rango"] = summary_var["max"] - summary_var["min"]
summary_var["iqr"] = summary_var["q3"] - summary_var["q1"]
summary_var["cv"] = summary_var["desv_std"] / summary_var["media"]
summary_var| media | varianza | desv_std | min | q1 | mediana | q3 | max | rango | iqr | cv | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Item_Weight | 12.857645 | 2.156169e+01 | 4.643456 | 4.555 | 8.773750 | 12.600000 | 16.850000 | 21.350000 | 16.795000 | 8.076250 | 0.361144 |
| Item_Visibility | 0.066132 | 2.662335e-03 | 0.051598 | 0.000 | 0.026989 | 0.053931 | 0.094585 | 0.328391 | 0.328391 | 0.067596 | 0.780224 |
| Item_MRP | 140.992782 | 3.878184e+03 | 62.275067 | 31.290 | 93.826500 | 143.012800 | 185.643700 | 266.888400 | 235.598400 | 91.817200 | 0.441690 |
| Item_Outlet_Sales | 2181.288914 | 2.912141e+06 | 1706.499616 | 33.290 | 834.247400 | 1794.331000 | 3101.296400 | 13086.964800 | 13053.674800 | 2267.049000 | 0.782335 |
La interpretación de estas medidas debe hacerse con cuidado. Tanto la varianza como la desviación estándar dependen de la escala en la que se mide cada variable. Por esta razón, no es conveniente comparar de forma directa la desviación estándar de Item_MRP con la de Item_Visibility, ya que ambas variables están expresadas en magnitudes muy distintas. Para una comparación más justa, resulta más útil el coeficiente de variación (cv), porque relaciona la dispersión con el tamaño del promedio.
A partir del coeficiente de variación, se observa que Item_Outlet_Sales (cv = 0.782) y Item_Visibility (cv = 0.780) son las variables con mayor dispersión relativa. Esto significa que, en ambos casos, los valores se encuentran bastante separados de su media y, por tanto, presentan una alta heterogeneidad. En términos prácticos, las ventas y la visibilidad no se comportan de manera uniforme en el dataset, sino que muestran diferencias importantes entre observaciones.
Por otro lado, Item_MRP presenta un coeficiente de variación intermedio (cv = 0.442). Aunque sus valores se distribuyen en un rango amplio, su variabilidad relativa es menor que la observada en ventas y visibilidad. Finalmente, Item_Weight es la variable menos dispersa en términos relativos (cv = 0.361), lo que sugiere que sus valores están más concentrados alrededor de la media.
Esta lectura puede reforzarse con el rango intercuartílico (iqr), que muestra la amplitud del 50 % central de los datos. En Item_Outlet_Sales, el valor de iqr = 2267.049 indica que incluso la parte central de la distribución presenta una dispersión considerable. En Item_MRP, el iqr = 91.817 también sugiere variabilidad, aunque menos extrema. En Item_Weight e Item_Visibility, estos valores deben interpretarse dentro de su propia escala, evitando comparaciones absolutas entre variables distintas.
En conjunto, estas medidas muestran que Item_Outlet_Sales y Item_Visibility son las variables más variables del conjunto, mientras que Item_Weight presenta un comportamiento relativamente más estable. Para el EDA, esto es importante porque indica que algunas variables requieren una exploración más cuidadosa de su dispersión, posibles valores extremos y diferencias entre grupos.
Visualización de dispersión con boxplots
Los boxplots o diagramas de caja son una herramienta muy útil en EDA porque permiten resumir, en una sola figura, varios aspectos importantes de una variable: su centro, su dispersión y la posible presencia de valores atípicos.
En cada boxplot:
- la línea dentro de la caja representa la mediana;
- la base inferior de la caja corresponde al primer cuartil (\(Q_1\));
- la base superior corresponde al tercer cuartil (\(Q_3\));
- la altura de la caja representa el rango intercuartílico (\(IQR = Q_3 - Q_1\)), es decir, la amplitud del 50 % central de los datos;
- los bigotes muestran hasta dónde se extienden los valores que todavía pueden considerarse dentro del comportamiento general de la variable;
- los puntos que quedan fuera suelen interpretarse como posibles valores atípicos.
Esto hace que el boxplot sea especialmente valioso, porque no solo muestra dónde está el centro de la distribución, sino también qué tan concentrados o dispersos están los datos alrededor de ese centro.
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(
rows=2, cols=2,
subplot_titles=vars_variabilidad
)
for i, col in enumerate(vars_variabilidad):
row = i // 2 + 1
col_pos = i % 2 + 1
fig.add_trace(
go.Box(
y=df[col].dropna(),
name=col,
boxpoints="outliers",
showlegend=False
),
row=row, col=col_pos
)
fig.update_yaxes(title_text=col, row=row, col=col_pos)
fig.update_layout(
title="Boxplots de variables seleccionadas",
height=700,
width=950
)
fig.show()Esta visualización permite notar rápidamente qué variables presentan mayor amplitud central, qué tan alejados aparecen los extremos y si existen observaciones fuera del patrón principal. En contextos de EDA, el boxplot es una transición natural entre el estudio de localización y el análisis de forma, porque revela tanto la posición de la mediana como la anchura del bloque central.
Rango frente a IQR: medidas sensibles y robustas
El rango es intuitivo, pero muy sensible a valores extremos. Si una sola observación toma un valor muy grande o muy pequeño, el rango cambia de forma drástica. El IQR, en cambio, describe la anchura del bloque central de observaciones y por ello suele ser una mejor medida cuando se desea resumir dispersión sin que la descripción dependa demasiado de extremos aislados.
Para visualizar esta idea, puede compararse el rango con el IQR en una tabla compacta.
robustez = summary_var[["rango", "iqr", "desv_std", "cv"]].sort_values("cv", ascending=False)
robustez| rango | iqr | desv_std | cv | |
|---|---|---|---|---|
| Item_Outlet_Sales | 13053.674800 | 2267.049000 | 1706.499616 | 0.782335 |
| Item_Visibility | 0.328391 | 0.067596 | 0.051598 | 0.780224 |
| Item_MRP | 235.598400 | 91.817200 | 62.275067 | 0.441690 |
| Item_Weight | 16.795000 | 8.076250 | 4.643456 | 0.361144 |
A partir de los resultados observados, se nota que en todas las variables el rango es considerablemente mayor que el IQR, lo cual indica que los valores más extremos amplían mucho la dispersión total respecto de la dispersión que presenta la zona central de los datos.
Por ejemplo, en Item_Outlet_Sales el rango es de 13053.67, mientras que el IQR es de 2267.05. Esto significa que, aunque el total de valores cubre un intervalo muy amplio, la mitad central de las observaciones se concentra en una región mucho más reducida. En otras palabras, la dispersión total de las ventas está fuertemente influida por valores extremos o muy alejados del centro.
Algo similar ocurre con Item_Visibility, donde el rango (0.3284) es casi cinco veces el IQR (0.0676). Esto sugiere que, aunque la mayor parte de los productos tiene niveles de visibilidad concentrados en una zona relativamente estrecha, existen algunos casos con valores bastante más alejados que expanden notablemente el rango.
En Item_MRP, el rango (235.60) también supera claramente al IQR (91.82), lo que indica que el precio de lista presenta variación importante, aunque la dispersión del bloque central es más moderada que la sugerida por los extremos. Por último, en Item_Weight, el rango (16.80) es aproximadamente el doble del IQR (8.08), lo que refleja una distribución relativamente más estable en comparación con las demás variables.
En conjunto, los resultados sugieren que Item_Outlet_Sales y Item_Visibility son variables donde la diferencia entre dispersión total y dispersión central es más marcada, mientras que Item_Weight presenta una estructura comparativamente más compacta. Por ello, en el EDA resulta recomendable usar el IQR junto con otras medidas robustas cuando se quiere describir la variabilidad sin que los extremos dominen la interpretación.
Comparación de dispersión entre grupos
La variabilidad también puede cambiar entre subpoblaciones. En Big Mart Sales una comparación razonable consiste en estudiar la dispersión de Item_Outlet_Sales según Outlet_Type.
by_outlet = (
df.groupby("Outlet_Type")["Item_Outlet_Sales"]
.agg(["count", "mean", "median", "std"])
.rename(columns={"count": "n", "mean": "media", "median": "mediana", "std": "desv_std"})
.sort_values("desv_std", ascending=False)
)
by_outlet| n | media | mediana | desv_std | |
|---|---|---|---|---|
| Outlet_Type | ||||
| Supermarket Type3 | 935 | 3694.038558 | 3364.9532 | 2127.760054 |
| Supermarket Type1 | 5577 | 2316.181148 | 1990.7420 | 1515.965558 |
| Supermarket Type2 | 928 | 1995.498739 | 1655.1788 | 1375.932889 |
| Grocery Store | 1083 | 339.828500 | 256.9988 | 260.851582 |
import plotly.express as px
fig = px.box(
df,
x="Outlet_Type",
y="Item_Outlet_Sales",
points="outliers",
title="Dispersión de Item_Outlet_Sales por Outlet_Type",
labels={
"Outlet_Type": "Tipo de Outlet",
"Item_Outlet_Sales": "Ventas por producto"
}
)
fig.update_layout(
xaxis_tickangle=45,
height=500,
width=900
)
fig.show()Esta figura permite observar que no solo cambia el centro de las ventas según tipo de outlet, sino también su grado de dispersión. Algunos grupos pueden presentar ventas más consistentes, mientras otros muestran una distribución más extendida. Esta observación será clave cuando más adelante se discuta heterogeneidad.
Coeficiente de variación y comparación relativa
En muchas aplicaciones interesa comparar dispersión entre variables con escalas diferentes. La desviación estándar por sí sola no siempre permite hacerlo de manera justa. El coeficiente de variación proporciona una medida adimensional que expresa la dispersión como proporción del promedio.
cv_table = summary_var[["media", "desv_std", "cv"]].sort_values("cv", ascending=False)
cv_table| media | desv_std | cv | |
|---|---|---|---|
| Item_Outlet_Sales | 2181.288914 | 1706.499616 | 0.782335 |
| Item_Visibility | 0.066132 | 0.051598 | 0.780224 |
| Item_MRP | 140.992782 | 62.275067 | 0.441690 |
| Item_Weight | 12.857645 | 4.643456 | 0.361144 |
Si una variable exhibe un coeficiente de variación alto, ello sugiere que su dispersión es grande en relación con su propio nivel medio. En el caso de ventas o visibilidad, este tipo de medida puede ser más informativa que la desviación estándar absoluta cuando se comparan fenómenos de distinta escala.
Histograma con bandas de dispersión
Otra visualización útil consiste en representar la media junto con bandas de una desviación estándar. No debe interpretarse como intervalo de confianza ni como regla universal de cobertura, pero sí como una guía visual de la amplitud típica alrededor del promedio.
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(
rows=2, cols=2,
subplot_titles=vars_variabilidad
)
for i, col in enumerate(vars_variabilidad):
s = df[col].dropna()
mu = s.mean()
sigma = s.std()
row = i // 2 + 1
col_pos = i % 2 + 1
# Histograma
fig.add_trace(
go.Histogram(
x=s,
nbinsx=30,
name=col,
showlegend=False
),
row=row, col=col_pos
)
# Línea de la media
fig.add_vline(
x=mu,
line_dash="dash",
line_width=2,
annotation_text=f"Media = {mu:.2f}",
annotation_position="top right",
row=row, col=col_pos
)
# Línea media - s
fig.add_vline(
x=mu - sigma,
line_dash="dot",
line_width=2,
annotation_text="Media - s",
annotation_position="top left",
row=row, col=col_pos
)
# Línea media + s
fig.add_vline(
x=mu + sigma,
line_dash="dot",
line_width=2,
annotation_text="Media + s",
annotation_position="top right",
row=row, col=col_pos
)
fig.update_xaxes(title_text=col, row=row, col=col_pos)
fig.update_yaxes(title_text="Frecuencia", row=row, col=col_pos)
fig.update_layout(
title="Histogramas con media y desviación estándar",
height=700,
width=1000,
bargap=0.05
)
fig.show()Esta visualización resulta especialmente útil para introducir una idea intuitiva: distribuciones con la misma media pueden diferir porque sus observaciones se encuentran más o menos alejadas de ese centro.
Ejercicio sugerido
Construya un segundo dashboard en Quarto que incluya:
- una tabla resumen con rango, desviación estándar, IQR y coeficiente de variación;
- boxplots para
Item_Weight,Item_Visibility,Item_MRPeItem_Outlet_Sales; - una comparación de la dispersión de
Item_Outlet_SalesporOutlet_Type; - un comentario breve donde se explique por qué el IQR es más robusto que el rango frente a valores extremos.