R数据科学学习笔记Note16使用p

长沙白癜风医院 http://pf.39.net/bdfyy/bdfyc/150505/4618893.html

本系列为《R数据科学》(RforDataScience)的学习笔记。相较于其他R语言教程来说,本书一个很大的优势就是直接从实用的R包出发,来熟悉R及数据科学。更新过程中,读者朋友如发现错误,欢迎指正。如果有疑问,也可以后台私信。希望各位读者朋友能学有所得!

使用purrr实现迭代(下)16.5映射函数

purrr包提供了一个函数族来完成这种循环操作。每种类型的输出都有一个相应的函数:

map()用于输出列表;map_lgl()用于输出逻辑型向量;map_int()用于输出整型向量;map_dbl()用于输出双精度型向量;map_chr()用于输出字符型向量。

每个函数都使用一个向量作为输入,并对向量的每个元素应用一个函数,然后返回和输入向量同样长度(同样名称)的一个新向量。向量的类型由映射函数的后缀决定。

可以使用这些函数来执行与for循环相同的操作。

library(tidyverse)df-tibble(a=rnorm(10),b=rnorm(10),c=rnorm(10),d=rnorm(10))map_dbl(df,mean)map_dbl(df,median)map_dbl(df,sd)

map_dbl(df,mean)abcd0.-0.-0..map_dbl(df,median)abcd0..-0..map_dbl(df,sd)abcd0....快捷方式

对于参数.f,你可以使用几种快捷方式来减少输入量。假设你想对某个数据集中的每个分组都拟合一个线性模型。以下这个简单示例将mtcars数据集拆分成3个部分(按照气缸的值分类),并对每个部分拟合一个线性模型:

models-mtcars%%split(.cyl)%%map(function(df)lm(mpg~wt,data=df))

因为R中创建匿名函数的语法比较繁琐,所以purrr提供了一种更方便的快捷方式——单侧公式:

models-mtcars%%split(.cyl)%%map(~lm(mpg~wt,data=.))

我们在以上示例中使用了.作为一个代词:它表示当前列表元素(与for循环中用i表示当前索引是一样的)。

当检查多个模型时,有时你会需要提取出像R^2这样的摘要统计量。要想完成这个任务,需要先运行summary()函数,然后提取出结果中的r.squared。我们可以使用匿名函数的快捷方式来完成这个操作:

models%%map(summary)%%map_dbl(~.r.squared)

models%%+map(summary)%%+map_dbl(~.r.squared)...

因为提取命名成分的这种操作非常普遍,所以purrr提供了一种更为简洁的快捷方式:使用字符串。

models%%map(summary)%%map_dbl("r.squared")

你还可以使用整数按照位置来选取元素:

x-list(list(1,2,3),list(4,5,6),list(7,8,9))x%%map_dbl(2)

x%%map_dbl(2)[1].6多参数映射

前面的映射函数都是对单个输入进行映射。但我们经常会有多个相关的输入需要同步迭代,这时候可以使用map2()和pmap()函数。例如,假设你想模拟几个均值不同的随机正态分布,我们已经知道了如何使用map()函数来完成这个任务:

mu-list(5,10,-3)mu%%map(rnorm,n=5)%%str()

mu%%+map(rnorm,n=5)%%+str()Listof3:num[1:5]4.....88:num[1:5]9.69....25:num[1:5]-3.03-3.04-1.63-3.23-1.48

如果还想让标准差也不同,那么该怎么办呢?其中一种方法是使用均值向量和标准差向量的索引进行迭代:

sigma-list(1,5,10)seq_along(mu)%%map(~rnorm(5,mu[[.]],sigma[[.]]))%%str()

seq_along(mu)%%+map(~rnorm(5,mu[[.]],sigma[[.]]))%%+str()Listof3:num[1:5]3.....38:num[1:5]7.....52:num[1:5]1.48-2...5-7.91

但是这种方法很难让人理解代码的本意。相反,我们应该使用map2()函数,它可以对两个向量进行同步迭代:

map2(mu,sigma,rnorm,n=5)%%str()

map2(mu,sigma,rnorm,n=5)%%str()Listof3:num[1:5]2.....03:num[1:5]8.....03:num[1:5]0.-6..-5..

map2()函数可以生成以下一系列函数调用:

map2

注意,每次调用时值发生变化的参数(这里是mu和sigma)要放在映射函数(这里是rnorm)的前面,值保持不变的参数(这里是n)要放在映射函数的后面。

和map()函数一样,map2()函数也是对for循环的包装:

map2-function(x,y,f,...){out-vector("list",length(x))for(iinseq_along(x)){out[[i]]-f(x[[i]],y[[i]],...)}out}

如果你想生成均值、标准差和样本数量都不相同的正态分布,那么就可以使用pmap()函数:

n-list(1,3,5)args1-list(n,mu,sigma)args1%%pmap(rnorm)%%str()

args1%%+pmap(rnorm)%%+str()Listof3:num6.1:num[1:3]12...74:num[1:5]6..-0.-9.27.pmap

如果没有为列表的元素命名,那么pmap()在调用函数时就会按照位置匹配。这样做比较容易出错,而且会让代码的可读性变差,因此最好使用命名参数:

args2-list(mean=mu,sd=sigma,n=n)args2%%pmap(rnorm)%%str()pmap_参数

