mcpField
S
seqpro

seqpro

github.com/ml4glandデータ・データベース14118 回インストール5 週間前に更新
リポジトリ

seqpro について

バイオインフォマティクスのワークフロー向けの Python/Rust 配列処理パッケージです。DNA、RNA、タンパク質のエンコード、変換、統計、ゲノム区間の入出力に対応し、可変長バッチには Rust ネイティブの Ragged コンテナを使えます。

主な機能

  1. 01配列のエンコード、デコード、パディング
  2. 02逆相補、k-mer シャッフル、ジッター
  3. 03GC 含量と塩基組成の計算
  4. 04可変長に対応する Rust ネイティブ Ragged 配列
  5. 05BED と GTF 区間の読み書き

活用シーン

  1. 01配列を one-hot 配列や整数トークンに変換
  2. 02DNA、RNA、タンパク質配列を拡張
  3. 03長さの異なる配列バッチを処理
  4. 04BED や GTF のゲノム区間を読み書き

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ml4gland/seqpro --skill seqpro

インストール後、Claude Code で/seqproを入力して読み込まれたか確認します。

詳細

seqpro

生物学的配列を高速に処理する Python/Rust パッケージです。ホットループには Python+NumPy+Numba、グラフアルゴリズムの処理には小規模な Rust 拡張機能(src/kshuffle.rs)、可変長のバッチには Rust ネイティブの Ragged 配列(_core.Ragged)を使用します。import seqpro as sp でインポートします。

使用する場面

  • DNA、RNA、タンパク質配列のエンコード/デコード(OHE、整数トークン、パディング)。
  • 配列の拡張:逆相補鎖、k-mer シャッフル、ジッター、ランダムな抽出。
  • 配列の統計量:GC 含量、ヌクレオチド組成、長さ。
  • 可変長バッチ(例:サイズの異なるピークや転写産物)→ sp.Ragged。
  • ゲノム区間の I/O:sp.bed、sp.gtf。

規約(作業メモリに読み込んでください)

  • 公開 API:エクスポートされるシンボルの全一覧は python/seqpro/__init__.py を参照してください。シンボルが存在すると決めつける前に、読み直してください。
  • 入力型(python/seqpro/_utils.py の SeqType):str、bytes、ネストされた str のリスト、または dtype が str_/object_/bytes_/uint8 の ndarray です。sp.cast_seqs(...) は文字列形式の入力を |S1 のバイト配列に正規化します。uint8(OHE)は変更されません。
  • 標準のメモリ内 dtype:文字列配列には |S1、one-hot 配列には uint8 を使用します。
  • 軸引数は必須で、明示的に指定します:ほとんどの関数では length_axis、また(OHE の場合は)ohe_axis を位置引数またはキーワード引数の整数として指定します。負のインデックスも使用できます。check_axes() は検証して早期にエラーを送出します。エラーを捕捉して握りつぶさないでください。
  • ライブラリコードで配列ごとに Python ループを使用しないでください。 ホットパスでは NumPy、_numba.py の Numba カーネル、または Rust 拡張機能を使用します。残基を for ループで処理しようとしている場合は、まず既存のベクトル化された処理または Numba カーネルを探してください。
  • アルファベットはシングルトンです:sp.DNA、sp.RNA、sp.AA。カスタムのアルファベットは sp.NucleotideAlphabet/sp.AminoAlphabet(python/seqpro/alphabets/_alphabets.py)で作成します。
  • AminoAlphabet.translate(seqs, ..., validate=False, unknown="X"):ヌクレオチドをアミノ酸に変換します。大文字と小文字を区別しません。小文字/ソフトマスクされた acgt は常に変換されます。unknown= は非標準コドン({A,C,G,T} 以外のすべて)の処理方法を指定します。1 文字(デフォルトは "X")を指定すると、不正なコドンごとに 1 つのマーカーを埋め込みます。リテラルの "drop" を指定すると、不正なコドンを削除して Ragged を返します(長さが異なるため、密な入力の場合も同様です)。validate=True は唯一の高速失敗経路です。N/IUPAC/one-hot 以外の入力がある場合、大文字と小文字を区別せずにエラーを送出し、正常に戻った場合は正確な変換を保証します。別個の error モードはありません。
  • 変換処理(python/seqpro/transforms/)は関数型の処理を呼び出し可能なオブジェクトとしてラップします。データパイプラインでは、インラインのラムダ式ではなくこれらを使用してください。

