这是「DeepSeek V4 × R语言数据科学」系列第六篇。 前五篇分别讲了工作台配置、数据处理、数据可视化、统计建模、机器学习。这一篇讲Quarto——你的分析报告从此不再需要手动复制粘贴数字。
01 先说一件让我印象很深的事
有个做临床研究的博士,给我发了一条消息:
王博士,我昨晚改了数据,把一个纳入标准调整了,样本量从268变成了241。现在要改的东西是:正文里提到样本量的地方有11处,Table 1要重新跑,三张图要重新出,讨论里引用的数字要逐一核对……
他说这个过程他以前做过,漏改了两处,被导师发现了。
我问他:你愿意花多长时间学一个工具,让你以后再遇到这种情况只需要运行一行命令?
他说:一个小时。
这篇文章教你学的那个工具,叫Quarto。学会了,数据改了,报告自动更新,一行代码搞定。
02 Quarto是什么,为什么要用它
用一句话说清楚: Quarto是一种把R代码和文字混在一起写的文件格式,运行之后自动生成Word文档或HTML报告,里面的数字和图表都是代码跑出来的,不是手动粘贴的。
换一种说法:你的分析报告里,每一个数字、每一张图、每一行统计结果,都直接来自你的R代码,而不是你手动复制进去的。
数据改了,重新运行一次,报告里所有的数字自动更新,不可能漏改,不可能改错。
和传统Word写法的对比:
传统写法(你现在可能的做法): R跑出结果 → 复制数字 → 粘贴进Word → 改数据 → 重新跑 → 逐一找Word里的数字 → 逐一替换 → 祈祷没有漏掉Quarto写法: 在Quarto文件里写代码和文字 → 数据改了 → 运行一次 → 报告自动更新完毕03 Quarto在前面系列文章里出现过几次
第一篇工作台配置里提到了renv管理包版本; 第三篇数据可视化里提到了图表导出; 第五篇机器学习里提到了可重复分析流程。
Quarto是把这一切串起来的最后一块:可重复的科研报告。
04 准备工作
1 安装Quarto
Quarto不是R包,是一个独立的软件。
访问:https://quarto.org/docs/get-started/
下载对应系统的安装包,安装即可。安装完成后,Positron会自动检测到Quarto。
2 验证安装
在Positron终端运行:
quarto --version看到版本号(如1.4.550),说明安装成功。
若是需要教程的配套qmd文件和资料包,拿过去就复用,可以进入我们的R语言AI编程社群,扫码添加?。

