R|ggplot2(二)|覆盖柱状图各种需求

浏览: 8000

image.png

目录

  • 赋权和所用数据类型
  • 分组作图与aes
  • 隐含参数解释
  • 分组条形图,三种展现形式,如不同组在一根柱子上堆叠还是并排放置
  • 柱子高低顺序排列
  • 正负条形图
  • 横向条形图
  • 饼图(这里没有专门画饼图的函数,饼图是柱状图的一种特例)
  • 分面
  • 柱子上标注文字

本文使用R自带数据集mpg。横轴使用class列,分组使用cyl列,数量使用dyspl列

赋权和所用数据类型

library(ggplot2)
ggplot(mpg,aes(x=class)) + geom_bar() # 查看class中每种类别的个数
ggplot(mpg,aes(x=class,y=displ)) + geom_bar(stat="identity") # 使用第二种数据类型,接受两个参数
ggplot(mpg,aes(x=class)) + geom_bar(aes(weight=displ)) # 使用displ对class赋权
ggplot(mpg,aes(x=class)) + geom_bar(aes(weight=rep(1,length(class)))) # 第一种相当于赋权全为1

image.png

我们会发现14图形相同,23图形相同。这里涉及到的数据样式和之前说的有所不同。

之前我们说画柱状图可以接受两种数据样式

  • 一种是 名字罗列
  • 一种是 名字-频数

本文第一种做法便是所谓的名字罗列,函数内部会自动数出每一类有多少个,柱子有多高。第二种的数据却不太一样,因为 名字-频数 是已经统计出每一个名字总共的频数,而这个数据集展现的名字却有重复的,这其实相当于是第三种数据样式,我暂且称之为赋权样式。

  • 赋权样式可以当成名字-频数来处理,函数内部会自动将相同的名字合并。
  • 也可以当成名字罗列的情况处理,同时指定权重,这就是第三个代码显示的方法。第四个图表示第一种只是查数,代表赋权全为1。

分组作图与aes

上一篇文章中,我们已经简单设置了柱状图中的 col fill width 等参数,它们都是直接放在geom_bar里面的,而有一些参数像x和y则是放在又套了一层aes,现在我们要对这个现象进行解释。

我们从分组作图入手,下面使用fill的5种尝试,3种不报错的形式中,只有最后一种实现了分组作图,我们来看一看他们之间的区别

# ggplot(mpg,aes(x=class)) + geom_bar(fill=cyl) # 报错
# ggplot(mpg,aes(x=class)) + geom_bar(fill=mpg$cyl) # 报错
ggplot(mpg,aes(x=class)) + geom_bar(fill=1:7)
ggplot(mpg,aes(x=class)) + geom_bar(aes(fill=cyl)) # 未成功完成分组
ggplot(mpg,aes(x=class)) + geom_bar(aes(fill=factor(cyl))) # 按照cyl分组

# 其他参数
ggplot(mpg,aes(x=class)) + geom_bar(aes(col=factor(cyl)))
ggplot(mpg,aes(x=class)) + geom_bar(aes(alpha=factor(cyl)))

ggplot(mpg,aes(x=class)) + geom_bar(fill="blue") # 显示蓝色
ggplot(mpg,aes(x=class)) + geom_bar(aes(fill="blue")) # 显示粉红色
ggplot(mpg,aes(x=class)) + geom_bar(aes(fill="a")) # 显示粉红色

image.png

(图形展示了前四张图)

