# inferencia-causal-practica-con-dowhy-y-grafos-dirigidos-dags
Inferencia Causal Práctica con DoWhy y Grafos Dirigidos (DAGs)
Volver a la portada del cursoConfiguración del Entorno con DoWhy
Sección 3 · Uso de DoWhy para Modelado Causal
Configuración del entorno con DoWhy
En este notebook dejas listo tu entorno y recorres por primera vez los cuatro pasos de DoWhy: modelar, identificar, estimar y refutar. El caso es una tienda en línea que envió cupones de descuento y quiere saber cuánto aumentan el gasto. Descarga el notebook adjunto y ejecútalo celda por celda.
Instalación
Crea un entorno virtual e instala la biblioteca:
# En la terminal:
# python -m venv .venv
# .venv\Scripts\activate (Windows)
# source .venv/bin/activate (macOS o Linux)
# pip install dowhy matplotlib
import dowhy, networkx, pandas, sklearn
print(dowhy.__version__, networkx.__version__, pandas.__version__, sklearn.__version__)
El material se probó con DoWhy 0.14, NetworkX 3.6, pandas 3.0, scikit-learn 1.8 y statsmodels 0.14 (estas dependencias llegan con DoWhy). No hace falta instalar Graphviz: si no está, DoWhy dibuja el grafo con NetworkX y Matplotlib.
Los datos: cupones y gasto
Generamos 4,000 clientes con semilla fija. Los clientes con más antigüedad y más visitas reciben más cupones (la tienda los prioriza) y además gastan más. Un sorteo decide parte de los envíos. El efecto verdadero del cupón es de 8 pesos:
import numpy as np
import pandas as pd
def generar_cupones(n=4000, semilla=42):
rng = np.random.default_rng(semilla)
antiguedad = rng.uniform(0, 10, n)
visitas = rng.poisson(5, n)
sorteo = rng.binomial(1, 0.5, n)
p = 1 / (1 + np.exp(-(-2.5 + 0.25 * antiguedad + 0.2 * visitas + 2.0 * sorteo)))
cupon = rng.binomial(1, p)
gasto = 50 + 8 * cupon + 4 * antiguedad + 3 * visitas + rng.normal(0, 10, n)
return pd.DataFrame(dict(antiguedad=antiguedad.round(2), visitas=visitas,
sorteo=sorteo, cupon=cupon, gasto=gasto.round(2)))
La comparación ingenua (gasto medio con cupón menos gasto medio sin cupón) da 17.02 pesos: más del doble del efecto real, porque mezcla el efecto del cupón con el hecho de que los cupones fueron a los mejores clientes.
Paso 1: modelar con un grafo
CausalModel recibe los datos, el tratamiento, el resultado y el grafo. El grafo puede ser un texto en formato DOT o GML, o un networkx.DiGraph:
from dowhy import CausalModel
df = generar_cupones()
grafo = """digraph {
antiguedad -> cupon; antiguedad -> gasto;
visitas -> cupon; visitas -> gasto;
sorteo -> cupon; cupon -> gasto
}"""
modelo = CausalModel(data=df, treatment="cupon", outcome="gasto", graph=grafo)
print(modelo.get_common_causes(), modelo.get_instruments())
modelo.view_model()
DoWhy lee el grafo y reconoce los confusores (antiguedad, visitas) y el instrumento (sorteo).
Paso 2: identificar
identify_effect() aplica los criterios del grafo y devuelve las expresiones (estimandos) que permiten calcular el efecto. Aquí encuentra un estimando de puerta trasera, que ajusta por los dos confusores, y uno de variable instrumental con el sorteo. Este paso no usa los datos: es puro razonamiento sobre el grafo.
Paso 3: estimar
estimando = modelo.identify_effect()
estimacion = modelo.estimate_effect(estimando, method_name="backdoor.linear_regression",
confidence_intervals=True)
print(round(estimacion.value, 3), estimacion.get_confidence_intervals().round(2))
La regresión con ajuste da 7.899, con intervalo del 95% de [7.21, 8.59], que contiene el 8 verdadero. El nombre del método tiene la forma estrategia.estimador: backdoor., iv. o frontdoor., seguido del estimador.
Paso 4: refutar
Un refutador cambia los datos de una forma cuyo resultado conoces y revisa si la estimación se comporta como debe. Con un tratamiento placebo (el cupón permutado al azar), el efecto debe desaparecer:
placebo = modelo.refute_estimate(estimando, estimacion,
method_name="placebo_treatment_refuter",
placebo_type="permute", random_seed=1)
print(placebo)
El nuevo efecto es 0.052, prácticamente cero: la estimación no es un artefacto del modelo.
Trampas comunes
- Guarda el tratamiento binario como 0/1 (entero), no como
bool: con pandas 3, el refutadoradd_unobserved_common_causefalla al escribir enteros en una columna booleana. - Los nombres de nodos del grafo deben coincidir exactamente con las columnas del DataFrame.
- Una variable del grafo que no está en los datos se trata como no observada; si no lo esperabas, revisa la ortografía.
- Fija
random_seeden los refutadores para que los resultados se puedan reproducir.
Cierre
Con estos cuatro pasos ya tienes el esqueleto de cualquier análisis causal del curso. En la siguiente sección los usarás con varios estimadores y con grafos donde el ajuste simple no funciona.