R 数据可视化 —— ggforce(注释和分面)

接上一节内容,这一节主要介绍 ggforce 提供的分组注释以及分面功能

3. 分组注释

ggforce 提供了一个很方便的分组注释功能。例如,我们想展示不同种类的鸢尾花在花瓣长度和宽度上的区别,使用 ggplot2,我们常用的绘制方式是

p <- ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
  geom_point(show.legend = FALSE)
p

不同种类用颜色来标注

使用 ggforce 可以为不同种类的点添加区域和文本注释,可以更容易的从图中分辨不同类别的点。主要包含 4 种不同形状的注释:

  • geom_mark_circle()
  • geom_mark_ellipse()
  • geom_mark_hull()
  • geom_mark_rect()

除了形状不同之外,其他都一样。

下面,我们来添加矩形注释

p + geom_mark_rect() 

只需添加一个函数,而不用做其他任何操作,就能为每个分组的点添加一个矩形框,简单快捷。

想要添加标签和箭头,也非常方便,直接设置 label 参数即可

p + geom_mark_rect(aes(label = Species))

标签和箭头的位置会自动优化调整,并不需要我们指定。

添加椭圆注释

p + geom_mark_ellipse(aes(label = Species))

圆形

p + geom_mark_circle(aes(label = Species))

添加不同的主题

p + geom_mark_rect(aes(label = Species), show.legend = FALSE) +
  theme_void()

但是,在有些情况下,使用矩形或者圆形注释并不好,或者说不够理想。这时就可以使用 geom_mark_hull 函数,来绘制更加复杂的多边形边界,随着分组的边界绘制线条

使用前,先安装依赖包 concaveman

install.packages("concaveman")

更换注释函数

p + geom_mark_hull(aes(label = Species)) +
  theme_void() 

这样的结果已经很不错了,还可以为每个分组区域添加填充色

p + 
  geom_mark_hull(aes(label = Species, fill = Species), show.legend = FALSE) +
  theme_void() 

ggforce 会自动为填充色添加透明度,以防分组的重叠区域被覆盖,也可以手动设置 alpha 参数,来调整透明度

另一个对 hull 函数比较重要的参数就是这是整个区域的大小,就是更改区域周围 padding 的大小。对应的参数是 expand,可以使用 unit 来指定大小

p + 
  geom_mark_hull(
    aes(label = Species, fill = Species), 
    show.legend = FALSE, expand = unit(3, "mm")) +
  theme_void() 

可以看到,区域边界更加紧密了

4. 分面

ggplot2 所提供的分面函数:facet_gridfacet_wrap,基本上可以满足大部分的需求。ggforce 扩展了分面的功能,能够适用于任何布局

4.1 分页

当变量过多时,facet_wrap()facet_grid() 生成的图片中,每个面板都被压缩得很小,例如

ggplot(diamonds) +
  geom_point(aes(carat, price), alpha = 0.1) +
  facet_wrap(~cut:clarity, ncol = 3)

ggforce 为这两个函数提供了一个分页版本,通过设置每一页的行列数,以及页数,可以对变量分页进行绘制,例如

ggplot(diamonds) +
  geom_point(aes(carat, price), alpha = 0.1) +
  facet_wrap_paginate(~cut:clarity, ncol = 3, nrow = 3, page = 2)

每页包含 33 列,绘制的是第二页。

对于 facet_grid 也是一样的

ggplot(diamonds) +
  geom_point(aes(carat, price), alpha = 0.1) +
  facet_grid_paginate(color~cut:clarity, ncol = 3, nrow = 3, page = 4)

可以使用 n_pages 来获取页数

> p <- ggplot(diamonds) +
  geom_point(aes(carat, price), alpha = 0.1) +
  facet_wrap_paginate(~cut:clarity, ncol = 3, nrow = 3, page = 1)

> n_pages(p)
[1] 5

4.2 内容缩放

ggplot2 的缩放包括:坐标系统的缩放和位置的缩放,而 ggforce 提供了一个更强大的函数:facet_zoom

例如,放大 versicolor 类的 x 轴对应的区域

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
    geom_point() +
    facet_zoom(x = Species == "versicolor")

或者 y 坐标轴对应的区域

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
  geom_point() +
  facet_zoom(y = Species == "versicolor")

或者,指定 xy 共同的区域

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
  geom_point() +
  facet_zoom(xy = Species == "versicolor")

为不同的轴指定不同的条件

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
    geom_point() +
    facet_zoom(x = Species != 'setosa', y = Species == 'versicolor')

