news 2026/9/28 6:45:42

从配体-受体推断到空间邻域观察:scRNA-seq之后PCF补上的关键一步——用TaoToken统一Key打通CODEX空间验证链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从配体-受体推断到空间邻域观察:scRNA-seq之后PCF补上的关键一步——用TaoToken统一Key打通CODEX空间验证链路

1. 为什么 scRNA-seq 的配体-受体推断总让人心里没底

做单细胞测序的人大多经历过这个阶段:跑完 CellChat 或 CellPhoneDB,拿到一张漂亮的配体-受体互作热图,CXCL12-CXCR4、KITLG-KIT、SELE-CD44 这些信号对赫然在列,通讯网络图里间充质基质细胞(MSC)的信号输出强度排在前列。你会觉得故事已经成型了——MSC 通过 CXCL12 支持 HSPC 维持,内皮细胞通过 SELE 参与髓系归巢,逻辑链条清晰。

但审稿人或组会上总有人问一句:这些细胞真的在组织里挨着吗?

这个问题很致命。scRNA-seq 的配体-受体推断本质上是从 mRNA 表达量出发的统计关联,它告诉你“A 细胞表达配体、B 细胞表达受体”,但 A 和 B 是否处于同一空间邻域、是否共享同一个生态位、是否真的存在物理接触的可能,转录组数据本身回答不了。你得到的是一张“可能性地图”,不是“实际接触记录”。

PCF(CODEX)空间蛋白成像补上的正是这一步。它能在同一张组织切片上同时标记几十种蛋白 marker,保留每个细胞的空间坐标,让你把 scRNA-seq 推断出的配体-受体候选列表,放到真实组织邻域里去验证“它们是否相邻”。空间转录组适合发现基因表达的空间格局,而 PCF 更适合围绕已知细胞群和蛋白 marker 做组织原位的关系确认。

这篇内容聚焦一个具体场景:你已经有 scRNA-seq 的配体-受体推断结果,想用 CODEX 空间邻域观察来验证候选互作对。我会给出可复制的 config.toml 骨架和 settings.json 配置片段,演示如何用 TaoToken 统一 Key 打通单细胞与空间蛋白数据的分析链路,并给出配体-受体候选列表在空间邻域中的验证动作与判读标准。

适合谁看:正在做骨髓、肿瘤微环境、淋巴组织等复杂组织生态位研究,手里有 scRNA-seq 数据、准备或已经拿到 CODEX 空间蛋白数据,想把两层证据串起来的研究生和博后。不需要你是生信老手,但需要你能跑 Python 脚本、能看懂基本的空间坐标数据结构。

2. TaoToken 前置:统一 Key 打通单细胞与空间蛋白分析链路

2.1 为什么这里需要一个统一 API 通道

单细胞和空间蛋白分析经常涉及多个工具链:CellChat 跑配体-受体推断、Seurat 或 Scanpy 做细胞注释、CODEX 数据用 Python 做邻域分析、有时候还要调模型辅助解读结果或生成分析代码。如果每个工具都单独配一套 API Key 和 endpoint,管理成本高,换环境时容易出错。

TaoToken 的做法是提供一个统一的 API 通道,兼容 OpenAI 风格的接口调用。你可以在一个地方管理 Key,然后在不同分析脚本里复用同一个 endpoint 和 Key。对于需要频繁切换工具链的空间组学流程来说,这能省掉不少配置上的麻烦。

官网地址:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=

API 地址:https://taotoken.net/api

2.2 获取 Key 与可用入口

进入控制台创建 API Key,然后根据你的使用场景选择对应入口:

  • 模型对话(用于辅助解读配体-受体结果、生成分析思路):https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model-chat
  • Coding Plan(长期跑分析脚本、Agent 式编码):https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding-plan
  • 控制台(管理 Key 和用量):https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console
  • API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api-keys
  • 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc

注意:Key 只存在环境变量或本地配置文件里,不要硬编码进脚本后提交到公开仓库。我习惯用.env文件加python-dotenv读取,下面配置片段会体现这一点。

