R包-ggplot2

主要基于《数据科学中的R语言》的学习笔记。

ggplot2介绍

ggplot2有一套优雅的绘图语法,包名中“gg”是grammar of graphics的简称。 Hadley Wickham将这套可视化语法诠释为:

一张统计图形就是从数据到几何形状(geometric object,缩写geom)所包含的图形属性(aesthetic attribute,缩写aes)的一种映射。

通俗解释:就是我们的数据通过图形的视觉元素表示出来。比如点的位置,如果坐标x值越大,水平方向离原点的位置就越远,数值越小,水平方向离原点的位置就越近。 数值的大小变成了视觉能感知的东西

ggplot()函数包括9个部件:

  • 数据 (data) (数据框)
  • 映射 (mapping)
  • 几何形状 (geom)
  • 统计变换 (stats)
  • 标度 (scale)
  • 坐标系 (coord)
  • 分面 (facet)
  • 主题 (theme)
  • 存储和输出 (output)

其中前三个是必需的。语法模板

1
2
ggplot(data = <DATA>) + 
<GEOM_FUNCTION>(mapping = aes(<MAPPINGS>))

此外,图形中还可能包含数据的统计变换(statistical transformation,缩写stats),最后绘制在某个特定的坐标系(coordinate system,缩写coord)中,而分面(facet)则可以用来生成数据不同子集的图形。

简单例子

用科考人员收集的企鹅体征数据来演示。

1
2
3
4
5
6
7
library(tidyverse)
penguins <- read_csv(here::here("demo_data", "penguins.csv")) %>%
janitor::clean_names() %>%
drop_na()

penguins %>%
head()

这里我们用到4个变量,species (企业种类), sex (性别), bill_length_mm (嘴峰长度) , bill_depth_mm (嘴峰深度) 。

1
2
3
penguins %>%
select(species, sex, bill_length_mm, bill_depth_mm) %>%
head(4)

我们想要考察嘴峰长度(bill_length_mm)与嘴峰深度(bill_depth_mm)之间的关联,先绘制这两个变量的散点图,。

1
2
ggplot(penguins) +
geom_point(aes(x = bill_length_mm, y = bill_depth_mm))

刚才看到的是位置上的映射,ggplot()还包含了颜色 (color)、形状 (shape) 以及透明度 (alpha) 等图形属性的映射。

比如我们在aes()里增加一个颜色映射color = species, 这样做就是希望,不同的企鹅类型, 用不同的颜色来表现。这里,企鹅类型有三组,那么就用三种不同的颜色来表示

1
2
ggplot(penguins) +
geom_point(aes(x = bill_length_mm, y = bill_depth_mm, color = species))

如果我们只是想把图中的点指定为某一种颜色,可以使用设置语句,例如

1
2
ggplot(penguins) +
geom_point(aes(x = bill_length_mm, y = bill_depth_mm), color = "blue")

注意这里 color = "blue" 不能放在 aes() 函数中,不然所有点就都是红色。

因为这种方式会将 ‘blue’ 映射到color轴,'blue’实际上是从c(‘blue’),是个长度为1的字符串,离散型变量,所以映射函数会把从c(‘blue’)映射到离散型的默认color轴,第一个颜色是红色,所以所有点是红色。注意映射函数接受长度为1或者长度和x,y等长的vector.

图层叠加

geom_point() 可以画散点图,也可以使用geom_smooth()绘制平滑曲线,

1
2
ggplot(penguins) +
geom_smooth(aes(x = bill_length_mm, y = bill_depth_mm))

我们也可以同时画出散点图和平滑曲线图,如下

1
2
3
ggplot(penguins) +
geom_point(aes(x = bill_length_mm, y = bill_depth_mm)) +
geom_smooth(aes(x = bill_length_mm, y = bill_depth_mm))

但是此时我们将相同的 aes() 函数写了两遍,我们可以将 aes() 函数放在 ggplot() 函数中,这样可以偷懒一点。

1
2
3
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +
geom_point() +
geom_smooth()

那么将 aes() 函数放在 ggplot() 函数中,和放在 geom_point() 有什么区别呢?二者其实是全局声明和局部声明的关系。如果全局变量和局部变量均有某个映射关系,使用局部变量中的映射关系,更详细的解释如下。

说白了,将 aes() 函数放在 ggplot() 函数中 就是为了偷懒,少打点字而已。

  • 映射关系aes(x = bill_length_mm, y = bill_depth_mm) 写在ggplot()里, 为全局声明。那么,当geom_point()画图时,发现缺少图形所需要的映射关系(点的位置、点的大小、点的颜色等等),就会从ggplot()全局变量中继承映射关系。
  • 如果映射关系aes(x = bill_length_mm, y = bill_depth_mm) 写在几何形状geom_point()里, 那么此处的映射关系就为局部声明, 那么geom_point()绘图时,发现所需要的映射关系已经存在,就不会继承全局变量的映射关系。

保存图片

可以使用ggsave()函数,将图片保存为所需要的格式,如”.pdf”, “.png”等, 还可以指定图片的高度和宽度,默认units是英寸,也可以使用”cm”, or “mm”.

