Skip to content

Bin Query

Binned multi-track query primitive. Walks fixed-size bins across chromosomes and queries signal_at() on each track per bin.

This is the shared primitive underneath annotate_chromatin() and bin_summarize. It produces raw per-bin signal values without classification or merging.

BinResult dataclass

BinResult(chrom: str, start: int, end: int, signal: float)

One bin's signal from a single track.

signal is computed by signal_at() before the object is yielded — it is never a lazy pointer to an outer-loop variable.

Parameters:

Name Type Description Default
chrom str

Chromosome name.

required
start int

Bin start (0-based, inclusive).

required
end int

Bin end (0-based, exclusive).

required
signal float

Pre-computed scalar signal for this bin.

required

Examples:

>>> BinResult("chr1", 0, 200, 3.14)
BinResult(chrom='chr1', start=0, end=200, signal=3.14)

bin_query

bin_query(track: 'Track', chrom_sizes: dict[str, int], bin_size: int) -> Iterator[BinResult]

Walk bins across chromosomes, querying a single track.

signal_at(chrom, start, end) is called before each yield — the float is computed inline during the sweep. No closures. No deferred evaluation. No lazy iterators in fields.

For multi-track queries, use bin_positions() and call signal_at() per track in the consuming code.

Parameters:

Name Type Description Default
track 'Track'

Track to query (must implement signal_at).

required
chrom_sizes dict[str, int]

Chromosome name to length mapping.

required
bin_size int

Bin size in base pairs.

required

Yields:

Type Description
BinResult

BinResult for each bin with signal already computed.

Raises:

Type Description
TypeError

If bin_size is not an int.

ValueError

If bin_size is less than 1.

Examples:

>>> for b in bin_query(atac, {"chr1": 500}, 200):
...     print(b.signal)
Source code in src/seqchain/primitives/bin_query.py
def bin_query(
    track: "Track",
    chrom_sizes: dict[str, int],
    bin_size: int,
) -> Iterator[BinResult]:
    """Walk bins across chromosomes, querying a single track.

    ``signal_at(chrom, start, end)`` is called **before** each
    ``yield`` — the float is computed inline during the sweep.
    No closures.  No deferred evaluation.  No lazy iterators in fields.

    For multi-track queries, use ``bin_positions()`` and call
    ``signal_at()`` per track in the consuming code.

    Args:
        track: Track to query (must implement ``signal_at``).
        chrom_sizes: Chromosome name to length mapping.
        bin_size: Bin size in base pairs.

    Yields:
        ``BinResult`` for each bin with ``signal`` already computed.

    Raises:
        TypeError: If *bin_size* is not an int.
        ValueError: If *bin_size* is less than 1.

    Examples:
        >>> for b in bin_query(atac, {"chr1": 500}, 200):
        ...     print(b.signal)  # doctest: +SKIP
    """
    for chrom, start, end in bin_positions(chrom_sizes, bin_size):
        yield BinResult(
            chrom=chrom,
            start=start,
            end=end,
            signal=track.signal_at(chrom, start, end),
        )