主讲老师第五课:数据清洗之 dplyr 的使用
可视化是展示结果的重要途径与手段,数据集如果用于统计与绘图, 需要满足一定的格式要求。Wickham 在 2014 年将其称之为整洁数据,也就是所说的 tidy data,其基本要求是每行一个观测, 每列一个变量, 每个单元格恰好有一个数据值。
当我们拿到一份数据,无论是 TCGA 下载的转录组数据,GEO 数据库中的芯片表达数据,亦或者是自己测序等途径得到的数据内容,数据格式正好完全符合我们需求的情况,是非常罕见的。一般来讲,我们需要对数据进行预处理,比如提取其中的某些变量值,变量的重命名,创建一些新的统计变量等等,从而使得数据格式达到输入要求,变得更容易处理。
在之前的训练营以及平时的交流中,我发现即使将整个绘图的代码摆在眼前,很多小伙伴仍然感到无从下手,不知道如何将数据整理到合适格式。数据集经常需要选行子集、选列子集、排序、定义新变量、横向合并、长宽转换等操作,而且经常会用若干个连续的操作分步处理关于这些内容,Hadley Wickham 大神已经考虑到了这些问题,并由此创造了一个新的数据处理方式,就是 tidyverse 中的一个核心 R 包---dplyr 包。他提到 dplyr 是一种新的数据处理的语法,提供了一组一致的动词来帮助您解决最常见的数据处理难题。
这几个函数分别是
1.按值筛选变量(filter());
2.对行进行重新排序(arrange());
3.按名称选取变量(select());
4.使用现有变量的函数创建新变量(mutate());
5.将多个值总结为一个摘要统计量(summarize())
关于这 5 个函数的使用方法,此外,magrittr 包的管道运算符%>%特别适用于分步处理过程,接下来,我们一起来看一下这几个函数的使用方法(数 据清洗.zip)。
同样的,把这个材料进行解压,然后点击 Rproject 文件进入,然后打开 R 的代码文件。
1. R 包的安装与读取以及数据的读取
1.1 R 包准备
首先我们需要加载 tidyverse 包,讲解中的 dplyr 包属于 tidyverse 包这是一个判断语句,require(tidyverse)的功能与 library()函数类似,也是读取 R 包,如果已经安装了这个包,则为 TRUE,如果没有则为 FALSE,接下来,通过感叹号!
来进行取反,TRUE 变成 FALSE,FALSE 变成 TURE。对于 if 语句的运行,必须是括号里面是 TRUE 才能往下运行,因此,如果已经安装了这个包,经过取反以后表示 FALSE,那么后面的语句就不会运行,而如果没有安装,取反后为 TRUE,接下来运行后面的安装 R 包语句,这个和普通的只有后面半句相比,多了一个判断过程,可以避免反复对同一个 R 包进行安装安装完成后使用 library()函数将其加载进来,在加载 tidyverse 包时,在输出信息中我们可以看到存在一些冲突信息。
它会告诉你在 dplyr 包中有两个函数与基础 R 包的函数存在冲突,比如说,对于filter()函数,它在 dplyr 包和 stats 包里面都存在,但是,尽管名字一样,两个的使用方法是不一样的。为了避免系统不认识,在这类相同名字的函数使用过程中,我们需要用这种形式,“包名字::函数名字”,比如说 dplyr::filter(),由于此时我们已经加载了 tidyverse 包,想使用基础 R 包的这两个函数时,需要提供其完整的名称分别是 stats::filter()和 stats::lag()两个。
1.2 数据准备
随后,将数据读取进来,在分析前,提取其前 4 行 4 列,我们先看一下这个数据的结构与内容当然,也可以直接在 RStudio 中通过 View(dat)来查看整个数据框内容,可以发现,在该数据集中,共包含 80 行,132 列内容,其中行名为患者的编号,第一列为患者的生存状态(fustat),第二列为患者的分(Stage),其后为 130 个不同的基因。
2.使用 filter()筛选行
先来回顾一下前面的知识点,对数据框行子集可以用行下标选取,如 df[8:12,],函数 head()取出数据框的前面若干行,tail()取出数据框的最后若干行。然而,dplyr 包的 filter()函数可以按条件选出符合条件的行组成的子集首先,我们来筛选一下 fustat 为 1,Stage 为 2 的所有患者运行以后,dplyr 包会执行相应的筛选操作,并返回一个新的数据框但是,需要注意的是,dplyr 包中的函数从来不会修改原数据框内容。因此,如果想要保存运行结果,需要将其赋值给一个新的变量。经过筛选,最终得到 7 个样本所组成的新数据集 dat2。在筛选的过程中,我们需要熟悉如何运用比较运算或者逻辑运算过程,来快速且准确的筛选变量信息。
2.1 比较运算符
之前介绍过,在 R 语言中,其提供了一整套用于比较运算的符号,分别是:>,>=,<,<=,!=,以及==六种。
大家可以运行后看下结果,大部分的比较方法与我们常规在数学或日常学习中的使用是一致的,但是,需要注意的是,(1).在此判断是否相等使用的是==,而不是=;(2).关于“浮点数”的问题。先来看两个例子是不是觉得十分神奇呢?这是因为计算机使用的是有限精度运算,每一步运算后其保存的均是一个近似值,因此,在比较这类运算结果时,我们不能使用==,应该借助另外一个 near()函数的帮助
2.2 逻辑运算符
关于逻辑运算符,大家之前实际上已经有接触了,常用的布尔逻辑运算符包括:&(与),|(或),以及!(非)三种
3.使用 arrange()排列行
arrange()函数的使用方法与 filter()函数比较相似,但是 arange()函数的作用是改变行的顺序。dplyr 包的 arrange()按照数据框的某一列或某几列排序, 返回排序后的结果。
3.1 将 Stage 按降序进行排列
3.2 将数据框以 fustat,Stage,以及 XYLT2 基因的表达量来进行排列
当列名不止一个时,它会自动使用后面的列在前面排序的基础上继续排列,也就是说呢, 当 fustat 相同时,根据 Stage 进行进一步的排列,依次往下。
4.使用 select()选择列
4.1 按名称选择列
4.2 选择在第一列 fustat 和第三列 XYLT2 之间的所有列
4.3 选择不在第一列 fustat 和第三列 XYLT2 之间的所有列
4.4 select()与 everything()联用
当我们想要将几个变量移动到数据框开头,需要同时借助 select()与 everything()的作用。
运行后,我们可以发现,第三列 XYLT2 和第四列 LSR 被移动到了整个数据框的前两列,除了 everything()以外,select()函数还有一些其他的辅助函数:
starts_with("abc") #匹配以“abc”开头的名称ends_with("xyz") #匹配以“xyz”结尾的名称contains("ijk") #匹配包含“ijk”的名称num_range("x", 1:3) #匹配 x1,x2 和 x3
5.使用 mutate()添加新变量
dplyr 包的 mutate()可以为数据框计算新变量, 返回含有新变量以及原变量的新数据框。在使用过程中,除了对已经的列进行操作,我们常常会添加新的列,来添加一些新的重要信息,这时,就需要借助 mutate()函数来完成操作。为了方便观察,我们用一个小一点的数据集来看结果rm(list = ls())表示一键清除所有当前环境变量,运行以后可以看到不管之前Environment 里面有多少内容,只要一运行全部清空。
5.1 使用 mutate()添加新变量
此时,新的三列信息已经添加到数据框中。在这里,需要注意以下三点内容:(1).dplyr 包中的函数从来不会修改原数据框内容,因此,如果想要保存运行结果,需要将其赋值给一个新的变量;(2). 函数 mutate()总是将新增的列添加在数据集的最后,那么,如果一旦面对大数据,想要便捷地看到新增列的信息,我们就可以使用上面内容提到的两个函数联用,select()和 everything()函数同时使用将新增列提到最前面,先新增,再提前(3). 这是一个十分有意思的现象,根据作者哈大爷的观点,一列信息你既然作为了行名,那么默认你其实并不重要,此时,在 mutate()函数的操作过程中,行名会被自动舍弃,因此,如果行名在后续分析中会进一步使用,需要同时新增一列(Patient)以保存行名
5.2 直接使用新生成的变量
考虑到有些变量会涉及多个操作过程,因此,在使用 mutate()函数的过程中,前一个语句新生成的变量可以直接在下一语句中进行使用,这样可以极大的简化了工作量
5.3 使用 transmute()函数**保留新变量**
如果只想保留新变量,可以直接使用 transmute()函数可以看到,经过 transmute()函数的转换,直接生成了一个只包含 Patient,exp1,和 exp2 三个变量的新数据框 dat4。当然,在 mutate()函数添加新列的同时,可以
与多种函数进行联用,在此,列举几个常用的函数和运算符号:
(1). 算术运算符: +,-,*, /,^;(2). 模运算符: %/%(整数除法), %%(求余);(3). 对数函数: log(),log2(),log10();(4). 累加和滚动聚合: cumsum()(累加和),cumprod()(累加积),commin()(累加最小值),cummax()(累加最大值);(5). 逻辑比较: <,<=,>,>=,!=。大部分函数可以直接根据其命名快速推断其具体的含义
6.使用 summarize()进行分组摘要
接下来,是 dplyr 包的最后一个核心函数就是 summarize()函数。
6.1 使用 summarize()进行摘要
通过 summarize()函数,可以将数据框折叠成一行,可以看到,与 mean()函数的联合,得到了 XYLT2 基因表达的平均值。此时,它的用法和我们前两天讲的apply()函数家族的数据汇总功能是比较相似的。
6.2 函数 group_by()与 summarize()联合使用
对于 summarize()函数,其强大之处在于与 group_by()的一起使用。group_by()可以将分析变量从整个数据集变成单个分组,然后通过 summarize()函数将相应的统计结果对应到相应的分组中。
从结果中我们可以看到,以变量 fustat 和 Stage 作为分组变量,最终得到了不同生存状态和分期状态下患者 XYLT2 基因的表达平均值
7.管道符号(%>%)的使用
当代码逐渐趋于复杂,我们要学习一个新的管理符号,就是管道符号(%>%)。
管道是一个强大的工具,通常是指将上一句运行得到的输出结果,作为下一句运行的输入数据,简单说,就是承上启下,下一句的开始,是上一句结束得到的结果。从前有座山,山里有座庙,庙里有个老和尚……一环扣一环。管道运算符特别适用于对同一数据集进行多次操作
7.1 管道符号的简单示例
通常,在使用过程中,对于“筛选,选择,排序”这三个命令,我们会分别使用一句代码来对应一个操作,虽然,在整个操作过程中并不会产生错误,但无疑会导致许多无用的中间 变量产生(x1,x2),我们并不关心中间步骤产生的信息,这样做,往往会影响我们的整体分析进度,此时,解决这个问题的另一种办法就是使用管道(%>%)。
在阅读代码时,我们可以将%>%读作“然后”,顺接整个代码语句,筛选,然后选择,然后排序,快速完成,来看下两次运行得到的结果使用 identical()函数判断两个结果,可以看到,x3 和 x4 两个数据框是完全相同的。
接着,我们再来改写一下前面 mutate 和 select 联用的代码其结果也都是一样的
7.2 多个函数结合管道符号联用
下面,我们进一步将学习的几个函数串联使用起来,group_by(),summarize(),filter(),以及 mutate()。
这个作为课后作业,对它拆分成为普通形式,看下两个的效果,整个过程,分组,
然后摘要统计,然后筛选,然后新增一列,最后得到分析结果
当然,如果对于初学者来说,简单的数据清洗过程可以像 EXCEL 一样点点点的话,那个体验可能会好很多,RStudio 里面也是存在类似的插件,可以通过点点点对简单的数据进行快速清洗,来看下这个的使用方法。