purrr替代循环引用知乎张敬信的说法:
❝用 R 写 「循环」 从低到高有三种境界:手动 for 循环,apply 函数族,purrr 包泛函式编程。 ❞
R写循环有三个境界:
其中,手动for循环我最常用,apply系列半吊子,purrr函数一窍不通,所以要学习一下。
函数的函数成为泛函式,map(x,f)中,map是函数,f也是函数,f是map的参数,那么map就是泛函数。
dat = data.frame(y1 = rnorm(10),y2 = rnorm(10)+10)
dat
map(dat,mean)
这里,map是函数,mean是map的参数,测试数据:
> dat = data.frame(y1 = rnorm(10),y2 = rnorm(10)+10)
> dat
y1 y2
1 0.5817177 10.355036
2 1.4852696 10.058380
3 1.0901313 11.682624
4 0.2128081 11.119148
5 0.8566806 10.547209
6 0.8843383 9.414661
7 0.4761166 11.070056
8 0.3465340 9.301207
9 1.1130833 10.087095
10 0.6286422 9.983994
> map(dat,mean)
$y1
[1] 0.7675322
$y2
[1] 10.36194
如果使用apply系列的lapply函数,是这样处理的:
> lapply(dat,mean)
$y1
[1] 0.7675322
$y2
[1] 10.36194
两者结果完全一致,
所以,这里map和apply都是泛函式函数。
这里先模拟数据:
> dat = data.frame(x1 =rnorm(10),x2 = rnorm(10),x3 = rnorm(10),x4 = rnorm(10))
> dat
x1 x2 x3 x4
1 1.203531098 -0.33361497 -0.05708186 0.9924523
2 -0.340909576 0.22388983 -1.42836870 -0.2116809
3 -1.710778506 0.31278696 0.17518334 0.8943420
4 -0.001525112 0.22099926 -0.62475728 -0.7134268
5 0.229862264 -1.76353518 -1.04137751 0.5086442
6 -0.382215060 0.06071203 -0.86489543 -0.7195158
7 1.175170874 -0.78679427 -0.23324264 -0.4412758
8 0.151216441 -1.09148795 0.54867008 0.1762894
9 -0.017814828 0.51608686 0.04602458 -0.5771657
10 -1.406719653 0.30200433 -0.18020801 0.3050708

这里,map函数,支持一元函数
map(dat,max)

这里map2可以支持二元函数,比如:
map2(dat$x1,dat$x2,~max(.x,.y))
上面的.x和.y表示datx1, datx2两个元素,~max表示匿名函数。上面需要用map2或者pmax,如果用map就失败:
> map(dat$x1,dat$x2,~max(.x,.y))
[[1]]
NULL
[[2]]
NULL

