imd 统计教程 · 统计图表与论文表格
← 教程首页 返回本模块

全网首发!R包fontsci,中英字体玩出花,中文期刊再也不乱码,一行代码搞定大论文

聂志强实战医学统计2026-03-05 22:16:44广东

全网首发,毕业大论文-中文期刊双语绘图R包。可以分享给学生用,主要还是解决多图大论文和中文期刊混合图的问题。每次修图代码变了,手动操作全都要批量手动一次。用R包可以一键搞定更新。可单纯中文,单纯英文,中英混排同行混排,自定义字体任意切换。需要见文末!进群8拿fontsci包,也可关注任务获取

用R在完成漂亮的英文SCI图表后,要转移到中文期刊或者硕博大论文,这时候中文字符乱码总是会出现“口口口”这种不显示中文,十分头痛。另外,中文字体修改设置起来也费劲,同一个坐标轴兼顾楷体times总是无法完成。这个问题许多年都没有人来处理。

尽管我3年前之前发表过一个showtext解决中英文同图混排方案,现在看起来处理有些青涩。后期我也尝试使用了几个方法来解决。但是目前为止,我都没有看到一个为了中文,兼顾英文的绘图代码,能随心所欲的指定中英双混字体。 这个问题的解决直到有一天我心血来潮,撰写国自然需要提供中文图片支持,于是又拾掇起来,顺便在这里把全部中英文双语混排解决内容分享给大家,一起学习我的思考思路和逻辑进化过程。最后,我把平时的心得加一起配合案例做了一个详细代码版本。同时,为了新手和医生们方便实用,我开发了新R包fontsci,一行代码就解决字体问题,欢迎大家使用。

一、showtext + sysfonts

用showtext同一句混排字体还是错误。请看图和代码。


pacman::p_load(ggplot2, showtext, sysfonts)
showtext_auto()
showtext_opts(dpi = 300)
# 字体注册(Windows:推荐绝对路径)
sysfonts::font_add("times""C:/Windows/Fonts/times.ttf")
sysfonts::font_add("msyh",  "C:/Windows/Fonts/msyh.ttc")
# 模拟数据(与你原始一致)
effect_sizes <- seq(0.2, 1.5, by = 0.01)
sample_sizes <- 500 / (effect_sizes^2)
df <- data.frame(Effect_Size = effect_sizes, Sample_Size = sample_sizes)
p1 <- ggplot(df, aes(x = Effect_Size, y = Sample_Size)) +
  geom_area(fill = "#FF9A9E", alpha = 0.25) +
  geom_line(color = "#FF6B81", linewidth = 1.2) +
  labs(
    title = "临床试验 (RCT) 中效应量与样本量的关系示意",
    subtitle = "The Relationship Between Effect Size and Sample Size",
    x = "预期的效应量(如 MD、差异率等)",
    y = "所需的总样本量"
  ) +
  theme_minimal() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    plot.background  = element_rect(fill = "white", color = NA),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    axis.line  = element_line(color = "#2D3436", linewidth = 0.6),
    axis.ticks = element_line(color = "#2D3436"),
    
    text = element_text(family = "msyh", size = 15, color = "#2D3436"),
    plot.title    = element_text(family = "msyh", face = "bold", hjust = 0.5),
    plot.subtitle = element_text(family = "times", hjust = 0.5, margin = margin(b = 12)),
    
    axis.title.x = element_text(family = "msyh", margin = margin(t = 10)),
    axis.title.y = element_text(family = "msyh", margin = margin(r = 10)),
    axis.text    = element_text(family = "times")
  ) +
  annotate("point", x = 0.35, y = 500/(0.35^2), color = "#7BED9F", size = 4) +
  annotate("text", x = 0.40, y = 500/(0.35^2), label = "效应量小:需极大的样本量",
           hjust = 0, color = "#2F3542", family = "msyh", size = 5) +
  annotate("point", x = 1.2, y = 500/(1.2^2), color = "#70A1FF", size = 4) +
  annotate("text", x = 1.2, y = 500/(1.2^2) + 900, label = "效应量大:只需较小样本量",
           hjust = 0.5, color = "#2F3542", family = "msyh", size = 5)
