# retrosheetpy: Complete LLM Reference & Developer Guide

> Fast, modular, and strongly-typed Retrosheet baseball data downloader, parser, and database ingestor for Python (3.11+).

---

## 1. Overview & Architecture

`retrosheetpy` is designed for sabermetricians, researchers, database administrators, and data engineers. It replaces legacy ad-hoc shell scripts and brittle scraping pipelines with a decoupled, high-performance architecture:

```
[ retrosheet.org ]
        │  (HTTP / ZIP / TXT / CSV)
        ▼
[ extract/ (Downloader & CacheManager) ]  <──>  ~/.cache/retrosheetpy/
        │  (Raw .EVA, .EVN, .ROS, gl*.txt, parkcode.txt, biofile0.csv, tran.txt)
        ▼
[ parse/ (EventParser, GameLogParser, ScheduleParser, BioParser, etc.) ]
        │  (Zero-copy Type Casting against schemas/)
        ▼
[ schemas/ (PyArrow Schemas: Events, Game Logs, Rosters, Parks, Schedules, Bio) ]
        │  (PyArrow Tables & RecordBatches)
        ▼
[ sink/ ]
   ├── dataframe.py  ──> PyArrow Table, Polars DataFrame, pandas DataFrame
   ├── duckdb_sink.py──> Embedded DuckDB tables (.duckdb)
   ├── file_sink.py  ──> Parquet, CSV
   └── dlt_sink.py   ──> dlt pipelines (Postgres, Snowflake, BigQuery)
```

### Key Design Principles:
1. **Decoupled:** Download, parsing, schema definitions, and database sinking operate independently.
2. **Strict Physical Types:** Every field is cast into strongly-typed Apache Arrow types (`date32`, `int8`, `int16`, `int32`, `bool_`, `string`). No loose text columns.
3. **Pre-Existing Table Alignment:** When ingesting into DuckDB or remote SQL warehouses, existing tables are respected. Matching columns are appended; missing columns with database defaults are left untouched.
4. **Lightweight Core:** Core installation has zero heavy cloud dependencies. Warehouse ingestion is available via the optional extra: `pip install "retrosheetpy[dlt]"`.

---

## 2. Installation

```bash
# Core library (DuckDB, PyArrow, Polars, pandas, Parquet)
pip install retrosheetpy

# With cloud warehouse connectors (PostgreSQL, Snowflake, BigQuery, Databricks)
pip install "retrosheetpy[dlt]"
```

---

## 3. Command-Line Interface (CLI)

The CLI uses concise, standard options:
- `-s, --dataset`: Dataset name or comma-separated list
- `-y, --years`: Year or range of years (e.g. `2024` or `2020-2024`)
- `-d, --dest`: Destination database (`duckdb`, `postgres`, `snowflake`, `bigquery`, `sqlite`)
- `-c, -f, --conn`: Connection string or output file path
- `-fmt, -f, --format`: Output file format (`parquet`, `csv`)
- `-o, --output`: Output file path
- `--force`: Force re-download even if already cached

### CLI Commands:
```bash
# 1. Download & cache archives locally without parsing
retrosheetpy download -s all -y 2024
retrosheetpy download -s schedules,bio,ejections,transactions

# 2. Ingest into DuckDB (embedded)
retrosheetpy ingest -d duckdb -c retrosheet.duckdb -s events,gamelogs -y 2020-2024

# 3. Ingest into Cloud Warehouses via dlt
retrosheetpy ingest -d postgres -c "postgresql://user:pass@localhost:5432/retrosheet" -s all -y 2024

# 4. Export to Parquet or CSV
retrosheetpy export -s gamelogs -y 2024 -f parquet -o gl2024.parquet
retrosheetpy export -s bio -f csv -o biofile.csv

# 5. Generate production SQL DDL (CREATE TABLE)
retrosheetpy ddl -s events --dialect postgres
retrosheetpy ddl -s schedules --dialect bigquery
retrosheetpy ddl -s bio --dialect snowflake

# 6. Cache inspection & cleanup
retrosheetpy cache
retrosheetpy cache --clear
```

---

## 4. Python API Reference

### Top-Level Functions (`import retrosheetpy as rs`)

#### `rs.events(year, backend="arrow", force=False)`
Parses regular season play-by-play events (164 columns) for specified year(s). Backends: `"arrow"`, `"polars"`, `"pandas"`.

#### `rs.postseason(year, backend="arrow", force=False)`
Parses Postseason play-by-play events (World Series, LCS, LDS, Wild Card) using the same 164-column schema.

#### `rs.allstar(year, backend="arrow", force=False)`
Parses All-Star Game play-by-play events using the 164-column schema.

#### `rs.game_logs(year, backend="arrow", force=False)`
Parses official game logs (161 columns) for specified year(s).

#### `rs.schedules(year, backend="arrow", force=False)`
Parses regular season game schedules (13 columns: date, game number, teams, leagues, day/night, postponement, makeup date).

#### `rs.rosters(year, backend="arrow", force=False)`
Parses team roster files (8 columns: player ID, name, bats, throws, team, position, year).

#### `rs.parks(backend="arrow", force=False)`
Parses the ballpark encyclopedia (`parkcode.txt`).

#### `rs.bio(backend="arrow", force=False)`
Parses master biographical database (32 columns: birth/death dates, locations, debut and last game dates, height, weight, HOF).

#### `rs.ejections(backend="arrow", force=False)`
Parses official ejections log (11 columns: game ID, date, ejectee, team, job, umpire, inning, reason).

