Indice / Practicas / EJ-6.8
EJ-6.8 · Resumen por cliente en dos niveles
202 líneas de pedido y 15 clientes. El resumen necesita dos agrupaciones encadenadas, porque el ticket promedio de un cliente no es el promedio de sus líneas: es el promedio de sus pedidos.
Qué hay que hacer
A partir de pedidos_lineas.csv, producir
resumen_cliente.csv: una fila por cliente, con cuántos pedidos hizo,
cuántas líneas, cuántas unidades, cuánto gastó y su ticket promedio.
ELI5Explicado facil
Un cliente hizo 2 pedidos: uno de $100 con cinco productos y otro de $200 con uno solo.
Su ticket promedio es $150 - el promedio de sus dos pedidos. Si promediás las seis líneas, te da $50, que no es el promedio de nada que le importe a nadie. El pedido de cinco productos pesaría cinco veces más solo por tener más renglones.
Archivos
- Entrada ·
pedidos_lineas.csv - Objetivo ·
resumen_cliente.csv - Herramienta ·
comparar.py
Los dos niveles
- Por pedido: agrupar por
(cliente_id, pedido)y sumar los importes. Cada pedido pasa a ser una fila. - Por cliente: agrupar el resultado por
cliente_id, contar pedidos y sumar totales.
Con una sola agrupación se puede calcular el total gastado, pero no la cantidad de pedidos ni el ticket. Ese es el motivo de la cadena.
Cómo saber si te salió
python comparar.py mi_salida.csv resumen_cliente.csv
Antes de arrancar, mirá los datos
| cliente_id | cliente | pedido | producto | cantidad | precio |
|---|---|---|---|---|---|
| 7 | Horacio Duarte | 1001 | Auriculares | 5 | 56.5 |
| 7 | Horacio Duarte | 1001 | Ratón | 2 | 15.58 |
| 7 | Horacio Duarte | 1001 | Disco SSD | 5 | 86.64 |
| 10 | Ciro Ortiz | 1002 | Teclado | 4 | 25.41 |
| 14 | Agustín Bravo | 1003 | Docking | 3 | 138.04 |
El contrato de salida
resumen_cliente.csv · 15 filas
| Columna | Tipo | Que lleva |
|---|---|---|
cliente_id | entero | La clave del cliente |
cliente | texto | Su nombre, completado desde sus otras lineas si falta |
pedidos | entero | Cuantos pedidos distintos hizo |
lineas | entero | Cuantas lineas de pedido tiene en total |
lineas_sin_importe | entero | Lineas donde falta la cantidad o el precio y no se pudo calcular el importe |
unidades | entero | Suma de cantidad de todas sus lineas |
total | decimal | Suma de cantidad por precio, redondeada a 2 decimales |
ticket_promedio | decimal | total dividido por pedidos, redondeado a 2 decimales |
Que hacer con cada defecto
| Defecto | Donde | Accion | Adonde va |
|---|---|---|---|
| el nombre del cliente falta en una linea | 1 linea del cliente 15 | completar completar desde las otras lineas del MISMO cliente_id, que si lo tienen. No es imputar: es recuperar un dato que esta en la tabla | resumen_cliente.csv |
| cantidad o precio ausentes | 3 lineas, en los clientes 5, 11 y 13 | el el importe de esa linea queda ausente y no suma al total. La linea se cuenta en lineas y en lineas_sin_importe | resumen_cliente.csv |
| una cantidad de 99.999 unidades | 1 linea del cliente 10 | NO NO se descarta: este ejercicio agrupa, no valida rangos. El resumen lo deja a la vista | resumen_cliente.csv |
| un precio de -10.00 y una cantidad de -2 | 2 lineas | NO NO se validan. Restan del total de su cliente, que es lo que dicen los datos | resumen_cliente.csv |
Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:
- completar el nombre faltante desde el mismo cliente_id
- calcular el importe de cada linea y redondearlo
- PRIMER nivel: agrupar por (cliente_id, pedido) y sumar el importe
- SEGUNDO nivel: agrupar por cliente_id, contar pedidos y sumar
- calcular el ticket promedio como total / pedidos
El ticket promedio es el promedio por PEDIDO, no por linea. Calcularlo como mean() del importe de las lineas da otro numero: un pedido de 5 lineas pesaria cinco veces mas que uno de 1. Y el redondeo del importe va por linea, antes de sumar, para que el total no dependa de cuantos decimales arrastre cada multiplicacion.
Conteos clave
| lineas de entrada | 202 |
|---|---|
| resumen_cliente.csv | 15 |
| pedidos distintos | 85 |
| lineas sin importe | 3 |
| cliente con mas pedidos | 9 |
| cliente con menos pedidos | 2 |
| por motivo |
Invariantes
Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:
len(resumen) == 15
resumen['cliente_id'].is_unique
int(resumen['lineas'].sum()) == 202
int(resumen['pedidos'].sum()) == 85
int(resumen['lineas_sin_importe'].sum()) == 3
(resumen['lineas_sin_importe'] <= resumen['lineas']).all()
(resumen['pedidos'] <= resumen['lineas']).all()
resumen['cliente'].notna().all()
(abs(resumen['ticket_promedio'] - resumen['total'] / resumen['pedidos']) < 0.01).all()
PISTASSi te trabas, abri esto
- Dos agrupaciones encadenadas: primero por (cliente, pedido) y despues por cliente. Con una sola no se puede contar pedidos ni sacar el ticket.
- nunique() sobre la columna pedido cuenta pedidos distintos; size() contaria lineas.
- El nombre que falta esta en otras lineas del mismo cliente: se recupera con un map desde una tabla de nombres, no se inventa.
- El cliente 10 tiene 100.042 unidades por una sola linea de 99.999. No hay que descartarla: hay que verla.
Así tiene que quedar
| cliente_id | cliente | pedidos | lineas | lineas_sin_importe | unidades | total | ticket_promedio |
|---|---|---|---|---|---|---|---|
| 1 | Fabián Zapata | 6 | 14 | 0 | 40 | 3517.96 | 586.33 |
| 2 | Diego Acosta | 5 | 11 | 0 | 31 | 3009.01 | 601.8 |
| 3 | Irene Ramos | 2 | 7 | 0 | 19 | 1104.85 | 552.42 |
| 4 | Rocío Molina | 5 | 9 | 0 | 33 | 2822.87 | 564.57 |
| 5 | José Acosta | 2 | 5 | 1 | 13 | 824.55 | 412.28 |
Una línea del cliente 15 no tiene el nombre, y las otras 22 líneas del
mismo cliente_id sí lo tienen. Completarlo desde ahí no es
imputar: es usar un dato que ya está en la tabla.
La diferencia con imputar una mediana es que acá no hay ninguna estimación: el valor correcto está a la vista, en otra fila.
Una sola línea con 99999 unidades le da a ese cliente un total de
5,7 millones cuando el resto se mueve entre 800 y 5.000. También hay una cantidad
negativa y un precio de -10.00.
Ninguno se descarta: este ejercicio agrupa, no valida rangos. Y el resumen los deja a la vista, que es lo que un resumen tiene que hacer. Decidir qué hacer con ellos exige una política, y esa política la escribe otro enunciado.