print(p1)
# 导出 PDF 
ggsave("RCT_effectsize_showtext.pdf", plot = p1, width = 8, height = 6, device = "pdf")

二、GB1

pdf(..., family="GB1") 是基础 R 时代的一种经典“妥协方案”。GB1 调用的是 Adobe 预置的基础中文字体包(通常是比较生硬的宋体)。它虽然能让中文显示出来(不变成方块),但缺点非常致命:它无法调用你电脑里现代、美观的字体(比如微软雅黑),更无法做到在同一行字里让中文显示为微软雅黑,而英文精确显示为 Times New Roman。 

比如下面的案例,简单粗暴直接GB1就不会乱码!但是问题也来了,对于追求极致美学的朋友尴尬症要发作。GB1不支持混排,它无法在一个词或一句话中同时处理两种字体家族!


pacman::p_load(ggplot2)
effect_sizes <- seq(0.2, 1.5, by = 0.01)
sample_sizes <- 500 / (effect_sizes^2)
df <- data.frame(Effect_Size = effect_sizes, Sample_Size = sample_sizes)

p1 <- ggplot(df, aes(x = Effect_Size, y = Sample_Size)) +
  geom_area(fill = "#FF9A9E", alpha = 0.25) +
  geom_line(color = "#FF6B81", linewidth = 1.2) +
  labs(
    title = "临床试验 (RCT) 中效应量与样本量的关系示意",
    subtitle = "The Relationship Between Effect Size and Sample Size",
    x = "预期的效应量(如 MD、差异率等)",
    y = "所需的总样本量"
  ) +
  theme_minimal() +
  theme(text = element_text(size = 15))  # 字体由 pdf 设备的 family 控制
p1 

# ? 关键:GB1 + GBK
pdf("RCT_effectsize_GB1.pdf", width = 8, height = 6,
    family = "GB1")
print(p1)
dev.off()

三、智荟思路原创解析

R 语言的传统绘图体系中,中英文字体无法精确混排,是因为原生的 ggplot2 把一段文字当成了一个不可分割的整体。智荟混排核心思路拆解为以下三个关键层级来理解:

  1. 痛点:原生 element_text() 的“一刀切”机制:在原生的 ggplot2 中,当我们使用 theme(axis.title.x = element_text(family = "msyh")) 时,系统底层就像拿着一个“微软雅黑”的印章,把 x 轴标签里的所有字符(不管是中文、英文还是数字)全部盖上这个字体的烙印。而原生函数没有解析能力,它看不懂你想把哪几个字单独拎出来换字体,只能“一刀切”。
  2. 破局点:引入 ggtext::element_markdown() 解析 HTML:这个函数的核心魔法,就是把传统的纯文本渲染器 element_text(),替换成了能够解析富文本(HTML/Markdown)的渲染器 element_markdown()。一旦替换成这个引擎,ggplot2 就不再把文字看作一坨死板的字符串,而是把它当作网页代码来读取。在 theme_zhihui_stat 中,我设置了 axis.title.x = element_markdown(family = "msyh")。这意味着这段“网页”的默认全局字体是微软雅黑。精准打击(局部覆盖): 当它在 labs() 里读到 RCT 这一段时,它会像浏览器解析网页一样,识别出  标签和里面的 CSS 样式 (style)。最终效果: 它会自动把“RCT”这三个字母从默认的微软雅黑中剥离出来,单独使用 Times New Roman 渲染,而标签外的中文字符依然保持微软雅黑。这就像在 Word 里用鼠标高亮选中英文,单独修改了字体一样。
  3. 底层支撑:showtext_auto() 的图形拦截 虽然 ggtext 负责“发号施令”(告诉系统这里要用 Times,那里要用微软雅黑),但最终要把这些美丽的字体画到 PDF 矢量图里,靠的是 showtext。showtext 会在底层拦截这些指令,去你的 Windows 系统字体文件夹里找到真正的 .ttf 和 .ttc 字体文件,然后把这些文字转化为数学几何路径(Path)画在 PDF 上。

