# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Install dependencies
uv sync

# Run tests
pytest tests/

# Run a single test file
pytest tests/test_compare.py

# Run with coverage
pytest --cov=src/acteval tests/

# Build
uv build
```

## Architecture

`acteval` is a Python library for evaluating synthetic activity schedules (daily time-use data) by comparing them to observed data.

### Data Flow

1. **Input**: pandas DataFrames with columns `pid` (person ID), `act` (activity), `start`, `end`, `duration`
2. **`Population`** (`population.py`): Converts DataFrames to numpy arrays once at construction; uses lazy properties for expensive derived quantities (act_enum_key, count_matrix, seq_key). Integer-encodes activities and person IDs for vectorized operations.
3. **Feature extraction** (`features/`): Each function takes a `Population` and returns `dict[str, tuple[ndarray, ndarray]]` — `{label: (values, weights)}`. Weights enable weighted aggregation across persons.
4. **Distance metrics** (`distance/`): `wasserstein.py` uses the POT library for EMD/optimal transport; `scalar.py` provides MAE, MSE, MAPE for scalar values.
5. **Evaluation pipeline** (`evaluate.py`): Organized into "jobs" — tuples of `(feature_fn, size_fn, description_job, distance_job)`. Target features are computed once; multiple synthetic populations are compared against them.
6. **Output**: Nested DataFrames with individual features, feature groups, and domain summaries, with per-synthetic-model statistics.

### Key Modules

- **`evaluate.py`**: Main engine — `evaluate()`, `compare()`, `Evaluator` class, `subsample_and_evaluate()`. The `Evaluator` class caches target features for repeated comparisons.
- **`population.py`**: Core data structure. Pre-computes pid groupings and person boundaries.
- **`features/`**: `structural.py` (sequence properties, feasibility), `frequency.py` (activity densities), `times.py` (start/end times, durations), `transitions.py` (n-gram patterns up to 4-grams), `participation.py` (activity rates), `creativity.py` (novelty detection via hashing).
- **`describe/`**: Aggregate statistics for frequency, timing, and transition patterns — used for reporting alongside distance metrics.
- **`filters.py`**: `filter_novel()` removes sequences already seen in the base population.
- **`ops.py`**: Aggregation operations (`average`, `feature_weight`).

### Public API

```python
from acteval import compare, evaluate, Evaluator

# Single comparison
result = compare(observed_df, synthetic_df, report_stats=True)

# Multiple models
result = compare(observed_df, {"model_a": df_a, "model_b": df_b})

# Reuse target features across comparisons
evaluator = Evaluator(observed_df)
result = evaluator.compare({"model_a": df_a})
```