3 安装R包
install.packages(c("tidyverse","gtsummary","survival","survminer","broom","flextable","knitr","kableExtra","here"))05 第一步:创建你的第一个Quarto文件
在Positron里:File → New File → Quarto Document
新建的.qmd文件会有这样的基础结构:
---title:"临床数据分析报告"author:"王博士"date:todayformat:docx:defaulthtml:defaultexecute:echo:falsewarning:falsemessage:false---YAML头部是整个文件的控制中心,先把它配置好。
06 第二步:YAML头部完整配置
这是最容易配错的地方,下面给你一个可以直接用的完整版本:
---title:"临床预测模型分析报告"subtitle:"基于机器学习的糖尿病风险预测"author:-name:"第一作者"affiliation:"某某大学附属医院"date:todaydate-format:"YYYY年MM月DD日"# 输出格式设置format:# Word格式(投稿用)docx:toc:true# 生成目录toc-depth:3# 目录深度number-sections:true# 章节编号reference-doc:template.docx# 自定义样式模板(可选)fig-width:7fig-height:5fig-dpi:300# HTML格式(在线查看/分享)html:toc:truetoc-float:truetheme:flatlycode-fold:true# 代码可折叠(读者可选择看代码)embed-resources:true# 把所有资源打包进单个HTML文件# 全局代码块设置execute:echo:false# 默认不显示代码(投稿报告)warning:false# 不显示警告信息message:false# 不显示提示信息cache:true# 缓存结果(改了别的地方不需要重新跑所有代码)# 图片设置fig-align:centerfig-cap-location:bottom# 表格设置tbl-cap-location:top---关键参数说明:
echo: false — 最重要的设置。投稿的报告不需要展示代码,只展示结果。
cache: true — 分析时间长的时候救命神器。跑完一次之后,没有改动的代码块直接用缓存结果,不重新运行。
embed-resources: true — 生成单个HTML文件,发给别人不用额外传图片文件夹。
07 第三步:代码块——Quarto的核心
Quarto里的R代码放在代码块里,用三个反引号包裹:
```{r}#| label: load-data#| cache: true#| echo: falselibrary(tidyverse)library(gtsummary)# 读取数据df <- readRDS(here::here("data", "processed", "clean_data.rds"))```代码块的选项用#|开头,常用的有:
#| label: 代码块名称(用于交叉引用)#| echo: false # 不显示代码#| cache: true # 缓存结果#| fig-cap: "图1 KM生存曲线" # 图表标题#| fig-width: 8 # 图宽(英寸)#| fig-height: 6 # 图高08 第四步:内联代码——让数字自动更新的关键
这是Quarto最强大的功能,也是最容易被忽视的。
普通写法(需要手动改):
本研究共纳入268例患者,其中男性145例(54.1%)。Quarto写法(数据变了自动更新):
本研究共纳入 `r nrow(df)` 例患者,其中男性 `r sum(df$sex == "男")` 例(`r round(mean(df$sex == "男") * 100, 1)`%)。当你把样本量从268改成241,重新运行报告,这段文字里的数字自动变成正确的值,不会漏改任何一处。
09 第五步:完整的临床分析报告模板
下面是一个完整的临床分析报告.qmd文件,可以直接复制使用:
---title: "临床数据分析报告"author: "研究者姓名"date: todayformat: docx: toc: true number-sections: true fig-dpi: 300execute: echo: false warning: false message: false cache: true---```{r}#| label: setup#| cache: falselibrary(tidyverse)library(gtsummary)library(survival)library(survminer)library(broom)library(here)# 读取数据df <-readRDS(here("data", "processed", "clean_data.rds"))# 定义关键变量(只需要改这里,下面全部自动更新)N_TOTAL <-nrow(df)N_MALE <-sum(df$sex == "男性")PCT_MALE <-round(mean(df$sex == "男性") * 100, 1)N_EVENT <-sum(df$status == 1)```## 研究概述本研究共纳入 `rN_TOTAL` 例患者,其中男性 `rN_MALE` 例(`rPCT_MALE`%),随访期间共发生终点事件 `rN_EVENT` 例。## 基线特征```{r}#| label:tbl-baseline#| tbl-cap: "表1 基线特征"table1 <-df |> select(-patient_id) |> # 这一步去掉了 ID 列 tbl_summary( by = group, statistic = list( all_continuous() ~ "{median} ({p25}, {p75})", all_categorical() ~ "{n} ({p}%)" ) ) |> add_p() |> add_overall() |> bold_labels()table1```两组患者在年龄(P=`r inline_text(table1, variable=age, column="p.value")`)、性别构成方面差异无统计学意义。## 生存分析```{r}#| label: fig-km#| fig-cap: "图1 两组患者的生存曲线"#| fig-width: 8#| fig-height: 7km_fit <- survfit(Surv(time, status) ~ group, data = df)ggsurvplot( km_fit, data = df, pval = TRUE, risk.table = TRUE, palette = c("#000000", "#888888"), ggtheme = theme_classic() + theme(text = element_text(family = "Arial", size = 11)), xlab = "随访时间(月)", ylab = "生存率", legend.title = "分组", legend = c(0.85, 0.85))``````{r}#| label: km-stats#| include: false# 提取中位生存时间km_summary <- summary(km_fit)$tablemedian_g1 <- km_summary["group=治疗组", "median"]median_g2 <- km_summary["group=对照组", "median"]logrank_p <- surv_pvalue(km_fit, df)$pval```治疗组和对照组的中位生存时间分别为`r median_g1` 个月和 `r median_g2` 个月,Log-rank检验显示两组差异`r ifelse(logrank_p < 0.05, "有统计学意义", "无统计学意义")`(P=`r round(logrank_p, 3)`)。## Cox回归```{r}#| label: tbl-cox#| tbl-cap: "表2 多因素Cox回归分析"cox_model <- coxph( Surv(time, status) ~ age + sex + stage + treatment, data = df)cox_model |> tbl_regression(exponentiate = TRUE) |> bold_p()```## 统计方法本研究采用R软件(版本`r R.version.string`)进行统计分析,共纳入`r N_TOTAL`例患者。采用Kaplan-Meier法估计生存曲线,Log-rank检验比较组间差异。以Cox比例风险回归模型进行多因素分析,结果以风险比(HR)及95%置信区间(CI)表示。P<0.05为差异有统计学意义。10 第六步:运行报告
在Positron终端运行:
# 生成Word文档quarto render my_analysis.qmd --to docx# 生成HTML报告quarto render my_analysis.qmd --to html# 同时生成两种格式quarto render my_analysis.qmd或者在Positron里直接点击.qmd文件右上角的Render按钮。
第一次运行需要几分钟(安装依赖),之后因为有缓存,重新运行只需要几秒钟。
11 第七步:自定义Word样式
Quarto生成的Word文档默认样式比较朴素,可以用自己的模板样式。
# 第一步:生成默认模板quarto pandoc -o template.docx --print-default-data-file reference.docx# 第二步:在Word里修改template.docx的样式(字体、行距、标题样式等)# 第三步:在YAML里指定使用这个模板# format:# docx:# reference-doc: template.docx12 第八步:用DeepSeek V4生成Quarto代码
COSTAR提示词——生成完整报告框架
把这段发给DeepSeek V4:
【C·背景】我要用Quarto写一份临床数据分析报告,数据结构如下:[粘贴 glimpse(df) 的完整输出]分析内容:- 基线特征表(按treatment分组)- KM生存曲线(time + status ~ treatment)- 多因素Cox回归(自变量:age, sex, stage, treatment)- 统计方法描述输出格式:Word文档和HTML(同时输出)运行环境:R 4.4.0,已安装tidyverse、gtsummary、survival、survminer【O·目标】生成完整的.qmd文件,要求:1. YAML头部配置(同时输出Word和HTML,cache=true,echo=false)2. Setup代码块(加载包,读取数据,计算关键统计数字存为变量)3. 使用内联代码 `r xxx` 让正文里的数字自动更新4. 每个图表有标签(label)和图题(fig-cap/tbl-cap)5. 统计方法部分用内联代码引用R版本【S·风格】符合临床SCI论文规范,图表格式满足期刊投稿要求【T·语气】解释三个最重要的设置:- cache: true 的作用- #| include: false 和 #| echo: false 的区别- 内联代码为什么比手动复制更可靠【A·受众】会R和ggplot2,但没用过Quarto的临床科研人员【R·格式】完整的.qmd文件内容(从---到最后),可以直接保存运行13 常见问题和解决方法
Q1:运行报错"render failed",什么原因?
最常见的原因是代码块里有报错。Quarto运行时会把所有代码跑一遍,任何一个代码块出错都会导致整个报告失败。
解决方法:先在普通的.R脚本里把所有代码跑通,确认没有报错,再放到Quarto文件里。
Q2:中文显示乱码怎么办?
# 在YAML头部加这两行lang:zhmainfont:"Noto Sans CJK SC"# 或者 "Arial Unicode MS"或者在代码块里设置:
#| label: setuplibrary(showtext)showtext_auto()font_add_google("Noto Sans SC", "noto")Q3:图片分辨率不够,期刊要求300DPI怎么设置?
format:docx:fig-dpi:300# 全局设置fig-width:8# 图宽(英寸)fig-height:6# 图高(英寸)或者在单个代码块里设置:
#| fig-dpi: 300#| fig-width: 8#| fig-height: 6Q4:cache让结果不更新了,怎么清除缓存?
quarto render my_analysis.qmd --cache-refresh或者删除项目文件夹里的_cache文件夹,再重新运行。
Q5:想在报告里引用表格和图的编号怎么做?
如表 @tbl-baseline 所示,两组基线特征无统计学差异。 KM曲线见图 @fig-km。
对应的代码块需要有对应的label
#| label: tbl-baseline#| tbl-cap: "表1 基线特征"Quarto会自动在"如表"后面填入正确的编号,调整顺序时编号自动更新。
这套流程和你现在的工作方式有什么不同
你现在的工作方式大概是这样的:
分析完成 ↓用ggsave导出图片文件 ↓打开Word,Insert图片 ↓把统计结果复制进去 ↓调整格式 ↓导师/审稿人说改数据 ↓重新跑代码 ↓重新导出图片 ↓回到Word,逐一替换数字 ↓检查有没有漏改 ↓祈祷Quarto之后的工作方式:
写.qmd文件(代码+文字混在一起) ↓运行一次 ↓Word和HTML都出来了 ↓导师/审稿人说改数据 ↓改数据 ↓再运行一次 ↓完成14 写在最后
Quarto解决的不是一个小问题,是你每次修改数据时会花掉的几个小时。
这几个小时,乘以你这辈子要做的论文数量,是一个不小的数字。
我见过的最惨的情况,是有人把KM曲线的图更新了,但正文里引用的中位生存时间忘记改了。投稿出去了,被审稿人发现。
用Quarto,这件事不会发生。数字直接从代码里来,改了代码就全改了,不可能只改图忘记改文字。
这一行学习成本,换来的是之后每篇论文都省掉的那几个小时。
今天所有的Quarto代码、完整的报告模板、各种场景的COSTAR提示词,整理成了配套资料包。
加我微信进入R语言AI编程群获取。
扫码添加?

加了微信,告诉我你平时写报告用的是什么方式——Word手动、R Markdown还是其他?我来聊聊Quarto能帮你优化哪个环节。
群里每周都有类似的工作流更新,遇到问题直接在群里发,我当天回。
如果你身边有每次改数据都要手动更新Word的同学,把这篇文章发给他。省的不只是时间,是每次投稿前的那份焦虑。
你现在写分析报告用的是什么方式?评论区说说,我来看看你的流程有没有可以省掉的步骤。