因为长度都是相同的,所以可以将各个参数保存在一个数据框中:

params-tribble(~mean,~sd,~n,5,1,1,10,5,3,-3,10,5)params%%pmap(rnorm)

params-tribble(+~mean,~sd,~n,+5,1,1,+10,5,3,+-3,10,5+)params%%+pmap(rnorm)[[1]][1]4.[[2]][1]20...821[[3]][1]-13.-10.-0.-5.4669[5]-6.调用不同函数

还有一种更复杂的情况:不但传给函数的参数不同,甚至函数本身也是不同的。

f-c("runif","rnorm","rpois")param-list(list(min=-1,max=1),list(sd=5),list(lambda=10))

为了处理这种情况,你可以使用invoke_map()函数:

invoke_map(f,param,n=5)%%str()

invoke_map(f,param,n=5)%%str()Listof3:num[1:5]-0.-0.-0.-0.-0.:num[1:5]2..63-1...82:int[1:5]11invoke_map

第一个参数是一个函数列表或包含函数名称的字符向量。第二个参数是列表的一个列表,其中给出了要传给各个函数的不同参数。随后的参数要传给每个函数。

第一个参数是一个函数列表或包含函数名称的字符向量。第二个参数是列表的一个列表,其中给出了要传给各个函数的不同参数。随后的参数要传给每个函数。

sim-tribble(~f,~params,"runif",list(min=-1,max=1),"rnorm",list(sd=5),"rpois",list(lambda=10))sim%%mutate(sim=invoke_map(f,params,n=10))16.7游走函数

如果调用函数的目的是利用其副作用,而不是返回值时,那么就应该使用游走函数,而不是映射函数。通常来说,使用这个函数的目的是在屏幕上提供输出或者将文件保存到磁盘——重要的是操作过程,而不是返回值。以下是一个非常简单的示例:

x-list(1,"a",3)x%%walk(print)

x%%+walk(print)[1]1[1]"a"[1].8for循环的其他模式

purrr还提供了其他一些函数,可以对for循环的其他模式进行抽象。虽然它们的使用频率比映射函数低,但了解一下还是有用的。

16.8.1预测函数

一些函数可以与返回TRUE或FALSE的预测函数一同使用。

keep()和discard()函数可以分别保留输入中预测值为TRUE和FALSE的元素:

iris%%keep(is.factor)%%str()

iris%%+keep(is.factor)%%+str()data.frame:obs.of1variable:Species:Factorw/3levels"setosa","versicolor",..:...

iris%%discard(is.factor)%%str()

iris%%+discard(is.factor)%%+str()data.frame:obs.of4variables:Sepal.Length:num5.14.94.74..44..44.9...Sepal.Width:num3..23.13.63.93.43.42.93.1...Petal.Length:num1.41.41.31.51.41.71.41.51.41.5...Petal.Width:num0.20.20.20.20.20.40.30.20.20.1...

some()和every()函数分别用来确定预测值是否对某个元素为真以及是否对所有元素为真:

x-list(1:5,letters,list(10))xx%%some(is_character)x%%every(is_vector)

x[[1]][1][[2]][1]"a""b""c""d""e""f""g""h""i""j""k""l""m"[14]"n""o""p""q""r""s""t""u""v""w""x""y""z"[[3]][[3]][[1]][1]10x%%+some(is_character)[1]TRUEx%%+every(is_vector)[1]TRUE

detect()函数可以找出预测值为真的第一个元素,detect_index()函数则可以返回这个元素的位置:

x-sample(10)xx%%detect(~.5)x%%detect_index(~.5)

x[1]71826435x%%+detect(~.5)[1]7x%%+detect_index(~.5)[1]1

head_while()和tail_while()分别从向量的开头和结尾找出预测值为真的元素:

x%%head_while(~.5)x%%tail_while(~.5)

x%%+head_while(~.5)[1]7x%%+tail_while(~.5)integer(0)16.8.2归约与累计

对于一个复杂的列表,有时你想将其归约为一个简单列表,方式是使用一个函数不断将两个元素合成一个。如果想要将两表间的一个dplyr操作应用于多张表,那么这种方法是非常适合的。例如,如果你有一个数据框列表,并想要通过不断将两个数据框连接成一个的方式来最终生成一个数据框:

dfs-list(age=tibble(name="John",age=30),sex=tibble(name=c("John","Mary"),sex=c("M","F")),trt=tibble(name="Mary",treatment="A"))dfs

dfsage#Atibble:1x2nameagechrdbl1John30sex#Atibble:2x2namesexchrchr1JohnM2MaryFtrt#Atibble:1x2nametreatmentchrchr1MaryA

dfs%%reduce(full_join)

dfs%%reduce(full_join)Joining,by="name"Joining,by="name"#Atibble:2x4nameagesextreatmentchrdblchrchr1John30MNA2MaryNAFA

或者你想要找出一张向量列表中的向量间的交集:

vs-list(c(1,3,5,6,10),c(1,2,3,7,8,10),c(1,2,3,4,8,9,10))vs%%reduce(intersect)

vs%%reduce(intersect)[1]

往期文章:

预览时标签不可点收录于话题#个上一篇下一篇


转载请注明:http://www.jinchuk.com/npbtp/7554.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了