vlambda博客
学习文章列表

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()
R语言必知必会之tidyverse(一):管道操作

管线,也支持 Base R 函数:

month.abb %>%     # 内置月份名缩写的字符串
sample(6) %>%
tolower() %>%
paste0(collapse = "|")
R语言必知必会之tidyverse(一):管道操作

使用管道的好处是:

(1)避免使用过多的中间变量;

(2)程序可读性大大增强:

管道操作的过程,读起来就是对原数据集依次进行一系列操作的过程。而非管道操作,读起来与操作的过程是相反的,比如同样实现上例:

paste0(tolower(sample(month.abb, 6)), collapse="|")

二. 使用管道操作

  1. 管道默认将输出传给下一个函数的第1个参数

. <- c(1, 3, 4, 5, NA)
mean(., na.rm=TRUE)
R语言必知必会之tidyverse(一):管道操作
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=", "))
R语言必知必会之tidyverse(一):管道操作


3. 输出传给不是第1个参数

若函数的第1个参数不是接受数据,需要手动放置"."

# 错误做法
# iris %>% plot(Sepal.Width~Petal.Width) # 相当于
# plot(x=iris, y=Sepal.Width~Petal.Width)
# 正确做法
iris %>% plot(Sepal.Width~Petal.Width, data = .)
R语言必知必会之tidyverse(一):管道操作


4. 管道中使用函数

(1) 有时候想在管道中使用匿名函数

month.abb %>%   # 选择子集
{.[1:3]}
R语言必知必会之tidyverse(一):管道操作
# 等同于
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()

R语言必知必会之tidyverse(一):管道操作

注意:管线函数总是一元的,只能接受一个参数,而且在管线中参数为一个".";若要在管线内传递多个参数,需要用通常的方式 function() 定义函数。

5. 通常操作的管道函数

你可能想要用[ 或 [[ 或 $访问"."的内容,例如选择数据框的子集,查看直方图中某个条形的值。可以用这些函数的前缀形式实现,但可读性不好:

iris %>%
filter(Petal.Length>2, Petal.Width>2) %>%
`[[`("Sepal.Length") %>%
hist(main="Histogram of 'Sepal.Length' after subsetting")

R语言必知必会之tidyverse(一):管道操作

magrittr 包提供了一些函数来实现这样的操作,更多函数可用 ?extract 查阅。

R语言必知必会之tidyverse(一):管道操作

iris %>%
head() %>%
extract(c("Sepal.Length","Sepal.Width")) # 提取列, 返回数据框

iris %>%
head() %>%
extract2("Sepal.Length") # 提取1列, 返回向量

注意:不同包中同名的函数,默认是选最近加载的包中的。建议用加前缀的方式“magrittr::”为这样的函数指定包。


参考文献:

  1. 1. Desi Quintans, Jeff Powell. Working in the Tidyverse. hiercourse.com/

  2. 2. Garrett Grolemund, Hadley Wickham. R for Data Science. r4ds.had.co.nz/