#============ 一、随机化分组 =================
# 自动安装并加载所需的包。ggplot2用于绘图,showtext用于字体渲染,ggtext用于解析HTML字体标签
pacman::p_load(ggplot2, showtext, ggtext) 
setwd("D:/聂个人文件/副业项目/广州智荟数据有限公司/B款环境与真实世界纵向数据/9.RCT")
#---本代码为实战医学统计A款课程,请勿商业传播,违者追溯获利至高20倍
# 【核心:接管底层渲染】开启全局 showtext 自动渲染。必须在绘图前运行!
showtext_auto()
# 【加载本地字体】给系统字体起个简短的代号。
# "times" 指代 Times New Roman,"msyh" 指代微软雅黑。
font_add("times""times.ttf")
font_add("msyh""msyh.ttc")
# 【构建模拟数据】根据预期效应量(反比平方)生成样本量曲线数据
effect_sizes <- seq(0.2, 1.5, by = 0.01)
sample_sizes <- 500 / (effect_sizes^2) 
df <- data.frame(Effect_Size = effect_sizes, Sample_Size = sample_sizes)
# 开始构建 ggplot 对象
p <- ggplot(df, aes(x = Effect_Size, y = Sample_Size)) +
# 添加曲线下方的半透明面积,alpha=0.25 控制透明度,营造现代数据可视化质感
  geom_area(fill = "#FF9A9E", alpha = 0.25) + 
# 绘制主曲线,linewidth=1.2 加粗线条
  geom_line(color = "#FF6B81", linewidth = 1.2) + 

# 【精细化标签】使用 HTML <span> 标签包裹纯英文部分,强制将其渲染为 times 字体。
# 未被包裹的中文字符将默认使用下方 theme 中设置的 msyh 字体。
  labs(
    title = "临床试验 (<span style='font-family:times'>RCT</span>) 中效应量与样本量的关系示意",
    subtitle = "The Relationship Between Effect Size and Sample Size",
    x = "预期的效应量 (<span style='font-family:times'>Effect Size</span>, 如 <span style='font-family:times'>MD</span>, 差异率等)",
    y = "所需的总样本量 (<span style='font-family:times'>Sample Size</span>)"
  ) +
# 使用极简主题作为基础底稿
  theme_minimal() +
# 【主题细节微调】覆盖默认设置,打造干净的排版
  theme(
    # 彻底移除灰色背景和网格线,确立纯白画布
    panel.background = element_rect(fill = "white", color = NA),
    plot.background = element_rect(fill = "white", color = NA),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    # 手动添加深灰色的 X 轴和 Y 轴实线及刻度线
    axis.line = element_line(color = "#2D3436", linewidth = 0.6),
    axis.ticks = element_line(color = "#2D3436"),
    # 设定全局默认字体为微软雅黑,字号15
    text = element_text(family = "msyh", size = 15, color = "#2D3436"),
    # 【核心:解析HTML】必须使用 element_markdown 替代 element_text,
    # 这样系统才能读懂 labs() 里的 <span> 标签,实现中英混排!
    plot.title = element_markdown(family = "msyh", size = 15, face = "bold", hjust = 0.5),
    axis.title.x = element_markdown(family = "msyh", size = 15, margin = margin(t = 10)),
    axis.title.y = element_markdown(family = "msyh", size = 15, margin = margin(r = 10)),
    # 副标题和坐标轴数字全是英文,直接使用 element_text 指定为 times 字体即可
    plot.subtitle = element_text(family = "times", size = 15, hjust = 0.5, margin = margin(b = 12)),
    axis.text = element_text(family = "times", size = 15) 
  ) +
   # 【添加自定义图例/文本标注】
  annotate("point", x = 0.35, y = 500/(0.35^2), color = "#7BED9F", size = 4) +
  annotate("text", x = 0.40, y = 500/(0.35^2), label = "效应量小:需极大的样本量"
           hjust = 0, color = "#2F3542", family = "msyh", size = 5) +
  annotate("point", x = 1.2, y = 500/(1.2^2), color = "#70A1FF", size = 4) +
  annotate("text", x = 1.2, y = 500/(1.2^2) + 900, label = "效应量大:只需较小样本量"
           hjust = 0.5, color = "#2F3542", family = "msyh", size = 5)
# 在控制台打印图形
print(p)
#--导出PDF
ggsave(
  filename = "RCT_effectsize_.pdf"
  plot = p, 
  width = 8, 
  height = 6
)

四、智荟中英混排fontsci包

