pycharm中配置r语言_【R语言】R语言中的循环

d9299e613f68acb6c8cec0d95376d9ee.png

编程中减少代码重复的两个工具,一是循环,一是函数。

循环,用来处理对多个同类输入做相同事情(即迭代),如对不同列做相同操作、对不同数据集做相同操作。

R语言有三种方式实现循环:

(1)for循环、while循环

(2)apply函数族

(3)泛型函数map

一. for循环、while循环

首先作两点说明:

(1)关于“for循环运行速度慢”的说法,实际上已经过时了,现在的R、Matlab等软件经过多年的内部优化已经不慢了,之所以表现出来慢,是因为你没有注意两个关键点:

  • 提前为保存循环结果分配存储空间;
  • 为循环体中涉及到的数据选择合适的数据结构。

(2)apply函数族和泛型函数map能够更加高效简洁地实现一般的for循环、while循环,但这不代表for循环、while循环就没用了,它们可以在更高的层次使用(相对于在逐元素级别使用)

1. 基本for循环

例如,有如下的tibble数据:

library(tidyverse)df <- tibble(a = rnorm(10),b = rnorm(10),c = rnorm(10),d = rnorm(10)
)

用复制-粘贴法,计算每一列的中位数:

median(df$a)
median(df$b)
median(df$c)
median(df$d) 

为了避免“粘贴-复制多于两次”,改用for循环实现:

output <- vector("double", ncol(df))     #1.输出
for (i in seq_along(df)) {               #2.迭代器output[[i]] <- median(df[[i]])         #3.循环体
}
output  #输出结果略

for循环有三个组件:

(1) 输出:output <- vector("double", length(x))

在循环开始之前,最好为输出分配足够的存储空间,这样效率更高:若每循环一次,就用c()合并一次,效率会很低下。

通常是用vector()函数创建一个给定长度的空向量,它有两个参数:向量类型(logical, integer, double, character等)、向量长度。

(2)迭代器:i in seq_along(df)

确定怎么循环:每次for循环将对i赋一个seq_along(df)中的值,可将i理解为代词it. 其中seq_along()是“1:length(df)”的安全版本,它能保证遇到长度为0的向量时,仍能正确工作:

y <- vector("double", 0)
seq_along(y) 

64ec4029d5bda87ed18e2c33266d4240.png
1:length(y)

d1f39b9102ecaf3cb8d5c666ab93ec0e.png

你可能不会故意创建长度为0的向量,但容易不小心创建,则会导致报错。

(3) 循环体:output[[i]] <- median(df[[i]])

即执行具体操作的代码,它将重复执行,每次对不同的i值。

第1次迭代将执行:output[[1]] <- median(df[[1]]),

第2次迭代将执行:output[[2]] <- median(df[[2]]),

……

2. for循环变种

基本的for循环有4个变种:

(1) 修改已存在的对象,创建的新对象

有时需要用for循环修改一个已存在的对象,例如,对数据框 df 的每一列做归一化:

rescale01 <- function(x) {rng <- range(x, na.rm = TRUE)(x - rng[1]) / (rng[2] - rng[1])
}
df$a <- rescale01(df$a)
df$b <- rescale01(df$b)
df$c <- rescale01(df$c)
df$d <- rescale01(df$d)

用for循环来做,先考虑其3个组件:

输出:已经存在,与输入相同。

迭代器:可以将数据框看作是多个列构成的列表,故可以用seq_along(df)来迭代每一列。

循环体:应用函数rescale01().

于是写出如下的for循环:

for (i in seq_along(df)) {df[[i]] <- rescale01(df[[i]])
}

通常来说,你可以用这种循环来修改一个列表或数据框,注意这里是用[[ ]], 而不是[ ]. 因为原子向量最好用[[ ]], 这样能清楚地表明你处理的是一个单独的元(而不是子集)。

(2) 循环模式