クイックリファレンス

タスク呼び出しメモ
入力を正規化sp.cast_seqs(x)→ `
one-hot エンコードsp.ohe(x, alphabet, length_axis=-1)OHE 次元の軸を末尾に追加
OHE をデコードsp.decode_ohe(x, alphabet, ohe_axis=-1)
トークン化/デトークン化sp.tokenize / sp.decode_tokens整数 ID。parallel=True/False は並列カーネルを有効/無効にします(デフォルトの None ではサイズに基づいて判定)。
パディングsp.pad_seqs(x, pad_val, length=...)
逆相補鎖sp.reverse_complement(x, alphabet, length_axis=-1)str/bytes/OHE に対応
k-mer シャッフルsp.k_shuffle(x, k, length_axis=-1, seed=...)Rust の _k_shuffle を呼び出します
ジッターsp.jitter(x, max_jitter, length_axis=-1)
ランダム配列sp.random_seqs(shape, alphabet, seed=...)
GC 含量sp.gc_content(x, length_axis=-1)
ヌクレオチド含量sp.nucleotide_content(x, alphabet, length_axis=-1)
カバレッジのビニングsp.bin_coverage(arr, bin_width, length_axis)
BED/GTF の I/Osp.bed.read_bedlike(...)、sp.gtf.read_gtf(...)polars/pyranges を利用
Ragged 文字列のハッシュrag.hash("sha256"|"md5"|"rapidhash")Rust カーネルを呼び出します。シードは rapidhash の場合のみ指定します

シグネチャとキーワード引数を正確に確認するには、docstring を直接参照してください(REPL では sp.<fn>? を実行するか、ソースを開いてください。各ファイルは短めです)。

Ragged — 可変長シーケンスのバッチ

sp.Ragged(python/seqpro/rag/_core.py を基盤としています)は、シーケンスの長さが異なるバッチの標準コンテナーです。Rust ネイティブのクラスであり、NDArrayOperatorsMixin を実装しています(ak.Array のサブクラスではありません)。**不規則な次元が 1 つ、または隣接する 2 つ(ネストされた場合。例:(batch, ploidy, None, None))**を持ち、基盤となるフラットな NumPy バッファーとオフセットにゼロコピーでアクセスできます。

概要

Ragged は次の 3 つの要素で構成されます。

  • data:形状 (total_elements, *fixed_trailing_dims) の、フラットで連続した NDArray です。rag.data からゼロコピーでアクセスできます。
  • offsets:int64 配列です。形状は (N+1,)(連続している一般的なケース)、または (2, N)(一部のスライス後の開始位置/終了位置)です。rag.offsets からアクセスできます。ネストされた配列では、不規則な軸ごとに 1 つずつ、外側の軸から順に並びます。
  • shape:(batch, None, ohe_dim) のようなタプルです。要素の 1 つが None の場合はそこが不規則な軸で、ネストされた配列では隣接する 2 つの要素が該当します。最初の不規則な軸のインデックスは rag.rag_dim で取得できます。

rag.lengths はオフセットからセグメントの長さを導出します(低コストで、ndarray を返します)。

構築
import numpy as np, seqpro as sp

# From lengths (most common — you have a flat numeric data buffer and per-segment lengths)
data = np.frombuffer(b"ACGTACGTACG", dtype=np.uint8)  # uint8 for char-as-number
lengths = np.array([4, 3, 4])
rag = sp.rag.Ragged.from_lengths(data, lengths)   # shape (3, None)

# From explicit offsets (also accepts S1 char arrays when shape has a None)
offsets = np.array([0, 4, 7, 11], dtype=np.int64)
char_data = np.frombuffer(b"ACGTACGTACG", dtype="S1")
rag = sp.rag.Ragged.from_offsets(char_data, shape=(3, None), offsets=offsets)  # shape (3, None)

# Empty with known shape
rag = sp.rag.Ragged.empty((10, None, 4), dtype=np.uint8)   # batch of 10 OHE seqs

Ragged.empty(shape, dtype) では、shape に None がちょうど 1 つ必要です。末尾の固定次元(例:OHE 軸)は None の後に指定します。

Ragged の使い方 — こうする、これは避ける
タスクこうするこれは避ける
フラットデータに対する一括数値演算rag.data[:] = ... または rag.data.view(...) — ゼロコピーですfor seq in rag: で反復する
np.ufunc を適用するそのまま呼び出します:np.exp(rag) — __array_ufunc__(NDArrayOperatorsMixin)経由でディスパッチされ、Ragged を返します手動でアンパックして再構築する
最上位の行数を数えるlen(rag) — shape[0] を返します(shape[0] が可変長軸の場合は例外が発生します)rag.shape[0] を手動で整数にキャストする
不透明文字列の Ragged のグループを1つインデックス指定するrag[i] → 文字列ごとに1つの bytes を持つ Ragged(len(rag[i]) == rag.lengths[i]);rag[i][j] は1つの bytes です連結された1つの bytes を期待する — これは0.22より前の動作で、文字列ごとの境界が暗黙に失われていました
先頭の軸が固定されたネスト配列(例:(b, p, ~v, ~w))をインデックス指定する固定軸にNumPyスタイルのキーを使います:rag[i] → (p, ~v, ~w)、rag[:, j] → (b, ~v, ~w);rag[i, j] == rag[i][j] で (n_v, ~w) まで掘り下げます行の倍数を見つけるために rag.offsets を手動で読む
可変長軸または後続軸をインデックス指定するNumPy/awkwardスタイルのタプルキーを使い、それぞれが次の出力軸に適用されます:rag[:, 0] → 各行の先頭要素(密配列;行が短すぎる場合は IndexError)、rag[:, 1:]/rag[:, -2:] → 行ごとのスライス(ゼロコピー)、(b, ~, K) に対する rag[:, :, k] → 後続列。ネスト配列でも動作します((b, ~v, ~w) に対する rag[:, :, 0] → 各ウィンドウの先頭)。行ごとにループする;ステップ付きスライス、可変長軸での高度なインデックス/マスクキー(2つの可変長軸のうち最初の軸を除く)、レコードに対する後続キーで NotImplementedError が発生することを期待する
先頭にサイズ1の軸を挿入するrag[np.newaxis] — 形状 (1, *old_shape) の Ragged を返しますfrom_offsets で手動再構築する
バイト/dtypeを再解釈するrag.view(np.uint8)np.asarray(rag).view(...)(可変長構造が失われます)
可変長でない軸を形状変更するrag.reshape(batch, None, k, 4)rag.data.shape を直接操作する
サイズ1の軸を削除するrag.squeeze(axis)(1次元に縮約される場合は ndarray を返します)
NumPyの密配列に変換するrag.to_numpy()(allow_missing に応じてパディングまたは例外)ループしてスタックする
連続バッファーにパックするrag.to_packed() または sp.rag.to_packed(rag) — Numbaで並列化されており、np.memmap でも安全です;すでにパック済みの場合は copy=False でゼロコピーのまま渡せますak.to_packed(rag)
密配列に変換し、右側を固定長までパディングするsp.rag.to_padded(rag, pad_value, *, length=None) — フラットバッファー用Numbaカーネルです;length=None はバッチ内の最大長までパディングし、明示的な length はパディングまたは切り詰めを行います;可変長軸が最後にある配列のみ、レコードは不可です手動スライスを使う rag.to_numpy()、または ak_str.rpad(約3倍遅く、awkwardの経路では余分な中間データが割り当てられます)
可変長軸に沿って連結するsp.rag.concatenate(rags, axis) — 可変長軸に沿って Ragged 配列のリストを連結します(axis は None 次元である必要があり、負の値も使用できます);Rust/rayonカーネルによるオフセット演算とバッファー経由のコピーを行います;数値dtype(int32、float32、…)に対応しますak.concatenate(rags, axis=…)
通常のawkward配列に変換するrag.to_ak()
レコードレイアウトの Ragged(複数フィールド)

同じオフセットオブジェクトを共有する単一フィールドの Ragged を辞書にまとめ、sp.rag.zip(または同等の Ragged.from_fields)を呼び出して構築します。結果はレコードレイアウトを持つ Ragged です。

import numpy as np, seqpro as sp
from seqpro.rag._utils import lengths_to_offsets

lengths = np.array([4, 3])
shared_offsets = lengths_to_offsets(lengths)

seq_rag   = sp.rag.Ragged.from_offsets(seq_flat,   shape=(2, None), offsets=shared_offsets)  # |S1
score_rag = sp.rag.Ragged.from_offsets(score_flat, shape=(2, None), offsets=shared_offsets)  # f4

batch = sp.rag.zip({"seq": seq_rag, "score": score_rag})   # → Ragged (record layout)
# equivalently: batch = sp.rag.Ragged.from_fields({"seq": seq_rag, "score": score_rag})
assert isinstance(batch, sp.rag.Ragged)

batch["score"].data[:] *= 2.0   # zero-copy mutation of the flat score buffer

入力は同じオフセットオブジェクトを共有する必要があります(各 from_offsets 呼び出しに同じ shared_offsets 配列を渡してください)。これにより、結果が単一の Ragged 次元を持つレコードになります。独立して構築した from_lengths の結果を渡すと、長さが同じでもオフセットオブジェクトが異なるため、ValueError が発生します。

  • rag.dtype は NumPy の構造化 dtype(例:[("seq","S1"),("score","f4")])を返します。これは記述子にすぎず、メモリ上の配置は AoS ではなく SoA です。
  • rag.data は単一の配列ではなく、フィールド名をキーとする辞書を返します。インデックスを指定する前に、必ず型を確認してください。
  • rag["field"] はゼロコピーで単一フィールドにアクセスでき、親のオフセットオブジェクトを共有します。その .data は、そのフィールドのフラットな NumPy バッファーです。
  • レコードレイアウトでの rag.to_numpy() は、辞書 {field: dense ndarray} を返します(フィールドがジャグ配列のままの場合は例外が発生します。密な変換には長さが一様である必要があります)。
  • レコードレイアウトでは view と apply は定義されていません。フィールドごとに操作してください。
  • batch.to_ak() は ak.zip のデフォルトと同様に、レコードを末端に配置します。(2, None) → 2 * var * {seq, score} です。0.23 より前は、先行軸が 1 つのレコードは、代わりに各行で結合されていました(depth_limit=1)。
  • 行を取り出す(整数 i を使った rag[i])と、各エントリがすべて同じ長さの辞書が返されます。数値/文字フィールドは ndarray、不透明文字列フィールドは bytes の Ragged です。これにより、zip(row["start"], row["alt"]) が正しく機能します。
文字列のハッシュ化

並列 Rust カーネルを使って、Ragged 内の各文字列(不透明文字列または S1 文字の末端、任意の深さ)をハッシュ化します。

digests = rag.hash("sha256")        # (N, 32) uint8, one digest per string
md5s    = rag.hash("md5")           # (N, 16) uint8
fast    = rag.hash("rapidhash")     # (N,) uint64
seeded  = rag.hash("rapidhash", seed=42)   # seed valid for rapidhash only
# equivalently: sp.rag.hash(rag, "sha256")

出力は文字列レベルより上の構造を維持します。文字列がグループ化されていない場合(フラットな (N, …)/先行固定次元)は通常の NumPy 配列になり、グループ化されている場合は外側のオフセットを再利用した Ragged になります(md5/sha256 では (G, None, 16/32)、rapidhash では (G, None) の uint64)。数値型およびレコードレイアウトの Ragged は拒否されます。

NumPy との相互運用 — 信頼できること

_core.Ragged は、awkward への依存なしに NDArrayOperatorsMixin と __array_ufunc__ を直接実装しています。

  • 非レコードの Ragged に対する NumPy ufunc(np.add、np.exp など)は Ragged を返します。レコードレイアウトでは NotImplementedError が発生するため、個々のフィールドに対して操作してください。
  • rag.to_packed()/sp.rag.to_packed(rag) は、連続したゼロ起点のバッファーを実体化する標準的な方法です。Numba で並列化されており、np.memmap でも安全に使えます。配列がすでにパック済みの場合にゼロコピーでそのまま返すには copy=False を使います(パック済みでない場合は ValueError が発生します)。
  • _core.Ragged で awkward(ak.*)API を使わないでください。バックエンドは ak.behavior を登録しなくなりました。awkward との相互運用が必要な場合は rag.to_ak() で ak.Array を取得できますが、ネイティブ API の使用を推奨します。

迷った場合は python/seqpro/rag/_core.py を確認してください。これは現在のバックエンドであり、真実の情報源はその docstring です。同じディレクトリ内の _layout.py、_ops.py、_utils.py には、関連する内部実装が含まれています。

よくある落とし穴
  • スライス後にオフセットのレイアウトが変わります。 rag.offsets が (N+1,) ではなく (2, N) の開始位置/終了位置になることがあります。(N+1,) を前提とするコードを実行する前に、rag.is_contiguous を確認するか、rag.to_packed() を呼び出してください。
  • レコードレイアウトでの rag.data は辞書です。 rag.data.shape のようなコードは失敗します。isinstance(rag.data, dict) で分岐するか、rag.parts を使って確認してください。
  • Ragged の shape には None が 1 つ、または隣接する None が 2 つ(ネスト型)必要です。 ジャグ配列の構造が一致しないデータから構築すると、__init__ で例外が発生します。判断に迷った場合は from_lengths/from_offsets を使ってください。
  • Rust の k-shuffle は、最後の軸が配列長である連続した uint8 を想定しています。 sp.k_shuffle がこの処理を行います。seqpro._k_shuffle を直接呼び出す場合は、レイアウトが適切であることを確認してください。

参照先(暗記せず、ソースを読んでください)

確認したい内容ファイル
公開インターフェースpython/seqpro/__init__.py
入力のキャスト/軸ヘルパーpython/seqpro/_utils.py
OHE/トークン/パディングpython/seqpro/_encoders.py
拡張python/seqpro/_modifiers.py
統計python/seqpro/_analyzers.py
アルファベットpython/seqpro/alphabets/_alphabets.py
可変長データpython/seqpro/rag/_core.py
変換(パイプラインオブジェクト)python/seqpro/transforms/
BED/GTFpython/seqpro/bed.py, gtf.py
Rust の k-shufflesrc/kshuffle.rs
使用例としてのテストtests/
レンダリング済みドキュメントsite/(docs/からビルド)

やってはいけないこと

  • ライブラリコードで、残基や位置を対象に Python の for ループを書かないでください。ベクトル化された演算を探すか、_numba.py の Numba カーネルを使うか、既存のカーネルを拡張してください。
  • 軸を決めつけないでください。必ず length_axis を(該当する場合は ohe_axis も)明示的に渡してください。
  • ユーザーコードから Ragged の内部(_layout、_rl、__init__ の近道)にアクセスしないでください。data、offsets、fields、from_lengths、from_offsets、from_fields、empty を使ってください。
  • Ragged に文字列を追加しないでください。ASCII バイト(|S1)のみを使ってください。
  • このスキルを更新せずに機能を追加したり、公開シグネチャを変更したりしないでください。CLAUDE.md を参照してください。

コメント