还可以将每个轴的范围分开绘制

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
    geom_point() +
    facet_zoom(x = Species != 'setosa', y = Species == 'versicolor', 
               split = TRUE)

使用 xlimylim 来确定范围

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
  geom_point() +
  facet_zoom(xlim = c(4, 5), ylim = c(1, 2), 
             split = TRUE)

设置区域的颜色

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
  geom_point() +
  facet_zoom(xlim = c(4, 5), ylim = c(1, 2), 
             split = TRUE) +
  theme(zoom = element_rect(fill = 'orange', colour = NA), validate = FALSE)

与注释一起使用

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
  geom_point(show.legend = FALSE) + 
  geom_mark_hull(
    aes(label = Species, fill = Species), 
    show.legend = FALSE, expand = unit(3, "mm")) +
  theme_no_axes() +
  facet_zoom(x = Species == "versicolor")

5. 标度

我们的数据一般都会带有相应的单位,比如身高 cm,体重 kg 等,但是我们在分析的时候一般会忽略数值的单位,而 ggforce 可以让我们在绘图时,加上数值单位

这一功能有 units 包提供

library(units)

定义单位

miles <- as_units('miles')
gallon <- as_units('gallon')
horsepower <- as_units('horsepower')

为数据添加单位,直接与单位相乘即可

> mtcars$consumption <- mtcars$mpg * (miles/gallon)
> mtcars$power <- mtcars$hp * horsepower

> head(mtcars$power)
Units: [horsepower]
[1] 110 110  93 110 175 105

绘制图形

ggplot(mtcars) +
  geom_point(aes(power, consumption, colour = factor(cyl)))

aes 中的变换会直接反映到图中

ggplot(mtcars) +
  geom_point(aes(power, 1/consumption, colour = factor(cyl)))

也可以使用 scale* 函数,设置 unit 参数,会自动变换为新的单位

ggplot(mtcars) +
  geom_point(aes(power, consumption, colour = factor(cyl))) +
  scale_x_unit(unit = 'W') +
  scale_y_unit(unit = 'km/l')

6. 变换

ggplot2 的变换其实使用的是 scales 包,而不是其自身的一部分。ggforce 扩展了 ggplot 的变换功能,包括对坐标轴的变换

6.1 单变量变换

这些变换主要针对 scales 包缺少的功能,如

幂变换
> p3 <- power_trans(3)
> p3$transform(1:5)
[1]   1   8  27  64 125
ggplot(mtcars) + geom_point(aes(mpg, cyl)) + scale_y_continuous(trans = p3)
逆变换

scales 包提供了逆线性变换,但是没有提供其他函数的逆变换,比例 log 的逆变换。

> p3r <- trans_reverser(p3)
> p3r
Transformer:  reverse-power of 3
> p3r$transform(1:5)
[1]   -1   -8  -27  -64 -125
ggplot(mtcars) + geom_point(aes(mpg, cyl)) + scale_y_continuous(trans = p3r)

6.2 坐标变换

径向变换

radial_trans 可以将半径和角度转换为笛卡尔坐标系下的 xy 轴坐标

line <- data.frame(
  x = seq(0, 10, length.out = 100), 
  y = seq(0, 10, length.out = 100)
)
r_trans <- radial_trans(r.range = c(0, 1), a.range = c(0, 2))
spiral <- r_trans$transform(r = line$x, a = line$y)
ggplot() + geom_path(aes(x, y), data = line, colour = 'red') + 
  geom_path(aes(x, y), data = spiral, colour = 'blue')

线性变换

线性变换系列涵盖了缩放/拉伸、旋转、剪切、反射和平移,所有的变换都是相对于原始位置进行的。

# 定义旋转、剪切和坐标平移
trans <- linear_trans(rotate(a), shear(1, 0), translate(x1, y1))
square <- data.frame(x = c(0, 0, 1, 1), y = c(0, 1, 1, 0))
# 旋转 60,x + 4, y + 8
square2 <- trans$transform(square$x, square$y, a = pi/3, x1 = 4, y1 = 8)
# 旋转 60,x + 2, y - 6
square3 <- trans$transform(square$x, square$y, a = pi/1.5, x1 = 2, y1 = -6)
square_all <- rbind(square, square2, square3)
square_all$group <- rep(1:3, each = 4)
ggplot(square_all, aes(x, y, group = group)) +
    geom_polygon(aes(fill = factor(group)), colour = 'black')
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容