Metadata-Version: 2.4
Name: ibdf-parser
Version: 0.1.0b0
Summary: Python reader for IBDF v3 genomic pair files
Requires-Python: >3.11.0
Description-Content-Type: text/markdown

# ibdf-parser

`ibdf-parser` is a Python package for reading the Identity-by-Descent Binary Format (IBDF v3).

It reconstructs genomic identity-by-descent (IBD) segment sharing spans from delta and checkpoint blocks on the fly, supports fast bounded-replay random seeking, and automatically resolves numeric sample IDs back to their string names using companion `.samples` files.

---

## Installation

This package is managed using `uv`. To install it in editable development mode in your virtual environment:

```bash
uv pip install -e .
```

To run the test suite:

```bash
uv run pytest
```

---

## Core Features

- **Drop-in standard file-like interface** (`ibdf.open`): Behaves like Python's built-in `gzip.open` and standard file handlers. You can read lines, use it in `for` loops, or read byte chunks.
- **Direct structural parsing** (`ibdf.IbdfFile`): Allows users to iterate directly over Python dataclasses or string TSV encoding/decoding overhead for versatility.
- **Genomic random access** (`seek_position` / `seek_index`): Allows binary-searching the index to seek directly to a target chromosome position or block index by replaying from the nearest prior checkpoint.
- **Breakpoint-by-breakpoint iteration** (`iter_breakpoints`): Streams the chromosome position by position, yielding the active sharing segments at each physical breakpoint.

---

## API Reference

### `ibdf.open(filename, mode='rt', sample_file=None)`

Opens an IBDF file and returns an `IbdfFile` instance configured in either file-like streaming mode or structured segment parsing mode.

- **`filename`** (str | Path): Path to the `.ibdf` binary file.
- **`mode`** (str): Open mode. Either `'r'` / `'rt'` (text mode, default), `'rb'` (binary mode), or `'segments'` (to iterate directly over `IbdSegment` NamedTuple objects).
- **`sample_file`** (str | Path | None): Optional path to a plain-text companion `.samples` file. If omitted, the reader will look for a companion file in the same directory (e.g., `data.samples` for `data.ibdf`). If found, it translates indices to sample names; otherwise, it defaults to using stringified numeric IDs.

### `ibdf.IbdSegment`

A read-only dataclass representing a reconstructed IBD segment:

- **`sample1`** (str | int): Name or ID of the first sample.
- **`sample2`** (str | int): Name or ID of the second sample (always guaranteed that `sample1 < sample2` lexicographically or numerically).
- **`start_bp`** (int): Genomic start position (base-pairs) of segment sharing.
- **`end_bp`** (int): Genomic end position (base-pairs) of segment sharing.
- **`cm`** (float): Genetic length of the segment in centiMorgans.

### Properties & Methods on the Opened Handle (`IbdfFile`)

- **`read(size=-1)`**: Read at most `size` characters/bytes.
- **`readline()`**: Read a single line.
- **`seek(0)`**: Reset the reader to the beginning of the file.
- **`seek_position(bp_pos)`**: Seek to the block at or immediately before the base-pair position `bp_pos`.
- **`seek_index(index)`**: Seek to the block at the given position index `index`.
- **`positions`** (property: `list[int]`): List of all genomic breakpoint positions stored in the file.
- **`current_position`** (property: `int | None`): The base-pair position of the next block to be parsed.
- **`iter_breakpoints()`**: Yield `(bp_pos, active_segments)` sequentially for every breakpoint.

---

## Usage Examples

### 1. Drop-In Flat TSV Reading (Text Mode)

Iterating over the opened file handle yields lines formatted as standard tab-separated values:
`{sample1}\t{sample2}\t{start_bp}\t{end_bp}\t{cm:.6f}\n`

```python
import ibdf

# Automatically finds 'data.samples' in the same folder if present
with ibdf.open("data.ibdf", "r") as f:
    for line in f:
        sample1, sample2, start, end, cm = line.strip().split("\t")
        print(f"{sample1} shares IBD with {sample2} from {start} to {end} ({cm} cM)")
```

### 2. High-Performance Direct Dataclass Parsing

Bypass formatting strings to TSV and parsing them back by iterating directly over `IbdSegment` dataclasses.

```python
import ibdf

# Open the parser directly
parser = ibdf.IbdfFile("data.ibdf")

# (Optional) Load sample names manually if they are not in a standard companion path
parser.sample_names = ["S0", "S1", "S2", "S3"]

# Yields structured IbdSegment objects
for segment in parser.iter_segments():
    print(segment.sample1, segment.sample2, segment.start_bp, segment.end_bp, segment.cm)

parser.close()
```

### 3. Bounded-Replay Genomic Seeking

Seek to any location on the chromosome without parsing the entire file from the beginning.

```python
import ibdf

with ibdf.open("data.ibdf", "r") as f:
    # Seek directly to breakpoint at/before 12,500,000 base-pairs
    # This automatically loads the nearest checkpoint and replays deltas
    f.seek_position(12500000)
    
    print(f"Parser position: {f.current_position}")
    
    # Continue reading from the target position
    for line in f:
        print(line.strip())
```

### 4. Step Breakpoint-by-Breakpoint

Inspect the active sharing pairs at each physical breakpoint.

```python
import ibdf

with ibdf.open("data.ibdf", "r") as f:
    for bp_pos, active_segments in f.iter_breakpoints():
        # bp_pos is the current breakpoint genomic coordinate
        # active_segments is a list of IbdSegment objects
        print(f"At breakpoint {bp_pos}, there are {len(active_segments)} active segments:")
        for segment in active_segments:
            print(f"  - Pair {segment.sample1} & {segment.sample2} started sharing at {segment.start_bp} ({segment.cm:.2f} cM)")
```