上面的结果说明了一下几个问题(按顺序一个一个看)

  • 第一次报错,在aes外面无法找到cyl对象,说明只有放在aes中,才能直接引用数据集的列名,而不需要使用$
  • 第二行,加了$仍然报错,是因为在aes外面的fill指向的是那7根柱子,所以应该接一个长度为7的向量。只有在aes中,数据才能和原数据框的每一行相对应
  • 知道了这个,读者就应该明白了上一部分赋权时,weight参数放在aes里面的原因了。
  • 第三行使用了长度为7的颜色向量,实现了每根柱子颜色不一样,但是无法按照cyl分组展示
  • 第四行没有实现分组,说明fill想要接分类变量(离散变量),需要是因子型数据
  • 第五行成功分组显示。其内在逻辑如下,请读者在脑海中想象一下:class中一个名字是一个柱子,每个柱子被拆分成n个短柱子(n为名字重复的次数),堆在一起。每个短柱子对应一个cyl值(一行),所有短柱子的cyl值相同则颜色相同,现在应该能想象出这样一幅画面:一共7根柱子,每根柱子上都是色彩斑斓的,由4个颜色组成。接下来,对于每根长柱子,把颜色相同的短柱子放在一起再重新堆起来,最后展示出来的便是分组作图的结果。
  • 第六七行表示除了fill,还有col和alpha都可以接分组变量实现分组展示,只是区分展示方式不一样。如果是画点图的话,对应起来可以用颜色、大小、形状等来区分,也都是用相应参数接分类变量实现的
  • 我们可以看出,我们没有指定具体的颜色,只是告诉函数,按照这个变量来分类,就会使用默认的颜色,同时生成图例。这样默认生成的图形本身就很大气美观,省去了很多优化的步骤,这是ggplot2包的一大优势

  • 最后一部分三个函数反映了在aes里面和外面定义颜色的区别。在aes外面定义蓝色就是蓝色,在aes里面其实”blue”被认为是一个分组的因子了,它本身是什么已经不重要了,它只是传达了,所有柱子都被认为分到了同一组,使用同一个颜色,而颜色则是使用默认的

隐含参数解释

当我们使用 罗列名字 的数据来作图的时候,其实函数内部计算出了每个名字的数量,这样才能代表每个柱子的高度,这个数值不是我们输进去的,但是我们可以引用它,但是只有在aes中使用才有效

柱状图的参数有以下两个

  • ..count.. 每根柱子多高
  • ..prop.. 每根短柱子占整个长柱子的百分比,如果没进行分组,则每根柱子对应的..prop..都是1
ggplot(mpg,aes(x=class)) + geom_bar() # 只接一个参数相当于第二个参数是..count..
ggplot(mpg,aes(x=class,y=..count..)) + geom_bar()
ggplot(mpg,aes(x=class,y=..prop..)) + geom_bar() # 每根柱子的prop都是1

# ggplot(mpg,aes(x=class)) + geom_bar(aes(fill=..count..)) # 连续性变量与后面因子型变量,看差别
ggplot(mpg,aes(x=class)) + geom_bar(aes(fill=factor(..count..)))
ggplot(mpg,aes(x=class)) + geom_bar(aes(group=factor(cyl),fill=..prop..))

image.png

(为了展示代码的真实结果,图有点丑,比如坐标轴标注重叠可以用之后学到的内容再进行调整)

后两个例子解释一下。

  • 因为..count..数的是每根长柱子的高度,所以每个长柱子的下分短柱子对应的fill值没有差异,差异体现在长柱子与长柱子之间,所以结果便是每根柱子颜色不一样。
  • 可以看到分类时用连续性变量,则使用的颜色是渐变的,而使用因子型变量则为分类颜色
  • 第二个例子为了让..prop..不再为1,需要分组,group接分组变量可以实现分组,但是它不像fill表示颜色,alpha表示透明度,它没有展示的途径,所以一般即使用group分组,做出图形也看不出来组别之间的区别。但是对于..prop..确是有影响的。这个例子只是为了展示..prop..这个变量,真正使用的时候不会画这样的图

分组条形图,三种展现形式

上面我们看到的分组条形图中,每根柱子上的不同组别短柱子是堆叠起来的,我们还可以实现并排放置和填充比例展示,只要使用position参数调整

ggplot(mpg,aes(x=class)) + geom_bar(aes(fill=factor(cyl)),position="stack") # position默认,堆叠
ggplot(mpg,aes(x=class)) + geom_bar(aes(fill=factor(cyl)),position="dodge") # 并排放置
ggplot(mpg,aes(x=class)) + geom_bar(aes(fill=factor(cyl)),position="fill") # 填充显示比例