3. 可复制配置:config.toml 骨架与 settings.json 片段

3.1 config.toml 骨架

这个 config.toml 用于统一管理分析流程中的 API 通道和空间邻域参数。放在项目根目录,Python 脚本通过tomllib(Python 3.11+)或tomli读取。

# config.toml # 单细胞 + CODEX 空间验证流程配置 [api] # TaoToken 统一 API 通道 base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" # 从环境变量读取,不写明文 model = "gpt-4o" # 按需替换为可用模型标识 timeout_seconds = 60 max_retries = 3 [scrna] # scRNA-seq 配体-受体推断结果路径 cellchat_output = "results/cellchat_net.rds" lr_pairs_file = "results/lr_candidates.csv" # 候选配体-受体列表 min_cells_per_group = 10 pval_threshold = 0.05 [codex] # CODEX 空间蛋白数据 cell_coords_file = "data/codex_cell_coords.csv" # 列: cell_id, x, y, marker_* marker_panel = ["CD34", "CD38", "CXCL12", "KIT", "SELE", "CD44", "CD31", "LEPR"] neighborhood_radius_um = 30 # 邻域半径,按组织类型调整 min_neighbors = 5 # 最少邻居数,低于此值标记为孤立 [neighborhood] # 空间邻域分析参数 method = "radius" # radius 或 knn knn_k = 10 # method=knn 时生效 permutation_n = 1000 # 置换检验次数 fdr_threshold = 0.1 [output] result_dir = "results/spatial_validation" save_plots = true plot_format = "pdf"

3.2 settings.json 配置片段

settings.json 用于存放更细粒度的运行参数,方便在不同数据集之间切换时只改这一个文件。

{ "project_name": "bm_niche_spatial_validation", "scrna": { "species": "human", "celltype_column": "cell_type", "ligand_receptor_pairs": [ {"ligand": "CXCL12", "receptor": "CXCR4", "source": "MSC", "target": "HSPC"}, {"ligand": "KITLG", "receptor": "KIT", "source": "MSC", "target": "HSPC"}, {"ligand": "SELE", "receptor": "CD44", "source": "Endothelial", "target": "Myeloid"} ] }, "codex": { "panel_version": "v2", "normalization": "arcsinh", "cofactor": 5, "segmentation_method": "watershed" }, "spatial_validation": { "test_type": "permutation", "distance_metric": "euclidean", "neighborhood_definition": "radius_um", "radius_um": 30, "report_metrics": [ "observed_neighbor_count", "expected_neighbor_count", "enrichment_score", "permutation_pval", "fdr" ] }, "api_usage": { "use_for": ["code_generation", "result_interpretation"], "max_tokens_per_call": 2000, "temperature": 0.2 } }

3.3 环境变量与读取脚本

# .env 文件(不要提交到 git) TAOTOKEN_API_KEY=你的Key
# load_config.py import os import json import tomllib from dotenv import load_dotenv load_dotenv() def load_config(config_path="config.toml", settings_path="settings.json"): with open(config_path, "rb") as f: config = tomllib.load(f) with open(settings_path, "r", encoding="utf-8") as f: settings = json.load(f) api_key = os.environ.get(config["api"]["api_key_env"]) if not api_key: raise ValueError(f"环境变量 {config['api']['api_key_env']} 未设置") config["api"]["api_key"] = api_key return config, settings if __name__ == "__main__": cfg, st = load_config() print("API base:", cfg["api"]["base_url"]) print("邻域半径:", cfg["codex"]["neighborhood_radius_um"], "um") print("候选配体-受体对:", len(st["scrna"]["ligand_receptor_pairs"]))

跑一下确认配置能正常读取:

python load_config.py

预期输出:

API base: https://taotoken.net/api 邻域半径: 30 um 候选配体-受体对: 3

4. 验证请求:配体-受体候选列表在空间邻域中的验证动作

4.1 从 scRNA-seq 结果提取候选列表

