# inferencia-causal-practica-con-dowhy-y-grafos-dirigidos-dags
Inferencia Causal Práctica con DoWhy y Grafos Dirigidos (DAGs)
Volver a la portada del cursoMétodos de Validación Causal
Sección 5 · Validación y Evaluación de Modelos Causales
Métodos de validación causal
En aprendizaje supervisado validas comparando predicciones con valores reales. En inferencia causal eso no es posible: nunca observas al mismo cliente con y sin cupón. Por eso la validación causal tiene dos frentes distintos. El primero pone a prueba el grafo contra los datos. El segundo pone a prueba la estimación con experimentos de los que conoces la respuesta. Ninguno demuestra que el efecto es correcto, pero ambos pueden demostrar que algo está mal.
Validar el grafo: independencias implicadas
Cada par de variables d-separadas en el DAG implica una independencia condicional en los datos. Si los datos la contradicen, el grafo está mal. DoWhy las enumera y las prueba con refute_graph(k), donde k es el tamaño exacto del conjunto condicionante. Usa correlación parcial para variables continuas e información mutua condicional para discretas.
modelo_f = CausalModel(data=df.astype(float), treatment="cupon",
outcome="gasto", graph=grafo)
print(modelo_f.refute_graph(k=1))
Con el grafo correcto de los cupones, las 3 independencias con un condicionante se cumplen. Si quitas la flecha visitas -> gasto, el grafo implica independencias que los datos no cumplen: con k=2 aparecen 2 y fallan las 2. Así se detecta un error que, en la estimación, solo se vería como un efecto de 10.505 en lugar de 8.
Dos detalles prácticos de DoWhy 0.14. Primero, k no es un máximo: prueba varios valores. Segundo, si el DataFrame tiene columnas enteras, la ruta de pruebas discretas falla con un KeyError; convertir a float con df.astype(float) lo evita.
Validar la estimación: refutadores
Los refutadores de refute_estimate se dividen en dos familias. Las pruebas negativas cambian los datos de forma que el efecto debe desaparecer:
placebo_treatment_refuter: reemplaza el tratamiento por una permutación aleatoria. En los cupones, el efecto pasa de 7.899 a 0.052.dummy_outcome_refuter: reemplaza el resultado por ruido independiente del tratamiento. El nuevo efecto es −0.004.
Las pruebas de estabilidad cambian los datos de forma que el efecto no debe moverse:
random_common_cause: añade un confusor aleatorio. Efecto 7.898.data_subset_refuter: reestima en submuestras del 80%. Efecto 7.909.
Cada refutador informa un valor p. Uno alto significa que el resultado es consistente con lo esperado; uno bajo es una alarma.
for metodo, extra in [("placebo_treatment_refuter", {"placebo_type": "permute"}),
("random_common_cause", {}),
("data_subset_refuter", {"subset_fraction": 0.8}),
("dummy_outcome_refuter", {})]:
r = modelo.refute_estimate(estimando, estimacion, method_name=metodo,
random_seed=1, **extra)
r = r[0] if isinstance(r, list) else r
print(metodo, round(r.new_effect, 3), r.refutation_result["p_value"])
Observa la línea r[0]: dummy_outcome_refuter devuelve una lista de refutaciones, no un solo objeto.
Análisis de sensibilidad: el confusor que no mediste
Ninguna prueba con datos puede descartar un confusor no medido. Lo que sí puedes hacer es preguntar qué tan fuerte tendría que ser para cambiar tu conclusión. add_unobserved_common_cause simula un confusor con fuerzas dadas sobre el tratamiento y el resultado:
rejilla = {}
for ft in [0.02, 0.05, 0.1]:
for fy in [1.0, 2.0, 4.0]:
r = modelo.refute_estimate(
estimando, estimacion, method_name="add_unobserved_common_cause",
confounders_effect_on_treatment="binary_flip",
confounders_effect_on_outcome="linear",
effect_strength_on_treatment=ft, effect_strength_on_outcome=fy, random_seed=1)
rejilla[(ft, fy)] = round(float(r.new_effect), 2)
print(min(rejilla.values()), max(rejilla.values())) # 6.1 7.5
Con binary_flip, la fuerza sobre el tratamiento es la probabilidad con la que el confusor invierte el valor del cupón; con linear, la fuerza sobre el resultado es un coeficiente de regresión. Con 0.05 y 2.0, el efecto baja de 7.90 a 6.97. Sobre toda la rejilla queda entre 6.10 y 7.50: ni el confusor más fuerte que probamos elimina el efecto. Esa frase, con las fuerzas explícitas, es la forma honesta de reportar robustez.
DoWhy también acepta listas en effect_strength_on_treatment y effect_strength_on_outcome, pero en nuestras pruebas con DoWhy 0.14 el resumen que devuelve esa llamada no coincidió con evaluar cada combinación por separado. Recorre la rejilla celda por celda: así sabes exactamente qué número corresponde a qué supuesto.
Trampas comunes
- Tratar los refutadores como prueba de validez: pasan aunque falte un confusor real, porque no pueden ver lo que no está en los datos.
- Tratamiento booleano:
add_unobserved_common_causeconbinary_flipfalla en pandas 3 si el tratamiento esbool. Usa 0/1. - Una sola semilla: los refutadores son aleatorios; fija
random_seedpara reproducir y considera repetir con varias. - Muchas pruebas de independencia: con decenas de pruebas al 5%, alguna fallará por azar. Busca patrones, no un fallo aislado.
Cierre
Un reporte causal creíble incluye las tres capas: el grafo con sus independencias comprobadas, la estimación con al menos una prueba negativa y una de estabilidad, y un análisis de sensibilidad que diga cuánta confusión oculta haría falta para cambiar la conclusión.