teLLMe Why (Ain't Nothing but a Jam): Exploratory Causal Analysis of Urban Driving Data
By Qiwei Li, Jorge Ortiz
"System combining PC algorithm with bootstrap stability and LLM-based query mapping for exploratory causal analysis of urban traffic data, outputting effect estimates and assumptions via a Causal Card."
Abstract
Traffic agencies now have access to large volumes of video-derived data for studying safety and congestion. Most of these data are observational and collected without interventions, which makes causal questions such as "How would rain change traffic density?" difficult to answer. We present teLLMe, a system for exploratory causal analysis of urban driving datasets. The system starts from a structured event table built from dashcam annotations and combines causal structure learning with the PC algorithm, bootstrap-based stability checks, and query-specific effect estimation using linear regression and DoWhy. Natural-language questions are mapped to structured causal queries through a schema-aware LLM, enabling users to specify treatments, outcomes, and subpopulations. teLLMe returns a "Causal Card" that summarizes effect estimates, adjustment sets, DAG support, and assumptions, followed by a short natural-language explanation. Case studies on BDD-derived traffic events show that the system can surface plausible relationships involving weather, peak hours, and traffic density, while making uncertainty and modeling choices explicit. The system is designed as a tool for hypothesis generation and expert reasoning rather than a source of definitive causal claims.
Technical Analysis & Implementation
Methodology§
tellMe performs exploratory causal analysis on structured event tables derived from dashcam video annotations. The pipeline has three stages:
1. Causal Structure Learning§
A directed acyclic graph (DAG) is learned using the PC algorithm (Spirtes et al., 2000). The PC algorithm starts with a complete undirected graph and iteratively removes edges based on conditional independence tests (e.g., Fisher's z-test or chi-square test). To increase robustness, bootstrap is applied: B samples (e.g., B=100) are drawn with replacement, a DAG is learned from each sample, and only edges appearing in more than 50% of bootstrap DAGs are retained:
$$\hat{G} = \{ (i,j) : \text{freq}(i,j) > 0.5 \}$$
The final DAG is oriented using Meek's rules.
2. Query Mapping via LLM§
Natural-language questions (e.g., "How does rain affect traffic density?") are converted to structured causal queries using a schema-aware LLM. The LLM is prompted with the event table schema (column names, data types, possible values) and outputs a JSON specifying the treatment variable, outcome variable, and optional subpopulation filters.
3. Causal Effect Estimation§
Given a query, the system identifies a valid adjustment set $\mathbf{Z}$ (causal sufficient set) from the learned DAG using the back-door criterion. The average treatment effect (ATE) is then estimated via linear regression:
$$Y = \beta_0 + \beta_1 T + \gamma^\top \mathbf{Z} + \epsilon$$
where $Y$ is the outcome, $T$ the treatment, and $\mathbf{Z}$ the adjustment set. Alternatively, DoWhy (Sharma & Kiciman, 2020) can be used for more robust estimation (e.g., propensity score matching, instrumental variables). The output is a "Causal Card" containing:
- Estimated ATE ($\hat{\beta}_1$)
- Standard error and 95% confidence interval
- p-value
- Adjustment set
- DAG support (edges present)
- Assumptions (e.g., no unobserved confounders, positivity)
- Short natural-language explanation generated by the LLM.
Implementation Details§
- PC algorithm: Implemented via the
causal-learnPython package. - Bootstrap: 100 resamples with edge stability threshold 0.5.
- LLM usage: GPT-4 or Llama-2-70b (depending on availability) with a structured prompt that includes schema and a few examples.
- Effect estimation:
statsmodelsOLS or DoWhy.
Code Snippet (Python)§
import numpy as np
from causallearn.search.ConstraintBased.PC import pc
from dowhy import CausalModel
import statsmodels.api as sm
# Step 1: Learn causal graph
data = load_event_table() # shape (N, M)
pc_graph = pc(data, alpha=0.05, indep_test='fisherz')
# Bootstrap stability would require looping; omitted for brevity.
# Step 2: Query mapping via LLM (simulated)
query = {
'treatment': 'rain',
'outcome': 'traffic_density',
'subpopulation': None
}
# Step 3: Estimate effect using DoWhy
model = CausalModel(
data=data,
treatment=query['treatment'],
outcome=query['outcome'],
graph=pc_graph_to_dag(pc_graph)
)
# Identify adjustment set via back-door
identified = model.identify_effect(proceed_when_unidentifiable=False)
# Estimate using linear regression
estimate = model.estimate_effect(
identified,
method_name='backdoor.linear_regression'
)
print(f"ATE: {estimate.value}")Key Equations§
PC algorithm uses conditional independence: $X \perp\!\!\!\perp Y \mid \mathbf{Z}$ tested via Fisher's z statistic. For continuous variables: $$z = \frac{1}{2} \ln\left(\frac{1 + \hat{\rho}_{XY|\mathbf{Z}}}{1 - \hat{\rho}_{XY|\mathbf{Z}}}\right) \sim \mathcal{N}(0, \frac{1}{N - |\mathbf{Z}| - 3})$$
The ATE from linear regression: $$\hat{\tau} = \mathbb{E}[Y | do(T=1), \mathbf{Z}] - \mathbb{E}[Y | do(T=0), \mathbf{Z}] = \hat{\beta}_1$$ where $\hat{\beta}_1$ is the OLS coefficient.
Limitations§
- Assumes no unobserved confounders after conditioning on adjustment set.
- The learned DAG may not capture true causal structure due to limited sample size or violations of causal sufficiency.
- The system is designed for hypothesis generation, not definitive causal claims.