为了节省大家时间和不想看三部分的基础思路,我开发了一个小型R包。需要大家用自己的绘图代码先跑,如果出现中文乱码或者字体不好,再用这个一键函数,存档PDF即可。美美哒的图就诞生了!注意,windows系统没问题,但是mac系统会有问题。就是Cairo包依赖 X11 图形库,而 macOS 默认不包含这些库,macbook用户需要手动安装XQuartz,https://www.xquartz.org/。这个自动函数就是fontsci,可以自动接管原始 ggplot 对象,实现中英混排、主题美化、防乱码 PDF 导出!

CRAN 上有几个处理字体和渲染的基础大牛包,但它们都有各自的局限性,并没有解决“混排”这个痛点:

showtext / sysfonts:完美解决了 R 语言全局加载自定义字体(尤其是中文字体)的问题,但它只能给整个文本应用同一种字体。如果设置了宋体,英文字母也会变成宋体自带的英文(通常比较难看,不符合 SCI 要求的 Arial/Times New Roman)。

ggtext:支持在 ggplot2 中使用 HTML/Markdown 语法。如果要实现混排,用户必须手动在每个标签里写 P-value = 0.05,这在处理成百上千的数据点或复杂的坐标轴时,工作量是灾难性的。fontsci巧妙地将正则自动匹配(自动提取英文和数字)与 ggtext 的底层渲染结合了起来,真正做到了“一键智能混排”。

为了真正解决中文字体问题,我开发了fontsci包。目前内置了6款中文、6款英文自由组合,也支持自定义字体指定

字体类别
代码输入名称 (填入参数的词)
对应的字体风格及系统文件
中文字体
 (font_cn)
"微软雅黑"
现代无衬线体,屏幕阅读清晰 (msyh.ttc)

"宋体"
传统衬线体,学术论文标配 (simsun.ttc)

"黑体"
粗壮醒目,适合做大标题 (simhei.ttf)

"楷体"
优雅柔和,具有手写韵味 (simkai.ttf)

"仿宋"
笔画均匀,常用于公文格式 (simfang.ttf)

"幼圆"
圆润可爱,适合轻松活泼的图表 (SIMYOU.TTF)
英文字体
 (font_en)
"Arial"
经典无衬线体,SCI 期刊最爱之一 (arial.ttf)

"Times New Roman"

 (或 "新罗马")
经典衬线体,学术出版绝对主流 (times.ttf)

"Calibri"
现代圆润无衬线体,视觉柔和 (calibri.ttf)

"Tahoma"
窄体无衬线,适合图例空间紧张时 (tahoma.ttf)

"Verdana"
宽体无衬线,小字号下依然极易辨认 (verdana.ttf)

"Courier New"
等宽字体,适合展示基因序列或代码 (cour.ttf)

下面是具体是6个案例,让你熟悉如何使用fontsci函数。其实非常非常简单,就一行代码!



#-------eg1 --------
# 实战医学统计B款课程:RCT 效应量与样本量关系 (函数化重构版)
#  ?? 原始图表  orifig---
effect_sizes <- seq(0.2, 1.5, by = 0.01)
sample_sizes <- 500 / (effect_sizes^2) 
df <- data.frame(Effect_Size = effect_sizes, Sample_Size = sample_sizes)
#---?? 原始绘图代码!乱码或非混排
orifig <- ggplot(df, aes(x = Effect_Size, y = Sample_Size)) +
  geom_area(fill = "#FF9A9E", alpha = 0.25) + 
  geom_line(color = "#FF6B81", linewidth = 1.2) + 
# ?? 关键修改! 显式添加连续型坐标轴,让 fontsci 能够成功拦截
  scale_x_continuous() + 
  scale_y_continuous() +
  labs(
    title = "临床试验 (RCT) 中效应量与样本量的关系示意",
    x = "预期的效应量 (Effect Size, 如 MD 等)",
    y = "所需的总样本量 (Sample Size)"
  )+