#### `rs.transactions(backend="arrow", force=False)`
Parses player transactions database (16 columns: dates, transaction ID, player ID, type, trading teams, draft round, pick).

#### `rs.to_duckdb(path, years=2024, datasets=(...), force=False)`
Directly ingests selected datasets into a DuckDB file.

#### `rs.to_parquet(path, dataset, years=2024, force=False)`
Exports a dataset into a snappy-compressed Parquet file.

#### `rs.to_dlt(destination, years=2024, datasets=(...), credentials=None)`
Runs a dlt pipeline to ingest datasets into PostgreSQL, Snowflake, BigQuery, etc.

#### `rs.get_ddl(dataset, dialect="postgres")`
Returns SQL DDL statement for target dialect (`postgres`, `duckdb`, `sqlite`, `snowflake`, `bigquery`).

#### `rs.download(years=None, datasets="all", force=False)`
Downloads raw zip/text files into local cache without reading them into memory.

---

## 5. Dataset Schema Summary

1. **`events` / `postseason` / `allstar` (`EVENT_SCHEMA` - 164 cols):**
   - Game & State: `GAME_ID` (string), `AWAY_TEAM_ID` (string), `INN_CT` (int16), `BAT_HOME_ID` (int8), `OUTS_CT` (int8), `BALLS_CT` (int8), `STRIKES_CT` (int8), `PITCH_SEQ_TX` (string).
   - Participants: `AWAY_SCORE_CT` (int16), `HOME_SCORE_CT` (int16), `BAT_ID` (string), `BAT_HAND_CD` (string), `RESP_BAT_ID` (string), `PIT_ID` (string), `PIT_HAND_CD` (string).
   - Event Details: `EVENT_TX` (string), `LEADOFF_FL` (bool), `PH_FL` (bool), `BAT_FLD_CD` (int8), `BAT_LINEUP_ID` (int8), `EVENT_CD` (int16), `BAT_DEST_ID` (int8), `RUN1_DEST_ID` (int8), `RUN2_DEST_ID` (int8), `RUN3_DEST_ID` (int8).

2. **`gamelogs` (`GAMELOG_SCHEMA` - 161 cols):**
   - Core: `date` (date32), `game_number` (int8), `day_of_week` (string), `visiting_team` (string), `visiting_score` (int16), `home_team` (string), `home_score` (int16), `outs_total` (int16), `day_night` (string), `park_id` (string), `attendance` (int32).
   - Line Scores & Stats: Team batting/pitching aggregates, umpires, managers, winning/losing/saving pitchers, starting lineups (positions 1-9 for both home and away).

3. **`schedules` (`SCHEDULE_SCHEMA` - 13 cols):**
   - `date` (date32), `game_num` (int8), `day_of_week` (string), `vis_team` (string), `vis_league` (string), `vis_game_num` (int16), `home_team` (string), `home_league` (string), `home_game_num` (int16), `day_night` (string), `park_id` (string), `postponed` (string), `makeup_date` (date32).

4. **`bio` (`BIO_SCHEMA` - 32 cols):**
   - `player_id` (string), `last_name` (string), `use_name` (string), `full_name` (string), `birth_date` (date32), `birth_city` (string), `birth_state` (string), `birth_country` (string), `death_date` (date32), `death_city` (string), `death_state` (string), `death_country` (string), `cemetery` (string), `debut_player` (date32), `last_player` (date32), `debut_coach` (date32), `last_coach` (date32), `debut_manager` (date32), `last_manager` (date32), `debut_umpire` (date32), `last_umpire` (date32), `bats` (string), `throws` (string), `height_inches` (int16), `weight_lbs` (int16), `hof` (string).

5. **`ejections` (`EJECTION_SCHEMA` - 11 cols):**
   - `game_id` (string), `date` (date32), `dh` (string), `ejectee_id` (string), `ejectee_name` (string), `team_id` (string), `job` (string), `umpire_id` (string), `umpire_name` (string), `inning` (int8), `reason` (string).

6. **`transactions` (`TRANSACTION_SCHEMA` - 16 cols):**
   - `primary_date` (date32), `time` (string), `approx_indicator` (string), `secondary_date` (date32), `approx_indicator_sec` (string), `transaction_id` (string), `player_id` (string), `type` (string), `from_team` (string), `from_league` (string), `to_team` (string), `to_league` (string), `draft_type` (string), `draft_round` (int16), `pick_number` (int16), `info` (string).

---

## 6. Sabermetric Code Snippet (RE24 in Polars)

```python
import polars as pl
import retrosheetpy as rs

# Load 2024 events into Polars
df = rs.events(2024, backend="polars")

# Define base state (0: empty, 1: occupied)
df = df.with_columns(
    (
        pl.when(pl.col("BASE1_RUN_ID").is_not_null()).then(1).otherwise(0)
        + pl.when(pl.col("BASE2_RUN_ID").is_not_null()).then(2).otherwise(0)
        + pl.when(pl.col("BASE3_RUN_ID").is_not_null()).then(4).otherwise(0)
    ).alias("base_state")
)

# 24-State Base/Out Matrix
re24 = (
    df.group_by(["OUTS_CT", "base_state"])
    .agg([
        pl.len().alias("occurrences"),
        pl.col("EVENT_RUNS_CT").mean().alias("run_expectancy"),
    ])
    .sort(["OUTS_CT", "base_state"])
)
print(re24)
```
