鉴于andrew大大的提议,现下刚好有闲就来写一系列资料整理套件的文章
版上比我熟这些套件的人也不少,如果不足的部分,再麻烦帮忙补充
目前想要介绍的套件有 (后面列一些我常用的function)
- magrittr : pipe operator如 '%>%', '%<>%', '%T>%', '%$%'
- data.table: class 'data.table', dcast.data.table, melt, fread,
duplicated, transform, rbindlist, merge
- reshape2 : dcast, melt (required for data.table)
- plyr : name_rows, colwise, mapvalues
- dplyr : mutate, summarise, arrange, filter, distinct, group_by, n,
select, rowwise, tbl_dt, mutate_each, summarise_each
- tidyr : gather, separate, spread
这篇的重点放在 magrittr,因为后面我会大量使用pipe operator来写程式码
我尽量每一个operator用简单一点的方式说明,如果不太了解再麻烦告知
程式码部分,我都会用` R 跟 ` 做引号
1. '%>%'
这个operator用来传递数值,避免过多的nest造成阅读上的困难
像是
a_list = list(1:6, 3:5, 4:7)
lapply(a_list, function(x) setdiff(sort(unique(unlist(a_list))), x))
第二行拆解阅读是很困难的,但是写成
a_list %>% unlist %>% unique %>% sort %>% {
lapply(a_list, setdiff, x = .)
}
阅读上会容易很多
举一个简单的例子,来说明 '%>%'的用法
` R
a = 1
f = function(a) a + 1
f(a)
a %>% f
a %>% f()
a %>% f(.)
`
跑上面的程式可以发现,最后四个output都一样
其实 %>% 做的就是把 左边变量 放进 右边函数里做执行
也就是说 f(a) 等同于 a %>% f (或是上面其他三种)
另外,可能会觉得 `a %>% f(.)`会很奇怪
在magrittr中, `.`就是用来代表%>%前面的变量
所以 a %>% f(.) 程式会把.的位置换成a,变成 f(a)
`.` 在magrittr的应用中,会占很大的比例
像是do.call, Reduce第一个input是function,第二个是list
我们通常传入list,所以此时必须用 . 做位置的控制
再者,c, cbind, rbind会根据位置不同来决定是合并于何处
也是一个很重要的问题,因此,用 `.`做传入位置的控制是必须的
针对这个,我给一段简单的程式码让你去试试看
` R
a_list = list(1:5, 3:7, 6:10)
a_list %>% do.call(rbind,, .)
a_list %>% Reduce(cbind, .)
1:5 %>% rbind(3:7, .)
1:5 %>% rbind(., 3:7)
f = function(x, a, b) a*x^2 + b
1:5 %>% f(., 2, 5) # 同 1:5 %>% f(2, 5)
1:5 %>% f(2, ., 5)
1:5 %>% f(2, 5, .)
`
再者,%>% 也可以传入一个block (用{}括住的部分)
像是前面提到的
` R
a_list = list(1:6, 3:5, 4:7)
a_list %>% unlist %>% unique %>% sort %>% {
lapply(a_list, setdiff, x = .)
}
`
先说明怎么阅读 %>%的部分
a_list %>% unlist %>% unique %>% sort 就是
a_list把全部元素合并(unlist),然后取唯一(unique),接着排列所有元素(sort)
就照着%>%的顺序去读就可以顺利解读
再来就是block的部分
{}括住之后,里面的只要不是其他%>%后面的 `.`都代表你前面传入的值
这样很难懂,举个例子
` R
1:2 %>% {
list(
cbind(9:10, .),
3:4 %>% cbind(9:10, .)
)
}
`
output长这样
[[1]]
.
[1,] 9 1
[2,] 10 2
[[2]]
.
[1,] 9 3
[2,] 10 4
可以看到第一个可以很直觉的解读,9:10是跟传入的1:2做行合并
而第二个`.`,因为前面有了一个新的 '%>%'
所以这一个`.`就被前面的 3:4取代
所以第二个output变成9:10跟3:4做行合并
2. '%<>%'
如果懂了 %>%, 这个就不难了
先看简单的例子 (add是magrittr提供用在 %>%上的 `+` (这部分请看最后面的补充))
` R
a = 1
a %>% add(1) # 同 a %>% '+'(1) or a %>% '+'(., 1)
a # 1
a %<>% add(1)
a # 2
`
这个例子可以看的出来 %<>% 除了传入变量之外,也会改变传入变量的值
也就是可以把 a %<>% add(1) 看成 a = a + 1
你如果有一串要做最后赋值给你传入的变量
只需要在第一个传导变量的operator做改变即可,举例来说:
` R
dat = data.frame(a = 1:3, b = 8:10)
dat = dat %>% rbind(dat)
dat2 = data.frame(a = 1:3, b = 8:10)
dat2 %<>% rbind(dat2)
all.eqaul(dat, dat2) # TRUE
`
3. '%T>%'
%T>% 只传递变量,不回传值,通常用来传递到不回传值的function上
像是plot, library, install.packages, plyr的 *_ply等
这个operator可以帮你把前面做好的值赋予一个变量
并且同时做后面function的动作,举例来说:
` R
dat = data.frame(a = rep(1:3,2), b = rnorm(6))
dat2 = dat %>% {tapply(.$b, .$a, sum)} %>%
{ data.frame(a=names(.) %>% as.integer, b = .)
} %T>% plot(.$a, .$b)
`
这里dat2就是一个新的data.frame,同时,我们也把a, b的scatter plot画出来
这部分可以用dplyr的group_by以及summarise完成
还没提到dplyr,所以我们先用替代方法做
这里顺便把第四个operator '%$%'一起说明
dat %>% {tapply(.$b, .$a, sum)} 会不会觉得很冗长,也很容易忘记要放'.$'
但是,'%$%'提供了直接把前面变量的元素 直接以名字做操作
再也不需要 .$name这么麻烦,直接用 name做你想要的操作就好
因此,那行就可以简单写成 dat %$% tapply(b, a, sum)
是不是就变得简单的很多?
4. '%$%'
前面提到了,这里就给一个例子就好
` R
a = 3
b = -2
x = rnorm(100)
y = a + b * x + rnorm(100)
fit = lm(y ~ x)
sigma_hat = fit %$% {crossprod(residuals) / df.residual}
`
下一章应该会介绍data.table跟reshape2,后会有期。
补充:
magrittr提供很多其他function的别名
像是 '+', '*', '[', '[[', '<- rownames()'等等
有兴趣请去magrittr的manual查看extract的部分
这个可以让你写pipe chain的时候更加顺手
像是
vals = 1:3 %>% data.frame(a = ., b = .^2) %>% set_rownames(LETTERS[1:3]) %>%
lm(b ~ a, data = .) %>% predict
不然你可能会这样写
dat = 1:3 %>% data.frame(a = ., b = .^2)
rownames(dat) = LETTERS[1:3]
vals = dat %>% lm(b ~ a, data = .) %>% predict
你可能只是要vals这个变量,你却还要多创一个dat这个暂存变量,而中断chain
[关键字]: magrittr