感谢关注天善智能,走好数据之路↑↑↑
欢迎关注天善智能,我们是专注于商业智能BI,人工智能AI,大数据分析与挖掘领域的垂直社区,学习,问答、求职一站式搞定!
本文作者:天善智能社区专家dwzb
天善智能社区地址:https://www.hellobi.com/
目录
赋权和所用数据类型
分组作图与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
我们会发现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")) # 显示粉红色

(图形展示了前四张图)
上面的结果说明了一下几个问题(按顺序一个一个看)
第一次报错,在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..))


(为了展示代码的真实结果,图有点丑,比如坐标轴标注重叠可以用之后学到的内容再进行调整)
后两个例子解释一下。
因为..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") # 填充显示比例
柱子高低顺序排列
我们使用两种方法
自己编写函数,根据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)))
我们可以看出x没有用factor的时候,横轴没有把所有的标签全标上,这表示把横轴当成连续性变量来看了,所以只标了一部分标签以表示大小关系。
fill也是作为离散分类变量,也应该接一个factor,我们可以看到不加时,图例是一个连续性渐变颜色的形式,这无法达到我们分类的要求。
2. 使用自己编写的函数
ggplot(mpg,aes(x=factor(class))) + geom_bar()
先看这张图,默认画图时横轴标度排列顺序为,按照首字母排序,这和因子型数据有关
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() # 柱子从低到高排列

这样柱子就从低向高排列了
3. 使用包中的reorder函数
x <- sample(letters[1:3],30,replace = T)y <- 1:30reorder(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")
横向条形图
ggplot(mpg,aes(x=class)) + geom_bar() + coord_flip()
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")

我们可以看出,这里所谓的饼图,只是把坐标轴做了圆形扭曲,只是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,也无法将两组分开。
文末彩蛋
推荐几个rstudio快捷键
ctrl + U 删除本行光标之前的所有内容
ctrl + K 删除本行光标之后的所有内容
ctrl + D 删除本行
天善智能社区地址:https://www.hellobi.com/
天善学院svip正限时特惠火爆报名中!包含业务知识一站通、Excel BI商业智能、七周成为数据分析师、对话大数据系列技术、R语言15案例、Python3网络爬虫实战案例、Python机器学习、Python数据科学家精华实战课程、深度学习模型和实战课程、数据分析报告共10套课程,其他课程只需五折即可,欢迎大家关注报名。https://www.hellobi.com/svip

网友评论