1
2
3
4
5
6
7
8
9
10
11
12
13
p1 <- penguins %>% 
ggplot(aes(x = bill_length_mm, y = bill_depth_mm)) +
geom_smooth(method = lm) +
geom_point(aes(color = species)) +
ggtitle("This is my first plot")

ggsave(
plot = p1,
filename = "my_plot.pdf",
width = 8,
height = 6,
dpi = 300
)

ggplot2之几何形状

先看一组数据

1
df <- read_csv("./demo_data/datasaurus.csv")
1
2
3
4
5
6
7
8
9
> df
# A tibble: 1,846 × 3
dataset x y
<chr> <dbl> <dbl>
1 dino 55.4 97.2
2 dino 51.5 96.0
3 dino 46.2 94.5
4 dino 42.8 91.4
5 dino 40.8 88.3

先用dataset分组后,然后计算每组下x的均值和方差,y的均值和方差,以及x,y两者的相关系数,我们发现每组数据下它们几乎都是相等的

1
2
3
4
5
6
7
8
df %>%
group_by(dataset) %>%
summarise(
across(everything(), list(mean = mean, sd = sd), .names = "{fn}_{col}")
) %>%
mutate(
across(is.numeric, round, 3)
)

如果上面代码不熟悉,可以用第 12 章的代码重新表达,也是一样的

