Indice / Practicas / EJ-6.3
EJ-6.3 · Anti-join: lo que esta en un solo sistema
Los productos que tiene un catálogo y el otro no. Es la operación más chica de la unidad y la que más se usa: casi todo control de calidad entre dos sistemas empieza acá.
Qué hay que hacer
A partir de los dos catálogos, producir catalogo_solo_a.csv
(14 productos) y catalogo_solo_b.csv (10 productos).
ELI5Explicado facil
Dos listas de asistencia de la misma clase. La pregunta no es quién está en las dos: es quién falta en cada una.
Y hay que contestarla dos veces, porque «los que están en A y no en B» y «los que están en B y no en A» son dos preguntas distintas con dos respuestas distintas.
Archivos
- Entrada ·
catalogo_sistema_a.csv - Entrada ·
catalogo_sistema_b.csv - Objetivo ·
catalogo_solo_a.csv - Objetivo ·
catalogo_solo_b.csv - Herramienta ·
comparar.py
No existe how="anti"
El anti-join se arma con dos cosas que ya conocés: un left con
indicator=True, filtrado por left_only. Tres líneas y
funciona para cualquier par de tablas.
Un detalle que ahorra trabajo: cruzá contra derecha[["sku"]] y no
contra la tabla entera. Así el resultado no trae columnas con sufijos que después hay
que sacar.
Cómo saber si te salió
python comparar.py mi_solo_a.csv catalogo_solo_a.csv
python comparar.py mi_solo_b.csv catalogo_solo_b.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_solo_a.csv · 14 filas
| Columna | Tipo | Que lleva |
|---|---|---|
sku | texto | La clave del producto |
nombre | texto | Tal cual viene de A |
precio | decimal | Tal cual viene, redondeado a 2 decimales. Puede quedar vacio |
stock | entero | Tal cual viene. Puede quedar vacio |
actualizado | texto | Tal cual viene |
catalogo_solo_b.csv · 10 filas
| Columna | Tipo | Que lleva |
|---|---|---|
sku | texto | La clave del producto |
nombre | texto | Tal cual viene de B |
precio | decimal | Tal cual viene, redondeado a 2 decimales |
stock | entero | Tal cual viene |
actualizado | texto | Tal cual viene |
Que hacer con cada defecto
| Defecto | Donde | Accion | Adonde va |
|---|---|---|---|
| SKU repetidos dentro de cada catalogo | SKU002 y SKU007 en A, SKU020 en B | deduplicar deduplicar por recencia antes de cruzar | una fila por sku en cada salida |
| precio y stock ausentes en A | SKU004 (precio) y SKU010 (stock, token NaN) | NO NO se corrigen: el ejercicio compara claves, no valida datos | catalogo_solo_a.csv, con el campo vacio |
| precios negativos en B | B | NO NO se validan. Ninguno cae en los 10 que estan solo en B, pero la politica va escrita igual | catalogo_solo_b.csv |
Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:
- deduplicar A y B por recencia
- left merge de A contra las claves de B, con indicator
- quedarse con left_only
- repetir dando vuelta las tablas
El anti-join no es un how: es un left con indicator filtrado por left_only. Cruzar contra df[['sku']] en vez de contra la tabla entera evita que aparezcan columnas con sufijos que despues hay que sacar.
Conteos clave
| sku unicos en A | 60 |
|---|---|
| sku unicos en B | 56 |
| en las dos fuentes | 46 |
| catalogo_solo_a.csv | 14 |
| catalogo_solo_b.csv | 10 |
| union de las dos | 70 |
| por motivo |
Invariantes
Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:
len(solo_a) == 14
len(solo_b) == 10
46 + 14 + 10 == 70
solo_a['sku'].is_unique
solo_b['sku'].is_unique
set(solo_a['sku']) & set(solo_b['sku']) == set()
set(solo_a['sku']) <= set(a['sku'])
set(solo_b['sku']) & set(a['sku']) == set()
PISTASSi te trabas, abri esto
- No existe how='anti'. Se arma con left + indicator + filtrar por left_only.
- Cruza contra derecha[['sku']] y no contra la tabla entera: asi no aparecen columnas con sufijos.
- Los dos archivos no pueden compartir ningun sku: si comparten alguno, algo salio mal.
- La comprobacion final es 46 + 14 + 10 = 70, que es la cantidad de sku distintos entre las dos fuentes.
Así tiene que quedar
| sku | nombre | precio | stock | actualizado |
|---|---|---|---|---|
| SKU001 | Artículo 001 | 205.25 | 22 | 2026-07-23 |
| SKU002 | Artículo 002 | 31.5 | 12 | 2026-08-20 |
| 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 |
| sku | nombre | precio | stock | actualizado |
|---|---|---|---|---|
| SKU061 | Artículo 061 | 73.2 | 63 | 2026-07-17 |
| SKU062 | Artículo 062 | 498.99 | 130 | 2026-08-16 |
| SKU063 | Artículo 063 | 455.53 | 104 | 2026-08-17 |
| SKU064 | Artículo 064 | 671.16 | 4 | 2026-08-14 |
| SKU065 | Artículo 065 | 31.45 | 6 | 2026-08-17 |
46 + 14 + 10 = 70: los que están en las dos, más los de cada lado,
dan la cantidad de SKU distintos entre los dos catálogos. Es la misma cuenta que
produce el outer de EJ-6.1, y sirve como comprobación
cruzada entre los dos ejercicios.
Y los dos archivos no pueden compartir ningún SKU: si comparten alguno, algo salió mal.
SKU002 y SKU007 aparecen dos veces en A, y los dos están
entre los 14 que faltan en B. Sin deduplicar, salen dos veces cada uno
en el informe y el archivo tiene 16 filas en vez de 14.