# Did Causal Analysis

> Difference-in-Differences causal analysis to identify demographic drivers of behavioral changes with p-value significance testing. Use for event effects, A/B testing, or policy evaluation. Use when this capability is needed.

- Skill: `tomevault-io/did-causal-analysis` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/did-causal-analysis`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/did-causal-analysis/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/did-causal-analysis

---


# Difference-in-Differences Framework

DiD framework for causal inference with automatic method selection (Multivariate vs Univariate) based on sample size.

## Methodology

### Intensive Margin
- **Definition**: How much behavior changed among participants (continuous outcome)
- **Data Structure**: Sparse data - only entities that participated (made purchases, had events, etc.)
- **Outcome**: Continuous (e.g., spending amount per purchaser)
- **Key Point**: Do NOT include non-participants or create complete grids - only analyze actual participants

### Extensive Margin  
- **Definition**: How many people changed behavior (binary 0/1 outcome)
- **Data Structure**: Complete panel - all entities × all periods (includes non-participants)
- **Outcome**: Binary (0/1 indicating participation)
- **Key Point**: MUST include non-participants to measure adoption ratess

### Regression Methods

Both methods use: `Y = α + βX + γPost + δ(X × Post) + ε` where δ is the DiD estimate.

**Multivariate Heterogeneous DiD**
- Estimates ALL features simultaneously in one regression
- Controls for confounding between features
- Used when: `n ≥ k × min_sample_ratio` (default: 10k samples minimum)

**Univariate DiD**
- Separate regression for EACH feature
- More robust with small samples
- Used when: Insufficient sample size, or as fallback

**CRITICAL**: Both use statsmodels regression to provide p-values for statistical inference.

## ⚠️ CRITICAL: Data Preparation Requirements

### Intensive Margin - Sparse Data (Purchasers/Participants Only)

**CORRECT approach** - Only aggregate actual transactions:
```python
# Group actual transactions by entity-period
intensive_df = transactions.groupby(['entity_id', 'period'])['value'].sum().reset_index()
# Result: Only rows where events occurred (e.g., 3,404 rows for active participants)
```

**Why this matters**: Intensive margin measures "how much MORE did participants engage", not "did non-participants start engaging". Including zeros for non-participants dilutes the effect and changes the research question.

### Extensive Margin - Complete Panel (All Users)

**CORRECT approach** - Create complete entity × group × period grid:
```python
# Create all possible combinations
from itertools import product
all_entities = entities_df['entity_id'].unique()
all_groups = target_groups  # e.g., categories, regions, products
all_periods = ['baseline', 'treatment']

complete_grid = pd.DataFrame(
    list(product(all_entities, all_groups, all_periods)),
    columns=['entity_id', 'period']
)

# Mark who participated (1) vs who didn't (0)
events_lookup = transactions[['entity_id',  'period']].drop_duplicates()
events_lookup['participated'] = 1

extensive_df = complete_grid.merge(events_lookup, how='left')
extensive_df['participated'] = extensive_df['participated'].fillna(0).astype(int)
# Result: All entities × groups × periods with binary outcome
```

**Why this matters**: Extensive margin measures "did the participation rate increase", which requires knowing both who participated AND who didn't participate.

## API
### Constructor Parameters `DIDAnalyzer(min_sample_ratio=10, significance_level=0.05, min_group_size=1)`
- `min_sample_ratio=10`: Samples-to-features ratio for multivariate DiD
- `significance_level=0.05`: P-value threshold 0.05-0.1 based on the context and requirement
- `min_group_size=1`: Min observations per group for univariate DiD


### `intensive_margin(df, features, value_col, top_n=None, sort_by='estimate', asc=False)`
Analyzes **intensive margin**: how much behavior changed among participants.

**Input Data**: Sparse entity-period data (only participants)
- `df`: Sparse DataFrame - only entities with participation
- `features`: List of demographic column names
- `value_col`: Continuous outcome column (e.g., 'amount', 'spending')
- `top_n`: Top N features (None = all)
- `sort_by`: 'estimate' or 'p_value'
- `asc`: True for ascending, False for descending

**Returns**: List of `{"feature": str, "did_estimate": float, "p_value": float, "method": str}`

### `extensive_margin(df, features, value_col='purchased', top_n=None, sort_by='estimate', asc=False)`
Analyzes **extensive margin**: how many people changed behavior.

**Input Data**: Complete entity-period panel (all entities including non-participants)
- `df`: Complete DataFrame - ALL entities × ALL periods (includes non-participants)
- `features`: List of demographic column names
- `value_col`: Binary outcome column (0/1), default 'purchased'
- `top_n`: Top N features (None = all)
- `sort_by`: 'estimate' or 'p_value'  
- `asc`: True for ascending, False for descending

**Returns**: List of `{"feature": str, "did_estimate": float, "p_value": float, "method": str}`

### Internal Method Selection Logic
- Tries Multivariate Heterogeneous DiD first (if sample size sufficient)
- Falls back to Univariate DiD if needed
- Both methods provide p-values via regression

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/benchflow-ai) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

