Saltar al contenido

Indice / Practicas / EJ-6.1

EJ-6.1 · Catalogo unificado de dos sistemas

Practica de: 6.3 merge avanzado 5.5 DuplicadosDificultad: A

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

Los tres problemas, en orden

  1. 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 merge ya no se distingue un duplicado de origen de una fila multiplicada por el cruce.
  2. Superposición parcial. 46 SKU están en las dos fuentes, 14 solo en A y 10 solo en B. Un inner perdería 24 productos.
  3. Contradicciones. De los 46 comunes, 18 tienen el nombre o el precio distinto. El merge los 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

catalogo_sistema_a.csv (entrada) · 62 filas, se muestran 5
skunombrepreciostockactualizado
SKU001Artículo 001205.25222026-07-23
SKU002Artículo 002695.67842026-08-06
SKU003Artículo 00331.891512026-08-09
SKU004Artículo 004vacio1952026-07-15
SKU005Artículo 005174.261262026-08-07

El contrato de salida

catalogo_unificado.csv · 70 filas

ColumnaTipoQue lleva
skutextoLa clave, unica en la salida
nombretextoEl de la version que gano
preciodecimalEl de la version que gano, redondeado a 2 decimales. Puede quedar vacio
stockenteroEl de la version que gano. Puede quedar vacio
actualizadotextoLa fecha de la version que gano
fuentetextoa, b o ambos, segun donde aparece el sku
conflictoentero1 si el sku esta en las dos fuentes y difieren en nombre o en precio; 0 si no

Orden de las filas: sku ascendente. Es unico en la salida, asi que no hace falta desempate. Indice: no se escribe: to_csv(index=False)

Que hacer con cada defecto

DefectoDondeAccionAdonde va
SKU002 y SKU007 repetidos en A; SKU020 repetido en B3 sku, 3 filas de mas entre los dos archivosdeduplicar deduplicar cada catalogo por recencia (actualizado, keep='last') ANTES de cruzar. Con na_position='first' para que una fila sin fecha nunca ganecatalogo_unificado.csv, una fila por sku
18 sku donde las dos fuentes difieren en nombre o precio9 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 conflictogana gana la version cuyo actualizado es MAS RECIENTE. Si las dos fechas empatan, gana A. La fila se marca con conflicto = 1catalogo_unificado.csv
precio ausente en A (SKU004) y stock con el token NaN2 celdasNO NO se imputan: el ejercicio unifica, no limpiacatalogo_unificado.csv, con el campo vacio
precios negativos en B (por ejemplo -19.99 en SKU017)BNO NO se validan en este ejercicio. Si ese precio es el mas reciente, gana igual, y la fila queda marcada con conflicto = 1catalogo_unificado.csv
Orden de comprobacion

Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:

  1. deduplicar A por recencia
  2. deduplicar B por recencia
  3. outer merge con indicator y validate='1:1'
  4. decidir fuente segun _merge
  5. elegir los valores segun la fecha mas reciente
  6. 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

Si estos numeros no te dan, algo se desvio antes de que llegues a comparar los archivos.

filas de A62
filas de B57
sku unicos en A60
sku unicos en B56
catalogo_unificado.csv70
fuente = ambos46
fuente = a14
fuente = b10
conflicto = 118
gano la version de B por ser mas reciente34
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

catalogo_unificado.csv (objetivo) · 70 filas, se muestran 5
skunombrepreciostockactualizadofuenteconflicto
SKU001Artículo 001205.25222026-07-23a0
SKU002Artículo 00231.5122026-08-20a0
SKU003Artículo 00331.891512026-08-09a0
SKU004Artículo 004vacio1952026-07-15a0
SKU005Artículo 005174.261262026-08-07a0
Si tu 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.

Una invariante que vale la pena escribir

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.