· 根据数值索引:for(i in seq_along(xs), 用x[[i]] 提取值。

· 根据元素值:for(x in xs). 若你只关心附带作用,这特别有用。例如绘图、保存文件等,因为很难高效率地保存这种结果。

· 根据名字:for(nm in names(xs)). 对每个名字,访问其对应的值 x[[nm]], 若你需要使用图形标题或文件的名称,这就很有用。当创建命名的输出时,确保按如下方式命名结果向量:

results <- vector("list", length(x))
names(results) <- names(x)

注:用数值索引迭代是最常用的形式,因为只要给定位置,名字和元素值都可以提取:

for (i in seq_along(x)) {name <- names(x)[[i]]value <- x[[i]]
}

(3) 结果长度未知

有时候,你可能不知道输出结果有多长。例如,你想要模拟一些长度随机的随机向量。你可能优先想到通过逐步增加长度的方法解决该问题:

means <- c(0, 1, 2)
output <- double()
for (i in seq_along(means)) {n <- sample(100, 1)output <- c(output, rnorm(n, means[[i]]))
}
str(output)

4289b91484a0e0082fbb0c1927e66a89.png

但这种做法很低效,因为每次迭代,R都要复制上一次迭代的全部数据,其复杂度为

.

一个好的方法是,先将结果保存为列表,等循环结束再将列表重组为一个单独的向量:

out <- vector("list", length(means))
for (i in seq_along(means)) {n <- sample(100, 1)out[[i]] <- rnorm(n, means[[i]])
}
str(out)

3e671ba05a85ec32d629f436f4898025.png
str(unlist(out))

f4b3b4060d6be7e5f74ec00d2b10271d.png

这里是用unlist()函数将一个向量的列表摊平为一个单独的向量。更严格的方法是用purrr包中的flatten_dbl(), 若输入不是double型的列表,将会报错。

还有两种结果长度未知的情形:

· 生成一个长字符串。不是用paste()函数将上一次的迭代结果拼接到一起,而是将结果保存为字符向量,再用函数paste(output, collapse= " ")合并为一个单独的字符串;

· 生成一个大的数据框。不是依次用rbind()函数合并每次迭代的结果,而是将结果保存为列表,再用dplyr包中的bind_rows(output)函数合并成一个单独的数据框。

所以,遇到上述模式时,要先转化为更复杂的结果对象,最后再做一步合并。

(4) 迭代次数未知(while循环)

有时候你甚至不知道输入序列有多长,这通常出现在做模拟的时候。例如,你可能想要在一行中循环直到连续出现3个“Head”,此时不适合用for循环,而是适合用while循环。

while循环更简单些,因为它只包含两个组件:条件、循环体:

while (condition) {#body
}

While循环是比for循环更一般的循环,因为for循环总可以改写为while循环,但while循环不一定能改写为for循环:

for (i in seq_along(x)) {#循环体
}
#等价于
i <- 1
while (i <= length(x)) {#循环体i <- i + 1
}

下面用while循环实现:抛一枚硬币直到连续出现3次“正面”,需要的次数:

flip <- function() sample(c("Tail", "Head"), 1)flips <- 0
nheads <- 0while (nheads < 3) {if (flip() == "Head") {nheads <- nheads + 1} else {nheads <- 0}flips <- flips + 1
}
flips

f2242c0f1695e853441286583f534196.png

while循环并不常用,但在模拟时常用,特别是在预先不知道迭代次数的情形。

二. apply函数族

apply函数族可以代替大部分的for循环、while循环,其大意是“应用(apply)”某函数(fun)到一系列的对象上。根据应用到的对象的不同,是一族apply函数。

常用的有:

  • 分组计算:apply()和tapply()
  • 循环迭代:lapply()和sapply()
  • 多变量计算:mapply()
  1. 函数apply()

apply()函数是最常用的代替for循环的函数。apply函数可以对矩阵、数据框、数组(二维、多维),按行或列进行循环计算,对子元素进行迭代,并把子元素以参数传递的形式给自定义的FUN函数中,并以返回计算结果。基本格式为:

apply(x, MARGIN=..., fun, ...)

其中,x为数据对象(矩阵、多维数组、数据框);

MARGIN=1表示按行,2表示按列;

fun表示要作用的函数。

x<-matrix(1:6, ncol=3)
x

0318b585315f9f1ddf8c8f92a20b3d32.png
apply(x,1,mean) #按行求均值

6158ee03e7390199902e526afca365dc.png
apply(x,2,mean) #按列求均值

f89534f81f91667e82f694c36563665f.png

2. 函数tapply()

按一组因子INDEX对数据列 x 分组,再分别对每组作用上函数fun。基本格式为:

tapply(x, INDEX, fun, ..., simplify=TRUE)

其中,x通常为向量;

INDEX为与x长度相同的因子列表(若不是因子,R会强制转化为因子);

simplify=TRUE且fun计算结果为标量值,则返回值为数组,若为FALSE,则返回值为list对象

dat <- data.frame(height=c(174,165,180,171,160), sex=c("F","F","M","M","F"))
tapply(dat$height,dat$sex, mean)   #计算分组均值: 不同sex对应的height的均值

7ed3e897f354e82f27c0807d9fc65569.png

3. 函数lapply()

该函数是一个最基础循环操作函数,用来对vector、list、data.frame逐元、逐成分、逐列分别应用函数fun,并返回和 x 长度同样的 list 作为结果。

基本格式为:

lapply(x, fun, ...)

其中,x为数据对象(列表、数据框、向量)。

x<-list(a=1:5, b=exp(0:3))
x

1fc2527b912d7468b41ef9210ef51e69.png
lapply(x, mean)

1497c7b810efa89c1f922ad14acb9a51.png

4. 函数sapply()

sapply() 是 lapply() 的简化版本,多了一个参数simplify,若simplify=FALSE,则同lapply(),若为TRUE,则将输出的list简化为向量或矩阵。基本格式为:

sapply(x, fun, ..., simplify=TRUE, USE.NAMES=...)

5. 函数mapply()

是函数sapply()的多变量版本,将对多个变量的每个参数作用某函数。基本格式为:

mapply(fun, ..., MoreArgs=NULL, SIMPLIFY=TRUE, USE.NAMES=TRUE)

其中,

MoreArgs为fun函数的其它参数列表;

SIMPLIFY为逻辑值或字符串,取值为TRUR时,将结果转化为一个向量、矩阵或高维阵列(但不是所有结果都可转化);

... 可以接收多个数据,mapply将fun应用于这些数据的第一个元素组成的数组,然后是第二个元素组成的数组,以此类推。

返回值是vector或matrix,取决于fun返回值是一个还是多个。

#重复生成列表list(x=1:2), 重复次数times=1:3,结果为列表
mapply(rep, times=1:3, MoreArgs = list(x=1:2))

3b1262bf5c4ba117218ac4297144eb4a.png
mapply(function(x,y) x^y, c(1:3), c(1:3))

211038d5285242fa2d2df0fed99e921d.png
mapply(function(x,y) c(x+y, x^y), c(1:3), c(1:3))  

c37fc8ee7beae64fef972e1fe08e6a8f.png
Alco <- data.frame(AlcoholDrunk=c("YES","YES","NO","YES","YES","YES",NA,"YES","YES","YES","YES","YES","YES","NO","NO","NO","NO","YES"), 
AmountDrunk=c(3.0, 1.0, NA ,3.0,  NA, 0.0,  NA, 0.0, NA, 1.7,  NA,  NA, 0.0,  NA,  NA,  NA,  NA, 2.0))

其中,变量AlcoholDrunk有三种取值,“YES”表示有饮酒史;“NO”表示无饮酒史;NA表示数据不可获取。

定义alcohol()函数实现功能:若AlcoholDrunk是NA,直接返回NA;若是NO,返回NO;否则返回变量AmountDrunk的值。因为需要传递两个变量的值,就需要用mapply()函数:

alcohol <- function(texVal, numVal){if(is.na(texVal)) {return("NA")}else if(texVal=="NO") {return("NO")}else if(is.na(numVal)) {return("amount Unknown")}else {return(numVal)}
}
mapply(alcohol, Alco$AlcoholDrunk, Alco$AmountDrunk)

cb58957578d963cc777e92ac67dcaa02.png

三. 泛型函数map

泛型函数,相当于数学中的“泛函”,即函数的函数。

“传递一个函数给另一个函数”是非常强大的思想,这也是R作为泛函型编程语言的表现之一。

注:apply函数族也属于泛型函数。

purrr包,提供的函数足以代替许多通常的for循环。虽然apply函数族也能解决类似的问题,但purrr包更具有一致性,从而也更容易学习。另外,purrr包还支持一些快捷用法,且所有函数都是用C语言写的,速度更快。

用purrr包的解决问题的逻辑是:

(1)针对列表每个单独的元,你怎么解决某问题?一旦你解决了该问题,purrr包就可以将你的求解推广到列表中的每一个元。

(2)若你正在解决一个复杂问题,你怎么把它分解成若干小问题,使得你能够逐步完成求解?用purrr包,你就可以将这些小问题的求解步骤用管道组合到一起。

  1. map函数族

“遍历一个向量,对每个元做相同的操作,并保存结果”,这种循环模式是如此常见,所以purrr包提供了一族map函数来做这件事。一个函数针对一种类型的输出:

  • map()—映射列表,基本等同于lapply()
  • map_lgl()—映射逻辑向量
  • map_int()—映射整数型向量
  • map_dbl()—映射浮点数向量
  • map_chr()—映射字符型向量

每个函数都接受一个输入向量,应用一个函数到每一个元,再返回与输入向量同名同长度的新向量;向量的类型由map函数的后缀所确定。

注:map_*()函数必须接受原子向量,可以是行、列向量。

例如,对前文的 数据框 df,

map_dbl(df, mean)        

b51588c9fbb563fd6408778dd8c60726.png
map_dbl(df, median)        

e05f4d53268070139533b6310ace94b2.png
map_dbl(df, sd)

f42e7bccb92116b9b3bf9877812c7590.png

与用for循环相比,map函数是聚焦在所执行的操作(mean(), median(), sd()),而不是循环遍历每个元并存储结果。若改用管道操作更明显:

df %>% map_dbl(mean)
df %>% map_dbl(median)
df %>% map_dbl(sd)

2. 多变量迭代的map函数

前面map函数族实现的都是对一个向量(单变量的数据)进行迭代操作。实际中,经常会用到针对多个变量进行并行迭代,这就需要用map2()或pmap()函数。

(1)两个变量的迭代:map2()

例如,根据

不同的参数组合,生成正态分布随机数:

d72306c55f230ff9260565461cc4c475.png
mu = c(5,10,-3)
sigma = c(1,5,10)
map2(mu, sigma, rnorm, n = 5)

2056ac472f0aead6cb9dcf69f2e73092.png

前文的mapply例子,也可以用map2来实现:

unlist(map2(Alco$AlcoholDrunk, Alco$AmountDrunk,alcohol))

(2)更多个变量迭代:pmap()

前面都是随机生成5个数,让个数也变起来:

f8a6867f521fea054eade5e2ad102132.png
n <- c(1,3,5)
args <- list(mean = mu, sd = sigma, n = n)
pmap(args, rnorm)

9a95c6d122e2155119ac69997bbc930f.png

主要参考文献:

  1. R for Data Science. Hadley Wickham, Garrett Grolemund,O'Reilly, 2017.
  2. 张良均,谢佳标,杨坦,肖刚. R语言与数据挖掘. 机械工业出版社,2016.
mapply函数​www.jianshu.com

原创作品,转载请注明。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/575145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

钉钉api 获取 accesstoken_Thinkphp5.X异常接管后通过钉钉机器人推送通知

大多团队在生产环境下都会关闭app_debug&#xff0c;所以相对应的错误信息就不能实时查看到。以下分享一个目前团队项目正在用的生产环境下相关技术人员能第一时间获取通知的方法&#xff1a;先上效果图&#xff1a;以上通知是在钉钉中显示&#xff01;下面介绍开发步骤&#x…

js中将有层级关系的一维数据转换为父子级关系的二维数据、菜单权限三级层级数据实现(树形结构数据)

很久没有发文了&#xff0c;今天发点不一样的&#xff0c;如下&#xff1a; <script>// 一维数据&#xff1a;let arrData[{ r_id: 1, role_name: 开发商, n_id: 1, txt: 主页 },{ r_id: 1, role_name: 开发商, n_id: 2, txt: 项目 },{ r_id: 1, role_name: 开发商, n_id…

修改器内置脚本编写_Node.js 中实践 Redis Lua 脚本

对别人的意见要表示尊重。千万别说&#xff1a;"你错了。"——卡耐基Lua 是一种轻量小巧的脚本语言&#xff0c;用标准 C 语言编写并以源代码形式开放&#xff0c;其设计目的是为了嵌入应用程序中&#xff0c;从而为应用程序提供灵活的扩展和定制功能。由于 Lua 语言…

项目上线,部署到服务器(腾讯服务器),http协议及https协议(微信小程序必须https协议才可发布)

一、准备服务器&#xff1a; 1.选择自己的服务器&#xff0c;这里有很多服务器比如&#xff0c;阿里服务器&#xff0c;腾讯服务器等&#xff0c;这里我选择腾讯服务器做测试&#xff0c;其官网&#xff1a;https://cloud.tencent.com/&#xff0c;微信扫码登录后如下进入首页…

队列的基本操作_算法与数据结构(五) 栈和队列

? 工欲善其事&#xff0c;必先利其器。栈和队列 - Stack And Queue栈如何理解栈呢&#xff1f;后进者先出&#xff0c;先进者后出&#xff0c;这就是典型的 "栈" 结构。04_栈和队列-栈结构从栈的操作特性上来看&#xff0c;栈是一种“操作受限”的线性表&#xff0c…

八邻域轮廓跟踪算法_结合mRMR选择和IFCM聚类的遥感影像分类算法

随着遥感成像技术的发展&#xff0c;高分遥感影像空间分辨率逐年提高&#xff0c;已经成为获取地物信息的主要数据来源之一。高分影像分类作为遥感影像处理的重要任务之一&#xff0c;在地理国情普查与监测、数字城市建设、城市规划等领域具有广阔的应用前景。高分影像具有纹理…

node.js中net模块、node实现tcp通信

node.js中net 一、net模块感知&#xff1a; net模块用于创建基于TCP&#xff08;或IPC&#xff09;通信的服务器或客户端&#xff0c;它是nodejs内置模块&#xff0c;直接使用require(‘net’)的方式引入&#xff0c;类似http模块。 二、创建一个tcp服务器&#xff1a; //1…

struts2--文件上传大小

struts2--文件上传大小 Struts2文件上传的大小限制问题 问题&#xff1a;上传大文件报错……解决&#xff1a;修改struts.xml文件中的参数如下<constant name"struts.multipart.maxSize" value"55000000"/><action name"UploadFile" cl…

element-plus中导航高亮不自动刷新问题

解决使用element-plus el-submenu时页面被this.$router.push(“xxx”)跳转后&#xff0c;导航菜单高亮不能自动刷新问题&#xff0c;需要注意以下几点&#xff1a; 在el-menu加上routerindex必须绑定路由的path,参考上面的例子&#xff0c;/不能少default-active设为当前路由&…

计算机二级考试python怎么报名_计算机二级报名流程和条件

对于第一次参加全国计算机二级考试的考生来说&#xff0c;他们对于计算机二级报名流程和条件不是清楚&#xff0c;小编这就来给大家梳理一下。计算机二级报名流程 一、报名 分为网上报名和现场报名。 网上报名&#xff1a;考生在规定时间内登录本省计算机资格网站&#xff0c;按…

操作对象_DOM进阶——HTML属性操作(对象属性)

上一节我们在“DOM基础”学习了对元素节点的操作&#xff0c;这两节介绍对属性节点的操作。属性节点操作有两种方式&#xff0c;一种是使用“对象属性”&#xff0c;另一种是“对象方法”。本节主要介绍“对象属性”的方式。对属性节点的操作涉及两种操作&#xff0c;分别是获取…

三级菜单数据实现,实现嵌套三级菜单数据

//将数据库中通过关联&#xff08;inner join&#xff09;查询多张表没有嵌套但有嵌套关系的数据进行处理&#xff0c;得到具有嵌套层级且嵌套的父级二级属性不可重复出现&#xff0c;具体实现如下&#xff1a;// 通过关联查询到数据库的数据&#xff08;格式&#xff09;&…

sap相关性不能被编译_经典综述编译丨生物硝化抑制丨NAT PLANTS:现代农业中的氮转化和生物硝化抑制作用...

点击蓝字↑↑↑“农作未来(FarmingFuture)”&#xff0c;轻松关注&#xff0c;农作制度研究与您同行&#xff01;编译&#xff1a;贾蓉 排版&#xff1a;王上原创微文&#xff0c;欢迎转发转载。文章信息原名&#xff1a;Nitrogen transformations in modern agriculture and …

Hadoop学习笔记(一)从官网下载安装包

Hadoop是一个分布式系统基础架构&#xff0c;由Apache基金会所开发。用户能够在不了解分布式底层细节的情况下&#xff0c;开发分布式程序。充分利用集群的威力进行快速运算和存储。要学习Hadoop从下载安装包開始打开Hadoop的官方站点&#xff0c;点击Download Hadoop或点击“G…

版本之间如何兼容_Spring Boot 2.4 版本的系统运行要求

名字Servlet 版本Tomcat 9.04.0Jetty 9.43.1Undertow 2.04.0Spring Boot 2.4.2-SNAPSHOT 版本要求至少 Java 8 及其以上版本&#xff0c;目前最高能够支持到 Java 15 &#xff08;包含 15&#xff09;的版本。 Spring 框架&#xff08;Framework&#xff09; 5.3.2 的版本或者以…

JavaScript异步处理问题,循环处理异步任务,并拿到数据,Nodejs循环异步任务接口处理

一、问题描述及解答&#xff1a; 在Promise(异步事件)中&#xff0c;通过遍历的方式处理数据&#xff0c;最后将带有数据的Promise通过return返回&#xff0c;在async/await处理机制中的到的数据是一个空数据组[]或不完整的数据&#xff0c;如下&#xff1a; 使用定时器后数据…

tomcat lifecyclelistener_大公司程序员带你死磕Tomcat系列(五)——容器

死磕Tomcat系列(5)——容器回顾在死磕Tomcat系列(1)——整体架构中我们简单介绍了容器的概念&#xff0c;并且说了在容器中所有子容器的父接口是Container。在死磕Tomcat系列(2)——EndPoint源码解析中&#xff0c;我们知道了连接器将请求过来的数据解析成Tomcat需要的ServletR…

获取当前周一日期_Excel工作表中最全的时间和日期函数,效率、办公必备

在Excel工作表中&#xff0c;函数也可以分为好几类&#xff0c;今天&#xff0c;小编带大家学习时间和日期函数。一、Excel工作表日期函数&#xff1a;Date。功能&#xff1a;返回特定日期的序列号。语法结构&#xff1a;Date(年,月,日)。目的&#xff1a;将制定的“年”、“月…

这样就算会了PHP么?-11

PHP中关于类的基本内容练习&#xff1a; <?phpclass SportObject{public $name;public $height;public $avirdupois;public function __construct($name, $height,$avirdupois) {$this->name $name;$this->height $height;$this->avirdupois $avirdupois;}func…

一个黑色全屏的计时器_我入手了一个1000多的智能手环,值吗?|Fitbit Charge 4测评...

入手Fitbit Charge 4了。作为一个喜欢晚上做运动的Boy&#xff0c;每次运动带着手机确实有够累赘&#xff0c;比如跑步的时候&#xff0c;掏手机看真的很麻烦&#xff0c;但手环只需抬手即可看时间、心率、步数这些&#xff0c;确实很方便。而且&#xff0c;有了手环之后&#…