TCGA 数据获取与整理
跟完这篇,您能从 GDC 或 UCSC Xena 任选一个入口,把某个癌种的表达矩阵、临床信息和生存随访三份数据取下来,看懂 TCGA 的样本条码,并把三者按病人对齐成一套「行是样本、能直接接分析」的配套数据。
TCGA 里有什么
TCGA(The Cancer Genome Atlas)是目前最常用的公共肿瘤多组学数据库,把上万例肿瘤样本按癌种整理成 33 个项目,每个项目一个癌种,用四个字母缩写标识(如 LIHC 肝细胞癌、BRCA 乳腺癌、LUAD 肺腺癌)。每个癌种下面,同一批病人往往同时有好几种组学数据,您真正常取的是下面这几类:
| 数据类型 | 内容 | 常用于 |
|---|---|---|
| 基因表达 | RNA-seq 的 counts / TPM / FPKM | 差异表达、生存、免疫浸润 |
| 临床信息 | 年龄、性别、分期、分级等 | 分组、协变量 |
| 生存随访 | 生存状态与随访时长 | KM 曲线、Cox 模型 |
| 体细胞突变 | 突变清单(MAF 文件) | 突变分析、TMB |
| 拷贝数变异 | 基因/片段的扩增与缺失 | 拷贝数分析 |
| DNA 甲基化 | 各位点甲基化水平 | 表观分析 |
这篇只聚焦最常配套使用的前三样:表达、临床、生存。它们分开存放,取下来后要靠病人条码拼到一起——后面几节的核心就是这个「取 + 对齐」。
先看懂 TCGA 条码
TCGA 给每个样本一串条码(barcode),对齐数据全靠它,务必先看懂结构。以 TCGA-DD-AAVP-01A-11R-A41C-07 为例,用短横线分段:
| 片段 | 例 | 含义 |
|---|---|---|
| Project | TCGA | 固定前缀 |
| TSS | DD | 组织来源机构 |
| Participant | AAVP | 病人编号 |
| Sample + Vial | 01A | 样本类型码(01) + 份次 |
| 其余 | 11R-A41C-07 | 提取份、板、中心等 |
两条最实用的规则记牢:
- 前 12 位
TCGA-DD-AAVP是病人条码——临床和生存表都是按病人(每人一行)组织的,对齐时以它为准。 - 第 14~15 位这两位数字是样本类型码,决定这管样本是肿瘤还是正常:
| 类型码 | 含义 |
|---|---|
01 | 原发实体瘤(Primary Tumor) |
02 | 复发实体瘤 |
06 | 转移灶(Metastatic) |
11 | 癌旁正常组织(Solid Tissue Normal) |
做「肿瘤 vs 正常」时靠这两位分组;只想留原发肿瘤,就筛 01。
两个入口:GDC 还是 UCSC Xena
取 TCGA 数据主流就两个门户,各有取舍,先按下表选,再往下看对应小节。
| 维度 | GDC(配 TCGAbiolinks) | UCSC Xena |
|---|---|---|
| 数据形态 | 原始或接近原始,字段最全 | 已归一化、整理好,开箱即用 |
| 拿原始 counts | 可以(STAR - Counts 即原始计数) | 部分数据集提供 |
| 补正常样本 | 只有 TCGA 自带的(很少) | 有 TCGA + GTEx 合并队列可补 |
| 上手方式 | 写 R 脚本下载、自己整理 | 网页点选,直接下文本文件 |
| 适合谁 | 要最全字段、做深入分析 | 想快速出图、初步筛线索 |
一句话经验:想快点把数据取下来出图,用 Xena;要最原始、最完整的字段做深入分析,用 GDC。 两条路下面各走一遍。
路线一:从 GDC 取(TCGAbiolinks)
GDC(Genomic Data Commons)是 TCGA 数据的官方门户,R 里用 TCGAbiolinks 按癌种、数据类型批量下载。分表达和临床两步。
第一步,取表达(原始 counts)。 走「查询 → 下载 → 整理」三个函数:
library(TCGAbiolinks)
library(SummarizedExperiment)
# 一、查询肝癌(LIHC)的 RNA-seq 表达,workflow 选 STAR - Counts 才是原始计数
query <- GDCquery(
project = "TCGA-LIHC",
data.category = "Transcriptome Profiling",
data.type = "Gene Expression Quantification",
workflow.type = "STAR - Counts"
)
GDCdownload(query) # 下载到本地
se <- GDCprepare(query) # 整理成 SummarizedExperiment 对象
# 二、取原始计数矩阵:行 = 基因,列 = 样本条码
counts <- assay(se, "unstranded")
assay() 里除了 "unstranded"(原始 counts,做 DESeq2 用它),同一对象还带 "tpm_unstrand"、"fpkm_unstrand" 等归一化好的矩阵,按需要取。差异表达为什么要用原始 counts,见 表达量单位。
第二步,取临床与生存字段。 GDC 的整理版临床表一个函数就够,它自带算生存所需的三个字段:
# 病人层级的临床信息,一人一行
clin <- GDCquery_clinic(project = "TCGA-LIHC", type = "clinical")
# 生存分析用的三个关键列:
# vital_status —— "Alive" / "Dead"
# days_to_death —— 死亡者:从诊断到死亡的天数
# days_to_last_follow_up —— 存活者:最后一次随访的天数
第三步,拼出生存表(time + event)。 生存分析要的是「随访时长 time + 结局 event(0/1)」两列,从上面字段推导:
library(dplyr)
surv <- clin %>%
transmute(
barcode = submitter_id, # 病人条码,如 TCGA-DD-AAVP
event = as.integer(vital_status == "Dead"), # 1 = 死亡,0 = 删失
time = ifelse(vital_status == "Dead",
days_to_death, # 死亡者用到死亡的天数
days_to_last_follow_up) # 存活者用到末次随访的天数
)
这里 time 单位是天,画图时想换成月常按惯例除以 30、换成年除以 365,只是换刻度、不改结论。生存分析怎么读、log-rank 和删失是怎么回事,见 生存分析入门。
路线二:从 UCSC Xena 取(推荐上手)
UCSC Xena 把 TCGA 数据整理成了规整、已归一化的文本矩阵,省掉大量清洗。它的 GDC 数据中心里,每个癌种通常配好三个文件,正好对上我们要的三样:
| 文件(以 LIHC 为例) | 内容 |
|---|---|
TCGA-LIHC.htseq_counts.tsv.gz | 表达矩阵(多为 log2 归一化值) |
TCGA-LIHC.GDC_phenotype.tsv.gz | 临床表型 |
TCGA-LIHC.survival.tsv | 生存表,已带 OS 与 OS.time |
网页上选好数据集直接下载,本地读入即可:
# 表达矩阵:行 = 基因,列 = 样本条码;Xena 上多为已归一化的 log2 值
expr <- read.delim("TCGA-LIHC.htseq_counts.tsv.gz",
row.names = 1, check.names = FALSE)
# 生存表:Xena 直接给好 OS(结局 0/1)与 OS.time(天),省去自己推导
surv <- read.delim("TCGA-LIHC.survival.tsv", header = TRUE)
# 常见列:sample(样本条码)、OS、OS.time
Xena 的表达值具体是哪种归一化(log2(count+1)、log2(tpm+0.001) 等),每个数据集页面的说明里都写着,用之前务必看一眼——归一化方式不同,能不能接差异表达、能不能直接比较,结论不一样。
Xena 还有一大长处:它提供把 TCGA 肿瘤与 GTEx 正常组织统一处理的合并队列,专门补 TCGA 正常样本太少的短板,是做「肿瘤 vs 正常」跨癌种图最常用的来源。用它时要记得这是两个来源拼在一起,批次差异要留个心眼,见 批次效应。
三者对齐:表达、临床、生存怎么配套
不管走哪条路线,取下来的三份数据都还是各管各的,最后一步是把它们按病人对齐成一套。难点在条码位数不一致:表达矩阵的列是样本级条码(15~16 位),临床和生存表是病人级(12 位)。四步对齐:
# 1) 只留原发肿瘤:样本类型码 "01" 在条码第 14~15 位
sample_type <- substr(colnames(counts), 14, 15)
tumor <- counts[, sample_type == "01"]
# 2) 把样本级条码截成 12 位病人条码,才能跟临床/生存对上
colnames(tumor) <- substr(colnames(tumor), 1, 12)
# 3) 一个病人可能有多管样本,去重,避免同一人算两次
tumor <- tumor[, !duplicated(colnames(tumor))]
# 4) 取三者共有的病人,并把顺序对齐一致
common <- intersect(colnames(tumor), surv$barcode)
tumor <- tumor[, common] # 表达按 common 排
surv <- surv[match(common, surv$barcode), ] # 生存表按同一顺序排
对齐后请一定做一次自查,这是最容易翻车、也最该核对的地方:
| 检查项 | 期望 | 不对时的隐患 |
|---|---|---|
| 表达列名与生存 barcode 完全一致 | all(colnames(tumor) == surv$barcode) 为 TRUE | 顺序错位会把 A 的表达配到 B 的结局 |
| 有没有留下正常样本没剔 | 只剩类型码 01 | 正常样本混进「肿瘤」组,分组被污染 |
| 一人多样本是否去了重 | 病人条码不重复 | 同一人重复计数,样本量虚高 |
| 对齐后样本量 | 与预期相差不大 | 掉太多说明某份数据缺失严重 |
顺序对齐比「都在就行」更重要:很多下游函数(比如生存分析)默认第 i 列表达对应第 i 行生存,一旦错位,跑得出结果但全是错的。用 match()(如上)或以病人条码做键合并,别只靠「碰巧顺序一样」。对齐好的这套数据,怎么进一步整理成各分析要的规范形状,见 表达矩阵整理。
常见坑与边界
TCGA 好用,但几个坑几乎人人都会踩,先知道能省很多返工:
- 正常样本很少。 多数癌种的癌旁正常只有个位数,有的几乎没有。做「肿瘤 vs 正常」时,要么接受小样本、结论说保守些,要么用 Xena 的 TCGA + GTEx 合并队列补,但要认下批次效应这笔账。
- 临床字段不齐、写法不一。 分期、分级等字段常有缺失,早年录入的措辞也不统一(如分期写法五花八门)。用作分组或协变量前,务必先清洗、统一取值。
time有 0 或负数、缺失。 随访天数偶有异常值或缺失,进生存分析前先过滤掉time缺失或非正的记录。- 一人多样本。 同一病人可能有多管肿瘤样本(不同份、不同部位),不去重会重复计数,务必按上一节的第 3 步处理。
- 生存终点不止一种。 上面用的是总生存(OS)。研究问题不同,可能要用无进展生存(PFI)、无病生存(DFI)等终点——社区有整理好的 TCGA 临床终点表把这些终点统一给出,做预后时按需选用,别只有 OS 一条路。
- 别把 TCGA 结论当终点。 它是回顾性的公共队列,跨批次、跨机构,适合筛线索、提假设;真正的结论仍需独立数据集验证与实验支撑。
把这几条放在心上,取数和对齐这一关就基本稳了。
在平台上跑一遍
上面「取数据 → 看懂条码 → 三者对齐」这一整套,在百沐一下不用写代码也能做:告诉平台您要哪个癌种、要表达和生存,它会替您取数、按病人对齐、剔掉正常样本与重复,直接交给您一套能接着分析的配套数据,并附上可复现的 R 代码。您要做的,是核对癌种和分组对不对。
上手见 组学智能体使用指南,或直接开始:https://app.baimuyixia.com 。
相关
- 单基因泛癌分析 — 取好 TCGA 数据后,横跨 33 个癌种扫一个基因
- 预后 signature 与风险模型 — 用对齐好的表达 + 生存建预后模型
- 生存分析入门 — 把生存表读成 KM 曲线与 Cox 结果
- GEO 数据库检索入门 — 换个公共库:怎么在 GEO 里找表达数据集