# (可选) 教学用点位与文字标注。由于这些属于图内注释而非坐标轴,
# 建议根据当前使用的中文字体 (MY_CN_FONT) 统一定义,保持美观。
  annotate("point", x = 0.35, y = 500/(0.35^2), color = "#7BED9F", size = 4) +
  annotate("text", x = 0.40, y = 500/(0.35^2), label = "效应量小:需极大的样本量"
           hjust = 0, color = "#2F3542",   size = 5) +

  annotate("point", x = 1.2, y = 500/(1.2^2), color = "#70A1FF", size = 4) +
  annotate("text", x = 1.2, y = 500/(1.2^2) + 900, label = "效应量大:只需较小样本量"
           hjust = 0.5, color = "#2F3542",  size = 5)
orifig
# 一键导出依然不变
# ggsave("RCT_effect_size_1.pdf", plot = orifig, width = 8, height = 6)


#-------?? 智荟一键中英文混排!-------
library(fontsci)
final_orifig <- fontsci::fontsci(orifig,
                                 font_cn = "微软雅黑",
                                 font_en = "Times New Roman",
                                 #自定义字体
                                 custom_cn_files = NULL,
                                 custom_en_files = NULL,
                                 enable_showtext = TRUE
)
ggsave(
  filename = "testeg1.pdf"
  plot = final_orifig, 
  width = 8, 
  height = 6,
bg = "white",
  device = cairo_pdf  
)

#-------eg2 --------
#导入必要的包
library(ggplot2)
#设置主题样式
theme_set(theme_minimal(base_size=10))
# 原始图,英文字很丑 模拟一个临床随访数据的趋势
p <- ggplot(data=data.frame(x=c(1, 2, 3), y=c(1.2, 2.5, 3.8)), aes(x=x, y=y)) +
# 添加折线和数据点,让图表更丰满
  geom_line(color="blue", linewidth=1) +
  geom_point(color="red", size=3) + 

# 重点测试区域:显式定义包含中英混排的坐标轴标签
  labs(
    title = "欢迎关注公众号:实战医学统计, zhihuishuju.com",
    x = "随访时间 (Follow-up Time, Months)",     # 新增:X 轴含义
    y = "生物标志物水平 (Biomarker Level, ng/mL)"# 新增:Y 轴含义
  ) +

# 原始的粗糙主题设置
  theme(
    plot.title = element_text(hjust=0.5, color="red", face="bold"),
    axis.text = element_text(color="black")
  )
p

# 把渲染好的图赋值给zhihui_hunpai
final_orifig <- fontsci( p )
final_orifig  
ggsave(
  filename = "testeg2.pdf"
  plot = final_orifig, 
  width = 4, 
  height = 3,
bg = "white"
)

#------eg3 ----------- 
set.seed(2026)
data <- data.frame(
  x = rep(1:10, 2),
  y = c(cumsum(runif(10, 20, 50) + c(rep(0,5), 100, rep(0,4))), cumsum(runif(10, 10, 30))),
  group = rep(c("公众号A(主运营号)""公众号B(矩阵引流号)"), each = 10)
)

p <- ggplot(data, aes(x = x, y = y, color = group, fill = group)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 3.5, shape = 21, stroke = 1.2, fill = "white") +
  labs(
    title = "公众号累计粉丝增长趋势分析",
    subtitle = "近10天核心账号运营数据追踪对比",
    x = "时间跨度",
    y = "累计粉丝数(人)",
    color = "账号类别", fill = "账号类别"
  ) +
  annotate("text", x = 4.8, y = 400, label = "此处发布爆款文章\n带来粉丝显著暴涨", size = 4, color = "#d93025", fontface = "bold", hjust = 0.5) +
  annotate("segment", x = 4.8, y = 365, xend = 5.9, yend = 365, arrow = arrow(length = unit(0.3, "cm")), color = "#d93025", linewidth = 0.8) +
  scale_x_continuous(breaks = 1:10, labels = paste0("第", 1:10, "天")) +
  scale_y_continuous() + 
  scale_color_manual(values = c("公众号A(主运营号)" = "#1a73e8""公众号B(矩阵引流号)" = "#34a853")) +
  theme_minimal(base_size = 14) + 
  theme(
    plot.title = element_text(color = "#333333", size = 18, face = "bold", hjust = 0.5, margin = margin(b = 8)),
    plot.subtitle = element_text(color = "#666666", size = 12, hjust = 0.5, margin = margin(b = 20)),
    legend.position = "top", legend.title = element_blank(), 
    legend.text = element_text(color = "#333333", size = 12), legend.margin = margin(b = -10),
    axis.line = element_line(colour = "#b3b3b3", linewidth = 0.8),
    axis.text.x = element_text(color = "#555555", size = 11, vjust = 1, margin = margin(t = 5)),
    axis.text.y = element_text(color = "#555555", size = 11, hjust = 1, margin = margin(r = 5)),
    axis.title.x = element_text(color = "#333333", size = 13, face = "bold", margin = margin(t = 15)),
    axis.title.y = element_text(color = "#333333", size = 13, face = "bold", margin = margin(r = 15)),
    panel.grid.major = element_line(color = "#e6e6e6", linetype = "dashed"), panel.grid.minor = element_blank(),
    panel.border = element_rect(fill = NA, color = "#e6e6e6", linewidth = 1, linetype = "solid"),
    plot.margin = margin(20, 30, 20, 20)
  )

