平时使用 ggplot2 绘图时,我们通常会追求线条整齐、布局规范和风格统一。但有些场景下,过于工整的图形反而显得严肃:比如课程演示、科普文章、创意报告、手账风海报,或者需要突出“草图”“讨论稿”感觉的展示材料。
最近发现了一个很有意思的 R 包——ggsketch。它可以把常见的 ggplot2 图形变成带有抖动线条、重复描边和排线填充效果的手绘图。
更重要的是,ggsketch 并不是先生成普通图形,再把图片转换成手绘效果,而是直接提供了一套原生的 ggplot2 图层函数。因此,它仍然能够正常配合 aes()、统计变换、坐标系统、分面和颜色标度使用。生成的结果也可以直接输出为 PNG、PDF 或 SVG,不依赖浏览器和 JavaScript。
github:https://github.com/orijitghosh/ggsketch

1. 安装 ggsketch
ggsketch 可以直接从 CRAN 安装:
install.packages("ggsketch")
也可以通过 pak 安装 GitHub 上的开发版本:
install.packages("pak")
pak::pak("orijitghosh/ggsketch")
加载绘图需要的包:
library(ggplot2)
library(ggsketch)
ggsketch 使用纯 R 实现,不需要编译,也不需要安装额外的 JavaScript 环境。
2. 第一个手绘柱状图
先创建一个简单的数据集:
df <- data.frame(
product = c("Alpha", "Bravo", "Charlie", "Delta"),
units = c(34, 51, 22, 47)
)
使用普通 ggplot2 绘制柱状图时,一般会使用 geom_col()。在 ggsketch 中,只需要将它替换成 geom_sketch_col():
ggplot(df, aes(product, units)) +
geom_sketch_col(
fill = "#7BAFD4",
seed = 1L
) +
labs(
title = "Units sold",
x = NULL,
y = "销量"
) +
theme_sketch()

这里有两个值得注意的地方。
第一个是 geom_sketch_col()。它对应 ggplot2 中的 geom_col(),但柱子的边框会变成略带抖动的双重描边,填充区域也可以呈现排线、涂鸦或者水彩效果。
第二个是 theme_sketch()。这个主题会对坐标轴、背景和文字等元素进行整体调整,让图形风格更加统一。
3. 手绘效果也可以复现
手绘图形通常带有随机扰动。如果每次运行代码后,线条形状都发生变化,会给论文排版、报告更新和自动化绘图带来麻烦。
ggsketch 为随机绘制过程提供了 seed 参数。相同的种子会生成相同的抖动线条,因此图形结果是可以复现的。
例如:
ggplot(df, aes(product, units)) +
geom_sketch_col(
fill = "#7BAFD4",
seed = 123
) +
theme_sketch()
也可以设置整个 R 会话的默认随机种子:
options(ggsketch.seed = 123L)
这样就不需要在每一个图层中重复设置 seed。
4. 常见图形基本都有手绘版本
ggsketch 提供的不只是柱状图。目前,它已经覆盖了大量常见的 ggplot2 图层。
折线图和散点图
geom_sketch_line()
geom_sketch_path()
geom_sketch_point()
例如:
ggplot(mtcars, aes(wt, mpg)) +
geom_sketch_point(
size = 3,
seed = 1
) +
geom_sketch_smooth(
method = "lm",
se = FALSE,
seed = 2
) +
theme_sketch()

由于这些图层仍然遵循 ggplot2 的图层语法,因此可以像普通散点图一样叠加回归线、颜色映射和分组变量。
柱状图、矩形图和棒棒糖图
geom_sketch_col()
geom_sketch_bar()
geom_sketch_rect()
geom_sketch_tile()
geom_sketch_lollipop()

分布类图形
geom_sketch_histogram()
geom_sketch_density()
geom_sketch_violin()
geom_sketch_boxplot()
geom_sketch_beeswarm()
geom_sketch_ridgeline()
geom_sketch_dotplot()
geom_sketch_ecdf()

例如,将箱线图改为手绘风格:
ggplot(mpg, aes(class, hwy)) +
geom_sketch_boxplot(
fill = "#F2C14E",
fill_style = "hachure",
seed = 1
) +
coord_flip() +
theme_sketch()

面积和区间类图形
geom_sketch_polygon()
geom_sketch_ribbon()
geom_sketch_area()
geom_sketch_smooth()

对比和变化类图形
geom_sketch_dumbbell()
geom_sketch_slope()
geom_sketch_bump()
geom_sketch_waterfall()
geom_sketch_funnel()
geom_sketch_pyramid()

此外,包中还提供了饼图、华夫饼图、树状矩形图、旭日图、雷达图、弦图、桑基类图形、甘特图、日历图、树状图以及简单的网络图和地图图层。








5. 多种手绘填充效果
手绘图的趣味性很大程度上来自填充纹理。ggsketch 的 fill_style 参数提供了多种填充方式,包括:
"hachure"
"cross_hatch"
"zigzag"
"zigzag_line"
"scribble"
"dots"
"dashed"
"stipple"
"pencil_shade"
"solid"
"watercolor"