假设你已经用 CellChat 跑完了配体-受体推断,输出了一张互作表。把它整理成lr_candidates.csv,至少包含这几列:

ligand,receptor,source_celltype,target_celltype,communication_prob,pval CXCL12,CXCR4,MSC,HSPC,0.021,0.003 KITLG,KIT,MSC,HSPC,0.015,0.011 SELE,CD44,Endothelial,Myeloid,0.009,0.028

4.2 空间邻域验证的核心逻辑

验证动作分三步:

第一步,在 CODEX 数据中根据 marker 表达把每个细胞注释到对应细胞类型。比如 CXCL12 高表达且 LEPR 阳性的细胞归为 MSC,CD34 和 KIT 双阳的归为 HSPC。

第二步,对每一对候选配体-受体,计算 source 细胞和 target 细胞在空间上的邻近距离分布。具体做法是:对每个 source 细胞,找它半径 R 内的所有 target 细胞,记录数量;然后对所有 source-target 对做置换检验,看实际观察到的邻接数量是否显著高于随机分布下的期望值。

第三步,计算 enrichment score 和 FDR,筛选出在空间上显著邻近的配体-受体对。

# spatial_validation.py import numpy as np import pandas as pd from scipy.spatial import cKDTree from statsmodels.stats.multitest import multipletests from load_config import load_config cfg, st = load_config() radius = cfg["codex"]["neighborhood_radius_um"] n_perm = cfg["neighborhood"]["permutation_n"] fdr_thr = cfg["neighborhood"]["fdr_threshold"] # 读取 CODEX 细胞坐标和注释 coords_df = pd.read_csv(cfg["codex"]["cell_coords_file"]) # 假设已有 cell_type 列 source_mask = coords_df["cell_type"] == "MSC" target_mask = coords_df["cell_type"] == "HSPC" source_coords = coords_df.loc[source_mask, ["x", "y"]].values target_coords = coords_df.loc[target_mask, ["x", "y"]].values # 构建 KDTree target_tree = cKDTree(target_coords) # 观察到的邻接数 observed = np.array([len(target_tree.query_ball_point(s, radius)) for s in source_coords]) observed_mean = observed.mean() # 置换检验:随机打乱 target 坐标 perm_means = [] rng = np.random.default_rng(42) for _ in range(n_perm): shuffled = target_coords[rng.permutation(len(target_coords))] tree = cKDTree(shuffled) counts = [len(tree.query_ball_point(s, radius)) for s in source_coords] perm_means.append(np.mean(counts)) perm_means = np.array(perm_means) expected_mean = perm_means.mean() enrichment = observed_mean / expected_mean if expected_mean > 0 else np.nan pval = (np.sum(perm_means >= observed_mean) + 1) / (n_perm + 1) print(f"观察邻接均值: {observed_mean:.2f}") print(f"期望邻接均值: {expected_mean:.2f}") print(f"富集分数: {enrichment:.2f}") print(f"置换 p 值: {pval:.4f}")

4.3 批量跑多对配体-受体

把上面的逻辑封装成函数,遍历settings.json里的ligand_receptor_pairs:

def validate_pair(coords_df, source_type, target_type, radius, n_perm=1000): src = coords_df[coords_df["cell_type"] == source_type][["x", "y"]].values tgt = coords_df[coords_df["cell_type"] == target_type][["x", "y"]].values if len(src) == 0 or len(tgt) == 0: return None tree = cKDTree(tgt) obs = np.array([len(tree.query_ball_point(s, radius)) for s in src]) obs_mean = obs.mean() rng = np.random.default_rng(42) perm = [] for _ in range(n_perm): shuf = tgt[rng.permutation(len(tgt))] t = cKDTree(shuf) perm.append(np.mean([len(t.query_ball_point(s, radius)) for s in src])) perm = np.array(perm) exp_mean = perm.mean() enrich = obs_mean / exp_mean if exp_mean > 0 else np.nan pval = (np.sum(perm >= obs_mean) + 1) / (n_perm + 1) return { "source": source_type, "target": target_type, "observed_mean": obs_mean, "expected_mean": exp_mean, "enrichment": enrich, "pval": pval } results = [] for pair in st["scrna"]["ligand_receptor_pairs"]: r = validate_pair(coords_df, pair["source"], pair["target"], radius, n_perm) if r: r["ligand"] = pair["ligand"] r["receptor"] = pair["receptor"] results.append(r) res_df = pd.DataFrame(results) res_df["fdr"] = multipletests(res_df["pval"], method="fdr_bh")[1] res_df.to_csv(f"{cfg['output']['result_dir']}/spatial_validation_results.csv", index=False) print(res_df[["ligand", "receptor", "enrichment", "pval", "fdr"]])

