生存分析是肿瘤基因组学研究中的核心方法之一,而TCGA数据库为研究者提供了丰富的临床随访数据。许多科研人员在完成基础生存分析后,往往面临一个共同挑战:如何将Kaplan-Meier曲线从简单的功能输出转变为具有发表质量的精美图表?这正是survminer包大显身手的领域。
在开始美化图表之前,我们需要确保基础环境配置正确。survminer包是ggplot2的扩展,专门用于生存分析可视化,它与survival包形成完美搭档。
首先安装必要的R包:
r复制if (!require("survival")) install.packages("survival")
if (!require("survminer")) install.packages("survminer")
if (!require("ggplot2")) install.packages("ggplot2")
加载TCGA数据后,典型的基础生存分析代码如下:
r复制library(survival)
library(survminer)
# 假设我们已经加载了TCGA数据并进行了预处理
tcga_data <- read.csv("tcga_clinical_data.csv")
# 创建生存对象
surv_obj <- Surv(time = tcga_data$OS_time,
event = tcga_data$OS_status)
# 按基因表达水平分组(示例:TP53表达高低分组)
tcga_data$TP53_group <- ifelse(tcga_data$TP53_expression > median(tcga_data$TP53_expression),
"High", "Low")
# 拟合生存曲线
fit <- survfit(surv_obj ~ TP53_group, data = tcga_data)
# 基础绘图
ggsurvplot(fit, data = tcga_data)
这个基础图表虽然功能完整,但缺乏专业论文所需的精致度和信息量。接下来我们将逐步提升它的表现力。
颜色方案是图表美化的第一步。survminer支持多种预设调色板,也可以完全自定义:
r复制# 使用期刊常用配色方案
plot1 <- ggsurvplot(
fit,
data = tcga_data,
palette = c("#E41A1C", "#377EB8"), # 红蓝经典组合
ggtheme = theme_classic2(base_size = 14), # 简洁的学术风格主题
font.main = c(16, "bold"), # 主标题字体
font.x = c(14, "plain"), # x轴标签字体
font.y = c(14, "plain"), # y轴标签字体
font.tickslab = c(12, "plain") # 刻度标签字体
)
期刊常用配色方案参考:
| 期刊风格 | 推荐配色方案 | 适用场景 |
|---|---|---|
| Nature | #0072B2, #D55E00 | 高对比度需求 |
| Lancet | #BC3C29, #0072B5, #E18727 | 三组比较 |
| JAMA | #374E55, #DF8F44, #00A1D5 | 多组别清晰区分 |
| NEJM | #6A6599, #79AF97, #FF9E29 | 柔和对比 |
置信区间和统计量是生存分析的核心元素,需要合理呈现:
r复制plot2 <- ggsurvplot(
fit,
data = tcga_data,
conf.int = TRUE, # 显示置信区间
conf.int.style = "ribbon", # 带状显示
conf.int.alpha = 0.2, # 透明度设置
pval = TRUE, # 显示p值
pval.method = TRUE, # 显示检验方法
pval.coord = c(30, 0.9), # p值位置
pval.size = 5, # p值字号
surv.scale = "percent" # y轴显示百分比
)
提示:当p值非常小时(<0.001),建议手动修改显示为"p < 0.001"而非具体数值,这符合多数期刊要求。
风险表能直观展示各时间点的风险人数,增强图表信息量:
r复制plot3 <- ggsurvplot(
fit,
data = tcga_data,
risk.table = TRUE, # 添加风险表
risk.table.title = "Number at risk",
risk.table.y.text = FALSE, # 不显示y轴文字
risk.table.height = 0.25, # 风险表高度比例
risk.table.col = "strata", # 按组别着色
tables.theme = theme_classic(), # 风险表主题
break.time.by = 12, # 每12个月一个刻度
xlim = c(0, 60), # x轴范围
axes.offset = FALSE # 避免图表偏移
)
对于复杂分析,可能需要组合多个图表元素:
r复制# 从Cox模型获取HR值
cox_model <- coxph(surv_obj ~ TP53_group, data = tcga_data)
hr_info <- summary(cox_model)
# 构建包含HR和p值的注释文本
hr_text <- sprintf("HR = %.2f (%.2f-%.2f)\nlog-rank p = %.3f",
hr_info$coefficients[2],
hr_info$conf.int[3],
hr_info$conf.int[4],
surv_pvalue(fit)$pval)
# 高级绘图
final_plot <- ggsurvplot(
fit,
data = tcga_data,
risk.table = TRUE,
surv.median.line = "hv", # 显示中位生存线
legend.title = "TP53 Expression",
legend.labs = c("High expression", "Low expression"),
font.legend = c(12, "plain"),
tables.height = 0.2,
ggtheme = theme_survminer() +
theme(plot.margin = unit(c(1,1,1,1), "cm"))
)
# 添加自定义注释
final_plot$plot <- final_plot$plot +
annotate("text",
x = 45, y = 0.2,
label = hr_text,
size = 5, hjust = 0) +
labs(title = "Overall Survival by TP53 Expression Level",
subtitle = "TCGA Pan-Cancer Dataset (n=1,000)")
# 调整风险表样式
final_plot$table <- final_plot$table +
theme(axis.title.y = element_blank(),
axis.line.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
论文图表需要满足期刊的分辨率和格式要求:
r复制# 保存为高分辨率TIFF(适合多数期刊)
tiff("survival_plot.tiff",
width = 180, height = 160, units = "mm", res = 300)
print(final_plot, newpage = FALSE)
dev.off()
# 保存为可编辑的PDF(适合进一步修改)
pdf("survival_plot.pdf",
width = 7, height = 6)
print(final_plot, newpage = FALSE)
dev.off()
不同期刊对图表有特定要求,以下是一些常见调整:
r复制# Nature风格调整
nature_style <- function(p) {
p + theme(
panel.grid.major = element_line(color = "grey90", size = 0.2),
panel.background = element_rect(fill = "white"),
axis.line = element_line(color = "black", size = 0.5),
legend.position = "top",
legend.title = element_text(size = 10),
legend.text = element_text(size = 9)
)
}
# Lancet风格调整
lancet_style <- function(p) {
p + theme(
panel.background = element_blank(),
axis.line = element_line(color = "black", size = 0.5),
legend.position = c(0.8, 0.9),
legend.background = element_rect(fill = "white", color = "black")
)
}
# 应用期刊风格
final_plot$plot <- nature_style(final_plot$plot)
当比较超过两组时,图表需要特别处理以避免混乱:
r复制# 创建三分组变量
tcga_data$TP53_tertile <- cut(tcga_data$TP53_expression,
breaks = quantile(tcga_data$TP53_expression,
probs = c(0, 0.33, 0.66, 1)),
labels = c("Low", "Medium", "High"))
# 拟合三组生存曲线
fit_multi <- survfit(surv_obj ~ TP53_tertile, data = tcga_data)
# 多组绘图
multi_plot <- ggsurvplot(
fit_multi,
data = tcga_data,
palette = "jco", # Journal of Clinical Oncology配色
pval = TRUE,
pval.method = TRUE,
risk.table = TRUE,
ncensor.plot = TRUE, # 显示删失数据分布
size = 1, # 线条粗细
break.time.by = 12,
legend.labs = c("Low (T1)", "Medium (T2)", "High (T3)"),
xlab = "Time since diagnosis (months)",
ylab = "Overall survival probability"
)
# 添加组间两两比较p值
pairwise_p <- pairwise_survdiff(surv_obj ~ TP53_tertile,
data = tcga_data,
p.adjust.method = "BH")
# 将比较结果添加到图表
multi_plot$plot <- multi_plot$plot +
annotate("text",
x = c(20, 20, 40), y = c(0.2, 0.15, 0.1),
label = c(paste("Low vs Medium: p =", format.pval(pairwise_p$p.value[1,2])),
paste("Low vs High: p =", format.pval(pairwise_p$p.value[1,3])),
paste("Medium vs High: p =", format.pval(pairwise_p$p.value[2,3]))),
size = 4, hjust = 0)
对于报告或网页展示,交互式图表能增强信息传达:
r复制# 安装plotly包
if (!require("plotly")) install.packages("plotly")
# 将ggplot生存图转换为交互式
library(plotly)
interactive_plot <- ggplotly(multi_plot$plot)
# 添加自定义悬停信息
interactive_plot <- interactive_plot %>%
layout(
hoverlabel = list(
bgcolor = "white",
font = list(size = 12)
),
annotations = list(
list(
x = 0.5,
y = -0.3,
text = "Hover over lines to see survival rates at specific time points",
showarrow = FALSE,
xref = "paper",
yref = "paper",
font = list(size = 10)
)
)
)
# 保存为HTML
htmlwidgets::saveWidget(interactive_plot, "interactive_survival.html")
在实际项目中,我发现最常被低估的细节是图表元素的字体一致性。许多期刊虽然不明确指定字体要求,但保持全文字体统一(通常Times New Roman或Arial)能显著提升专业感。另一个实用技巧是使用cowplot包将生存曲线、风险表和基因表达分布图组合成一个复合图表,这在展示分子标志物与预后的关系时特别有效。