其中:
- hachure 是单方向排线;
- cross_hatch 是交叉排线;
- scribble 类似随手涂鸦;
- dots 使用点状纹理;
- pencil_shade 模拟铅笔阴影;
- watercolor 模拟半透明水彩颜料。
例如,可以比较几种不同的填充方式:
ggplot(df, aes(product, units)) +
geom_sketch_col(
aes(fill = product),
fill_style = "cross_hatch",
seed = 1
) +
guides(fill = "none") +
theme_sketch()
还可以通过下面几个参数继续控制排线:
hachure_angle
hachure_gap
fill_weight
例如:
ggplot(df, aes(product, units)) +
geom_sketch_col(
fill = "#80B1D3",
fill_style = "hachure",
hachure_angle = –45,
hachure_gap = 5,
fill_weight = 1,
seed = 1
) +
theme_sketch()
6. 调整线条的“潦草程度”
ggsketch 提供了几个通用参数,用来控制手绘线条的外观。
roughness
roughness 控制线条抖动的幅度。
roughness = 0
接近用尺子画出的直线。
roughness = 1
是比较自然的默认手绘效果。
roughness = 3
会产生更加松散和夸张的草图效果。
bowing
bowing 控制线段向外弯曲的程度。数值越大,线条越不像严格的直线。
n_passes
n_passes 控制一条边被重复描绘多少次。
n_passes = 2
通常会形成经典的手绘双线效果。
综合起来,可以写成:
ggplot(df, aes(product, units)) +
geom_sketch_col(
fill = "#B3DE69",
roughness = 2,
bowing = 1.5,
n_passes = 2,
seed = 1
) +
theme_sketch()
7. 配合颜色映射和分面使用
因为 ggsketch 是按照 ggplot2 扩展机制实现的,所以仍然可以使用熟悉的美学映射:
ggplot(mpg, aes(class, fill = drv)) +
geom_sketch_bar(
position = "dodge",
seed = 1
) +
scale_fill_brewer(palette = "Set2") +
theme_sketch()
也可以进行分面:
ggplot(mpg, aes(displ, hwy, colour = drv)) +
geom_sketch_point(seed = 1) +
facet_wrap(~ year) +
theme_sketch()
这也是 ggsketch 与一些后期图片处理方案的重要区别:它生成的并不是一个无法继续修改的图片特效,而是真正参与 ggplot2 图形语法的绘图图层。
8. 添加手绘标注
除了数据图层,ggsketch 还提供了多种手绘标注函数,例如:
annotate_sketch()
geom_sketch_bracket()
annotate_sketch_arrow()
annotate_sketch_callout()
annotate_sketch_highlight()
annotate_sketch_underline()
geom_sketch_text_repel()

这些元素比较适合课程讲解、数据故事和演示文稿,因为它们能够引导读者关注图中的重点信息。
9. 不只是“抖动线条”
ggsketch 还有一个比较特别的功能:通过排线密度表现连续的明暗变化。
例如,geom_sketch_engrave() 可以根据数值大小改变线条密度,产生类似蚀刻画、版画或者纸币纹理的效果。普通的图案填充包通常是在区域内重复铺设某个固定图案,而这种方法会真正根据数值计算排线密度。
相关函数包括:
geom_sketch_engrave()
geom_sketch_shade()
scale_engrave()

对于地形表面、二维密度或需要表现连续色调的图形,这种效果会很有辨识度。
10. 保存图形
由于 ggsketch 使用 R 的原生图形系统实现,普通的 ggsave() 就可以保存结果:
p <- ggplot(df, aes(product, units)) +
geom_sketch_col(
fill = "#7BAFD4",
seed = 1
) +
theme_sketch()
ggsave(
"ggsketch_plot.png",
plot = p,
width = 8,
height = 5,
dpi = 300
)
也可以输出 PDF 或 SVG:
ggsave("ggsketch_plot.pdf", p)
ggsave("ggsketch_plot.svg", p)
包中还提供了专用的 ggsketch_save() 辅助函数。由于图形最终是原生的 grid grob,它可以在屏幕、PNG、PDF 和 SVG 等不同设备上正常绘制。
11. 哪些场景适合使用 ggsketch?
ggsketch 并不一定适合所有数据可视化场景。
在正式论文、审计报告或者强调测量精度的图表中,传统的简洁风格通常更加稳妥。但在下面这些场景中,手绘风格可能会带来不错的效果:
- 数据分析课程和教学演示;
- 科普文章和公众号推文;
- 创意数据故事;
- 工作坊或头脑风暴材料;
- 项目早期方案和概念草图;
- 儿童教育和轻松主题展示;
- 希望弱化“机器生成感”的信息图。
手绘图形的价值不只是“好看”。它还可以向读者传达一种语气:这张图可能是在帮助大家理解概念、探索思路,而不是在展示一个不可质疑的最终结论。
12. 小结
ggsketch 可以看作是 ggplot2 的一套手绘图层扩展。
它的主要特点包括:
如果已经熟悉 ggplot2,使用 ggsketch 的学习成本并不高。很多时候,只需要把:
geom_col()
替换为:
geom_sketch_col()
再加上:
theme_sketch()
一张普通的数据图,就能立即变成更轻松、更有故事感的手绘图。
官方文档中还提供了各类图层的完整示例和效果展示,感兴趣的读者可以继续探索。
网硕互联帮助中心





评论前必须登录!
注册