4.4 判读标准

跑完上面的脚本,你会得到一张表。怎么判断哪些配体-受体对在空间上真的成立:

指标含义判读建议
enrichment > 1.5实际邻接数明显高于随机期望提示空间邻近有生物学意义
permutation pval < 0.05置换检验显著排除随机分布的可能
FDR < 0.1多重检验校正后仍显著适合作为后续实验验证的候选
observed_mean < min_neighbors邻接数太少可能是细胞类型注释或分割问题

如果某对配体-受体在 scRNA-seq 中通讯概率高,但在空间邻域中 enrichment 接近 1 且 p 值不显著,说明这两个细胞类型在组织中并不特别邻近,推断的互作可能不是直接接触型的,或者需要更大的邻域半径来捕捉。

5. 本篇常见错排查

5.1 邻域半径设多少合适

这是最常被问的问题。30 μm 是一个常用的起点,但不同组织差异很大。骨髓生态位中细胞密度高,20-30 μm 可能就覆盖了直接邻接;肿瘤组织中细胞间隙大,可能需要 50 μm 甚至更大。

我的做法是先用 30 μm 跑一遍,然后画一个邻接数随半径变化的曲线,看曲线在哪个半径开始趋于平缓。那个拐点附近的半径通常比较合理。

radii = [10, 20, 30, 40, 50, 60] for r in radii: tree = cKDTree(target_coords) counts = [len(tree.query_ball_point(s, r)) for s in source_coords] print(f"半径 {r} um: 平均邻接数 {np.mean(counts):.2f}")

5.2 细胞类型注释对不上

CODEX 的 marker panel 和 scRNA-seq 的细胞类型定义往往不完全一致。比如 scRNA-seq 里你分出了 MSC 亚群 1、2、3,但 CODEX 只有 LEPR 和 CXCL12 两个 marker,只能粗略归为 MSC。

处理办法:在 settings.json 里把 scRNA-seq 的亚群映射到 CODEX 能识别的粗粒度类型,然后在结果解读时注明“空间验证在粗粒度层面进行”。不要强行用 scRNA-seq 的精细亚群去套 CODEX 数据,那样只会引入噪声。

5.3 置换检验跑得太慢

如果 source 细胞有几千个、n_perm 设了 1000,纯 Python 循环会很慢。两个优化方向:一是用cKDTree.query_ball_point的向量化版本,二是把 n_perm 降到 500 先看趋势,确认有信号后再用 1000 或 2000 跑最终结果。

# 向量化查询,比逐个循环快很多 counts = target_tree.query_ball_point(source_coords, radius, return_length=True) observed_mean = counts.mean()

5.4 API 调用超时或返回空

如果你用 TaoToken 的 API 辅助生成分析代码或解读结果,遇到超时先检查timeout_seconds是否设得太短。网络波动时 60 秒可能不够,可以调到 120。另外确认base_url写的是https://taotoken.net/api,不要多加路径后缀。

import openai client = openai.OpenAI( api_key=cfg["api"]["api_key"], base_url=cfg["api"]["base_url"], timeout=cfg["api"]["timeout_seconds"] ) resp = client.chat.completions.create( model=cfg["api"]["model"], messages=[{"role": "user", "content": "解释一下空间邻域富集分数的含义"}], temperature=0.2 ) print(resp.choices[0].message.content)

