厚缊

诹图——ggvwline简介

厚缊 / 2020-02-18


ggvwline最早的版本是2019年5月学习ggplot2时尝试封装的,但是当时对ggplot2的运行机制、R包开发以及R的一些习惯均不是很熟悉,导致这一如此好玩的包使用体验非常不好。最近的半年,因为ggcor的缘故,反复摸索折腾,感觉自己真的学到了很多东西,能包装一个更简洁好用的ggvwline,所以就趁着间隙,重新写了主要的函数。

ggvwlinevwline包的封装,提供了一组可以绘制可变宽度的geom函数,但是目前的感觉是“cooln but useless”,若是谁有更好的应用想法,可以联系我。

安装

ggvwline还处于测试阶段,需要从github安装:

## install.packages("devtools")
devtools::install_github("houyunhuang/ggvwline")

几个主要函数

ggvwline的核心就是8个geom_*()函数,其中geom_quad_line()geom_bezier_line()用来画二次和贝塞尔曲线,geom_vwline()geom_vwcurve()等六个函数用来处理可变宽度曲线。从本质上说,可变宽度曲线是多边形(polygon),因此有填充色。

二次和贝塞尔曲线

这两个函数在ggforce里面都有另外的实现,若是没有特殊的用途可能会在以后的版本中删除。

library(ggvwline)
library(ggplot2)

## 二次曲线
df <- data.frame(
  x = rnorm(3),
  y = rnorm(3)
)
ggplot(df, aes(x, y)) + geom_quad_line()

## 贝塞尔曲线
df2 <- data.frame(
  x = rnorm(4),
  y = rnorm(4)
)
ggplot(df2, aes(x, y)) + geom_bezier_line()

可变宽度曲线(一)

geom_vwline()geom_vwcurve()geom_vwxspline()可以分别对应线段(geom_segment())、曲线(geom_curve())和样条曲线(geom_smooth()),不同的是可变宽度曲线可以单独指定曲线上每个点位处的宽度。所有可变宽度曲线均有可映射变量width

df3 <- data.frame(
  x = 1:10,
  y = 1:10,
  width = 2 * sin(seq(0, pi, length.out = 10))
)
## 默认宽度1厘米
ggplot(df3, aes(x, y)) + geom_vwline() 

## 跨度映射
ggplot(df3, aes(x, y, width = width)) + geom_vwline()

## 默认宽度单位是厘米,可以手动调整
ggplot(df3, aes(x, y, width = width)) + 
  geom_vwline(width_units = "inches")

## 平滑
ggplot(df3, aes(x, y, width = width)) + 
  geom_vwline(width_units = "inches", step_width = FALSE)

## 左右分开映射
ggplot(df3, aes(x, y, width_left = 1/3 * width, width_right = 2/3 * width)) + 
  geom_vwline(width_units = "inches", step_width = FALSE) +
  geom_path(colour = "white")

geom_vwcurve()可以用来画密度曲线。

d <- density(rnorm(200))
df4 <- data.frame(
  x = (d$x - min(d$x)) / diff(range(d$x)),
  y = "A",
  width = 0.2 * d$y / max(d$y)
)
ggplot(df4, aes(x, y, width = width)) + geom_vwcurve(width_units = "native")

geom_vwxspline()是可变宽度的样条曲线。

df5 <- data.frame(
  x = 0:3 / 3,
  y = c(0.5, 0.7, 0.3, 0.5),
  width = c(0, 0.5, 0.5, 0)
)

ggplot(df5, aes(x, y, width = width)) + geom_vwxspline()

ggplot(df5, aes(x, y, width = width)) + 
  geom_vwxspline(width_units = "native")

可变宽度曲线(二)

这块主要带偏移量的可变宽度曲线,包括geom_offset_bezier()geom_offset_xspline()两个函数。

df6 <- data.frame(
  x = c(.2, .4, .6, .8),
  y = c(-.05, .05, -.05, .05),
  width = c(0.5, 1, 1.5, 2)
  )

ggplot(df6, aes(x, y, width = width)) + geom_offset_bezier()

ggplot(df6, aes(x, y, width = width)) +
  geom_offset_bezier(colour = "black", fill = NA, debug = TRUE)

ggplot(df6, aes(x, y, width = width)) + geom_offset_xspline()

ggplot(df6, aes(x, y, width = width)) +
  geom_offset_xspline(colour = "black", fill = NA, debug = TRUE)

可变宽度曲线(三)

最魔性的就是带笔刷的可变宽度曲线geom_brush_xspline(),能玩出很多不同的花样。

带填充色的箭头

df7 <- data.frame(
  x = c(0.2, 0.8, 0.2, 0.5, 0.8), 
  y = c(2/3, 2/3, 0.2, 0.5, 0.2),
  group = c("A", "A", "B", "B", "B"))

width <- widthSpline(grid::unit(c(12, 12, 25, 0), "mm"), d = c(0, 0.7, 0.7, 1),
                  shape = 0.01, rep = TRUE)

ggplot(df7, aes(x, y, group = group, fill = group)) + 
  geom_brush_xspline(width0 = width) +
  geom_brush_xspline(width0 = width) +
  coord_cartesian(xlim = c(0,1), ylim = c(0, 1))

带箭头的曲线

df8 <- data.frame(x = c(0.2, 0.5, 0.8),
                   y = c(0.3, 0.7, 0.3))

arrowBrush <- list(x=c(-1, 1, -1, 0), y=c(-1, 0, 1, 0))
ggplot(df8, aes(x, y)) +
  geom_brush_xspline(brush = arrowBrush, spacing = 1:2) +
  geom_brush_xspline(width = 0.02, colour = 'grey40', fill = "grey40") +
  coord_cartesian(xlim = c(0.1, 0.9), ylim = c(0.2, 0.8))

循环示意图

t <- seq(0, 2 * pi, length.out = 45)[-45]
df9 <- data.frame(x = cos(t),
                  y = sin(t),
                  group = rep(LETTERS[1:4], each = 11))

width1 <- widthSpline(grid::unit(c(0, 5, 12, 0), "mm"), d = c(0, 0.8, 0.8, 1),
                      shape = 0.01, rep = TRUE)
width2 <- widthSpline(grid::unit(c(5, 5, 12, 0), "mm"), d = c(0, 0.8, 0.8, 1),
                      shape = 0.01, rep = TRUE)

ggplot(df9, aes(x, y, group = group, fill = group)) + 
  geom_brush_xspline(width0 = width1) +
  coord_fixed(xlim = c(-1.1, 1.1), ylim = c(-1.1, 1.1))

ggplot(df9, aes(x, y, group = group, fill = group)) + 
  geom_brush_xspline(width0 = width2) +
  coord_fixed(xlim = c(-1.1, 1.1), ylim = c(-1.1, 1.1))

河流图

我不知道河流图还有哪些实现方法,但是用ggvwline来处理绝对简单。

## 生成模拟数据
data <- data.frame(
  x = rep(Sys.Date() + 1:6, 2),
  val = runif(12),
  group = rep(LETTERS[1:2], each = 6)
)

ggplot(data, aes(x, y = 1, width = val, fill = group, group = group)) +
  geom_vwxspline(width_units = "native", alpha = 0.5)

小结

ggvwline的应用场景还不是很清楚,但是这么魔性的东西我感觉总是有用的,具体怎么用后续再慢慢琢磨吧。