Indice / Practicas / EJ-6.1
EJ-6.1 · Catalogo unificado de dos sistemas
Dos sistemas venden los mismos productos y ninguno tiene el catálogo completo. Se superponen a medias, cada uno trae sus propios duplicados, y en 18 productos dicen cosas distintas. Hay que producir un solo catálogo.
Qué hay que hacer
A partir de catalogo_sistema_a.csv y
catalogo_sistema_b.csv, producir catalogo_unificado.csv:
una fila por SKU, con los datos de la versión más reciente, de qué
fuente vino y si hubo conflicto.
ELI5Explicado facil
Dos sucursales tienen su propia lista de precios. Comparten muchos productos, cada una tiene algunos que la otra no, y en varios los precios no coinciden.
Armar una lista sola no es solo juntarlas: hay que decidir, producto por producto, a cuál le creés. Y hay que dejar anotado en cuáles tuviste que decidir, porque esos son los que alguien va a querer revisar.
Archivos
- Entrada ·
catalogo_sistema_a.csv - Entrada ·
catalogo_sistema_b.csv - Objetivo ·
catalogo_unificado.csv - Herramienta ·
comparar.py
Los tres problemas, en orden
- Duplicados dentro de cada catálogo. A tiene 62 filas para 60
SKU; B tiene 57 para 56. Hay que resolverlo antes de cruzar: después
del
mergeya no se distingue un duplicado de origen de una fila multiplicada por el cruce. - Superposición parcial. 46 SKU están en las dos fuentes, 14 solo
en A y 10 solo en B. Un
innerperdería 24 productos. - Contradicciones. De los 46 comunes, 18 tienen el nombre o el
precio distinto. El
mergelos trae todos y no resuelve ninguno.
La política, que la fija el contrato
Cuando las dos fuentes discrepan, gana la versión con
actualizado más reciente, y si las dos fechas empatan, gana A.
Con esos datos, B gana 34 veces, A gana 10, y hay 1 empate.
No es la única política posible - podría ganar siempre A, o podría no resolverse y solo informarse - pero es la que está escrita. Ese es el punto: elegir la fuente ganadora es una decisión de negocio, no un detalle técnico, y cambiarla produce otro archivo.
Cómo saber si te salió
python comparar.py mi_salida.csv catalogo_unificado.csv
Antes de arrancar, mirá los datos
| sku | nombre | precio | stock | actualizado |
|---|---|---|---|---|
| SKU001 | Artículo 001 | 205.25 | 22 | 2026-07-23 |
| SKU002 | Artículo 002 | 695.67 | 84 | 2026-08-06 |
| SKU003 | Artículo 003 | 31.89 | 151 | 2026-08-09 |
| SKU004 | Artículo 004 | vacio | 195 | 2026-07-15 |
| SKU005 | Artículo 005 | 174.26 | 126 | 2026-08-07 |
El contrato de salida
catalogo_unificado.csv · 70 filas
| Columna | Tipo | Que lleva |
|---|---|---|
sku | texto | La clave, unica en la salida |
nombre | texto | El de la version que gano |
precio | decimal | El de la version que gano, redondeado a 2 decimales. Puede quedar vacio |
stock | entero | El de la version que gano. Puede quedar vacio |
actualizado | texto | La fecha de la version que gano |
fuente | texto | a, b o ambos, segun donde aparece el sku |
conflicto | entero | 1 si el sku esta en las dos fuentes y difieren en nombre o en precio; 0 si no |
Que hacer con cada defecto
| Defecto | Donde | Accion | Adonde va |
|---|---|---|---|
| SKU002 y SKU007 repetidos en A; SKU020 repetido en B | 3 sku, 3 filas de mas entre los dos archivos | deduplicar deduplicar cada catalogo por recencia (actualizado, keep='last') ANTES de cruzar. Con na_position='first' para que una fila sin fecha nunca gane | catalogo_unificado.csv, una fila por sku |
| 18 sku donde las dos fuentes difieren en nombre o precio | 9 se contradicen en el nombre, 11 en el precio, 3 en las dos cosas, y en 1 (SKU033) B no tiene nombre. Los cuatro casos cuentan como conflicto | gana gana la version cuyo actualizado es MAS RECIENTE. Si las dos fechas empatan, gana A. La fila se marca con conflicto = 1 | catalogo_unificado.csv |
| precio ausente en A (SKU004) y stock con el token NaN | 2 celdas | NO NO se imputan: el ejercicio unifica, no limpia | catalogo_unificado.csv, con el campo vacio |
| precios negativos en B (por ejemplo -19.99 en SKU017) | B | NO NO se validan en este ejercicio. Si ese precio es el mas reciente, gana igual, y la fila queda marcada con conflicto = 1 | catalogo_unificado.csv |
Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:
- deduplicar A por recencia
- deduplicar B por recencia
- outer merge con indicator y validate='1:1'
- decidir fuente segun _merge
- elegir los valores segun la fecha mas reciente
- marcar conflicto comparando nombre y precio
La deduplicacion va ANTES del cruce: despues ya no se sabe si dos filas iguales vienen de un duplicado o de la multiplicacion del merge. Y validate='1:1' se pone justamente para que el programa falle si alguien salteo ese paso.
Conteos clave
| filas de A | 62 |
|---|---|
| filas de B | 57 |
| sku unicos en A | 60 |
| sku unicos en B | 56 |
| catalogo_unificado.csv | 70 |
| fuente = ambos | 46 |
| fuente = a | 14 |
| fuente = b | 10 |
| conflicto = 1 | 18 |
| gano la version de B por ser mas reciente | 34 |
| empataron las fechas (gana A) | 1 |
| por motivo |
Invariantes
Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:
len(unificado) == 70
unificado['sku'].is_unique
46 + 14 + 10 == 70
int((unificado['fuente'] == 'ambos').sum()) == 46
int(unificado['conflicto'].sum()) == 18
unificado.loc[unificado['fuente'] != 'ambos', 'conflicto'].eq(0).all()
set(unificado['fuente']) == {'a', 'b', 'ambos'}
set(unificado['sku']) == set(a['sku']) | set(b['sku'])
PISTASSi te trabas, abri esto
- Deduplicar primero. Si el outer te da 71 filas en vez de 70, te falta ese paso, y la fila de mas es SKU020.
- validate='1:1' convierte ese error en una excepcion con el nombre de la clave repetida, en vez de una fila silenciosa.
- conflicto solo puede valer 1 donde fuente es 'ambos': un sku que esta en una sola fuente no puede contradecirse con nadie.
- Comparar fechas como texto funciona porque estan en formato AAAA-MM-DD, que ordena igual como texto que como fecha. Con cualquier otro formato habria que convertirlas.
Así tiene que quedar
| sku | nombre | precio | stock | actualizado | fuente | conflicto |
|---|---|---|---|---|---|---|
| SKU001 | Artículo 001 | 205.25 | 22 | 2026-07-23 | a | 0 |
| SKU002 | Artículo 002 | 31.5 | 12 | 2026-08-20 | a | 0 |
| SKU003 | Artículo 003 | 31.89 | 151 | 2026-08-09 | a | 0 |
| SKU004 | Artículo 004 | vacio | 195 | 2026-07-15 | a | 0 |
| SKU005 | Artículo 005 | 174.26 | 126 | 2026-08-07 | a | 0 |
outer da 71 filas
Te falta deduplicar. La fila de más es SKU020, que aparece dos veces
en B y se cruzó dos veces con su par de A.
Poné validate="1:1" en el merge: convierte esa fila
silenciosa en una excepción que además nombra la clave repetida. Es
la diferencia entre encontrar el problema en dos segundos o en media hora.
conflicto solo puede valer 1 donde fuente es
"ambos": un producto que está en una sola fuente no puede contradecirse
con nadie. Es un assert de una línea y atrapa media docena de errores
distintos de una sola vez.