# 3. 渲染与导出
# ==========================================
final_orifig <- fontsci( p)
ggsave(
  filename = "testeg3.pdf"
  plot = final_orifig, 
  width = 8, 
  height = 6,
bg = "white",
  device = cairo_pdf  
)

我还开发了一个参数支持自定义字体,大家喜欢什么字体可以去网站下载,放到文件夹路径就可以了!比如https://font.chinaz.com/210126109110.htm,手写字体很可爱的。




#--------eg5 自定义字体---------
setwd("D:/聂个人文件/我的公众号/中英文字体混排")
library(ggplot2)
library(fontsci)
# 1. 准备您的基础图形
# 1. 准备基础图形 (补全坐标轴参数,打破 Waiver 机制)
p <- ggplot(mtcars, aes(mpg, wt)) + 
  geom_point() + 
  scale_x_continuous() +   # ?? 显式声明 X 轴,让 fontsci 能拦截数字
  scale_y_continuous() +   # ?? 显式声明 Y 轴,让 fontsci 能拦截数字
  labs(
    title = "测试:米开星空下的邂逅 P-value < 0.05",
    x = "Miles per Gallon (mpg)"# 顺便加上规范的轴标签,测试英文字体效果
    y = "Weight (1000 lbs)"
  )
p
# 2. 定义自定义字体路径 (必须是 list 格式,指定 regular)
my_custom_cn <- list(regular = "./米开星空下的邂逅.ttf")
# 3. 传入 fontsci 函数
p_new <- fontsci(p, 
                 font_en = "新罗马",               # 英文依然用自带字典里的 Arial
                 custom_cn_files = my_custom_cn)  # 中文强行挂载外部 ttf

p_new
ggsave(
  filename = "自定义字体.PDF"
  plot = p_new, 
  width = 6, 
  height = 4,
bg = "white",
  device = cairo_pdf  # 强烈建议保持 cairo_pdf 渲染,防止中文字体在 PDF 中变乱码或重叠
)


# 3. 定义自定义字体路径 (必须是 list 格式,指定 regular)
my_custom_cn <- list(regular = "./米开星空下的邂逅.ttf")
my_custom_en <- list( regular = "D:/fonts/oblar-2vnyk.otf")
# 3. 传入 fontsci 函数
p_new2 <- fontsci(p, 
                 custom_cn_files = my_custom_cn,   
                 custom_en_files = my_custom_en) 

p_new2

ggsave(
  filename = "自定义字体2.PDF"
  plot = p_new2, 
  width = 6, 
  height = 4,
bg = "white",
  device = cairo_pdf  # 强烈建议保持 cairo_pdf 渲染,防止中文字体在 PDF 中变乱码或重叠
)

利用fontsci还可以做成酷炫的手绘风格海报,切换手绘字体即可!



#------eg6--------- 
pacman::p_load(ggplot2, fontsci,showtext, ggtext,Cairo) 
# 1. 模拟临床患者基因表达与疗效评分数据
set.seed(2026)
df <- data.frame(
  Biomarker = rnorm(200, mean = 50, sd = 15)
)
df$Response <- df$Biomarker * 0.4 + rnorm(200, mean = 20, sd = 10)
df$Group <- ifelse(df$Biomarker > 55, "High Expression (高表达组)""Low Expression (低表达组)")

