mcpField
A
alterlab-jaspar

alterlab-jaspar

github.com/alterlab-ieu数据与数据库7866 次安装5 周前更新
代码仓库

关于 alterlab-jaspar

alterlab-jaspar 是通过 JASPAR REST API 获取整理过的转录因子结合谱的技能。研究人员可用它检索矩阵,开展基序扫描、调控变异评估和富集分析。

主要功能

  1. 01转录因子谱检索
  2. 02PFM 矩阵获取
  3. 03PFM 转 PWM
  4. 04DNA 序列基序扫描
  5. 05双链扫描
  6. 06变异对结合位点影响分析

适用场景

  1. 01扫描启动子中的转录因子结合位点
  2. 02评估 SNP 对基序结合的影响
  3. 03分析 ChIP-seq 或 ATAC-seq 峰的基序富集

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/alterlab-ieu/alterlab-academic-skills --skill alterlab-jaspar

安装后在 Claude Code 中输入/alterlab-jaspar确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
api_reference.md
scripts/
query_jaspar.py

详细介绍

JASPAR 数据库

概述

JASPAR(https://jaspar.elixir.no/)是公认的开放访问数据库,收录经过整理、非冗余的转录因子(TF)结合谱,并以位置频率矩阵(PFM)的形式存储。当前版本 JASPAR 2026 新增了 265 个 CORE 谱,并将 41 个 UNVALIDATED 谱升级为 CORE,因此六个分类群中最新版本的 CORE 谱共计 2,633 个(这是实时 API 返回的数量)。每个谱均来源于实验(ChIP-seq、SELEX、HT-SELEX、蛋白质结合微阵列等)并经过整理。JASPAR 2026 还推出了深度学习(DL)集合,其中包含 1,259 个基于 240 种人类 TF 的 ENCODE ChIP-seq 数据训练的 BPNet 模型;这些模型可在网站上浏览和下载,但不会通过 /api/v1/matrix/ 提供。

关键资源:

脚本

scripts/query_jaspar.py — 查询 JASPAR REST API(仅使用标准库,将 JSON 输出到标准输出):

python scripts/query_jaspar.py search --name CTCF --species 9606   # latest-version profiles only
python scripts/query_jaspar.py search --name CTCF --all-versions   # include historical versions
python scripts/query_jaspar.py matrix MA0139.1                     # fetch a matrix (PFM)

search 默认使用 version=latest(每个谱返回一行);传入 --all-versions 可查看所有历史版本。--species 会映射到 API 的 tax_id 参数(NCBI 分类学 ID)。

何时使用此技能

在以下场景中使用 JASPAR:

  • TF 结合位点预测:扫描 DNA 序列,寻找 TF 的潜在结合位点
  • 调控变异解读:GWAS/eQTL 变异是否会破坏 TF 结合基序?
  • 启动子/增强子分析:哪些 TF 可能结合某个调控元件?
  • 基因调控网络构建:通过基序扫描将 TF 与其靶基因关联起来
  • TF 家族分析:比较某个 TF 家族的结合谱(例如,所有同源异型框因子)
  • ChIP-seq 分析:查找在 ChIP-seq 峰中富集的已知 TF 基序
  • ENCODE/ATAC-seq 解读:将开放染色质区域与 TF 结合谱匹配
不适用场景
场景改用
某个变异与已发表 GWAS 中哪些性状相关alterlab-gwas
某个变异是否会改变特定组织中的基因表达(eQTL)alterlab-gtex
获取基因组序列、坐标或 VEP 后果alterlab-ensembl
处理 ChIP-seq/ATAC-seq 覆盖度轨迹和热图alterlab-deeptools
使用序列进行表达/染色质的深度学习预测alterlab-borzoi

核心功能

1. JASPAR REST API

基础 URL:https://jaspar.elixir.no/api/v1/

import requests

BASE_URL = "https://jaspar.elixir.no/api/v1"

def jaspar_get(endpoint, params=None):
    url = f"{BASE_URL}/{endpoint}"
    response = requests.get(url, params=params, headers={"Accept": "application/json"})
    response.raise_for_status()
    return response.json()
2. 搜索 TF 谱
def search_jaspar(
    tf_name=None,
    species=None,
    collection="CORE",
    tf_class=None,
    tf_family=None,
    page=1,
    page_size=25
):
    """Search JASPAR for TF binding profiles."""
    params = {
        "collection": collection,
        "page": page,
        "page_size": page_size,
        "format": "json"
    }
    if tf_name:
        params["name"] = tf_name
    if species:
        # NCBI taxonomy ID, e.g. "9606". The API silently ignores a `species=` param
        # (you get every taxon back), so it must be sent as `tax_id`.
        params["tax_id"] = species
    if tf_class:
        params["tf_class"] = tf_class
    if tf_family:
        params["tf_family"] = tf_family

    return jaspar_get("matrix", params)

# Examples:
# Search for human CTCF profile
ctcf = search_jaspar("CTCF", species="9606")
print(f"Found {ctcf['count']} CTCF profiles")

# Search for all homeobox TFs in human. Class/family strings follow TFClass and
# must match exactly ("Homeodomain" returns 0 rows; the stored class is
# "Homeo domain factors") — read the "class" field of a known matrix to get it.
hox_tfs = search_jaspar(tf_class="Homeo domain factors", species="9606")

# Search for a TF family
nfkb = search_jaspar(tf_family="NF-kappaB")
3. 获取指定矩阵(PFM/PWM)
def get_matrix(matrix_id):
    """Fetch a specific JASPAR matrix by ID (e.g., 'MA0139.1' for CTCF)."""
    return jaspar_get(f"matrix/{matrix_id}/")

# Example: Get CTCF matrix
ctcf_matrix = get_matrix("MA0139.1")

# Matrix structure (actual API fields; no "consensus" or "length" key — derive
# motif length from len(pfm["A"])):
# {
#   "matrix_id": "MA0139.1",
#   "name": "CTCF",
#   "base_id": "MA0139",
#   "version": 1,
#   "collection": "CORE",
#   "tax_group": "vertebrates",
#   "pfm": { "A": [...], "C": [...], "G": [...], "T": [...] },  # 19 columns for CTCF
#   "species": [{"tax_id": 9606, "name": "Homo sapiens"}],
#   "class": ["C2H2 zinc finger factors"],
#   "family": ["More than 3 adjacent zinc fingers"],
#   "type": "ChIP-seq",
#   "uniprot_ids": ["P49711"],
#   "pubmed_ids": ["17512414"],
#   "sequence_logo": "https://jaspar.elixir.no/static/logos/svg/MA0139.1.svg"
# }
4. 将 PFM/PWM 下载为矩阵
import numpy as np

def get_pwm(matrix_id, pseudocount=0.8):
    """
    Fetch a PFM from JASPAR and convert to PWM (log-odds).
    Returns numpy array of shape (4, L) in order A, C, G, T.
    """
    matrix = get_matrix(matrix_id)
    pfm = matrix["pfm"]

    # Convert PFM to numpy
    pfm_array = np.array([pfm["A"], pfm["C"], pfm["G"], pfm["T"]], dtype=float)

    # Add pseudocount
    pfm_array += pseudocount

    # Normalize to get PPM
    ppm = pfm_array / pfm_array.sum(axis=0, keepdims=True)

    # Convert to PWM (log-odds relative to background 0.25)
    background = 0.25
    pwm = np.log2(ppm / background)

    return pwm, matrix["name"]

# Example
pwm, name = get_pwm("MA0139.1")  # CTCF
print(f"PWM for {name}: shape {pwm.shape}")
max_score = pwm.max(axis=0).sum()
print(f"Maximum possible score: {max_score:.2f} bits")
5. 扫描 DNA 序列以查找 TF 结合位点
import numpy as np
from typing import List, Tuple

NUCLEOTIDE_MAP = {'A': 0, 'C': 1, 'G': 2, 'T': 3,
                  'a': 0, 'c': 1, 'g': 2, 't': 3}

def scan_sequence(sequence: str, pwm: np.ndarray, threshold_pct: float = 0.8) -> List[dict]:
    """
    Scan a DNA sequence for TF binding sites using a PWM.

    Args:
        sequence: DNA sequence string
        pwm: PWM array (4 x L) in ACGT order
        threshold_pct: Fraction of max score to use as threshold (0-1)

    Returns:
        List of hits with position, score, and matched sequence
    """
    motif_len = pwm.shape[1]
    max_score = pwm.max(axis=0).sum()
    min_score = pwm.min(axis=0).sum()
    threshold = min_score + threshold_pct * (max_score - min_score)

    hits = []
    seq = sequence.upper()

    for i in range(len(seq) - motif_len + 1):
        subseq = seq[i:i + motif_len]
        # Skip if contains non-ACGT
        if any(c not in NUCLEOTIDE_MAP for c in subseq):
            continue

        score = sum(pwm[NUCLEOTIDE_MAP[c], j] for j, c in enumerate(subseq))

        if score >= threshold:
            relative_score = (score - min_score) / (max_score - min_score)
            hits.append({
                "position": i + 1,  # 1-based
                "score": score,
                "relative_score": relative_score,
                "sequence": subseq,
                "strand": "+"
            })

    return hits

# Example: Scan a promoter sequence for CTCF binding sites
# (windows containing non-ACGT bases such as N are skipped by scan_sequence)
promoter = "AGCCCGCGAGGTGGCAGTTGCCTGGAGCAGGATCAGCAGATC"
pwm, name = get_pwm("MA0139.1")
hits = scan_sequence(promoter, pwm, threshold_pct=0.75)
for hit in hits:
    print(f"  Position {hit['position']}: {hit['sequence']} (score: {hit['score']:.2f}, {hit['relative_score']:.0%})")
6. 扫描两条链
def reverse_complement(seq: str) -> str:
    complement = {'A': 'T', 'T': 'A', 'C': 'G', 'G': 'C', 'N': 'N'}
    return ''.join(complement.get(b, 'N') for b in reversed(seq.upper()))

def scan_both_strands(sequence: str, pwm: np.ndarray, threshold_pct: float = 0.8):
    """Scan forward and reverse complement strands."""
    fwd_hits = scan_sequence(sequence, pwm, threshold_pct)
    for h in fwd_hits:
        h["strand"] = "+"

    rev_seq = reverse_complement(sequence)
    rev_hits = scan_sequence(rev_seq, pwm, threshold_pct)
    seq_len = len(sequence)
    for h in rev_hits:
        h["strand"] = "-"
        h["position"] = seq_len - h["position"] - len(h["sequence"]) + 2  # Convert to fwd coords

    all_hits = fwd_hits + rev_hits
    return sorted(all_hits, key=lambda x: x["position"])
7. TF 结合的变异影响
def variant_tfbs_impact(ref_seq: str, alt_seq: str, pwm: np.ndarray,
                          tf_name: str, threshold_pct: float = 0.7):
    """
    Assess impact of a SNP on TF binding by comparing ref vs alt sequences.
    Both sequences should be centered on the variant with flanking context.
    """
    ref_hits = scan_both_strands(ref_seq, pwm, threshold_pct)
    alt_hits = scan_both_strands(alt_seq, pwm, threshold_pct)

    max_ref = max((h["score"] for h in ref_hits), default=None)
    max_alt = max((h["score"] for h in alt_hits), default=None)

    result = {
        "tf": tf_name,
        "ref_max_score": max_ref,
        "alt_max_score": max_alt,
        "ref_has_site": len(ref_hits) > 0,
        "alt_has_site": len(alt_hits) > 0,
    }
    if max_ref and max_alt:
        result["score_change"] = max_alt - max_ref
        result["effect"] = "gained" if max_alt > max_ref else "disrupted"
    elif max_ref and not max_alt:
        result["effect"] = "disrupted"
    elif not max_ref and max_alt:
        result["effect"] = "gained"
    else:
        result["effect"] = "no_site"

    return result

查询工作流

工作流 1:查找启动子中的所有 TF 结合位点
import requests, numpy as np

# 1. Get relevant TF matrices (e.g., all human TFs in CORE collection)
#    tax_id (not species) filters by organism; page_size is capped at 1000
response = requests.get(
    "https://jaspar.elixir.no/api/v1/matrix/",
    params={"tax_id": "9606", "collection": "CORE", "version": "latest",
            "page_size": 1000, "page": 1, "format": "json"}
)
matrices = response.json()["results"]

# 2. For each matrix, compute PWM and scan promoter
promoter = "CCCGCCCGCCCGCCGCCCGCAGTTAATGAGCCCAGCGTGCC"  # Example

all_hits = []
for m in matrices[:10]:  # Limit for demo
    matrix_data = requests.get(f"https://jaspar.elixir.no/api/v1/matrix/{m['matrix_id']}/").json()
    pfm = matrix_data["pfm"]
    pfm_arr = np.array([pfm["A"], pfm["C"], pfm["G"], pfm["T"]], dtype=float) + 0.8
    ppm = pfm_arr / pfm_arr.sum(axis=0)
    pwm = np.log2(ppm / 0.25)

    hits = scan_sequence(promoter, pwm, threshold_pct=0.8)
    for h in hits:
        h["tf_name"] = m["name"]
        h["matrix_id"] = m["matrix_id"]
    all_hits.extend(hits)

print(f"Found {len(all_hits)} TF binding sites")
for h in sorted(all_hits, key=lambda x: -x["score"])[:5]:
    print(f"  {h['tf_name']} ({h['matrix_id']}): pos {h['position']}, score {h['score']:.2f}")
工作流 2:SNP 对 TF 结合的影响(调控变异分析)
  1. 获取 SNP 两侧的基因组序列(每侧 ±20 bp)
  2. 构建 ref 和 alt 序列
  3. 使用所有相关 TF PWM 进行扫描
  4. 报告因该 SNP 而新增或丢失结合位点的 TF
工作流 3:基序富集分析
  1. 确定一组峰序列(例如,来自 ChIP-seq 或 ATAC-seq)
  2. 使用 JASPAR PWM 扫描所有峰
  3. 比较峰序列与背景序列中的命中率
  4. 报告显著富集的基序(使用 Fisher 精确检验或 FIMO 风格的评分)

可用集合

当前 JASPAR API 提供两个集合(通过 GET /matrix/?collection=...&version=latest 验证数量):

集合描述最新版本配置文件(2026-09)
CORE非冗余、经整理且经过实验验证的配置文件2,633
UNVALIDATED推断得出或基于实验数据,但尚未验证~1,030

JASPAR 2026 深度学习(DL)集合中的 BPNet 模型托管在网站上(可下载模型;通过 tangermeme 框架扫描基序),不在矩阵 API 中。

旧版集合(PHYLOFACTS、CNE、POLII、FAM、SPLICE)曾出现在较早的 JASPAR 文献中,但当前 API 返回 0 条结果,请勿依赖它们。

最佳实践

  • 大多数分析使用 CORE 集合——验证最充分且非冗余
  • 阈值选择:从头预测常用最高分数的 80%;高置信度分析使用 90%
  • 始终扫描两条链——TF 可以沿任一方向结合
  • 变异分析时提供侧翼上下文:每侧至少 (motif_length - 1) bp
  • 考虑背景:PWM 分数是相对于均匀(0.25)背景计算的;请根据实际 GC 含量进行调整
  • 有 ChIP-seq 数据时进行交叉验证——基序扫描会产生许多假阳性
  • 使用 Biopython 的 motifs 模块进行完整功能的扫描:from Bio import motifs

其他资源

评论

支持 Markdown