上一篇:R语言-分析与绘图(2):使用rvest库抓取和分析网页信息
@Keren Wang:函数名称不用记,用的时候可以查;但是为了对名称、功能和用法有印象,还是建议多写代码训练一下肌肉记忆,不然考试的时候可能查也来不及。
学习如何收集和分析数据是商业/金融/市场领域中的一项关键技能,但是如果没有有效表征结果的能力,这些数据可能毫无意义。“数据可视化”一词描述了将信息在可视化的上下文中展现出来的过程,以帮助我们以更完整和有效的方式理解数据。 我们可以用图表可以显示多个变量之间的关系,或者以图形方式突出显示不同数据集之间的差异。
本模块介绍了一些使用R绘图的基本技术,特别是扩展了使用tidyverse工具包中的ggplot2的知识。我们将使用来自NASA系外行星档案的真实天文数据来支持本章节的数据可视化学习。除了我们介绍到的内容,你也可以在 R cookbook for graphs 获取有用的学习资源。
首先,我们加载tidyverse库并读取行星数据表planets.csv
。在提前被告知该表有32行是表头(介绍表格的内容,而非有用的数据)的情况下,我们使用read_delim()
函数中的skip = 32
参数来指定“在读取数据前先跳过32行”。
1 | library(tidyverse) |
示例:使用ggplot2库绘制第一个曲线图
ggplot()
函数
ggplot2是tidyverse里绘图的库。ggplot(data=df, aes(x,y))
函数的作用是使用df
这张表中的x
和y
两列作为x轴和y轴,在二维坐标系下绘制图像。如果写成data = NULL
,则需要自己建立两个长度相同的向量x和向量y作为横轴和纵轴的坐标。注意,这一步只是绘制出来了画布p
,我们还没有定义曲线。如果只执行ggplot()
函数,我们将会得到下面的结果:
1 | # 建立两个向量x和y作为横轴和纵轴坐标 |
用geom_line()
函数添加曲线
接下来我们需要在画布p
上添加元素(点、线等)。ggplot2库规定我们使用加号+
表示在图上增加元素。比如想要在刚才建立的画布p
上绘制一个曲线,我们需要在p
上使用加号叠加一个geom_line()
函数,默认画的是黑色实线。
1 | p <- ggplot(NULL,aes(x,y)) # NULL表示我们不从表中取数据,直接使用自己创建的向量作为数据源 |
曲线出来了,但不是很好看。这是因为我们在geom_line()
函数中没有定义样式。下面尝试把刚才的代码修改一下,把绘制曲线的函数改为geom_line(colour = 'red', linetype=2)
,其中colour = 'red'
代表曲线颜色是红色,linetype=2
代表我们使用长虚线(你可以试着修改这个数字,看看各个数字分别代表什么样式)。当然你也可以只定义颜色或者只定义线条种类。
1 | p <- ggplot(NULL,aes(x,y)) # NULL表示我们不从表中取数据,直接使用自己创建的向量作为数据源 |
用geom_point()
函数添加坐标点
曲线画完了,如果我们想把这些点都标出来呢?同样地,ggplot2也提供了geom_point()
函数用来画点,默认画的是黑色实心点。
1 | p <- ggplot(NULL,aes(x,y)) # NULL表示我们不从表中取数据,直接使用自己创建的向量作为数据源 |
如果你觉得不好看,当然也可以自定义点的样式。我们把画点的函数修改为geom_point(colour="blue", shape=0)
,colour="blue"
含义为点的颜色为蓝色,shape=0
的含义为正方形空心点(你可以试着修改这个数字,看看各个数字分别代表什么样式)。同样地,你也可以只定义颜色或者形状。
1 | # 建立两个向量x和y作为横轴和纵轴坐标 |
坐标轴和图表的标题:xlab()
,ylab()
和ggtitle()
这三个就没啥好理解的啦,xlab()
和ylab()
明显就是设定x和y轴的标题(不写这两个函数的话就默认像上面一样显示x和y),ggtitle()
也很显然就是设定图表的标题。这三个默认都不支持中文哈,如果需要显示中文的话可以找我帮你解决(可能和python的matplotlib一样需要修改字体文件)。运行一下试试:
1 | p <- p + xlab("hahahahaha") + ylab("hehehehehe") + ggtitle("lalalalala") |
设定图片尺寸:使用options()
函数
图画的实在是太小了怎么办?可以使用options(repr.plot.width=8, repr.plot.height=6)
来解决。这句话的意思就是把宽度和高度设为某个值,这个值你可以自定义。
1 | options(repr.plot.width=8, repr.plot.height=6) |
效果如下:
使用NASA行星数据画基本散点图
绘制轨道周期和轨道偏心率关系的散点图
在本节中,我们将使用数据库中的两个关键参数来创建2D图形:pl_orbopriod(轨道周期period)和pl_orbeccen(轨道偏心率eccentricity)。轨道周期定义了行星完成绕恒星轨道所花费的时间;轨道偏心率是介于0和1之间的数字,它确定行星轨道是完美的圆形(0)还是椭圆形(1)。
数据预处理——分组、按需取出子集
首先,由于我们想比较用不同方法发现的系外行星的属性,因此我们可以使用之前学习过的分组统计方法,对所有行星的数据按照“pl_discmethod”列也就是“发现方式”对数据进行分组和频率的统计。
1 | # 按照“pl_discmethod”列对数据进行分组并生成频率统计表 |
假设我们想要把Transit、Radial Velocity、Imaging三种“发现方式”的行星放到一个表里去作图,我们可以用两种方法制作这样一个数据集。第一种是使用filter()
先按照发现方式分别把这三种过滤到三个表中,再通过一个叫做rbind()
的函数把这三个表堆叠起来,就合并成了一个表。
1 | # 分别过滤出三个发现方式的所有数据 |
第二种方式是在过滤器中直接使用“或”,也就是|
符号,来取三个条件的并集。(显然这种更推荐)
1 | plot.df <- kdf %>% |
这两行代码的输出和上面的基本是一样的,如果看上去不太一样,那是因为堆叠是一种一种叠起来,而多条件筛选显然是在原表中一行一行筛选的,顺序会不太一样,总归没什么区别就是了。
按照取出的数据集画散点图
我们来厘清一下要求:绘制散点图,横轴是轨道周期,纵轴是轨道偏心率,用颜色区分不同的“发现方式”——这个功能可以在生成画布的时候使用colour = pl_discmethod
参数来实现。ggplot2中,如果我们想让颜色colour或者尺寸size这种样式的差别来区分不同类别,需要在ggplot()
的参数aes()
中补充形如aes(x, y, size=…, colour=…)这种形式的参数。而如果我们只是想设定画线或者画点的尺寸,则是在geom_line()
或者geom_point()
函数中指定colour=..., size=...
等,同时使用scale_colour_discrete(name='name')
或者scale_size=(name=name)
来绘制图例和图例的title。这一点要记得区分一下。
首先输入xy轴参数和颜色参数生成画布,然后再加入绘制散点的函数geom_point()
,似乎就大功告成了。我们来写一下代码:
1 | # 用 pl_discmethod 这一列数据来区分颜色 |
啊这……点怎么都挤在一起了,右边空空荡荡,左边几乎变成一条实线,这能看出来个啥?所以我们不能就此罢休,还是要继续搞一下。
我们发现,问题主要是出在因为x轴的尺度上。y轴的数据只在0-1的区间上变化,而轨道周期除了极个别的大数值以外,也在较小的数字区间上密集分布。这种不均匀分布我们称之为倾斜分布/偏斜分布。这时候如果我们能把刻度转为非线性的形式,就能够清晰地展示更多数据。
我们尝试在绘图过程中增加scale_x_log10()
把x轴尺度转换成以10为底的对数刻度来绘制这个散点图(对应的如果数据在y轴上倾斜分布可以用scale_y_log10()
)。同时,我也希望能够使用scale_colour_discrete(name = "Discovery Method")
给图例命名成“Discovery Method”,毕竟“pl_discmethod”很少有人能理解是什么意思。
1 | # 和上面一样的代码 |
这样效果就很棒棒了。
“这张图显示了有关我们太阳系的一些有趣信息。 尽管我们系统中的所有行星都具有相似的低偏心率,但系外行星的偏心率范围更广,从0到0.8。 同样,低偏心率的行星的周期很短,这表明它们离恒星非常近,这是我们在太阳系中没有观察到的,这使我们成为稀有且独特的行星系。”
绘制恒星质量和轨道周期关系的散点图
下面要求绘制x轴和y轴均使用对数尺度的恒星质量(st_mass)与行星轨道周期(pl_orbper)的函数关系,我们试试给数据点指定size为10且颜色为蓝色,看看效果是否很怪异。
1 | # 数据点指定size为10且颜色为蓝色,注意不需要区分类的情况下,样式参数是写在aes外面的 |
绘制更复杂的散点图
接下来这段代码可能看起来很唬人,但是分解开来理解还是比较直观的。
我们的目的是使用颜色区分“行星平衡温度”的差别,希望散点的颜色不再是简单的红绿蓝,而是可以在一个色带中平滑过度,就好像用红外线成像仪拍出来的热力图一样,颜色越冷代表温度越低,颜色越暖代表温度越高,颜色总体在红和蓝之间过渡。
但是我们会遇到一些阻力。首先是平滑过渡的“jet colours”在R语言中并没有定义出来,我们需要单独使用一个向量、利用colorRampPalette()
调色盘函数创建一个平滑过渡的jet色带。代码如下:
1 | # 自己去定义色谱范围:蓝色、靛青、亮黄、红色作为四个基准点过渡的平滑色带 |
第二个是在原始数据表中,有些星球由于尚未探测,所以温度标注为“NA”。这对于画图的函数来说是一个困扰,因为无法给它分配颜色。我们希望能够简单地将NA的数据改写为1,保证图像顺利生成。is.na(table["col"])
可以返回一个向量,这个向量包含了tablename表内col这一列的值为NA的所有行号。我们使用这个行号和col作为下标,把所有NA改为1。
1 | # is.na(kdf['pl_eqt'])输出kdf表内pl_eqt这一列为NA的所有行序号的向量,把这些行的pl_eqt改为1 |
最后一个问题就是,前面创建的色带怎么设置为我们画图的颜色?还记得上面有一个给图例命名的函数,我们只需要在这个函数里加一个colours=jet.colors(10)
就可以调用我们的色带,其中10的意思是我们使用这个色带中的10个色阶,也就是只用这个色带中的10个“断点”作为我们绘图的颜色。
了解了这些,我们来看看完整的代码。
1 | # 自己去定义色谱范围:蓝色、靛青、亮黄、红色作为四个基准点过渡的平滑色带 |
结果非常的amazing啊!温度分布一目了然,处于左上角的明显温度偏高。@Keren Wang 请问这个你用excel能画嘛?三个维度,平滑过渡,画死你。
当然你也可以试着用size来反应温度的差别:温度越高点越大。这部分试着自己理解一下权当复习了,反正我也是自己试出来的(用你老师的话说:多试几遍run several more goes),直接放代码:
1 | # is.na(kdf['pl_eqt'])输出kdf表内pl_eqt这一列为NA的所有行序号的向量,把这些行的pl_eqt改为1 |
或者在彩色图的基础上直接改:
1 | # 自己去定义色谱范围:蓝色、靛青、亮黄、红色作为四个基准点过渡的平滑色带 |
这几种呈现方式都是非常综合且直观的。小贴士:如果你不想要某个图例,那就把guide设置成None就好了,比如上面这个例子里我们想把size的示例去掉,只用改一句:
看看是不是你不想要的图例已经没有了?
使用geom_histogram()
函数绘制直方图
我们的数据表中有一列pl_disc记录了这个行星被发现的年份。如果我们想知道每年有多少行星被发现,也就是一个年份为横坐标的频度图,那么直方图会是一个比较合适的选择。geom_histogram(binwidth=2, fill="red")
表示绘制宽度为2、填充色为红色的直方图。还是依照原来的老样子,我们绘制aes(pl_disc)
的画布(如果aes只有一个参数,那么它会去统计这个参数的频率分布),然后在上面增加直方图。
1 | # 创建画布并添加宽度为2、填充色为红色的直方图 |
曲线、散点、直方图,这就是使用R语言绘图的三个最基础的内容,更多的方法可以在书中、网上探索。