Indice / Practicas / EJ-6.5
EJ-6.5 · Panel de sucursal por mes
200 transacciones sueltas, y hay que entregar un panel de 5 sucursales por 7 meses. La parte que decide el resultado no es el pivote: es lo que se descarta antes.
Qué hay que hacer
A partir de transacciones_sucursales.csv, producir:
panel_sucursal_mes.csv- una fila por sucursal, una columna por mes, con la suma de importes.rechazos_tx.csv- las 5 transacciones que no se pudieron usar, con el motivo.
ELI5Explicado facil
Es armar la planilla del año: sucursales en las filas, meses en las columnas, y en cada casillero cuánto se movió.
El problema es que entre los comprobantes hay uno sin fecha, otro con la fecha ilegible y dos sin importe. Si los metés igual, la planilla sale con una columna de un mes que no existe. Hay que apartarlos primero y anotar cuáles fueron.
Archivos
- Entrada ·
transacciones_sucursales.csv - Objetivo ·
panel_sucursal_mes.csv - Objetivo ·
rechazos_tx.csv - Herramienta ·
comparar.py
La trampa: el pivote no valida
Una transacción tiene la fecha escrita literalmente fecha_invalida. Si
sacás el mes cortando los primeros 7 caracteres del texto, esa fila produce el «mes»
fecha_i, y pivot_table arma una columna con ese
nombre sin decir nada.
El panel sale con 8 columnas donde hay 7 meses, y las cuatro sucursales que no
tienen ninguna transacción rota reciben un 0 en esa columna - que
afirma «no hubo movimiento» sobre un mes que no existe.
Por eso el mes se saca de la fecha ya convertida y validada, nunca del texto crudo.
Cómo saber si te salió
python comparar.py mi_panel.csv panel_sucursal_mes.csv
python comparar.py mi_rechazos.csv rechazos_tx.csv
Antes de arrancar, mirá los datos
| id | fecha | sucursal | tipo | categoria | importe |
|---|---|---|---|---|---|
| TX0001 | 2026-07-25 | Centro | Ingreso | Ventas | 4441.09 |
| TX0002 | 2026-04-17 | Norte | Gasto | Servicios publicos | 3340.4 |
| TX0003 | 2026-06-11 | Centro | Gasto | Marketing | 2686.81 |
| TX0004 | 2026-03-09 | Centro | Ingreso | Servicios | 1741.92 |
| TX0005 | 2026-04-11 | Centro | Ingreso | Servicios | 3832.7 |
El contrato de salida
panel_sucursal_mes.csv · 5 filas
| Columna | Tipo | Que lleva |
|---|---|---|
sucursal | texto | El nombre de la sucursal |
2026-01 | decimal | Suma de importes de ese mes, redondeada a 2 decimales |
2026-02 | decimal | Idem |
2026-03 | decimal | Idem |
2026-04 | decimal | Idem |
2026-05 | decimal | Idem |
2026-06 | decimal | Idem |
2026-07 | decimal | Idem |
rechazos_tx.csv · 5 filas
| Columna | Tipo | Que lleva |
|---|---|---|
id | texto | El id de la transaccion descartada |
columna | texto | En que columna estaba el problema |
valor_original | texto | El valor tal cual venia |
motivo | texto | Uno del vocabulario cerrado del curso |
Que hacer con cada defecto
| Defecto | Donde | Accion | Adonde va |
|---|---|---|---|
| fecha vacia | 1 fila, TX0045 | descartar descartar | rechazos_tx.csv con motivo ausente |
| fecha escrita 'fecha_invalida' | 1 fila, TX0012 | descartar. descartar. Si se le cortan los primeros 7 caracteres produce el mes 'fecha_i', que el pivote acepta como columna | rechazos_tx.csv con motivo fecha_invalida |
| importe ausente: campo vacio y token N/A | 2 filas, TX0118 y TX0140 | descartar descartar | rechazos_tx.csv con motivo ausente |
| tipo escrito 'Ingresso' | 1 fila, TX0067 | descartar. descartar. NO se corrige a 'Ingreso': el enunciado no autoriza a adivinar la intencion | rechazos_tx.csv con motivo dominio_desconocido |
| una transaccion de 99.999.999,99 en la sucursal Sur | 1 fila | NO NO se descarta: es un valor absurdo pero no invalido, y este ejercicio no valida rangos. Queda a la vista en el panel, que es justamente para lo que sirve un panel | panel_sucursal_mes.csv |
| categoria ausente en 1 fila | 1 fila | NO NO afecta: la categoria no participa del panel | panel_sucursal_mes.csv |
Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:
- fecha vacia
- fecha invalida
- importe ausente
- tipo fuera del dominio
- recien despues: sacar el mes de la fecha YA VALIDADA y pivotar
El mes se saca de la fecha convertida, nunca del texto crudo. Cortar los primeros 7 caracteres de 'fecha_invalida' da 'fecha_i', y pivot_table arma una columna con ese nombre sin decir nada. Validar antes de pivotar es la unica forma de que el panel tenga exactamente los meses que existen.
Conteos clave
| filas de entrada | 200 |
|---|---|
| transacciones validas | 195 |
| rechazos_tx.csv | 5 |
| panel_sucursal_mes.csv | 5 |
| meses del panel | 7 |
| celdas del panel | 35 |
| por motivo | ausente: 3, fecha_invalida: 1, dominio_desconocido: 1 |
Invariantes
Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:
len(panel) == 5
len(panel.columns) == 8
len(rechazos) == 5
195 + 5 == 200
panel['sucursal'].is_unique
panel.drop(columns='sucursal').notna().all().all()
abs(panel.drop(columns='sucursal').to_numpy().sum() - validas['importe'].sum()) < 0.01
set(rechazos['motivo']) <= {'ausente', 'fecha_invalida', 'dominio_desconocido'}
PISTASSi te trabas, abri esto
- Si tu panel sale con 8 meses y uno se llama 'fecha_i', sacaste el mes del texto crudo en vez de la fecha validada.
- El aggfunc por defecto de pivot_table es 'mean'. Sin declararlo, el panel muestra promedios y los numeros son plausibles igual.
- reset_index() baja la sucursal del indice a columna, y columns.name = None saca el encabezado extra que deja el pivote.
- La suma de todas las celdas del panel tiene que dar la suma de los importes validos. Es el assert que atrapa las filas descartadas por claves ausentes.
Así tiene que quedar
| sucursal | 2026-01 | 2026-02 | 2026-03 | 2026-04 | 2026-05 | 2026-06 | 2026-07 |
|---|---|---|---|---|---|---|---|
| Centro | 12199.04 | 6194.21 | 21897.63 | 18818.64 | 19081.28 | 19428.66 | 36855.81 |
| Este | 11326.56 | 22356.94 | 37122.28 | 3164.88 | 17060.45 | 26024.02 | 7189.87 |
| Norte | 13916.99 | 17228.04 | 7845.09 | 15774.22 | 24692.55 | 6246.81 | 16185.71 |
| Oeste | 22827.87 | 10431.05 | 29278.14 | 23545.06 | 21254.8 | 14040.39 | 12412.12 |
| Sur | 16317.24 | 32258.93 | 29246.31 | 7431.76 | 100012246.28 | 33434.32 | 11379.44 |
| id | columna | valor_original | motivo |
|---|---|---|---|
| TX0012 | fecha | fecha_invalida | fecha_invalida |
| TX0045 | fecha | vacio | ausente |
| TX0067 | tipo | Ingresso | dominio_desconocido |
| TX0118 | importe | vacio | ausente |
| TX0140 | importe | N/A | ausente |
La celda de Sur en 2026-05 dice más de 100 millones, cuando el
resto del panel se mueve entre 3.000 y 37.000. Sale de una sola transacción de
99.999.999,99.
Y no se descarta: este ejercicio valida la estructura (fecha, importe, tipo), no los rangos. Que quede a la vista no es un defecto del panel: es para lo que sirve un panel. Un número así en una planilla de siete columnas se ve en un segundo; entre 200 filas sueltas, no.
aggfunc="sum", porque el default es "mean" y produce un
panel de promedios con números perfectamente creíbles. Y fill_value=0,
que acá es correcto porque un hueco significa «esa sucursal no movió nada ese mes» y
todos los meses están representados.