R语言——高级循环2

参考资料:学习R

1、遍历数组

         lapply和vapply、sapply都可用于矩阵和数组上,但他们的行为往往不是我们想要的。这三个函数把矩阵和数组看作向量,将目标函数作用于每个元素上(沿列往下移动)。而更为常见的是,当药把函数作用于一个数组时,我们希望能按行或列应用它们。这里需要安装matlab包

install.packages("matlab")
library(matlab)

        magic函数将创建一个方阵:n×n的、从1排到n^2的数字矩阵,其行数和列数相等。

        一个需要我们把函数应用到每行上的经典问题——计算行的总数或其他统计量。apply函数提供了类似的按行/列计算的等效函数,它以一个矩阵、维数和函数作为参数。维数为1代表把函数应用于每一行;2代表把函数应用于每一列(或更大的数字代表更高维的数组)。

magic4<-magic(4)
print(magic4)
apply(magic4,1,sum)
apply(magic4,1,toString)
apply(magic4,2,toString)

        apply函数也可用于数据框,尽管对于这种混合的数据类型来说不太常见(例如,如果其中有一列是字符类型的,很显然我们不能再它上面计算总和或乘积):

baldwins<-data.frame(
  name=c("Alec","Daniel","Billy","Stephen"),
  date_of_birth=c("1958-Apr-03","1960-Oct-05","1963-Feb-21","1966-May-12"),
  n_spouses=c(2,3,1,1),
  n_children=c(1,5,3,2)
)
print(baldwins)
apply(baldwins,1,toString)
apply(baldwins,2,toString)

        当我们把函数按列应用于数据框上时,apply和sapply的行为相同(数据框可以被认为是非嵌套的列表,其中的元素都是相同的长度):

        将sapply(或apply)与range函数结合使用能非常迅速地确定我们的数据范围。

sapply(baldwins,range)
apply(baldwins,2,range)

2、多个输入的应用函数

        lapply的缺点之一是它的函数参数只能循环作用于单个向量参数。另一个缺点是:对于作用于每个元素的函数,我们不能访问该元素的名称。

        mapply是“多参数列表应用”(multiple argument list apply)的简称,它能让我们传入尽可能多的向量作为参数,这解决了上面的第一个问题。常见的用法是传入一个列表,再传入另一个列表作为前者的名字,这就解决了第二问题。有点烦人的是,为了任意数目的向量参数,参数的顺序改变了。对于mapply,每一个传递的参数都是函数。

msg<-function(name,factors){
  ifelse(
    length(factors)==1,
    paste(name,"is prime"),
    paste(name,"has factors",toString(factors))
  )
}
print(prime_factors)
mapply(msg,names(prime_factors),prime_factors)

         即时向量化(Instant Vectorization)Vectorize是mapply的包装函数。通常它接受一个标量作为输入参数,并返回一个新的接受向量的函数。下例中的函数不是向量化的,因为它使用了switch,而这需要它的输入参数为标量,如果把向量传入到函数中,则会报错:

baby_gender_report<-function(gender){
  switch(
    gender,
    male="It's a boy!",
    female="It's a girl!",
    "Um..."
  )
}
gender<-c("male","female","other")
baby_gender_report(gender)

        从理论上讲,完全可以重新一个函数,使它变成向量化的。但更简单的方法是使用Vectorize函数:

vec_baby_gender_report<-Vectorize(baby_gender_report)
vec_baby_gender_report(gender)

3、拆分—应用—合并(split-apply-combine)

        在研究数据时一个很常见的问题是:如何对那些已被分成不同小组的变量进行统计计算。以下是经典的道路交通安全游戏Frogger的得分情况:

frogger_scores<-data.frame(
  players=rep(c("Tom","Dick","Harry"),times=c(2,5,3)),
  scores=round(rlnorm(10,8),-1)
)
print(frogger_scores)

        计算每个文件的平均得分需要分三个步骤。首先,我们按玩家来分开数据集:

scores_by_player<-with(
  frogger_scores,
  split(scores,players)
)
print(scores_by_player)

        然后,我们将mean函数应用于每个元素

list_of_means_by_player<-lapply(scores_by_player,mean)
print(list_of_means_by_player)

        

        最后,我们把结果合并到单个向量中:

mean_by_player<-unlist(list_of_means_by_player)
print(mean_by_player)

        如果使用vapply或sapply函数,最后两步可以简化为一步,但拆分—应用—合并是非常常用的,所以我们必须简化它。方法就是使用tapply函数,它能依次执行所有的三个步骤,一气呵成:

with(
  frogger_scores,
  tapply(scores,players,mean)
)

        还有几个其他的tapply包装函数,例如by和aggregate,它们的功能相同,但接口稍有不同。在SQL中,拆分—应用—合并其实就是group by操作。

4、plyr包

        *apply 函数家族都很强大, 但三个缺点使得它们不是那么易用。 首先, 名字有点晦涩。

        其次, 参数的使用不完全一致。 大多数的函数都以数据对象为首个参数, 函数为第二参数。 但 mapply 的顺序却与此相反, 而 tapply 还要加上一个函数作为它的第三个参数。 数据参数有时是 X, 有时是 object; 而简化参数有时是 simplify, 有时是 SIMPLIFY。

        第三, 输出的形式不太可控。 如果要把结果作为数据框返回或丢弃它, 都需要花一些心思才能做到。

        这时,plyr 包就派上用场了。它包含一系列名为 **ply 的函数,其中的空格(星号)分别代表输入和输出的形式。例如:

        llply 的输入参数是列表,它将函数应用于每个元素上,并返回一个列表,这使它成为 lapply 的一个替代函数。

        laply以一个列表作为参数,并返回一个数组,这个酷似sapply。

        raply函数能取代replicate,还有rlply和rdply函数能分别返回列表或数据框,还有一个r_ply函数能丢弃结果(在绘图时有用):

library(plyr)
llply(prime_factors,unique)
laply(prime_factors,length)
raply(5,runif(1))  # 输出数组
rlply(5,runif(1))  # 输出列表
rdply(5,runif(1))  # 输出数据框
r_ply(5,runif(1))  # 丢弃输出

        plyr包中最常用的函数是ddply,它的输入和输出都是数据框,它可以替换tapply函数。其有点是易于同时计算多个列:

        对于ddply函数,我们即可以用colwise告诉ddply调用函数应用于每一列(除了第二个参数以外),也可以使用summarize对指定的列进行操作。

        使用colwize指定时会更快,但我们必须对每一列重复同样的事情,而summarize更灵活,但需要我们输入更多的内容。

# 为frogger_scores增加一个level列
frogger_scores$level<-floor(log(frogger_scores$scores))
ddply(
  frogger_scores,
  .(players),
  colwise(mean)  # 除了player之外,对每个列调用mean函数
)
ddply(
  frogger_scores,
  .(players),
  summarize,
  mean_score=mean(scores),  # 对score调用mean
  max_level=max(level)  # 对level求max值
)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值