Metadata-Version: 2.4
Name: equitrends
Version: 0.1.1
Summary: Python port of EquiTrends pre-trend equivalence testing tools.
Author: Xuanyu Cai, Wenli Xu
Project-URL: Repository, https://github.com/gorgeousfish/equitrends-py
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: matplotlib>=3.8
Requires-Dist: numpy>=2.0
Requires-Dist: pandas>=2.0
Requires-Dist: PyYAML>=6.0
Requires-Dist: scipy>=1.12
Provides-Extra: progress
Requires-Dist: tqdm>=4.0; extra == "progress"
Provides-Extra: docs
Requires-Dist: sphinx>=7.0; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == "docs"
Dynamic: license-file

# equitrends

Equivalence Tests for Pre-Trends in DiD Estimation

[![Python](https://img.shields.io/badge/Python-3.10%2B-blue)](https://www.python.org/)
![Version](https://img.shields.io/badge/version-0.1.0-informational)
![License](https://img.shields.io/badge/license-AGPL--3.0-blue)

![Reversing the Burden of Proof](image/image.png)

`equitrends` is a Python package implementing equivalence tests for pre-trends in Difference-in-Differences (DiD) designs, based on [Dette &amp; Schumann (2024)](https://doi.org/10.1080/07350015.2024.2308121), published in the *Journal of Business & Economic Statistics*.

## Contents

- [Overview](#overview)
- [Requirements](#requirements)
- [Installation](#installation)
- [Data requirements](#data-requirements)
- [Quick start](#quick-start)
- [API reference](#api-reference)
- [Test selection guide](#test-selection-guide)
- [Methodology](#methodology)
- [RMS test alpha restriction](#rms-test-alpha-restriction)
- [Result objects](#result-objects)
- [Visualization](#visualization)
- [CLI entry points](#cli-entry-points)
- [Examples](#examples)
- [Citation](#citation)
- [Authors](#authors)
- [License](#license)

## Overview

Standard pre-trend tests in DiD designs test the null hypothesis of *exact* parallel trends (H₀: β = 0). This approach suffers from fundamental limitations:

1. **Failure to reject ≠ Evidence in favor**: Low statistical power may prevent detection of actual violations. Failing to reject does not support the parallel trends assumption.
2. **Conditional bias amplification**: Roth (2022) demonstrates that conditioning on passing traditional pre-tests can *amplify* DiD bias when violations exist.
3. **No explicit threshold**: Traditional tests provide no framework for determining what constitutes a "negligible" deviation from parallel trends.

`equitrends` implements *equivalence* tests that **reverse the burden of proof**: the null hypothesis is that deviations are *large* (H₀: ‖β‖ ≥ threshold), and rejection provides statistical evidence that deviations are *small*. This approach:

- Requires explicit justification of the equivalence threshold
- Controls Type I error (falsely concluding equivalence)
- Increases statistical power with sample size
- Allows researchers to quantify the smallest threshold at which equivalence holds

### Key features

- **Three equivalence hypotheses**: maximum, mean, and RMS (Dette & Schumann, 2024, Section 3.1)
- **Minimum equivalence threshold**: compute the smallest threshold at which equivalence can be concluded
- **Multiple inference methods for the maximum test**: IU (analytical), spherical bootstrap, and wild bootstrap
- **Visualization**: coefficient plots with equivalence bounds (`plot_equivtest`)
- **CLI tools**: JSON-emitting command-line entry points for scripted workflows
- **Bundled empirical dataset**: Di Tella & Schargrodsky (2004) crime panel

## Requirements

- **Python 3.10** or higher
- **numpy** ≥ 2.0
- **pandas** ≥ 2.0
- **scipy** ≥ 1.12
- **matplotlib** ≥ 3.8

## Installation

```bash
pip install equitrends
```

From a local checkout (editable install):

```bash
cd equitrends-py
pip install -e .
```

## Data requirements

**Before using this package, ensure your data meets the following requirements:**

| Requirement                             | Description                                                                                                                                              |
| :-------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Panel structure**               | Panel data with individual (`id`) and time (`period`) identifiers. **Both balanced and unbalanced panels are supported.**                      |
| **Minimum pre-treatment periods** | At least one pre-treatment period (*T* ≥ 1). More periods increase statistical power.                                                                 |
| **Treatment group indicator**     | Binary variable coded as 0 (control) or 1 (treated).                                                                                                     |
| **Block adoption design**         | All treated units must receive treatment at the same time. Staggered adoption requires cohort-specific analysis (see Dette & Schumann, 2024, Section 5). |
| **Complete time-group cells**     | Each time period must contain observations in both treatment and control groups.                                                                         |

### Unbalanced panels

The package automatically detects and handles unbalanced panels (where individuals have different numbers of observed time periods). No special syntax is required—simply pass the data as usual.

### Data completeness

When some time periods lack observations for either group, the placebo regression cannot be estimated correctly. Ensure at least one observation per time-treatment cell, or restrict analysis to time periods with complete coverage.

## Quick start

### Empirical example: Di Tella & Schargrodsky (2004)

This example replicates the empirical application in Dette & Schumann (2024, Section 7) using the Buenos Aires crime data from Di Tella & Schargrodsky (2004). The dataset contains monthly car theft counts for 876 Buenos Aires city blocks (April–December 1994), of which 37 blocks received police protection after a July terrorist attack.

```python
from equitrends import equiv_test, load_dataset, plot_equivtest

# Load and prepare data
panel = load_dataset("MonthlyPanel")
panel = panel.rename(columns={"observ": "ID", "totrob": "Y", "mes": "period"}).copy()
panel["G"] = (panel["distanci"] == 0).astype(int)

# Maximum test (IU method, cluster-robust SE)
result = equiv_test(
    type="max",
    method="IU",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[4, 5, 6, 7],
    base_period=7,
    vcov="cluster",
    cluster="ID",
)
print(result)
```

[Outpu](https://github.com/gorgeousfish/equitrends-py):

```
==================================================
Equivalence Tests for Pre-trends in DiD Estimation
==================================================
Type: Intersection Union
Significance level: 0.05
Alternative hypothesis: the maximum placebo effect does not exceed the equivalence threshold.
---
Abs. Estimate  Std. Error  Min. Equiv. Threshold
        0.032     0.06167                 0.1313
        0.009     0.03652                 0.0605
        0.073     0.04515                 0.1472
---
No. placebo coefficients estimated: 3
Base period: 7

Balanced Panel:
 + No. pre-treatment periods: 4
 + No. individuals: 876
 + Total no. observations: 3504
```

```python
# Mean test
result_mean = equiv_test(
    type="mean",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[4, 5, 6, 7],
    base_period=7,
)

# RMS test
result_rms = equiv_test(
    type="rms",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[4, 5, 6, 7],
    base_period=7,
    seed=2024,
)

# Visualize results
ax = plot_equivtest(result)
```

### Bootstrap methods (maximum test only)

```python
# Spherical bootstrap (assumes spherical errors; Theorem 1)
result_boot = equiv_test(
    type="max",
    method="boot",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[4, 5, 6, 7],
    base_period=7,
    B=1000,
    seed=12345,
)

# Wild bootstrap (recommended for non-spherical errors; Remark 1(c))
result_wild = equiv_test(
    type="max",
    method="wild",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[4, 5, 6, 7],
    base_period=7,
    B=1000,
    seed=12345,
)
```

### Control variables (conditional parallel trends)

The `x` argument includes additional control variables in the TWFE placebo regression (Dette & Schumann, 2024, Section 5). Time-invariant covariates are absorbed by the individual fixed effects during double demeaning.

```python
result = equiv_test(
    type="max",
    method="IU",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[4, 5, 6, 7],
    base_period=7,
    x=["edpub", "estserv", "banco"],
    vcov="cluster",
    cluster="ID",
)
```

## API reference

| Function                        | Description                                         |
| :------------------------------ | :-------------------------------------------------- |
| `equiv_test`                  | Unified interface for all three tests (recommended) |
| `max_equiv_test`              | Maximum absolute coefficient test (IU/Boot/Wild)    |
| `mean_equiv_test`             | Mean coefficient test                               |
| `rms_equiv_test`              | Root mean square (RMS) test                         |
| `plot_equivtest`              | Coefficient plot with equivalence bounds            |
| `prepare_equivtest_plot_data` | Extract plot-ready data without rendering           |
| `sim_panel_data`              | Generate R-compatible simulated panel               |
| `load_dataset`                | Load bundled example datasets                       |
| `list_datasets`               | Inspect available bundled datasets                  |

### Unified syntax

```python
from equitrends import equiv_test

result = equiv_test(
    type="max|mean|rms",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    # ... options
)
```

### Core options

| Parameter               | Description                                              |
| :---------------------- | :------------------------------------------------------- |
| `type`                | Test type:`"max"`, `"mean"`, or `"rms"` (required) |
| `data`                | pandas DataFrame with panel data (required)              |
| `y`                   | Outcome variable name (required)                         |
| `id`                  | Panel identifier column (required)                       |
| `g`                   | Treatment group indicator 0/1 (required)                 |
| `period`              | Time period variable (required)                          |
| `equiv_threshold`     | Equivalence threshold; omit to compute minimum threshold |
| `alpha`               | Significance level; default 0.05                         |
| `pretreatment_period` | List of pre-treatment periods to include                 |
| `base_period`         | Base period for placebo construction                     |
| `x`                   | List of control variable names                           |

Options specific to `type="max"`:

| Parameter  | Description                                          |
| :--------- | :--------------------------------------------------- |
| `method` | Inference method:`"IU"`, `"boot"`, or `"wild"` |
| `B`      | Bootstrap replications; default 1000                 |
| `seed`   | Random seed for bootstrap                            |

Options specific to `type="rms"`:

| Parameter     | Description                                            |
| :------------ | :----------------------------------------------------- |
| `no_lambda` | Number of subsamples for self-normalization; default 5 |
| `seed`      | Random seed for subsampling                            |

Variance estimator options (available for IU/mean; not for bootstrap or RMS):

| Parameter   | Description                                               |
| :---------- | :-------------------------------------------------------- |
| `vcov`    | Variance estimator type; see table below                  |
| `cluster` | Cluster variable name (required for cluster-robust types) |

**Variance estimator types (`vcov`):**

| Type                      | Description                                                             |
| :------------------------ | :---------------------------------------------------------------------- |
| `"ols"`                 | Homoskedastic OLS variance (default)                                    |
| `"robust"` / `"hc1"`  | HC1 heteroskedasticity-robust (White, 1980)                             |
| `"hc2"`                 | HC2 leverage-adjusted (MacKinnon & White, 1985)                         |
| `"hc3"`                 | HC3 more conservative leverage adjustment (Davidson & MacKinnon, 1993)  |
| `"hac"`                 | Arellano (1987) HAC estimator for panel data                            |
| `"cluster"` / `"cr0"` | CR0 cluster-robust without small-sample adjustment; requires`cluster` |
| `"cr1"`                 | CR1 cluster-robust with G/(G-1) adjustment; requires`cluster`         |
| `"hc1_cluster"`         | HC1 cluster-robust with small-sample adjustment; requires`cluster`    |

## Test selection guide

`equitrends` offers three equivalence tests with different properties. Choose based on your research context:

| Feature                    | Maximum test               | Mean test                   | RMS test                   |
| :------------------------- | :------------------------- | :-------------------------- | :------------------------- |
| **Hypothesis**       | max\|βₜ\| < δ           | \|β̄\| < τ               | β_RMS < ζ                |
| **Measures**         | Largest single violation   | Average violation           | Root mean square           |
| **Cancellation**     | No                         | Yes (opposing signs cancel) | No                         |
| **Sensitivity**      | Any single large deviation | Systematic directional bias | Balanced across deviations |
| **Conservativeness** | Most conservative          | Least conservative          | Moderate                   |

### Recommendations

1. **Maximum test (`type="max"`)**: Start here as the default, conservative choice.

   - Detects any single large violation
   - Use `method="IU"` for analytical inference or `method="wild"` for non-spherical errors
   - Recommended when you want to rule out *any* substantial pre-trend violation
2. **Mean test (`type="mean"`)**: Use when violations are expected to be monotone (same sign).

   - More powerful when deviations are directionally consistent
   - **Caution**: Opposing violations may cancel out, leading to false equivalence
3. **RMS test (`type="rms"`)**: Use as a general-purpose alternative.

   - Balances sensitivity across all placebo coefficients
   - No cancellation problem
   - Self-normalized (no variance estimation required)

### Interpreting minimum thresholds

When `equiv_threshold` is omitted, `equitrends` reports the smallest equivalence threshold (δ\*, τ\*, or ζ\*) at which equivalence can be concluded at the specified significance level. Compare this to your estimated treatment effect:

- **δ\* << estimated ATT**: Strong evidence for negligible pre-trends
- **δ\* ≈ estimated ATT**: Pre-trend violations may explain the treatment effect
- **δ\* >> estimated ATT**: Insufficient evidence for parallel trends; consider alternative designs

## Methodology

Let $\beta = (\beta_1,\ldots,\beta_T)'$ denote the vector of placebo (pre-treatment) coefficients from the TWFE placebo regression (Dette & Schumann, 2024, Eq. (2.5)). `equitrends` implements three equivalence hypotheses (Section 3.1):

1. **Maximum deviation (Eq. (3.1))**:

$$
H_0: \|\beta\|_{\infty} \ge \delta \quad \text{vs.} \quad H_1: \|\beta\|_{\infty} < \delta, \qquad \|\beta\|_{\infty}=\max_{l\in\{1,\ldots,T\}}|\beta_l|
$$

2. **Mean deviation (Eq. (3.2))**:

$$
\bar{\beta}=\frac{1}{T}\sum_{l=1}^{T}\beta_l, \qquad H_0: |\bar{\beta}| \ge \tau \quad \text{vs.} \quad H_1: |\bar{\beta}| < \tau
$$

3. **RMS deviation (Eq. (3.3))**:

$$
\beta_{\mathrm{RMS}}=\sqrt{\frac{1}{T}\sum_{l=1}^{T}\beta_l^2}, \qquad H_0: \beta_{\mathrm{RMS}} \ge \zeta \quad \text{vs.} \quad H_1: \beta_{\mathrm{RMS}} < \zeta
$$

### Inference methods for the maximum test

- **IU (Intersection-Union, analytical)**: For each placebo coefficient *t* = 1, ..., *T*, the test rejects H0 iff all |beta_t| < Q(alpha), where Q denotes the alpha-quantile of the folded normal distribution with mean delta and variance sigma_tt/n (Dette & Schumann, 2024, Eq. (4.4)). Computationally attractive but conservative for large *T*.
- **Bootstrap** (`method="boot"`): Generates bootstrap samples under the constraint on beta using constrained OLS, then computes the empirical alpha-quantile as the critical value (Dette & Schumann, 2024, Theorem 1). Assumes spherical errors. More powerful than IU for *T* > 1.
- **Wild bootstrap** (`method="wild"`): Replaces i.i.d. bootstrap errors with Rademacher-weighted residuals, making the test robust to heteroskedasticity and serial correlation (Dette & Schumann, 2024, Remark 1(c)). Recommended for non-spherical errors.

### Inference for the mean test

The mean test rejects H0 whenever the absolute sample mean of placebo coefficients falls below the alpha-quantile of the folded normal with mean tau and variance 1'Sigma1/(nT^2) (Dette & Schumann, 2024, Eq. (4.12)).

### Inference for the RMS test

The RMS test uses a self-normalized statistic based on subsampling (Dette & Schumann, 2024, Theorem 2). It rejects H0 whenever beta_RMS^2 < zeta^2 + Q_W(alpha) * V_n, where Q_W(alpha) is the alpha-quantile of the limiting distribution (a functional of Brownian motion) and V_n is computed from subsample estimates (Eq. (4.18)). This test is pivotal and does not require variance estimation.

## RMS test alpha restriction

The RMS test supports only:

$$
\alpha \in \{0.01, 0.025, 0.05, 0.1, 0.2\}
$$

This reflects the implementation based on critical values for the limiting distribution in Dette & Schumann (2024, Theorems 2-3).

## Result objects

Every test call returns a typed result object. The object keeps the fitted panel available for interactive inspection, while `to_dict()` returns a machine-readable payload for scripts, CLI JSON, and manuscript tables.

```python
result = equiv_test(
    type="max", method="IU", data=panel,
    y="Y", id="ID", g="G", period="period",
    pretreatment_period=[4, 5, 6, 7], base_period=7,
)

payload = result.to_dict()
```

### Common keys

| Key                           | Description                                                      |
| :---------------------------- | :--------------------------------------------------------------- |
| `test_type`                 | Test type:`"max"`, `"mean"`, or `"rms"`                    |
| `method`                    | Inference method (max test only)                                 |
| `significance_level`        | Significance level used                                          |
| `equiv_threshold`           | Specified threshold (or`None` if omitted)                      |
| `equiv_threshold_specified` | `True` if threshold was specified                              |
| `base_period`               | Base period for placebo construction                             |
| `num_individuals`           | Number of individuals (panels)                                   |
| `num_periods`               | Number of pre-treatment periods                                  |
| `num_observations`          | Total number of observations                                     |
| `is_panel_balanced`         | `True` if balanced panel                                       |
| `placebo_names`             | Names of placebo coefficient periods                             |
| `minimum_equiv_threshold`   | Smallest threshold at which equivalence holds (if not specified) |
| `reject_null_hypothesis`    | `True` if H₀ rejected (if threshold specified)                |

Type-specific keys for `type="max"`:

| Key                          | Description                                           |
| :--------------------------- | :---------------------------------------------------- |
| `placebo_coefficients`     | Placebo coefficient vector                            |
| `abs_placebo_coefficients` | Absolute values of placebo coefficients               |
| `max_abs_coefficient`      | Maximum absolute placebo coefficient                  |
| `placebo_coefficients_se`  | Standard errors (IU method)                           |
| `minimum_equiv_thresholds` | Per-coefficient minimum thresholds (IU method)        |
| `iu_critical_values`       | Critical values per coefficient (threshold specified) |
| `bootstrap_critical_value` | Bootstrap critical value (boot/wild methods)          |
| `B`                        | Number of bootstrap replications                      |

## Visualization

`plot_equivtest` creates coefficient plots of placebo coefficients with equivalence bounds. Pass a result object directly:

```python
from equitrends import equiv_test, load_dataset, plot_equivtest

panel = load_dataset("MonthlyPanel")
panel = panel.rename(columns={"observ": "ID", "totrob": "Y", "mes": "period"}).copy()
panel["G"] = (panel["distanci"] == 0).astype(int)

result = equiv_test(
    type="max", method="IU", data=panel,
    y="Y", id="ID", g="G", period="period",
    pretreatment_period=[4, 5, 6, 7], base_period=7,
    vcov="cluster", cluster="ID",
)

# Basic plot
ax = plot_equivtest(result)

# Plot with confidence intervals
ax = plot_equivtest(result, ci=True)

# Publication-quality plot
ax = plot_equivtest(
    result,
    ci=True,
    connect=True,
    figsize=(7.2, 4.4),
    title="Pre-trend Equivalence Analysis",
    subtitle="Minimum max-IU equivalence bound: ±0.1472",
)
```

### Plot-ready data extraction

To inspect the plotting payload without rendering a Matplotlib figure:

```python
from equitrends import prepare_equivtest_plot_data, EquivTestPlotData

plot_data = prepare_equivtest_plot_data(result)
assert isinstance(plot_data, EquivTestPlotData)
plot_frame = plot_data.to_frame()
```

Output:

```
   period  relative_time  coefficient  is_base_period
0       4             -3     0.032221           False
1       5             -2     0.008867           False
2       6             -1     0.072947           False
3       7              0     0.000000            True
```

## CLI entry points

The installed console scripts emit JSON to stdout:

| Command           | Description                                |
| :---------------- | :----------------------------------------- |
| `equivtest`     | Unified interface (specify`--type`)      |
| `maxequivtest`  | Maximum test                               |
| `meanequivtest` | Mean test                                  |
| `rmsequivtest`  | RMS test                                   |
| `equivsim`      | Generate simulated panel data (CSV output) |

```bash
# Run maximum test from CSV input
equivtest --type max --data panel.csv --y Y --id ID --g G \
    --period period --pretreatment-period 1 2 3 4 --base-period 4

# Save result to file
equivtest --type max --data panel.csv --y Y --id ID --g G \
    --period period --pretreatment-period 1 2 3 4 --base-period 4 \
    --output result.json

# Check version
equivtest --version

# Generate simulated panel
equivsim --preperiods 4 --rcompat --beta 0 0 0 0 --seed 123 --output sim.csv
```

## Examples

### Simulated panel data

This example demonstrates the package using simulated data with parallel trends satisfied.

```python
from equitrends import equiv_test, sim_panel_data

# Generate simulated panel data (N=100 individuals, T=5 pre-treatment periods)
sim = sim_panel_data(
    N=100,
    tt=5,
    beta=[0.0, 0.0, 0.0, 0.0, 0.0],
    p=1,
    gamma=[1.0],
    het=0,
    phi=0.0,
    sd=1.0,
    burnins=10,
    seed=12345,
)

# Run maximum test with IU method
result = equiv_test(
    type="max",
    method="IU",
    data=sim,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[1, 2, 3, 4, 5],
    base_period=5,
)
print(result)
```

Output:

```
==================================================
Equivalence Tests for Pre-trends in DiD Estimation
==================================================
Type: Intersection Union
Significance level: 0.05
Alternative hypothesis: the maximum placebo effect does not exceed the equivalence threshold.
---
Abs. Estimate  Std. Error  Min. Equiv. Threshold
        0.054     0.41158                 0.5022
        0.139     0.41158                 0.7663
        0.180     0.41158                 0.8309
        0.043     0.41158                 0.4117
---
No. placebo coefficients estimated: 4
Base period: 5

Balanced Panel:
 + No. pre-treatment periods: 5
 + No. individuals: 100
 + Total no. observations: 500
```

### Testing with a pre-specified threshold

```python
from equitrends import equiv_test, load_dataset

panel = load_dataset("MonthlyPanel")
panel = panel.rename(columns={"observ": "ID", "totrob": "Y", "mes": "period"}).copy()
panel["G"] = (panel["distanci"] == 0).astype(int)

# Test whether max violation < 0.2
result = equiv_test(
    type="max",
    method="IU",
    data=panel,
    y="Y",
    id="ID",
    g="G",
    period="period",
    pretreatment_period=[4, 5, 6, 7],
    base_period=7,
    equiv_threshold=0.2,
)
print(result)
```

Output:

```
==================================================
Equivalence Tests for Pre-trends in DiD Estimation
==================================================
Type: Intersection Union
Alternative hypothesis: the maximum placebo effect does not exceed the equivalence threshold of 0.2.
---
Abs. Estimate  Std. Error  Critical Value
        0.032     0.05021         0.11740
        0.009     0.05021         0.11740
        0.073     0.05021         0.11740
Reject H0: TRUE
---
No. placebo coefficients estimated: 3
Base period: 7

Balanced Panel:
 + No. pre-treatment periods: 4
 + No. individuals: 876
 + Total no. observations: 3504
```

**Interpretation**: At α = 0.05, all absolute placebo coefficients fall below the critical value of 0.117, so H₀ (non-equivalence) is rejected. We conclude that the maximum absolute placebo coefficient is less than 0.2, supporting negligible pre-trend violations.

### Bundled dataset

```python
from equitrends import list_datasets, load_dataset

# Inspect available datasets
datasets = list_datasets()
# {'MonthlyPanel': 'Di Tella and Schargrodsky (2004) crime data for empirical examples'}

# Load the bundled panel
panel = load_dataset("MonthlyPanel")
# Shape: (9636, 12)
# Columns: ['observ', 'barrio', 'calle', 'altura', 'institu1', 'institu3',
#            'distanci', 'edpub', 'estserv', 'banco', 'totrob', 'mes']
```

## Citation

If you use this package in your research, please cite both the Python implementation and the methodology paper:

**APA Format:**

> Cai, X., & Xu, W. (2026). *equitrends: Python package for equivalence tests for pre-trends in DiD* (Version 0.1.0) [Computer software]. GitHub. https://github.com/gorgeousfish/equitrends
>
> Dette, H., & Schumann, M. (2024). Testing for Equivalence of Pre-Trends in Difference-in-Differences Estimation. *Journal of Business & Economic Statistics*, 42(4), 1289–1301. https://doi.org/10.1080/07350015.2024.2308121

**BibTeX:**

```bibtex
@software{equitrends2026python,
  title={equitrends: Python package for equivalence tests for pre-trends in DiD},
  author={Cai, Xuanyu and Xu, Wenli},
  year={2026},
  version={0.1.0},
  url={https://github.com/gorgeousfish/equitrends}
}

@article{dette2024testing,
  title={Testing for Equivalence of Pre-Trends in Difference-in-Differences Estimation},
  author={Dette, Holger and Schumann, Martin},
  journal={Journal of Business \& Economic Statistics},
  volume={42},
  number={4},
  pages={1289--1301},
  year={2024},
  publisher={Taylor \& Francis},
  doi={10.1080/07350015.2024.2308121}
}
```

## Authors

**Python Implementation:**

- **Xuanyu Cai**, City University of Macau
  Email: [xuanyuCAI@outlook.com](mailto:xuanyuCAI@outlook.com)
- **Wenli Xu**, City University of Macau
  Email: [wlxu@cityu.edu.mo](mailto:wlxu@cityu.edu.mo)

**Methodology:**

- **Holger Dette**, Department of Mathematics, Ruhr University Bochum
- **Martin Schumann**, School of Business and Economics, Maastricht University

## License

AGPL-3.0 License. See [LICENSE](LICENSE).
