R数据可视化-ggplot2的几何对象与统计变换

  • 几何对象执行着图层的实际渲染,控制着生成的图像类型。
    • geom_point()将会生成散点图
    • geom_line()将会生成折线图
    • geom_bar()将会生成柱状图
    • geom_boxplot()将会生成箱型图
    • geom_histogram()将会生成直方图
  • 统计变换通常以某种方式对数据信息进行汇总。
    • stat_smooth()添加光滑曲线

任何一个ggplot2图层都包括stat和geom两部分,stat_geom_是两种绘图方法这个说法是不对的,下面我们简单解释下。

举个例子:

1
2
3
4
5
x <- c(rnorm(100,14,5),rep(20,20)) 
y <- c(rnorm(100,14,5),rep(20,20))
p <- ggplot(data= NULL, aes(x = x, y = y))
p <- p + geom_point(color = "darkred")
p

我们查看码源会发现geom_point()这个几何对象的默认stat是identity,即不做任何统计变换:

1
2
3
4
5
6
7
function (mapping = NULL, data = NULL, stat = "identity", position = "identity", 
..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
{
layer(data = data, mapping = mapping, stat = stat, geom = GeomPoint,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, ...))
}

从代码我们可以发现,坐标(20,20)这个点的数据事实上有20个,但由于没做统计转换(20,20)这个点被画了20次,因此我们看到的点其实是最后一次画的那个点。

因此,下面我们按照某一点出现的频率换算成大小(指定stat = "sum")来作图:

1
2
3
4
5
x <- c(rnorm(100,14,5),rep(20,20)) 
y <- c(rnorm(100,14,5),rep(20,20))
p <- ggplot(data= NULL, aes(x = x, y = y))
p <- p + geom_point(color = "darkred",stat = "sum")
p

根据以上,我们可以发现一个单纯的geom_point里面也是带有stat_的,同样,我们也可以用stat_sum作为主函数来绘制这幅图,里面有参数geom,这里我们设置成geom = “point”

1
2
3
4
5
x <- c(rnorm(100,14,5),rep(20,20)) 
y <- c(rnorm(100,14,5),rep(20,20))
p <- ggplot(data= NULL, aes(x = x, y = y))
p <- p + stat_sum(color = "darkred",geom = "point")
p

画出来的图跟前面用geom_point函数是一样的。

因此,我们需要认识到stat_geom_是两种绘图方法这个说法是不对的,其实它们是ggplot2每一个图层绘制都必须有的,是一个图层的一体两面

下面简单介绍几个常用的绘图函数。

geom_point()散点图

绘制散点图可以使用geom_point()函数,气泡图(bubble chart)也是一个散点图,只不过点的大小由一个变量(size)来控制。

散点图潜在的最大问题是过度绘图:当一个位置或相邻的位置上出现有多个点,就可能把点绘制在彼此之上,这会严重扭曲散点图的视觉外观,我们可以通过使点变得透明或者设置点的形状来解决该问题。

geom_point()参数如下:

1
geom_point(mapping = NULL, data = NULL, stat = "identity", position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)

这里我从数据集中随机选出了100个样本,作为一个小数据集:

1
2
3
4
5
# 让样本可重复
set.seed(1410)

# 原始数据的一个容量为100的随机样本
dsmall <- diamonds[sample(nrow(diamonds),100),]

下面是散点图的绘制:

1
2
p <- ggplot(data = dsmall, mapping = aes(x = carat, y = price))
p <- p + geom_point(aes(color = color, size = cut), shape = 19, alpha = 0.5)

geom_bar()柱状图

离散单变量的柱状图

明细数据集绘制柱状图-左图:

1
2
p <- ggplot(data = diamonds, mapping = aes(x = cut))
p <- p + geom_bar()

geom_bar()函数中的stat参数的默认值为stat = 'count',即观测数量,统计的是每个离散变量出现的频次。

汇总好的数据集绘制柱状图-右图:

1
2
p <- ggplot(data = diamonds, mapping = aes(x = cut, y = price))
p <- p + geom_bar(stat = "identity")

我们只需要在geom_bar()中更改默认的countidentity就可以接受两个变量作图。

分组柱状图

分组柱状图一共有三种展现形式,我们可以使用position参数调整:

  • 并排放置position="dodge"
  • 堆叠position="stack"
  • 填充比例position="fill"
1
2
3
4
5
6
7
8
9
10
p <- ggplot(data = diamonds, mapping = aes(x = cut,fill = color))

# 并排放置-左图
p + geom_bar(position = "dodge")

# 堆叠-中图
p + geom_bar(position = "stack")

# 填充比例-右图
p + geom_bar(position = "fill")

柱状图排序

其实前面我们在画柱状图时,使用的变量并不规范,当横坐标是离散型变量时,x参数接的应该是一个因子型数据(factor)

当x没有用因子型数据的时候,横轴可能会没有把所有的标签全标上,这表示把横轴当成连续性变量来看了,所以只标了一部分标签以表示大小关系。

同样的,fill也是作为离散分类变量,也应该接一个factor,我们可以看到不加时,图例是一个连续性渐变颜色的形式。