支持两个,或者两个以上的多元函数,默认是对行进行操作:
> pmap(dat,max)
[[1]]
[1] 1.203531
……
上面的也可以写为:
pmap(dat,~max(..1,..2,..3,..4))
下面三种写法是等价的。
pmap.x..1map(dat,max)
map(dat,~max(.x))
map(dat,~max(..1))
和上面一元map用法一样,下面三种也是等价的:
map2(dat$x1,dat$x2, max)
map2(dat$x1,dat$x2, ~max(.x,.y))
map2(dat$x1,dat$x2, ~max(..1,..2))
因为多元的,不会有..x, ..y, ..z表示,只会有..1, ..2, ..3表示,所以下面两种也是等价的:
pmap(dat,max)
pmap(dat,~max(..1,..2,..3,..4))
比如,要计算每一列的平均值,允许缺失值,需要用到参数na.rm = T,可以这样写:
> map(dat,~mean(.x,na.rm = T))
$x1
[1] -0.1100182
$x2
[1] -0.2338953
$x3
[1] -0.3660053
$x4
[1] 0.02137338
这里,用到了匿名函数,可以把匿名函数的参数,写在匿名函数里面。
这里,因为map函数的用法是:map(.x, .f, ...),其中
所以,计算每一列的平均值,也可以写为:
> map(dat,mean,na.rm=T)
$x1
[1] -0.1100182
$x2
[1] -0.2338953
$x3
[1] -0.3660053
$x4
[1] 0.02137338
这里使用我的R包learnasreml中的MET数据,进行测试。
MET数据是作物两年多点的产量试验数据,这里我们分别对每一个地点的品种进行方差分析。
library(learnasreml)
data(MET)
head(MET)
summary(MET)
> library(learnasreml)
> data(MET)
> head(MET)
Year Location Rep Cul Yield
1 2009 KN 1 EarlyCanada 56.236
2 2009 KN 1 CalhounGray 74.167
3 2009 KN 1 StarbriteF1 32.601
4 2009 KN 1 CrimsonSweet 74.167
5 2009 KN 1 GeorgiaRattlesnake 64.794
6 2009 KN 1 FiestaF1 70.907
> summary(MET)
Year Location Rep Cul Yield
2009:200 CI:80 1:100 CalhounGray : 40 Min. : 4.034
2010:200 FL:80 2:100 CrimsonSweet : 40 1st Qu.: 45.166
KN:80 3:100 EarlyCanada : 40 Median : 67.647
SC:80 4:100 FiestaF1 : 40 Mean : 67.473
TX:80 GeorgiaRattlesnake: 40 3rd Qu.: 88.616
Legacy : 40 Max. :180.906
(Other) :160 NA's :3
数据包括:
这里,我们对每一个地点的品种,进行方差分析,常规的做法是:
loc1 = MET[MET$Location == "CI",]
loc2 = MET[MET$Location == "FL",]
loc3 = MET[MET$Location == "KN",]
loc4 = MET[MET$Location == "SC",]
loc5 = MET[MET$Location == "TX",]
提取每一个地点的数据,单独保存。然后对每一个地点进行方差分析:
summary(aov(Yield ~ Cul, data=loc1))
summary(aov(Yield ~ Cul, data=loc2))
summary(aov(Yield ~ Cul, data=loc3))
summary(aov(Yield ~ Cul, data=loc4))
summary(aov(Yield ~ Cul, data=loc5))
如果使用map函数进行批量建模:
MET %>% split(.$Location) %>% map(.,~aov(Yield ~ Cul,.) %>% summary)
结果如下:
> MET %>% split(.$Location) %>% map(.,~aov(Yield ~ Cul,.) %>% summary)
$CI
Df Sum Sq Mean Sq F value Pr(>F)
Cul 9 8696 966.2 3.193 0.00277 **
Residuals 69 20879 302.6
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
1 observation deleted due to missingness
$FL
Df Sum Sq Mean Sq F value Pr(>F)
Cul 9 10151 1127.9 1.565 0.143
Residuals 69 49723 720.6
1 observation deleted due to missingness
$KN
Df Sum Sq Mean Sq F value Pr(>F)
Cul 9 8236 915.1 4.038 0.000338 ***
Residuals 70 15863 226.6
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
$SC
Df Sum Sq Mean Sq F value Pr(>F)
Cul 9 24478 2719.8 5.6 8.42e-06 ***
Residuals 70 33996 485.7
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
$TX
Df Sum Sq Mean Sq F value Pr(>F)
Cul 9 5784 642.6 1.326 0.24
Residuals 69 33429 484.5
1 observation deleted due to missingness
walk和map函数组合上类似,不同的是walk不返回结果,比如你要保存数据时,就可以用walk函数系列。
上面的MET数据,我们可以将数据按照品种分组,批量的保存名为地点的数据csv中。
data(MET)
head(MET)
MET %>% group_nest(Location) %>% pwalk(~ write.csv(.y,paste0(.x,".csv")))
结果,每个地点都有一个csv文件:

上面的csv文件,批量读取,然后合并再一起
re = map_dfr(file,read.csv)
https://zhuanlan.zhihu.com/p/168772624
https://mp.weixin.qq.com/s/H6nwbkePeeMhDCEwlC1PUA