"""Etapa 3d: análise complementar da validação (NÃO pré-registrada). Feita depois de ver os resultados de validar.py, a pedido de revisão, para qualificar o teste binomial. Não altera o critério nem o resultado principal. 1. Tamanho efetivo do teste: o teste de Poisson é discreto, então a probabilidade de um sinal "replicar" por acaso (RMP real = 1) é, em geral, MENOR que os 5% nominais. Para cada sinal, calculamos essa probabilidade exata a partir dos óbitos esperados em 2015–2019. 2. Número esperado de replicações sem excesso = soma dessas probabilidades, e a distribuição exata do total (Poisson-binomial), SOB A HIPÓTESE SIMPLIFICADORA de independência entre os sinais. Essa hipótese não vale plenamente: há municípios com mais de um sinal, grupos que se sobrepõem ("todos os cânceres" contém os demais) e referências estaduais compartilhadas. Por isso, os valores de p desta tabela não devem ser a principal sustentação da conclusão. Uso: python validar_complementar.py (depois de validar.py) """ import numpy as np import pandas as pd from scipy import stats from config import RESULTADOS ALFA = 0.05 def tamanho_efetivo(e): """P(teste unilateral de Poisson dar p < ALFA | óbitos ~ Poisson(e)).""" if not e or e <= 0: return 0.0 k = int(stats.poisson.ppf(1 - ALFA, e)) while stats.poisson.sf(k - 1, e) >= ALFA: # menor k com P(X >= k) < ALFA k += 1 while k > 0 and stats.poisson.sf(k - 2, e) < ALFA: k -= 1 return float(stats.poisson.sf(k - 1, e)) def poisson_binomial_sf(probs, k): """P(soma de Bernoullis independentes >= k).""" dist = np.zeros(len(probs) + 1) dist[0] = 1.0 for p in probs: dist[1:] = dist[1:] * (1 - p) + dist[:-1] * p dist[0] *= (1 - p) return float(dist[k:].sum()) def main(): s = pd.read_csv(RESULTADOS / "validacao_sinais.csv") s["tamanho_efetivo"] = s["esp_uf_val"].map(tamanho_efetivo) conjuntos = { "a_apos_criterios_geograficos": s[s["contexto"] == "prioritário"], "b_todos_os_sinais": s, "c_esofago_apos_criterios": s[(s["grupo"] == "esofago") & (s["contexto"] == "prioritário")], "d_capitais_entornos_polos": s[s["contexto"] != "prioritário"], } linhas = [] for nome, d in conjuntos.items(): k = int(d["replica"].sum()) linhas.append({ "conjunto": nome, "sinais": len(d), "municipios_distintos": d["cod_ibge"].nunique(), "replicam": k, "esperado_nominal_5pct": ALFA * len(d), "esperado_tamanho_efetivo": d["tamanho_efetivo"].sum(), "tamanho_efetivo_medio": d["tamanho_efetivo"].mean(), "p_poisson_binomial_indep": poisson_binomial_sf(d["tamanho_efetivo"].to_numpy(), k), }) out = pd.DataFrame(linhas) out.to_csv(RESULTADOS / "validacao_complementar.csv", index=False, float_format="%.4g") pd.set_option("display.width", 200) print(out.round(4).to_string(index=False)) return out if __name__ == "__main__": main()