1
2
3
4
5
6
7
8
9
df %>%
group_by(dataset) %>%
summarize(
mean_x = mean(x),
mean_y = mean(y),
std_dev_x = sd(x),
std_dev_y = sd(y),
corr_x_y = cor(x, y)
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## # A tibble: 13 × 6
## dataset mean_x mean_y std_dev_x std_dev_y corr_x_y
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 away 54.3 47.8 16.8 26.9 -0.0641
## 2 bullseye 54.3 47.8 16.8 26.9 -0.0686
## 3 circle 54.3 47.8 16.8 26.9 -0.0683
## 4 dino 54.3 47.8 16.8 26.9 -0.0645
## 5 dots 54.3 47.8 16.8 26.9 -0.0603
## 6 h_lines 54.3 47.8 16.8 26.9 -0.0617
## 7 high_lines 54.3 47.8 16.8 26.9 -0.0685
## 8 slant_down 54.3 47.8 16.8 26.9 -0.0690
## 9 slant_up 54.3 47.8 16.8 26.9 -0.0686
## 10 star 54.3 47.8 16.8 26.9 -0.0630
## 11 v_lines 54.3 47.8 16.8 26.9 -0.0694
## 12 wide_lines 54.3 47.8 16.8 26.9 -0.0666
## 13 x_shape 54.3 47.8 16.8 26.9 -0.0656

那么,我们是否能得出结论,每组的数据长的差不多呢?然而,我们画图发现

事实上,每张图都相差很大。所以,这里想说明的是,眼见为实。换句话说,可视化是数据探索中非常重要的部分。本章的目的就是带领大家学习ggplot2基本的绘图技能。

学习目标

图形语法

图形语法 “grammar of graphics” (“ggplot2” 中的gg 就来源于此) 使用图层(layer)去描述和构建图形,下图是ggplot2图层概念的示意图

图形部件

一张统计图形就是从数据到几何形状(geometric object,缩写geom)所包含的图形属性(aesthetic attribute,缩写aes)的一种映射。

  1. data: 数据框data.frame (注意,不支持向量vector和列表list类型)

  2. aes: 数据框中的数据变量映射到图形属性。什么叫图形属性?就是图中点的位置、形状,大小,颜色等眼睛能看到的东西。什么叫映射?就是一种对应关系,比如数学中的函数b = f(a)就是ab之间的一种映射关系, a的值决定或者控制了b的值,在ggplot2语法里,a就是我们输入的数据变量,b就是图形属性, 这些图形属性包括:

    • x(x轴方向的位置)
    • y(y轴方向的位置)
    • color(点或者线等元素的颜色)
    • size(点或者线等元素的大小)
    • shape(点或者线等元素的形状)
    • alpha(点或者线等元素的透明度)
  3. geoms: 几何形状,确定我们想画什么样的图,一个geom_***确定一种形状。更多几何形状推荐阅读这里

    • geom_bar()
    • geom_density()
    • geom_freqpoly()
    • geom_histogram()
    • geom_violin()
    • geom_boxplot()
    • geom_col()
    • geom_point()
    • geom_smooth()
    • geom_tile()
    • geom_density2d()
    • geom_bin2d()
    • geom_hex()
    • geom_count()
    • geom_text()
    • geom_sf()
  4. stats: 统计变换

  5. scales: 标度

  6. coord: 坐标系统

  7. facet: 分面

  8. layer: 增加图层

  9. theme: 主题风格

  10. save: 保存图片

开始

R语言数据类型,有字符串型、数值型、因子型、逻辑型、日期型等。 ggplot2会将字符串型、因子型、逻辑型默认为离散变量,而数值型默认为连续变量,将日期时间为日期变量

  • 离散变量: 字符串型, 因子型, 逻辑型
  • 连续变量: 双精度数值, 整数数值
  • 日期变量: 日期, 时间, 日期时间

导入数据

1
gapdata <- read_csv("./demo_data/gapminder.csv")

检查数据

是否有缺失值

1
2
3
4
gapdata %>%
summarise(
across(everything(), ~ sum(is.na(.)))
)
1
2
3
4
## # A tibble: 1 × 6
## country continent year lifeExp pop gdpPercap
## <int> <int> <int> <int> <int> <int>
## 1 0 0 0 0 0 0
  • country 代表国家
  • countinet 表示所在的洲
  • year 时间
  • lifeExp 平均寿命
  • pop 人口数量
  • gdpPercap 人均GDP

基本绘图

柱状图

常用于一个离散变量

1
2
3
gapdata %>%
ggplot(aes(x = continent)) +
geom_bar()

按照出现次数,从低到高排序

1
2
3
gapdata %>%
ggplot(aes(x = reorder(continent, continent, length))) +
geom_bar()

翻转坐标轴,使用 coord_flip() 函数

1
2
3
4
gapdata %>%
ggplot(aes(x = reorder(continent, continent, length))) +
geom_bar() +
coord_flip()

ggplot2之标度

标度

22章,我们了解到ggplot2中,映射是数据转化到图形属性,这里的图形属性是指视觉可以感知的东西,比如大小,形状,颜色和位置等。我们今天讨论的标度(scale)是控制着数据到图形属性映射的函数,每一种标度都是从数据空间的某个区域(标度的定义域)到图形属性空间的某个区域(标度的值域)的一个函数。

简单点来说,标度是用于调整数据映射的图形属性。 在ggplot2中,每一种图形属性都拥有一个默认的标度,也许你对这个默认的标度不满意,可以就需要学习如何修改默认的标度。比如, 系统默认"a"对应红色,"b"对应蓝色,我们想让"a"对应紫色,"b"对应橙色。

图形属性和变量类型

还是用我们熟悉的ggplot2::mpg,可能有同学说,我画图没接触到scale啊,比如

1
library(tidyverse)
1
2
3
mpg %>% 
ggplot(aes(x = displ, y = hwy)) +
geom_point(aes(colour = class))

事实上,根据映射关系和变量名,我们将标度写完整,应该是这样的

1
2
3
4
5
6
ggplot(mpg, aes(x = displ, y = hwy)) + 
geom_point(aes(colour = class)) +

scale_x_continuous() +
scale_y_continuous() +
scale_colour_discrete()

如果每次都要手动设置一次标度函数,那将是比较繁琐的事情。因此ggplot2使用了默认了设置,如果不满意ggplot2的默认值,可以手动调整或者改写标度,比如

1
2
3
4
5
6
ggplot(mpg, aes(x = displ, y = hwy)) + 
geom_point(aes(colour = class)) +

scale_x_continuous(name = "This is my x axis") +
scale_y_continuous(name = "This is my y axis") +
scale_colour_brewer()

坐标轴和图例是同样的东西

丰富的标度体系

注意到,标度函数是由”_“分割的三个部分构成的:

scale - 视觉属性名 (e.g., colour, shape or x) - 标度名 (e.g., continuous, discrete, brewer).

每个标度函数内部都有丰富的参数系统

1
2
3
4
5
6
7
8
9
10
scale_colour_manual(
palette = function(),
limits = NULL,
name = waiver(),
labels = waiver(),
breaks = waiver(),
minor_breaks = waiver(),
values = waiver(),
...
)
  • 参数name,坐标和图例的名字,如果不想要图例的名字,就可以 name = NULL
  • 参数limits, 坐标或图例的范围区间。连续性c(n, m),离散型c("a", "b", "c")
  • 参数breaks, 控制显示在坐标轴或者图例上的值(元素)
  • 参数labels, 坐标和图例的间隔标签
    • 一般情况下,内置函数会自动完成
    • 也可人工指定一个字符型向量,与breaks提供的字符型向量一一对应
    • 也可以是函数,把breaks提供的字符型向量当做函数的输入
    • NULL,就是去掉标签
  • 参数values 指的是(颜色、形状等)视觉属性值,
    • 要么,与数值的顺序一致;
    • 要么,与breaks提供的字符型向量长度一致
    • 要么,用命名向量c("数据标签" = "视觉属性")提供
  • 参数expand, 控制参数溢出量
  • 参数range, 设置尺寸大小范围,比如针对点的相对大小

下面,我们通过具体的案例讲解如何使用参数,把图形变成我们想要的模样。

案例详解

先导入一个数据

1
gapdata <- read_csv("./demo_data/gapminder.csv")
1
2
3
4
5
6
newgapdata <- gapdata %>% 
group_by(continent, country) %>%
summarise(
across(c(lifeExp, gdpPercap, pop), mean)
)
newgapdata

画散点图

1
2
3
4
newgapdata %>% 
ggplot(aes(x = gdpPercap, y = lifeExp)) +
geom_point(aes(color = continent, size = pop)) +
scale_x_continuous()
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2019-2025 Vincere Zhou
  • 访问人数: | 浏览次数:

请我喝杯茶吧~

支付宝
微信