# 2. 构建测试图表 (满是中英文和符号的混合)
p_test <- ggplot(df, aes(x = Biomarker, y = Response, color = Group)) +
  geom_point(alpha = 0.6, size = 3.5) +
  geom_smooth(method = "lm", color = "#d93025", fill = "#fce8e6", linetype = "dashed") +

# ?? 显式声明坐标轴 (fontsci 拦截必需)
  scale_x_continuous() +
  scale_y_continuous() +
  scale_color_manual(values = c("High Expression (高表达组)" = "#e64a19",
                                "Low Expression (低表达组)" = "#1976d2")) +

# ?? 核心测试区:密集的混排文本
  labs(
    title = "Target Gene 表达量与临床获益 (Clinical Benefit) 的相关性分析",
    subtitle = "Pearson R = 0.45, P-value < 0.001 (95% CI: 0.32 - 0.56)",
    x = "靶基因相对表达量 (Relative Expression Level, Log2 Transformed)",
    y = "客观缓解率评估分数 (Objective Response Score, ORS)",
    color = "患者亚组 (Subgroup)"
  ) +

# ?? 图内注释测试区:包含符号 ≤, <,以及中英文换行
  annotate("text", x = 15, y = 70, 
           label = "重点关注区域 (Hotspot):\n当 Expression ≤ 30 时,\n疗效出现显著下降 (P < 0.05)"
           hjust = 0, color = "#424242", size = 5, fontface = "bold") +

  theme_minimal(base_size = 14) +
  theme(
    legend.position = "top",
    plot.title = element_text(face = "bold", size = 16, color = "#222222"),
    plot.subtitle = element_text(color = "#555555", margin = margin(b = 15)),
    axis.title.x = element_text(margin = margin(t = 12)),
    axis.title.y = element_text(margin = margin(r = 12))
  )

#中文用宋体(学术)或雅黑(现代),英文用 Arial 或 Times New Roman
 p_final <- fontsci(p_test, font_cn = "微软雅黑", font_en = "Arial")
# 打印查看
 p_final

# 1. 在原图基础上,直接修改 theme 颜色
p_test_colored <- p_test + 
  theme(
    # 主标题设为红色 (也可以用 Hex 颜色码如 "#FF0000")
    plot.title = element_text(color = "red"),
    
    # X轴和Y轴的标题 (Title) 设为蓝色
    axis.title.x = element_text(color = "blue"),
    axis.title.y = element_text(color = "blue"),
    
    # 如果想让 X轴和Y轴的刻度数字 (Text) 也变成蓝色,加上这两行:
    axis.text.x = element_text(color = "blue"),
    axis.text.y = element_text(color = "blue")
  )

my_custom_cn <- list(regular = "./米开星空下的邂逅.ttf")
my_custom_en <- list( regular = "./chocolatetower.ttf")
#deathrattle.otf 还可以其他otf等字体
# 3. 传入 fontsci 函数
p_new3 <- fontsci(p_test_colored, 
                  custom_cn_files = my_custom_cn,   
                  custom_en_files = my_custom_en) 

p_new3

ggsave(
  filename = "自定义字体3.PDF"
  plot = p_new3, 
  width = 8, 
  height = 6,
bg = "white",
  device = cairo_pdf  # 强烈建议保持 cairo_pdf 渲染,防止中文字体在 PDF 中变乱码或重叠
)

五、发散思维

1 最近阅读量低迷都是很好的内容却没人看。上一篇预试验样本量软件,无论rct、还是队列设计都是硬刚需!非常棒的内容,BMJ去年9月见刊的,为什么如此优秀内容还不如朋友AI随便生成的短文,后者有3万阅读量。这是一个很奇葩的推送规则。我询问粉丝才发现个可怕的现象。公腾讯居然不给推送粉丝内容了,真是服了!无语+伤心。

2 为了获取第一时间公众号新发内容,大家可以定时点击公众号,打开后才有内容刷新!或者嫌麻烦,就进群8经常有内容和方法代码分享。老粉丝就不用进8群了,别占位了。

小结

fontsci包一行代码fontsci (p),p是旧图。中文英文自定义字体随心所欲,还支持医学统计高频符号!

PS: 需要首发fontsci包,进群8或 点赞+星标公众号或 转发朋友圈后截图发后台,3选1即可。有效期10天