"""07b. Cotejo impl1 (05_screens.sql + 06_medir.py) frente a impl2 (07_impl2.py). Tolerancias de DEFINICIONES_PREVIAS §5.""" import duckdb, json con = duckdb.connect() con.execute("CREATE VIEW a AS SELECT * FROM read_parquet('/c/interno/sc_impl1.parquet') WHERE en_pc") con.execute("CREATE VIEW b AS SELECT * FROM read_parquet('/c/interno/sc_impl2.parquet')") L = ["# 07 · Cotejo de la segunda implementación\n", "impl1 = DuckDB SQL (ventanas `rank`/`count … RANGE`, Gini por pares, mediana `median`) + Python puro (AUC por grupos de empates, " "bootstrap con `randrange`). impl2 = Python puro desde la base `b` (población, PBL fiable, mercados y etiqueta recalculados; Gini " "por la fórmula ordenada; AUC por conteo explícito de pares; bootstrap con `choices` y bisect sobre pesos acumulados). " "Limitación: las dos las escribe el mismo ejecutor; impl2 se escribió desde el texto sellado sin copiar código de impl1.\n", "\n## 1. Por lote (PC)\n", "| Variable | lotes impl1 | lotes impl2 | casan | ambos nulos | uno nulo | máx \\|Δ\\| | ¿≤ 1e−9? |", "|---|---|---|---|---|---|---|---|"] ok_all = True n1 = con.execute("SELECT count(*) FROM a").fetchone()[0]; n2 = con.execute("SELECT count(*) FROM b").fetchone()[0] for v in ['rango', 'sep', 'baja_cond', 'rep_igual', 'estab', 'ute_rivales', 'pnc', 'p_rango', 'p_sep', 'p_baja', 'p_rep', 'p_est', 's3', 's5', 'pct_s3']: r = con.execute(f"""SELECT count(*), count(*) FILTER (WHERE a.{v} IS NULL AND b.{v} IS NULL), count(*) FILTER (WHERE (a.{v} IS NULL) <> (b.{v} IS NULL)), max(abs(a.{v}::DOUBLE - b.{v}::DOUBLE)) FROM a JOIN b ON a.id_canonical = b.id""").fetchone() ok = r[2] == 0 and (r[3] is None or r[3] <= 1e-9) ok_all &= ok L.append(f"| {v} | {n1} | {n2} | {r[0]} | {r[1]} | {r[2]} | {r[3] if r[3] is not None else '—'} | {'sí' if ok else '**no**'} |") j1 = json.load(open('/c/interno/06_auc.json')); j2 = json.load(open('/c/interno/07_impl2.json')) L += ["\n## 2. AUC puntual (tolerancia 1e−9)\n", "| Candidato | Grupo | impl1 | impl2 | \\|Δ\\| | ¿ok? |", "|---|---|---|---|---|---|"] for k, g in j2['auc'].items(): for grp, d in g.items(): a1 = (j1['por_cartel'].get(grp) or j1['conjunto'] if grp == 'conjunto' else j1['por_cartel'][grp])[k]['auc'] if grp != 'conjunto' else j1['conjunto'][k]['auc'] a2 = d['auc'] dd = None if (a1 is None or a2 is None) else abs(a1 - a2) ok = (a1 is None and a2 is None) or (dd is not None and dd <= 1e-9) ok_all &= ok L.append(f"| {k} | {grp} | {a1} | {a2} | {dd} | {'sí' if ok else '**no**'} |") L += ["\n## 3. IC95 bootstrap de S3 (tolerancia 0,02 por extremo; generadores distintos)\n", "| Grupo | impl1 | impl2 | máx \\|Δ\\| | ¿ok? |", "|---|---|---|---|---|"] for grp, d in j2['boot_s3'].items(): s = j1['conjunto']['S3'] if grp == 'conjunto' else j1['por_cartel'][grp]['S3'] dd = max(abs(s['lo'] - d['lo']), abs(s['hi'] - d['hi'])) ok = dd <= 0.02 L.append(f"| {grp} | [{s['lo']:.4f}; {s['hi']:.4f}] ({s['boot_validas']} válidas) | [{d['lo']:.4f}; {d['hi']:.4f}] ({d['validas']} válidas) | {dd:.4f} | {'sí' if ok else '**no** (ver nota)'} |") c2 = j2['cantabria'] c1 = json.load(open('/c/interno/08_cantabria.json')) if __import__('os').path.exists('/c/interno/08_cantabria.json') else None L += ["\n## 4. D2 Cantabria: cifras agregadas\n", f"impl2: {json.dumps(c2)}", f"\nimpl1: {json.dumps(c1['agregado'] if c1 else None)}"] L.append(f"\n**Por lote y AUC: {'todo dentro de tolerancia' if ok_all else 'HAY DISCREPANCIAS'}.**") open('/c/salidas/07_cotejo_impl2.md', 'w').write("\n".join(L) + "\n") print("\n".join(L))