image.png

柱子高低顺序排列

我们使用两种方法

  • 自己编写函数,根据factor原理来进行排序
  • 使用包中自带的reorder函数

1.首先我们要理解ggplot2包中factor的使用

其实前面我们在画柱状图时,使用的变量并不规范,当横坐标是离散型变量时,像柱状图这样一根一根柱子这样,x参数接的应该是一个因子型数据,我们直接x=name在这里没显示出什么错误,但是看看如下例子

aa <- data.frame(a=sample(1:7,30,replace=T),
b=sample(1:3,30,replace=T))

ggplot(aa,aes(x=a)) + geom_bar()
ggplot(aa,aes(x=factor(a))) + geom_bar()
ggplot(aa,aes(x=factor(a))) + geom_bar(aes(fill=b))
ggplot(aa,aes(x=factor(a))) + geom_bar(aes(fill=factor(b)))

image.png

我们可以看出x没有用factor的时候,横轴没有把所有的标签全标上,这表示把横轴当成连续性变量来看了,所以只标了一部分标签以表示大小关系。

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

2.使用自己编写的函数

ggplot(mpg,aes(x=factor(class))) + geom_bar()

image.png

先看这张图,默认画图时横轴标度排列顺序为,按照首字母排序,这和因子型数据有关

mpg$class[1:5]
# [1] "compact" "compact" "compact" "compact" "compact"
fclass <- factor(mpg$class)
fclass[1:5]
# [1] compact compact compact compact compact
# Levels: 2seater compact midsize minivan pickup subcompact suv

这里我们可以看出,转化为因子型之后数据的level的排列方式,不是根据元素出现的前后顺序来排列的(否则第一根柱子应该是compact),而是按照首字母排列的(2seater)。

所以ggplot2柱状图横轴排列顺序的本质就是根据factor的levels,所以我们只要改变这个factor的level,就可以调整柱子排列顺序。我们通过定义一个函数来实现level的改变

reorder_size <- function(x) {
factor(x, levels = names(sort(table(x))))
}
ggplot(mpg, aes(reorder_size(class))) + geom_bar() # 柱子从低到高排列

image.png

这样柱子就从低向高排列了

3.使用包中的reorder函数

x <- sample(letters[1:3],30,replace = T)
y <- 1:30
reorder(x,y,sum)

我们可以看到reorder函数接3个参数,最后返回的向量是一个因子型向量,其主体还是x,只是改变了levels,改变的原则是:按照x对y进行分组,对每一组组成的向量计算后面的函数,最后根据计算结果从小到大指定x中元素的levels

使用这个函数来画条形图的原理:根据class对一个全是1的向量分组求和(相当于计算了class中每一个元素出现的个数),再根据求和结果指定levels

ggplot(mpg,aes(reorder(class,rep(1,length(class)),sum)))+geom_bar() # 作图结果和上面相同

正负条形图

只要数据是负数,就能画出往下方的条形图

d <- data.frame(a=letters[1:7], b=c(4,-6,5,-4,-3,6,4))
ggplot(d,aes(a,b)) + geom_bar(stat="identity")
ggplot(d,aes(a,b)) + geom_bar(aes(fill=factor((b>0)+1)),stat="identity")

image.png

横向条形图

ggplot(mpg,aes(x=class)) + geom_bar() + coord_flip()

image.png

coord_flip 表示横纵坐标位置互换

饼图(这里没有专门画饼图的函数,饼图是柱状图的一种特例)

ggplot(mpg, aes(class))+geom_bar()+coord_polar(theta = "y")
ggplot(mpg, aes(class))+geom_bar()+coord_polar(theta = "x")
ggplot(mpg, aes(class))+geom_bar(aes(fill=drv))+coord_polar(theta = "y")
ggplot(mpg, aes(class))+geom_bar(aes(fill=drv))+coord_polar(theta = "x")

image.png