如果返回空内容,先确认模型标识是否正确,再检查 Key 是否有余额。控制台里能看到用量记录。

5.5 CODEX 坐标文件和 marker 列对不上

cell_coords_file要求至少有cell_id、x、y三列,marker 列以marker_开头。如果你的文件列名不同,在读取后做一次重命名:

coords_df = pd.read_csv(cfg["codex"]["cell_coords_file"]) coords_df = coords_df.rename(columns={"X_coord": "x", "Y_coord": "y"})

确认cell_type列存在且取值和 settings.json 里的 source/target 名称一致。大小写敏感,MSC和msc会被当成两种类型。

6. 把两层证据串起来:从候选列表到空间验证的完整链路

回到最初的问题:scRNA-seq 告诉你“哪些细胞可能交流”,PCF(CODEX)告诉你“它们是否真的挨着”。这两层证据串起来,你的配体-受体推断才从“计算预测”变成“有空间观察支撑的候选互作”。

实际操作中,我建议把流程固定成四步:先从 CellChat 输出里筛出 p 值显著、通讯概率靠前的配体-受体对;然后在 CODEX 数据里做细胞类型注释,把 scRNA-seq 的精细亚群映射到 CODEX 能识别的粗粒度类型;接着用上面的脚本跑空间邻域富集分析,得到 enrichment 和 FDR;最后把 scRNA-seq 的通讯概率和空间富集分数放在一张表里对照,优先关注两层都显著的配体-受体对。

这套流程跑通之后,你手里就有一份“转录层推断 + 空间层验证”的候选列表,后续做免疫荧光共染或功能实验时,靶点选择会更有依据。

如果你在配置 TaoToken 通道或跑空间邻域脚本时遇到报错,可以先查接入文档里的接口说明,或者到 API Keys 页面确认 Key 状态。长期跑分析流程的话,Coding Plan 入口更适合频繁调用的场景。模型对话入口可以用来辅助解读富集结果或生成分析代码片段,但记得把生物学判断留给自己——模型给的是思路,不是结论。

本文仅为科研技术方法介绍,不涉及疾病诊断、治疗建议、疗效预测、用药指导或临床决策。文中提及的研究发现均来自学术文献,相关分析结果需结合更多实验和研究进一步验证,不构成任何医疗意见。

参考文献:Bandyopadhyay S, Duffy MP, Ahn KJ, et al. Mapping the cellular biogeography of human bone marrow niches using single-cell transcriptomics and proteomic imaging. Cell. 2024;187(12):3120-3140.e29.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 6:45:19

粒子群算法优化Kmeans聚类:居民用电负荷曲线分群实践

居民用电行为分析这几年被越来越多人捡起来做&#xff0c;原因很简单&#xff1a;智能电表普及之后&#xff0c;营销侧手里攒了一批按15分钟甚至5分钟一条的负荷曲线数据&#xff0c;东西是好东西&#xff0c;但大多数数据躺在数据库里根本没被充分利用。我做这个项目的时候&am…

作者头像 李华
网站建设 2026/9/28 6:45:04

基于正则化逻辑回归的微芯片质检模型与Matlab实现

如果只是拿一条直线去做分类&#xff0c;这批微芯片的质检数据会把你的验证集准确率按在 60% 以下。我接手这条微型产线的不良品检测需求时&#xff0c;特征只有两个——两项物理测试读数&#xff0c;标签是合格或不合格&#xff0c;一共一百多个样本&#xff0c;看起来再简单不…

作者头像 李华
网站建设 2026/9/28 6:43:59

AI工程实战:从数据管道到模型监控的体系化搭建指南

1. 先分清&#xff1a;你是在做AI实验&#xff0c;还是在做AI工程“ai-engineering-from-scratch”这个项目名挂在我仓库里已经大半年了。最初我以为它只是一条学习路线图&#xff0c;把算法从线性回归讲到Transformer就算完成。直到自己亲自带过几个真实落地的AI项目&#xff…

作者头像 李华