我们会发现,转化为因子型之后数据的排列方式,不是根据元素出现的前后顺序,而是按照首字母顺序。现在我们想让x轴按y轴数值大小排序

自定义函数

我们可以自定义一个函数来实现柱子从高到低排列:

1
2
3
4
5
6
7
8
9
10
11
12
# 因子型数据,按照首字母排序-左图
p <- ggplot(data = diamonds, mapping = aes(x = factor(cut)))
p <- p + geom_bar()

# 自定义函数
reorder_size <- function(x) {
factor(x, levels = names(sort(table(x), decreasing = TRUE)))
}

# 从高到低排序-右图
p <- ggplot(data = diamonds, mapping = aes(x = reorder_size(cut)))
p <- p + geom_bar()

reorder()函数

reorder()函数用法:

1
reorder(x, X, FUN = mean, ..., order = is.ordered(x))
  • x:因子型向量
  • X:用来排序的数值型向量
  • FUN:汇总数据的函数
  • ...FUN的参数(可选)
  • order:我们可以通过这个参数直接指定因子型向量的顺序

按照x对X进行分组,对每一组组成的向量计算后面的函数,最后根据计算结果从小到大指定x中元素的顺序。

下面是reorder()实现柱子从高到低排列,结果和前面自定义函数是一样的:

1
2
p <- ggplot(data = diamonds, mapping = aes(reorder(cut,rep(1,length(cut)),sum)))
p <- p + geom_bar()

我们可以根据cut对一个全是1的向量分组求和(相当于计算了cut中每一个元素出现的个数),再根据求和结果指定顺序。

柱状图添加标签文字

我们可以使用geom_text()为柱状图添加文本,显示柱状图的高度,并调整文本的位置和大小。

1
2
3
p <- ggplot(data = diamonds, mapping = aes(x = factor(cut),fill = color))
p <- p + geom_bar(position = "fill")
p <- p + geom_text(stat = "count",mapping = aes(label = ..count..), size = 5, colour = 'white', vjust = 1, hjust = .5, position = position_fill(0.9))
  • label设置
    • stat="count"时,设置文本的标签需要使用一个特殊的变量 aes(label=..count..), 表示的是变量值的数量
    • stat="identity"时,设置文本的标签需要设置y轴的值,aes(label=price),表示的变量的值
  • size设置:标签字体大小,默认值为5号
  • color设置:标签文字的颜色
  • vjust设置:调整标签位置,1为分界线,越大于1,标签越在柱状图上界下方,反之则越在柱状图上界上方
  • hjust设置:hjust = 0.5将标签水平居中放置
  • position设置:这里的图形位置与标签位置摆放必须一致,即图形位置geom_bar()函数设置为position = 'fill',那么标签位置geom_text()函数设置为position = position_fill(0.9)参数

正负柱状图

只要数据是负数,就能画出往下方的柱状图:

1
2
3
4
5
6
7
8
d <- data.frame(a=letters[1:7], b=c(4,-6,5,-4,-3,6,4))
p <- ggplot(d,aes(a,b))

# 左图
p1 <- p + geom_bar(stat="identity")

# 右图
p2 <- p + geom_bar(aes(fill=factor((b>0)+1)),stat="identity")

geom_boxplot()箱型图

geom_boxplot()函数中有outlier开头的多个参数,用于修改离群点的属性:

  • outlier.colour:离群点的颜色
  • outlier.fill:离群点的填充色
  • outlier.shape:离群点的形状
  • outlier.size:离群点的大小
  • outlier.alpha:离群点的透明度

箱形图可以用fill参数指定填充颜色,color参数指定边框颜色。

下面是一个示例:

1
2
p <- ggplot(data = diamonds, mapping = aes(x = cut, y = price))
p <- p + geom_boxplot(fill = "white", color = "darkgreen", outlier.shape=21, outlier.size=4, outlier.stroke = 1, outlier.color = "gray", outlier.fill = "orange")

在添加有多分类变量时,箱线图默认使用的position参数是dodge,使用堆积方式stack、堆积百分比fill来呈现多维箱线图是会失败的。

geom_histogram()直方图

直方图函数geom_histogram()与柱形图函数geom_bar()大致相同。

直方图组距调整

1
2
3
4
5
6
# 默认组距-左图
p <- ggplot(data = diamonds, mapping = aes(x = price))
p + geom_histogram()

# binwidth参数控制直方图组距大小-右图
p + geom_histogram(binwidth = 1)

分组直方图

直方图参数中添加颜色映射来区分不同组,这时默认直方图输出为堆积直方图。我们也可以通过设置position参数对多序列柱形进行簇状、堆积百分比转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
p <- ggplot(data = diamonds, mapping = aes(x = price, fill = factor(cut)))

# 默认分组直方图(stack)-左上图
p + geom_histogram()

# 不做任何转换(identity)-右上图
p + geom_histogram(position = 'identity')

# 簇状分组柱状图(dodge)-左下图
p + geom_histogram(position = 'dodge')

# 堆叠百分比分组柱状图(fill)-右下图
p + geom_histogram(position = 'fill')

参考


----------- 本文结束啦感谢您阅读 -----------

赞赏一杯咖啡