我们可以看出,这里所谓的饼图,只是把坐标轴做了圆形扭曲,只是theta参数接”x”和”y”不一样,扭曲的方式不一样

  • theta=”y”时,是把y轴方向扭曲了,柱子都变成了弯的
  • theta=”x”时,是把x轴方向扭曲了,柱子都从同一个中心出发

加上颜色分类变量之后,也只是在相应柱子上分了组

有的读者可能会疑惑,这里要讲的不是饼图吗,这里展示的都只是和圆形搭边,却不是正常我们看到的饼图。

其实正常形式的饼图是theta=”y”时的一种特例:只有一根柱子

所以当我们拿到数据的时候,想要画饼图,对数据的思考和柱状图是不一样的。这一点和基础作图函数是不一样的。
比如还是class这一列

  • 基础函数中,画饼图和画柱状图的思路是一样的,让x=class
  • ggplot2的饼图中,则是fill=class,为了保持一根柱子,x=1
ggplot(mpg, aes(1, fill=class))+geom_bar()+coord_polar(theta = "y")

分面

分面条形图其实就是分组条形图的另外一种展现形式,也是拿用来分组的变量将数据拆分,为了更方便地使用分面功能,这里不局限在柱状图,我们使用点图来完成。

两个函数,两种思路

  • facet_grid 函数,按照一个指标分成m个,按照另一个指标分成n个,排列方式就是m*n式
  • facet_wrap函数的思路和matrix创建的思路相似,现有一串,再指定每行放几个

看下面的帮助文档中的例子就大概懂了,其他参数细节可以自己查看帮助文档学习

p <- ggplot(mpg, aes(displ, cty)) + geom_point()
ggplot(mpg, aes(displ, cty)) + geom_point(aes(color=factor(cyl)))
p + facet_grid(. ~ cyl)
p + facet_grid(drv ~ .)
p + facet_grid(drv ~ cyl)

ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(~class)

ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(~class, nrow = 4)

ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(~ cyl + drv)

ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(c("cyl", "drv"))

柱子上标注文字

下面我们通过两个例子来解释如何在柱状图柱子上方标数字

df <- data.frame(
x = factor(c(1, 1, 2, 2)),
y = c(1, 3, 2, 1),
grp = c("a", "b", "a", "b")
)

ggplot(df, aes(x)) + geom_bar() +
geom_text(aes(label=..count..),
stat="count", color = "red",
vjust = -0.5, size = 5)

ggplot(data = df, aes(x, y, group = grp)) +
geom_col(aes(fill = grp), position = "dodge") +
geom_text(aes(label = y), position = position_dodge(0.9))

看第一个图的参数,需要注意一下几点

  • stat=”count” 因为geom_text函数默认 stat = “identity”,这种情况在前面说柱状图的时候遇到过,是接受两个参数xy时使用的,而此处只有一个x,因此要改成数数的”count”
  • 上面改成count之后,函数会自动计算出一个变量 ..count..,表示每根柱子的高度,这正是我们要使用的,它只能在aes之中使用,令label=..count..即让每根柱子上标签对应该值
  • color调整颜色,size调整字体大小
  • vjust 调整竖直方向位置
    (读者可以修改参数体验变化,这样可以理解的更深刻)

看第二个图的参数,需要注意一下几点

  • 此时使用数据为xy,因此正好使用默认的stat = “identity”,不用修改
  • 这次涉及到分组,要让每个组的柱子上分别标注上,所以要告诉函数geom_text如何分组。这里没有在geom_text函数中重新设置,所以继承了前面的group = grp,按照grp分组。
  • label = y前面已经按照grp分组,所以y产生了4个值
  • position = position_dodge(0.9) 如果没加这个参数,每一类的两个数值都在一条竖直线上显示,这里是告诉函数柱子的宽度,让两组的数分开放置
  • 如果没有group,即使有position,也无法将两组分开。

专栏信息

专栏主页:Data Analysis
专栏目录:目录

文末彩蛋

推荐几个rstudio快捷键

  • ctrl + U 删除本行光标之前的所有内容
  • ctrl + K 删除本行光标之后的所有内容
  • ctrl + D 删除本行
推荐 0
本文由 dwzb 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册