Lo que se sale de lo normal
Lo que vamos a construir hoy
Apertura
¿Qué pedidos se salen de lo normal?
Dos horas en seis momentos
Hoy buscamos los pedidos que se salen de lo normal y presentamos el proyecto final: un tablero para la gerencia.
Otra persona del curso revisa tus tres frases sobre una ciudad
| Criterio | Pregunta |
|---|---|
| Pareja | ¿El centro va con su dispersión: la media con la desviación, la mediana con el RIC? |
| Precisión | ¿Cada frase da una cifra con su unidad y dice de qué ciudad habla? |
| El pedido raro | ¿Dice por qué parece raro y comparado con qué? |
Un pedido tardó 200,9 minutos: ¿demora real o error?
- Es el pedido más lento de Tingua: la pregunta quedó abierta en la lista de chequeo de la sesión 1
- En las cajas de la sesión 3, es el punto más alto de Bucaramanga
Todavía no lo sabemos. Una regla puede marcarlo, pero no dice qué es. Antes de decidir hacen falta dos preguntas.
Ante cada pedido raro, dos preguntas: por qué está lejos y respecto a qué
¿Por qué está lejos?
La causa: un error de registro, un valor real y raro, o un dato censurado por un tope del registro.
¿Lejos respecto a qué?
La referencia: todos los pedidos o los de su grupo, como su ciudad o su franja.
Los atípicos
¿Por qué está lejos? ¿Lejos respecto a qué?
Las vallas de Tukey se ponen a 1,5 RIC de la caja
$$ \begin{aligned} \text{valla inferior} &= Q_1 - 1{,}5\,\text{RIC} \\[6pt] \text{valla superior} &= Q_3 + 1{,}5\,\text{RIC} \end{aligned} $$
En el tiempo de entrega, \(Q_1 = 27{,}9\), \(Q_3 = 40{,}4\) y \(\text{RIC} = 12{,}5\) min: 1,5 RIC son 18,75 min, y las vallas quedan en 9,15 y 59,15 min.
En el tiempo, 6 pedidos quedan por debajo de la valla y 240 por encima
tiempo = pedidos["tiempo_entrega_min"]
q1, q3 = tiempo.quantile(0.25), tiempo.quantile(0.75)
ric = q3 - q1
valla_inf = q1 - 1.5 * ric
valla_sup = q3 + 1.5 * ric
print(f"Vallas: {valla_inf:.2f} y {valla_sup:.2f} min")
# «Fuera» es estrictamente más allá: < y >, no <= ni >=
print("Por debajo:", (tiempo < valla_inf).sum(), "pedidos")
print("Por encima:", (tiempo > valla_sup).sum(), "pedidos")
#> Vallas: 9.15 y 59.15 min
#> Por debajo: 6 pedidos
#> Por encima: 240 pedidos- Fuera es estrictamente más allá de la valla:
<y> - 246 candidatos: el 2,1 % de los pedidos
- Con 3 RIC, la valla de los extremos queda en 77,9 min: 66 pedidos
La caja dibuja las vallas: sus puntos sueltos son los 246 candidatos
- La caja va de \(Q_1\) a \(Q_3\); la línea es la mediana
- Los bigotes llegan a 9,6 y 59,1 min: al último pedido que no pasa cada valla
- Los puntos sueltos son los 246 candidatos, dibujados en 164 puntos; la caja no dice qué son
- La caja no muestra la forma: para eso, el histograma
El puntaje z dice a cuántas desviaciones de la media está un pedido
$$ z = \frac{x - \bar{x}}{s} $$
Por convención, un pedido es candidato si \(|z| > 3\): si está a más de tres desviaciones de la media, por encima o por debajo.
El puntaje z marca 75 pedidos, todos dentro de los 240 de la valla
media, s = tiempo.mean(), tiempo.std()
z = (tiempo - media) / s
print("|z| > 3:", (z.abs() > 3).sum(), "pedidos")
marcados = tiempo[z.abs() > 3]
print("¿Todos por encima de la valla?", bool((marcados > valla_sup).all()))
print(f"Media - 3s = {media - 3 * s:.1f} min")
#> |z| > 3: 75 pedidos
#> ¿Todos por encima de la valla? True
#> Media - 3s = -2.8 min- \(|z| > 3\) marca el 0,6 % de los pedidos
- \(\bar{x} - 3s\) es negativo: ningún pedido rápido puede quedar marcado
- Con una cola a la derecha, \(z\) solo marca por un lado
Los atípicos inflan la desviación y así se esconden: es el enmascaramiento
fuera = (tiempo < valla_inf) | (tiempo > valla_sup)
print(f"s con todos los pedidos: {s:.2f} min")
print(f"s sin los {fuera.sum()} atípicos: {tiempo[~fuera].std():.2f} min")
#> s con todos los pedidos: 12.66 min
#> s sin los 246 atípicos: 8.96 min- Los 246 candidatos suben la desviación de 8,96 a 12,66 min
- Con una \(s\) más grande, la línea de \(z = 3\) se aleja, y menos pedidos la cruzan
- Las vallas usan cuartiles, que casi no se mueven
La valla corta en 59,15 min y z = 3 en 73,1: la valla ve más candidatos
- Entre las dos líneas están los pedidos que la valla marca y \(z\) no
- La cola sigue hasta 200,9 min: sus barras son tan bajas que no se ven
- Con cola a la derecha, la valla, que usa cuartiles, es la regla que conviene
La regla marca 1082 pedidos por tener muchos productos: ¿son 1082 pedidos raros?
- En
n_productos, \(Q_1 = 1\) y \(Q_3 = 3\): la valla superior queda en 6 - Fuera quedan los pedidos de más de 6 productos
No. Es un conteo con cola a la derecha: 370 pedidos tienen exactamente 6 productos, el valor de la valla, y los 1059 de 7 a 17 siguen la misma cola. Lo raro son 23 pedidos de 20 a 44 productos, después de un hueco: ninguno tiene 18 ni 19.
En la caja, 1082 pedidos son 26 puntos; las barras muestran el hueco de 17 a 20
Los 240 pedidos lentos son dos grupos: 174 moderados y 66 extremos
lentos = pedidos[tiempo > valla_sup].copy()
lentos["grupo"] = pd.cut(lentos["tiempo_entrega_min"],
bins=[valla_sup, valla_ext, 250],
labels=["moderado", "extremo"])
lentos["llueve"] = (lentos["lluvia"] == "Sí") * 100 # 100 o 0
lentos["bogota"] = (lentos["ciudad"] == "Bogotá") * 100
resumen = lentos.groupby("grupo", observed=True).agg(
pedidos=("id_pedido", "count"),
km_mediana=("distancia_km", "median"),
pct_lluvia=("llueve", "mean"),
pct_bogota=("bogota", "mean"))
resumen.round(2)
#> pedidos km_mediana pct_lluvia pct_bogota
#> grupo
#> moderado 174 9.60 52.30 77.01
#> extremo 66 3.35 16.67 40.91- Moderados, de 59,15 a 77,9 min: 9,6 km de mediana, 52,3 % con lluvia y 77,0 % en Bogotá
- Extremos, más de 77,9 min: 3,35 km y poca lluvia
- En todos los pedidos: 3,1 km de mediana y 24,1 % con lluvia
47 de 48 extremos calificados tienen 1 estrella: el de 200,9 min fue una demora real
extremos = pedidos[tiempo > valla_ext]
calificados = extremos["calificacion"].dropna()
print("Extremos calificados:", len(calificados),
"· con 1 estrella:", (calificados == 1).sum())
columnas = ["id_pedido", "ciudad", "distancia_km", "lluvia",
"tiempo_entrega_min", "calificacion"]
pedidos.loc[tiempo.idxmax(), columnas]
#> Extremos calificados: 48 · con 1 estrella: 47
#> id_pedido P08193
#> ciudad Bucaramanga
#> distancia_km 7.0
#> lluvia No
#> tiempo_entrega_min 200.9
#> calificacion 1
#> Name: 8192, dtype: object- Contamos estrellas: la calificación nunca se promedia
- Nada lo hace imposible: es un pedido real y raro
- Su calificación fue de 1 estrella: se conserva y se investiga
Con la valla global, Bogotá aporta 161 de los 240 pedidos lentos
ciudad = pedidos["ciudad"]
grupos = tiempo.groupby(ciudad)
q1c, q3c = grupos.quantile(0.25), grupos.quantile(0.75)
valla_c = q3c + 1.5 * (q3c - q1c)
lento_global = tiempo > valla_sup
lento_propia = tiempo > ciudad.map(valla_c)
pd.DataFrame({
"valla propia": valla_c,
"lentos (global)": lento_global.groupby(ciudad).sum(),
"lentos (propia)": lento_propia.groupby(ciudad).sum(),
}).round(2)
#> valla propia lentos (global) lentos (propia)
#> ciudad
#> Barranquilla 49.45 10 34
#> Bogotá 63.40 161 86
#> Bucaramanga 49.46 14 27
#> Cali 53.15 25 40
#> Medellín 55.55 30 59- La valla global compara con todos; la propia, con los de su ciudad
- En Barranquilla, 55 min es atípico: su valla es de 49,45
- En el total, 55 min es normal: 24 pedidos de Barranquilla son atípicos solo en su ciudad
Cada caja usa la valla de su ciudad; la línea naranja es la valla global
- Cada caja calcula su valla: 49,45 min en Barranquilla y 63,4 en Bogotá
- La línea naranja, 59,15 min, es la misma para todas
- Barranquilla y Bucaramanga: el mismo 2,3 % de pedidos sobre su valla; los de Bucaramanga llegan más lejos
20 pedidos tienen exactamente 14,0 km: «14» significa «14 o más»
- La app registra hasta 14 km: es un dato censurado
- La mediana y los cuartiles siguen siendo exactos
- La media pasa a ser un piso: la verdadera es igual o mayor
- En la caja, los 20 pedidos de 14 km son un solo punto: la caja no muestra la censura
Qué hacer con cada candidato depende de su causa
| Pregunta | Si la respuesta es sí | Qué se hace |
|---|---|---|
| ¿Es imposible? | Un error | Se corrige en la fuente o se marca como faltante |
| ¿Está en el tope del registro? | Un dato censurado | Se marca: no se borra ni se trata como exacto |
| ¿Es real y raro? | Un atípico real | Se conserva, se investiga y se informa con medidas robustas |
En Tingua no hay valores imposibles: 0 filas repetidas, 0 códigos repetidos y 0 valores ≤ 0 (celda [errores]).
Un atípico es una pregunta, no un error
La regla señala candidatos; la causa y la referencia deciden qué son. El pedido de 200,9 min no era un error: era la demora que la gerencia necesita ver.
Exploración multidimensional
¿Dónde y cuándo tardan más los pedidos?
Un cubo cruza dimensiones con medidas
Dimensiones
Por dónde se corta: la ciudad, la franja, la lluvia, la semana.
Medidas
Lo que se resume: el tiempo, el valor, cuántos pedidos.
Operaciones
Filtrar una parte, desglosar en más detalle y pivotar filas y columnas.
Es lo que hacen las tablas dinámicas de Excel, Power BI y Looker Studio.
La tabla dinámica: en cada ciudad, la lluvia suma de 6,2 a 7,9 min a la mediana
tabla = pd.pivot_table(pedidos, values="tiempo_entrega_min",
index="ciudad", columns="lluvia",
aggfunc="median", observed=True,
margins=True, margins_name="Total")
tabla.round(1)
#> lluvia No Sí Total
#> ciudad
#> Barranquilla 28.0 35.8 28.8
#> Bogotá 36.1 43.5 38.2
#> Bucaramanga 27.0 33.2 28.4
#> Cali 29.8 36.0 31.1
#> Medellín 31.9 38.4 33.6
#> Total 31.9 40.0 33.7values: la medida ·indexycolumns: las dimensionesaggfunc="median": la mediana, como textomargins=True: la fila y la columna «Total»
Bogotá le saca 9,4 min a Barranquilla en el total y 8,1 sin lluvia: ¿por qué?
Las dos diferencias salen de la misma tabla dinámica, con la mediana: columna «Total» y columna «No».
Por la composición. En Bogotá llueve en el 28,5 % de los pedidos y en Barranquilla en el 10,6 %: la columna «Total» de Bogotá lleva más pedidos con lluvia, que son más lentos.
con_lluvia = (pedidos["lluvia"] == "Sí").groupby(ciudad).mean()
(con_lluvia * 100).round(1)
#> ciudad
#> Barranquilla 10.6
#> Bogotá 28.5
#> Bucaramanga 19.7
#> Cali 20.9
#> Medellín 27.9
#> Name: lluvia, dtype: float64Desglosar: lejos y con lluvia, el 59,3 % de los pedidos es lento
tramo = pd.cut(distancia, bins=[0, 4.4, 7.7, 14], right=True,
labels=["hasta 4,4 km", "4,4 a 7,7 km", "más de 7,7 km"])
lento = (tiempo > valla_sup) * 100
pd.pivot_table(pedidos.assign(tramo=tramo, lento=lento),
values="lento", index="tramo", columns="lluvia",
aggfunc="mean", observed=True).round(1)
#> lluvia No Sí
#> tramo
#> hasta 4,4 km 0.6 0.6
#> 4,4 a 7,7 km 1.1 4.2
#> más de 7,7 km 20.7 59.3- Los cortes van explícitos: 4,4 km es \(Q_3\) y 7,7, la valla de la distancia
- Con
right=True, los 26 pedidos de 7,7 km exactos no cuentan como lejanos - Cerca, la lluvia casi no cambia nada; lejos, los lentos pasan del 20,7 al 59,3 %
Un total mezcla grupos: antes de comparar, desglosa
Bogotá y Barranquilla se acercan de 9,4 a 8,1 min cuando se comparan sin lluvia.
El proyecto final
¿Cómo va la operación cada semana?
El proyecto final es un tablero semanal para la gerencia de Tingua
- Quiénes: en parejas; un trío solo si el grupo es impar
- Con qué datos: solo
pedidos.csv, el archivo de todo el curso - Con qué herramienta: Streamlit, Shiny (en Python o en R), Power BI o Looker Studio
- Cuándo: se entrega por la plataforma del curso antes de la sesión 8 y se presenta en ella; el formato de las presentaciones se anuncia en la sesión 7
La lista mínima: cuatro indicadores semanales, cada uno con su definición
Cuatro indicadores
- Los pedidos de la semana
- El tiempo típico y su dispersión: mediana y RIC
- El % de pedidos por encima de una promesa justificada (por ejemplo, 45 min), con n
- La satisfacción: % de 4 y 5 estrellas entre los calificados, con cuántos se calificaron
Y además
- Un filtro por ciudad, un selector de semana y una comparación
- Un texto para la gerencia: tres hallazgos con cifras y una recomendación
- Una nota sobre los datos: fuente, calidad, atípicos, tope de 14 km, semanas incompletas, grupos pequeños e IA
¿Qué gráfica responde cada pregunta de la gerencia?
- ¿Cómo cambia el número de pedidos semana a semana?
- ¿En qué ciudad se tarda más?
- ¿Cómo se reparten los tiempos de entrega?
- ¿Cómo se reparten por medio de pago los pedidos de cada ciudad?
- ¿Los pedidos más lejanos tardan más?
1 tendencia: una línea · 2 comparación: barras · 3 distribución: histograma o caja · 4 composición: barras apiladas al 100 % · 5 relación: un diagrama de dispersión
El tablero avanza en cuatro hitos y se evalúa desde tres miradas
| Quién evalúa | Cómo |
|---|---|
| Tú (autoevaluación) | Tu desempeño y tu aporte a la pareja |
| Otra pareja (coevaluación) | Un formulario de tres criterios |
| El docente (heteroevaluación) | La rúbrica: datos y medidas, técnicas y herramienta, visualización, interpretación y funcionamiento |
Práctica y cierre
Treinta minutos con el cuaderno (notebook): tres ejercicios con verificador.
Práctica guiada: tres ejercicios en el notebook
Abrir el notebook de la sesión 4 en Colab. Antes del ejercicio 1: Entorno de ejecución → Ejecutar anteriores. Las respuestas escritas van en «Tus respuestas», que no se verifica.
¿Error, real y raro, censurado o atípico solo en su grupo?
- Un tiempo de entrega de −5 min
- Un pedido de una empresa con 44 productos
- Una ruta registrada con 14,0 km
- Un pedido de 55 min en Barranquilla
1 error: es imposible · 2 real y raro: se conserva y se investiga · 3 censurado: «14 o más» · 4 atípico en su ciudad (valla de 49,45 min) y normal en el total (59,15)
Lo que te llevas hoy
- Ante cada candidato, dos preguntas: ¿por qué está lejos? y ¿lejos respecto a qué?
- Las vallas usan cuartiles; el puntaje z usa la media y la desviación, que los atípicos inflan
- «Fuera» es estrictamente fuera; los puntos de una caja son candidatos, no errores
- Un valor en el tope del registro es censurado: se marca, no se borra
- La tabla dinámica cruza dimensiones; un total mezcla grupos
La sesión 5 abre con las preguntas de tu tablero
- Termina la práctica: los verificadores revisan los tres ejercicios
- Elige pareja y herramienta: Streamlit, Shiny, Power BI o Looker Studio
- Escribe tres preguntas de la gerencia, cada una con el indicador que la responde. Otra pareja las revisará al abrir la sesión 5
- Lee el enunciado completo y la rúbrica en la página del proyecto
Próxima sesión: ¿qué tan satisfecha está la clientela? Métodos de muestreo y el sesgo de no respuesta.