pycharm中配置r语言_【R语言】R语言中的循环
編程中減少代碼重復(fù)的兩個(gè)工具,一是循環(huán),一是函數(shù)。
循環(huán),用來(lái)處理對(duì)多個(gè)同類輸入做相同事情(即迭代),如對(duì)不同列做相同操作、對(duì)不同數(shù)據(jù)集做相同操作。
R語(yǔ)言有三種方式實(shí)現(xiàn)循環(huán):
(1)for循環(huán)、while循環(huán)
(2)apply函數(shù)族
(3)泛型函數(shù)map
一. for循環(huán)、while循環(huán)
首先作兩點(diǎn)說(shuō)明:
(1)關(guān)于“for循環(huán)運(yùn)行速度慢”的說(shuō)法,實(shí)際上已經(jīng)過(guò)時(shí)了,現(xiàn)在的R、Matlab等軟件經(jīng)過(guò)多年的內(nèi)部?jī)?yōu)化已經(jīng)不慢了,之所以表現(xiàn)出來(lái)慢,是因?yàn)槟銢](méi)有注意兩個(gè)關(guān)鍵點(diǎn):
- 提前為保存循環(huán)結(jié)果分配存儲(chǔ)空間;
- 為循環(huán)體中涉及到的數(shù)據(jù)選擇合適的數(shù)據(jù)結(jié)構(gòu)。
(2)apply函數(shù)族和泛型函數(shù)map能夠更加高效簡(jiǎn)潔地實(shí)現(xiàn)一般的for循環(huán)、while循環(huán),但這不代表for循環(huán)、while循環(huán)就沒(méi)用了,它們可以在更高的層次使用(相對(duì)于在逐元素級(jí)別使用)
1. 基本for循環(huán)
例如,有如下的tibble數(shù)據(jù):
library(tidyverse)df <- tibble(a = rnorm(10),b = rnorm(10),c = rnorm(10),d = rnorm(10) )用復(fù)制-粘貼法,計(jì)算每一列的中位數(shù):
median(df$a) median(df$b) median(df$c) median(df$d)為了避免“粘貼-復(fù)制多于兩次”,改用for循環(huán)實(shí)現(xiàn):
output <- vector("double", ncol(df)) #1.輸出 for (i in seq_along(df)) { #2.迭代器output[[i]] <- median(df[[i]]) #3.循環(huán)體 } output #輸出結(jié)果略for循環(huán)有三個(gè)組件:
(1) 輸出:output <- vector("double", length(x))
在循環(huán)開(kāi)始之前,最好為輸出分配足夠的存儲(chǔ)空間,這樣效率更高:若每循環(huán)一次,就用c()合并一次,效率會(huì)很低下。
通常是用vector()函數(shù)創(chuàng)建一個(gè)給定長(zhǎng)度的空向量,它有兩個(gè)參數(shù):向量類型(logical, integer, double, character等)、向量長(zhǎng)度。
(2)迭代器:i in seq_along(df)
確定怎么循環(huán):每次for循環(huán)將對(duì)i賦一個(gè)seq_along(df)中的值,可將i理解為代詞it. 其中seq_along()是“1:length(df)”的安全版本,它能保證遇到長(zhǎng)度為0的向量時(shí),仍能正確工作:
y <- vector("double", 0) seq_along(y) 1:length(y)你可能不會(huì)故意創(chuàng)建長(zhǎng)度為0的向量,但容易不小心創(chuàng)建,則會(huì)導(dǎo)致報(bào)錯(cuò)。
(3) 循環(huán)體:output[[i]] <- median(df[[i]])
即執(zhí)行具體操作的代碼,它將重復(fù)執(zhí)行,每次對(duì)不同的i值。
第1次迭代將執(zhí)行:output[[1]] <- median(df[[1]]),
第2次迭代將執(zhí)行:output[[2]] <- median(df[[2]]),
……
2. for循環(huán)變種
基本的for循環(huán)有4個(gè)變種:
(1) 修改已存在的對(duì)象,創(chuàng)建的新對(duì)象
有時(shí)需要用for循環(huán)修改一個(gè)已存在的對(duì)象,例如,對(duì)數(shù)據(jù)框 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循環(huán)來(lái)做,先考慮其3個(gè)組件:
輸出:已經(jīng)存在,與輸入相同。
迭代器:可以將數(shù)據(jù)框看作是多個(gè)列構(gòu)成的列表,故可以用seq_along(df)來(lái)迭代每一列。
循環(huán)體:應(yīng)用函數(shù)rescale01().
于是寫出如下的for循環(huán):
for (i in seq_along(df)) {df[[i]] <- rescale01(df[[i]]) }通常來(lái)說(shuō),你可以用這種循環(huán)來(lái)修改一個(gè)列表或數(shù)據(jù)框,注意這里是用[[ ]], 而不是[ ]. 因?yàn)樵酉蛄孔詈糜肹[ ]], 這樣能清楚地表明你處理的是一個(gè)單獨(dú)的元(而不是子集)。
(2) 循環(huán)模式
· 根據(jù)數(shù)值索引:for(i in seq_along(xs), 用x[[i]] 提取值。
· 根據(jù)元素值:for(x in xs). 若你只關(guān)心附帶作用,這特別有用。例如繪圖、保存文件等,因?yàn)楹茈y高效率地保存這種結(jié)果。
· 根據(jù)名字:for(nm in names(xs)). 對(duì)每個(gè)名字,訪問(wèn)其對(duì)應(yīng)的值 x[[nm]], 若你需要使用圖形標(biāo)題或文件的名稱,這就很有用。當(dāng)創(chuàng)建命名的輸出時(shí),確保按如下方式命名結(jié)果向量:
results <- vector("list", length(x)) names(results) <- names(x)注:用數(shù)值索引迭代是最常用的形式,因?yàn)橹灰o定位置,名字和元素值都可以提取:
for (i in seq_along(x)) {name <- names(x)[[i]]value <- x[[i]] }(3) 結(jié)果長(zhǎng)度未知
有時(shí)候,你可能不知道輸出結(jié)果有多長(zhǎng)。例如,你想要模擬一些長(zhǎng)度隨機(jī)的隨機(jī)向量。你可能優(yōu)先想到通過(guò)逐步增加長(zhǎng)度的方法解決該問(wèn)題:
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)但這種做法很低效,因?yàn)槊看蔚?#xff0c;R都要復(fù)制上一次迭代的全部數(shù)據(jù),其復(fù)雜度為
.一個(gè)好的方法是,先將結(jié)果保存為列表,等循環(huán)結(jié)束再將列表重組為一個(gè)單獨(dú)的向量:
out <- vector("list", length(means)) for (i in seq_along(means)) {n <- sample(100, 1)out[[i]] <- rnorm(n, means[[i]]) } str(out)str(unlist(out))這里是用unlist()函數(shù)將一個(gè)向量的列表攤平為一個(gè)單獨(dú)的向量。更嚴(yán)格的方法是用purrr包中的flatten_dbl(), 若輸入不是double型的列表,將會(huì)報(bào)錯(cuò)。
還有兩種結(jié)果長(zhǎng)度未知的情形:
· 生成一個(gè)長(zhǎng)字符串。不是用paste()函數(shù)將上一次的迭代結(jié)果拼接到一起,而是將結(jié)果保存為字符向量,再用函數(shù)paste(output, collapse= " ")合并為一個(gè)單獨(dú)的字符串;
· 生成一個(gè)大的數(shù)據(jù)框。不是依次用rbind()函數(shù)合并每次迭代的結(jié)果,而是將結(jié)果保存為列表,再用dplyr包中的bind_rows(output)函數(shù)合并成一個(gè)單獨(dú)的數(shù)據(jù)框。
所以,遇到上述模式時(shí),要先轉(zhuǎn)化為更復(fù)雜的結(jié)果對(duì)象,最后再做一步合并。
(4) 迭代次數(shù)未知(while循環(huán))
有時(shí)候你甚至不知道輸入序列有多長(zhǎng),這通常出現(xiàn)在做模擬的時(shí)候。例如,你可能想要在一行中循環(huán)直到連續(xù)出現(xiàn)3個(gè)“Head”,此時(shí)不適合用for循環(huán),而是適合用while循環(huán)。
while循環(huán)更簡(jiǎn)單些,因?yàn)樗话瑑蓚€(gè)組件:條件、循環(huán)體:
while (condition) {#body }While循環(huán)是比f(wàn)or循環(huán)更一般的循環(huán),因?yàn)閒or循環(huán)總可以改寫為while循環(huán),但while循環(huán)不一定能改寫為for循環(huán):
for (i in seq_along(x)) {#循環(huán)體 } #等價(jià)于 i <- 1 while (i <= length(x)) {#循環(huán)體i <- i + 1 }下面用while循環(huán)實(shí)現(xiàn):拋一枚硬幣直到連續(xù)出現(xiàn)3次“正面”,需要的次數(shù):
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 } flipswhile循環(huán)并不常用,但在模擬時(shí)常用,特別是在預(yù)先不知道迭代次數(shù)的情形。
二. apply函數(shù)族
apply函數(shù)族可以代替大部分的for循環(huán)、while循環(huán),其大意是“應(yīng)用(apply)”某函數(shù)(fun)到一系列的對(duì)象上。根據(jù)應(yīng)用到的對(duì)象的不同,是一族apply函數(shù)。
常用的有:
- 分組計(jì)算:apply()和tapply()
- 循環(huán)迭代:lapply()和sapply()
- 多變量計(jì)算:mapply()
apply()函數(shù)是最常用的代替for循環(huán)的函數(shù)。apply函數(shù)可以對(duì)矩陣、數(shù)據(jù)框、數(shù)組(二維、多維),按行或列進(jìn)行循環(huán)計(jì)算,對(duì)子元素進(jìn)行迭代,并把子元素以參數(shù)傳遞的形式給自定義的FUN函數(shù)中,并以返回計(jì)算結(jié)果。基本格式為:
apply(x, MARGIN=..., fun, ...)其中,x為數(shù)據(jù)對(duì)象(矩陣、多維數(shù)組、數(shù)據(jù)框);
MARGIN=1表示按行,2表示按列;
fun表示要作用的函數(shù)。
x<-matrix(1:6, ncol=3) xapply(x,1,mean) #按行求均值apply(x,2,mean) #按列求均值2. 函數(shù)tapply()
按一組因子INDEX對(duì)數(shù)據(jù)列 x 分組,再分別對(duì)每組作用上函數(shù)fun。基本格式為:
tapply(x, INDEX, fun, ..., simplify=TRUE)其中,x通常為向量;
INDEX為與x長(zhǎng)度相同的因子列表(若不是因子,R會(huì)強(qiáng)制轉(zhuǎn)化為因子);
simplify=TRUE且fun計(jì)算結(jié)果為標(biāo)量值,則返回值為數(shù)組,若為FALSE,則返回值為list對(duì)象
dat <- data.frame(height=c(174,165,180,171,160), sex=c("F","F","M","M","F")) tapply(dat$height,dat$sex, mean) #計(jì)算分組均值: 不同sex對(duì)應(yīng)的height的均值3. 函數(shù)lapply()
該函數(shù)是一個(gè)最基礎(chǔ)循環(huán)操作函數(shù),用來(lái)對(duì)vector、list、data.frame逐元、逐成分、逐列分別應(yīng)用函數(shù)fun,并返回和 x 長(zhǎng)度同樣的 list 作為結(jié)果。
基本格式為:
lapply(x, fun, ...)其中,x為數(shù)據(jù)對(duì)象(列表、數(shù)據(jù)框、向量)。
x<-list(a=1:5, b=exp(0:3)) xlapply(x, mean)4. 函數(shù)sapply()
sapply() 是 lapply() 的簡(jiǎn)化版本,多了一個(gè)參數(shù)simplify,若simplify=FALSE,則同lapply(),若為TRUE,則將輸出的list簡(jiǎn)化為向量或矩陣。基本格式為:
sapply(x, fun, ..., simplify=TRUE, USE.NAMES=...)5. 函數(shù)mapply()
是函數(shù)sapply()的多變量版本,將對(duì)多個(gè)變量的每個(gè)參數(shù)作用某函數(shù)?;靖袷綖?#xff1a;
mapply(fun, ..., MoreArgs=NULL, SIMPLIFY=TRUE, USE.NAMES=TRUE)其中,
MoreArgs為fun函數(shù)的其它參數(shù)列表;
SIMPLIFY為邏輯值或字符串,取值為TRUR時(shí),將結(jié)果轉(zhuǎn)化為一個(gè)向量、矩陣或高維陣列(但不是所有結(jié)果都可轉(zhuǎn)化);
... 可以接收多個(gè)數(shù)據(jù),mapply將fun應(yīng)用于這些數(shù)據(jù)的第一個(gè)元素組成的數(shù)組,然后是第二個(gè)元素組成的數(shù)組,以此類推。
返回值是vector或matrix,取決于fun返回值是一個(gè)還是多個(gè)。
#重復(fù)生成列表list(x=1:2), 重復(fù)次數(shù)times=1:3,結(jié)果為列表 mapply(rep, times=1:3, MoreArgs = list(x=1:2))mapply(function(x,y) x^y, c(1:3), c(1:3))mapply(function(x,y) c(x+y, x^y), c(1:3), c(1:3)) 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”表示無(wú)飲酒史;NA表示數(shù)據(jù)不可獲取。
定義alcohol()函數(shù)實(shí)現(xiàn)功能:若AlcoholDrunk是NA,直接返回NA;若是NO,返回NO;否則返回變量AmountDrunk的值。因?yàn)樾枰獋鬟f兩個(gè)變量的值,就需要用mapply()函數(shù):
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)三. 泛型函數(shù)map
泛型函數(shù),相當(dāng)于數(shù)學(xué)中的“泛函”,即函數(shù)的函數(shù)。
“傳遞一個(gè)函數(shù)給另一個(gè)函數(shù)”是非常強(qiáng)大的思想,這也是R作為泛函型編程語(yǔ)言的表現(xiàn)之一。
注:apply函數(shù)族也屬于泛型函數(shù)。
purrr包,提供的函數(shù)足以代替許多通常的for循環(huán)。雖然apply函數(shù)族也能解決類似的問(wèn)題,但purrr包更具有一致性,從而也更容易學(xué)習(xí)。另外,purrr包還支持一些快捷用法,且所有函數(shù)都是用C語(yǔ)言寫的,速度更快。
用purrr包的解決問(wèn)題的邏輯是:
(1)針對(duì)列表每個(gè)單獨(dú)的元,你怎么解決某問(wèn)題?一旦你解決了該問(wèn)題,purrr包就可以將你的求解推廣到列表中的每一個(gè)元。
(2)若你正在解決一個(gè)復(fù)雜問(wèn)題,你怎么把它分解成若干小問(wèn)題,使得你能夠逐步完成求解?用purrr包,你就可以將這些小問(wèn)題的求解步驟用管道組合到一起。
“遍歷一個(gè)向量,對(duì)每個(gè)元做相同的操作,并保存結(jié)果”,這種循環(huán)模式是如此常見(jiàn),所以purrr包提供了一族map函數(shù)來(lái)做這件事。一個(gè)函數(shù)針對(duì)一種類型的輸出:
- map()—映射列表,基本等同于lapply()
- map_lgl()—映射邏輯向量
- map_int()—映射整數(shù)型向量
- map_dbl()—映射浮點(diǎn)數(shù)向量
- map_chr()—映射字符型向量
每個(gè)函數(shù)都接受一個(gè)輸入向量,應(yīng)用一個(gè)函數(shù)到每一個(gè)元,再返回與輸入向量同名同長(zhǎng)度的新向量;向量的類型由map函數(shù)的后綴所確定。
注:map_*()函數(shù)必須接受原子向量,可以是行、列向量。
例如,對(duì)前文的 數(shù)據(jù)框 df,
map_dbl(df, mean) map_dbl(df, median) map_dbl(df, sd)與用for循環(huán)相比,map函數(shù)是聚焦在所執(zhí)行的操作(mean(), median(), sd()),而不是循環(huán)遍歷每個(gè)元并存儲(chǔ)結(jié)果。若改用管道操作更明顯:
df %>% map_dbl(mean) df %>% map_dbl(median) df %>% map_dbl(sd)2. 多變量迭代的map函數(shù)
前面map函數(shù)族實(shí)現(xiàn)的都是對(duì)一個(gè)向量(單變量的數(shù)據(jù))進(jìn)行迭代操作。實(shí)際中,經(jīng)常會(huì)用到針對(duì)多個(gè)變量進(jìn)行并行迭代,這就需要用map2()或pmap()函數(shù)。
(1)兩個(gè)變量的迭代:map2()
例如,根據(jù)
和 不同的參數(shù)組合,生成正態(tài)分布隨機(jī)數(shù):mu = c(5,10,-3) sigma = c(1,5,10) map2(mu, sigma, rnorm, n = 5)前文的mapply例子,也可以用map2來(lái)實(shí)現(xiàn):
unlist(map2(Alco$AlcoholDrunk, Alco$AmountDrunk,alcohol))(2)更多個(gè)變量迭代:pmap()
前面都是隨機(jī)生成5個(gè)數(shù),讓個(gè)數(shù)也變起來(lái):
n <- c(1,3,5) args <- list(mean = mu, sd = sigma, n = n) pmap(args, rnorm)主要參考文獻(xiàn):
原創(chuàng)作品,轉(zhuǎn)載請(qǐng)注明。
總結(jié)
以上是生活随笔為你收集整理的pycharm中配置r语言_【R语言】R语言中的循环的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c mysql bulk,MySqlBu
- 下一篇: linux如何得到本机地址,Linux下