R语言必知必会之tidyverse(一):管道操作
征得张敬信老师同意,本号将转载张老师关于R语言tidyverse包一系列文章,为大家进行医学研究提供助力。
来源:R&Python数据科学 知乎专栏
作者简介:张敬信,高校数学教师,研究兴趣:数学,数学建模,数据挖掘,机器学习,Matlab, R语言,Python,Mathematica,Latex
链接:https://www.zhihu.com/people/huc_zhangjingxin
前言
Hadley 大神的 tidyverse 包,将数据处理和建模整个流程所涉及到的:读入、清洗、重塑、汇总、可视化、探索、建模、结果展示的整个流程,都以一种“优雅”的方式整合到了一起。
我之前学了一遍 Hadley 的配套书《R for Data Science》,虽然比较系统全面,但重点部分有点过于基础,实用性欠缺了点。最近偶然看到一个非常好且精炼的进阶资料《Working in the Tidyverse》,主要以它为主,翻译并学习记录下来。
强烈建议大家,抛弃流传甚广的陈旧的数据操作方面的 R 语法,全面改用 tidyverse。
——————————————————————————————
下面先从第零篇,管道操作,开始。
一. 简介
magrittr 包引入了管道操作,能够通过管道将数据从一个函数传给另一个函数,从而用若干函数构成的管线依次变换你的数据。
先看一个例子,对数据集 warpbreaks,按分类变量 wool 和 tension 分组,对连续变量 breaks 做分组汇总,分别计算均值、中位数、标准差:
warpbreaks %>%
group_by(wool, tension) %>%
summarise_at(vars(breaks), list(~mean(.), ~median(.), ~sd(.)))
管道运算符%>%的意思是:将左边的运算结果,以输入的方式传给右边函数。若干个函数通过管道链接起来,叫做管线(pipeline)。
"ATMOSPHERE" %>% str_replace("SPHERE", "SQUARE") %>% str_to_lower() %>% print()
管线,也支持 Base R 函数:
month.abb %>% # 内置月份名缩写的字符串
sample(6) %>%
tolower() %>%
paste0(collapse = "|")
使用管道的好处是:
(1)避免使用过多的中间变量;
(2)程序可读性大大增强:
管道操作的过程,读起来就是对原数据集依次进行一系列操作的过程。而非管道操作,读起来与操作的过程是相反的,比如同样实现上例:
paste0(tolower(sample(month.abb, 6)), collapse="|")
二. 使用管道操作
管道默认将输出传给下一个函数的第1个参数
. <- c(1, 3, 4, 5, NA)
mean(., na.rm=TRUE)
c(1, 3, 4, 5, NA) %>% mean(., na.rm=TRUE)
c(1, 3, 4, 5, NA) %>% mean(na.rm = TRUE) # "."可以省略
2. 输出可以在右边使用多次
在右边的函数调用中需要借助".":
c(1, 3, 4, 5) %>% plot(., main=paste(., collapse=", "))
# 第1个"."可以省略, 其余的"."不能省略
c(1, 3, 4, 5) %>% plot(main=paste(., collapse=", "))
3. 输出传给不是第1个参数
若函数的第1个参数不是接受数据,需要手动放置"."
# 错误做法
# iris %>% plot(Sepal.Width~Petal.Width) # 相当于
# plot(x=iris, y=Sepal.Width~Petal.Width)
# 正确做法
iris %>% plot(Sepal.Width~Petal.Width, data = .)
4. 管道中使用函数
(1) 有时候想在管道中使用匿名函数
month.abb %>% # 选择子集
{.[1:3]}
# 等同于
month.abb %>%
(function(.) {.[1:3]})
(2) 也可以将整个管线定义为一个函数,需要将"."作为管线的第1个值,将整个管线赋值给一个名字(函数名)
random_sepal_data <- # 函数名
. %>% # 用"."作为管线的开始
select(Species, starts_with("Sepal")) %>%
sample_n(5) %>%
arrange(desc(Sepal.Length))
iris %>%
random_sepal_data()
注意:管线函数总是一元的,只能接受一个参数,而且在管线中参数为一个".";若要在管线内传递多个参数,需要用通常的方式 function() 定义函数。
5. 通常操作的管道函数
你可能想要用[ 或 [[ 或 $访问"."的内容,例如选择数据框的子集,查看直方图中某个条形的值。可以用这些函数的前缀形式实现,但可读性不好:
iris %>%
filter(Petal.Length>2, Petal.Width>2) %>%
`[[`("Sepal.Length") %>%
hist(main="Histogram of 'Sepal.Length' after subsetting")
magrittr 包提供了一些函数来实现这样的操作,更多函数可用 ?extract 查阅。
iris %>%
head() %>%
extract(c("Sepal.Length","Sepal.Width")) # 提取列, 返回数据框
iris %>%
head() %>%
extract2("Sepal.Length") # 提取1列, 返回向量
注意:不同包中同名的函数,默认是选最近加载的包中的。建议用加前缀的方式“magrittr::”为这样的函数指定包。
参考文献:
1. Desi Quintans, Jeff Powell. Working in the Tidyverse. hiercourse.com/
2. Garrett Grolemund, Hadley Wickham. R for Data Science. r4ds.had.co.nz/