本章內(nèi)容
? 數(shù)學(xué)和統(tǒng)計(jì)函數(shù)
? 字符處理函數(shù)
? 循環(huán)和條件執(zhí)行
? 自編函數(shù)
? 數(shù)據(jù)整合與重塑
在第4章脾还,我們審視了R中基本的數(shù)據(jù)集處理方法伴箩,本章我們將關(guān)注一些高級(jí)話題。本章分為三個(gè)基本部分鄙漏。在第一部分中嗤谚,我們將快速瀏覽R中的多種數(shù)學(xué)、統(tǒng)計(jì)和字符處理函數(shù)泥张。為了讓這一部分的內(nèi)容相互關(guān)聯(lián)呵恢,我們先引入一個(gè)能夠使用這些函數(shù)解決的數(shù)據(jù)處理問題。在講解過這些函數(shù)以后媚创,再為這個(gè)數(shù)據(jù)處理問題提供一個(gè)可能的解決方案渗钉。
接下來,我們將講解如何自己編寫函數(shù)來完成數(shù)據(jù)處理和分析任務(wù)。首先鳄橘,我們將探索控制程序流程的多種方式声离,包括循環(huán)和條件執(zhí)行語(yǔ)句。然后瘫怜,我們將研究用戶自編函數(shù)的結(jié)構(gòu)术徊,以及在編寫完成如何調(diào)用它們。
最后鲸湃,我們將了解數(shù)據(jù)的整合和概述方法赠涮,以及數(shù)據(jù)集的重塑和重構(gòu)方法。在整合數(shù)據(jù)時(shí)暗挑,你可以使用任何內(nèi)建或自編函數(shù)來獲取數(shù)據(jù)的概述笋除,所以你在本章前兩部分中學(xué)習(xí)的內(nèi)容將會(huì)派上用場(chǎng)。
一個(gè)數(shù)據(jù)處理難題
要討論數(shù)值和字符處理函數(shù)炸裆,讓我們首先考慮一個(gè)數(shù)據(jù)處理問題垃它。一組學(xué)生參加了數(shù)學(xué)、科學(xué)和英語(yǔ)考試烹看。為了給所有學(xué)生確定一個(gè)單一的成績(jī)衡量指標(biāo)国拇,需要將這些科目的成績(jī)組合起來。
另外惯殊,你還想將前20%的學(xué)生評(píng)定為A酱吝,接下來20%的學(xué)生評(píng)定為B,依次類推靠胜。最后掉瞳,你希望按字母順序?qū)W(xué)生排序。數(shù)據(jù)如表5-1所示浪漠。
觀察此數(shù)據(jù)集陕习,馬上可以發(fā)現(xiàn)一些明顯的障礙。首先址愿,三科考試的成績(jī)是無法比較的该镣。由于它們的均值和標(biāo)準(zhǔn)差相去甚遠(yuǎn),所以對(duì)它們求平均值是沒有意義的响谓。你在組合這些考試成績(jī)之前损合,必須將其變換為可比較的單元。其次娘纷,為了評(píng)定等級(jí)嫁审,你需要一種方法來確定某個(gè)學(xué)生在前述得分上百分比排名。再次赖晶,表示姓名的字段只有一個(gè)律适,這讓排序任務(wù)復(fù)雜化了辐烂。為了正確地將其排序,需要將姓和名拆開捂贿。
以上每一個(gè)任務(wù)都可以巧妙地利用R中的數(shù)值和字符處理函數(shù)完成纠修。在講解完下一節(jié)中的各種函數(shù)之后,我們將考慮一套可行的解決方案厂僧,以解決這項(xiàng)數(shù)據(jù)處理難題扣草。
數(shù)值和字符處理函數(shù)
本節(jié)我們將綜述R中作為數(shù)據(jù)處理基石的函數(shù),它們可分為數(shù)值(數(shù)學(xué)颜屠、統(tǒng)計(jì)辰妙、概率)函數(shù)和字符處理函數(shù)。在闡述過每一類函數(shù)以后汽纤,我將為你展示如何將函數(shù)應(yīng)用到矩陣和數(shù)據(jù)框的列(變量)和行(觀測(cè))上(參見5.2.6節(jié))上岗。
數(shù)學(xué)函數(shù)
表5-2列出了常用的數(shù)學(xué)函數(shù)和簡(jiǎn)短的用例。
對(duì)數(shù)據(jù)做變換是這些函數(shù)的一個(gè)主要用途蕴坪。例如,你經(jīng)常會(huì)在進(jìn)一步分析之前將收入這種存在明顯偏倚的變量取對(duì)數(shù)敬锐。數(shù)學(xué)函數(shù)也被用作公式中的一部分背传,用于繪圖函數(shù)(例如 x 對(duì) sin(x) )和在輸出結(jié)果之前對(duì)數(shù)值做格式化。
表5-2中的示例將數(shù)學(xué)函數(shù)應(yīng)用到了標(biāo)量(單獨(dú)的數(shù)值)上台夺。當(dāng)這些函數(shù)被應(yīng)用于數(shù)值向量径玖、矩陣或數(shù)據(jù)框時(shí),它們會(huì)作用于每一個(gè)獨(dú)立的值颤介。例如梳星, sqrt(c(4, 16, 25)) 的返回值為 c(2,4, 5) 。
統(tǒng)計(jì)函數(shù)
常用的統(tǒng)計(jì)函數(shù)如表5-3所示滚朵,其中許多函數(shù)都擁有可以影響輸出結(jié)果的可選參數(shù)冤灾。舉例來說:y <- mean(x)
提供了對(duì)象 x 中元素的算術(shù)平均數(shù),而:z <- mean(x, trim =0.05, na.rm=TRUE
則提供了截尾平均數(shù)辕近,即丟棄了最大5%和最小5%的數(shù)據(jù)和所有缺失值后的算術(shù)平均數(shù)韵吨。請(qǐng)使用help() 了解以上每個(gè)函數(shù)和其參數(shù)的用法。
要了解這些函數(shù)的實(shí)戰(zhàn)應(yīng)用移宅,請(qǐng)參考代碼清單5-1归粉。這段代碼演示了計(jì)算某個(gè)數(shù)值向量的均值和標(biāo)準(zhǔn)差的兩種方式。
x <- c(1, 2, 3, 4, 5, 6, 7, 8)
mean(x)
sd(x)
n <- length(x)
meanx <- sum(x)/n
css <- sum((x - meanx)^2)
sdx <- sqrt(css / (n-1))
meanx
sdx
第二種方式中修正平方和( css )的計(jì)算過程是很有啟發(fā)性的:
(1) x 等于 c(1, 2, 3, 4, 5, 6, 7, 8) 漏峰, x 的平均值等于4.5( length(x) 返回了 x 中元素的數(shù)量)糠悼;
(2) (x – meanx) 從 x 的每個(gè)元素中減去了4.5,結(jié)果為 c(-3.5, -2.5, -1.5, -0.5, 0.5,1.5, 2.5, 3.5) 浅乔;
(3) (x – meanx)^2 將 (x - meanx) 的每個(gè)元素求平方倔喂,結(jié)果為 c(12.25, 6.25, 2.25,0.25, 0.25, 2.25, 6.25, 12.25) ;
(4) sum((x - meanx)^2) 對(duì) (x - meanx)^2) 的所有元素求和,結(jié)果為42滴劲。
R中公式的寫法和類似MATLAB的矩陣運(yùn)算語(yǔ)言有著許多共同之處攻晒。(我們將在附錄E中具體關(guān)注解決矩陣代數(shù)問題的方法。)
數(shù)據(jù)的標(biāo)準(zhǔn)化
默認(rèn)情況下班挖,函數(shù) scale() 對(duì)矩陣或數(shù)據(jù)框的指定列進(jìn)行均值為0鲁捏、標(biāo)準(zhǔn)差為1的標(biāo)準(zhǔn)化:
newdata <- scale(mydata)
要對(duì)每一列進(jìn)行任意均值和標(biāo)準(zhǔn)差的標(biāo)準(zhǔn)化,可以使用如下的代碼:
newdata <- scale(mydata)*SD + M
其中的 M 是想要的均值萧芙, SD 為想要的標(biāo)準(zhǔn)差给梅。在非數(shù)值型的列上使用 scale() 函數(shù)將會(huì)報(bào)錯(cuò)。
要對(duì)指定列而不是整個(gè)矩陣或數(shù)據(jù)框進(jìn)行標(biāo)準(zhǔn)化双揪,你可以使用這樣的代碼:
newdata <- transform(mydata, myvar = scale(myvar)*10+50)
此句將變量 myvar 標(biāo)準(zhǔn)化為均值50动羽、標(biāo)準(zhǔn)差為10的變量。你將在5.3節(jié)數(shù)據(jù)處理問題的解決方法中用到 scale() 函數(shù)渔期。
概率函數(shù)
你可能在疑惑為何概率函數(shù)未和統(tǒng)計(jì)函數(shù)列在一起运吓。(你真的對(duì)此有些困惑,對(duì)吧疯趟?)雖然根據(jù)定義拘哨,概率函數(shù)也屬于統(tǒng)計(jì)類,但是它們非常獨(dú)特信峻,應(yīng)獨(dú)立設(shè)一節(jié)進(jìn)行講解倦青。概率函數(shù)通常用來生成特征已知的模擬數(shù)據(jù),以及在用戶編寫的統(tǒng)計(jì)函數(shù)中計(jì)算概率值盹舞。
在R中产镐,概率函數(shù)形如 :
[dpqr]distribution_abbreviation()
其中第一個(gè)字母表示其所指分布的某一方面:
d = 密度函數(shù)(density)
p = 分布函數(shù)(distribution function)
q = 分位數(shù)函數(shù)(quantile function)
r = 生成隨機(jī)數(shù)(隨機(jī)偏差)
常用的概率函數(shù)列于表5-4中。
我們不妨先看看正態(tài)分布的有關(guān)函數(shù)踢步,以了解這些函數(shù)的使用方法癣亚。如果不指定一個(gè)均值和一個(gè)標(biāo)準(zhǔn)差,則函數(shù)將假定其為標(biāo)準(zhǔn)正態(tài)分布(均值為0贾虽,標(biāo)準(zhǔn)差為1)逃糟。密度函數(shù)( dnorm )、分布函數(shù)( pnorm )蓬豁、分位數(shù)函數(shù)( qnorm )和隨機(jī)數(shù)生成函數(shù)( rnorm )的使用示例見表5-5绰咽。
x <- pretty(c(-3, 3), 30)
y <- dnorm(x)
plot(x, y, type = "l", xlab = "NormalDeviate", ylab = "Density", yaxs = "i")
pnorm(1.96)
qnorm(0.9, mean = 500, sd = 100)
rnorm(50, mean = 50, sd = 10)
[圖片上傳失敗...(image-69f4c8-1577187687248)]
如果讀者對(duì) plot() 函數(shù)的選項(xiàng)不熟悉,請(qǐng)不要擔(dān)心地粪。這些選項(xiàng)在第11章中有詳述取募。 pretty()在本章稍后的表5-7中進(jìn)行了解釋。
設(shè)定隨機(jī)數(shù)種子
在每次生成偽隨機(jī)數(shù)的時(shí)候蟆技,函數(shù)都會(huì)使用一個(gè)不同的種子玩敏,因此也會(huì)產(chǎn)生不同的結(jié)果斗忌。你可以通過函數(shù) set.seed() 顯式指定這個(gè)種子,讓結(jié)果可以重現(xiàn)(reproducible)旺聚。代碼清單5-2給出了一個(gè)示例织阳。這里的函數(shù) runif() 用來生成0到1區(qū)間上服從均勻分布的偽隨機(jī)數(shù)。
> runif(5)
> [1] 0.03119642 0.51657814 0.86438179 0.74237510 0.69981268
> runif(5)
> [1] 0.07797611 0.90215220 0.46832530 0.28086833 0.86071339
通過手動(dòng)設(shè)定種子砰粹,就可以重現(xiàn)你的結(jié)果了唧躲。這種能力有助于我們創(chuàng)建會(huì)在未來取用的,以及可與他人分享的示例碱璃。
生成多元正態(tài)數(shù)據(jù)
在模擬研究和蒙特卡洛方法中弄痹,你經(jīng)常需要獲取來自給定均值向量和協(xié)方差陣的多元正態(tài)分布的數(shù)據(jù)。 MASS 包中的 mvrnorm() 函數(shù)可以讓這個(gè)問題變得很容易嵌器。其調(diào)用格式為:
mvrnorm(n, mean, sigma)
其中 n 是你想要的樣本大小肛真, mean 為均值向量,而 sigma 是方差?協(xié)方差矩陣(或相關(guān)矩陣)爽航。代
碼清單5-3從一個(gè)參數(shù)如下所示的三元正態(tài)分布中抽取500個(gè)觀測(cè)蚓让。
...
字符處理函數(shù)
數(shù)學(xué)和統(tǒng)計(jì)函數(shù)是用來處理數(shù)值型數(shù)據(jù)的,而字符處理函數(shù)可以從文本型數(shù)據(jù)中抽取信息岳掐,或者為打印輸出和生成報(bào)告重設(shè)文本的格式凭疮。舉例來說,你可能希望將某人的姓和名連接在一起串述,并保證姓和名的首字母大寫,抑或想統(tǒng)計(jì)可自由回答的調(diào)查反饋信息中含有穢語(yǔ)的實(shí)例
(instance)數(shù)量寞肖。一些最有用的字符處理函數(shù)見表5-6纲酗。
請(qǐng)注意,函數(shù) grep() 新蟆、 sub() 和 strsplit() 能夠搜索某個(gè)文本字符串( fixed=TRUE )或某個(gè)正則表達(dá)式( fixed=FALSE 觅赊,默認(rèn)值為 FALSE )。正則表達(dá)式為文本模式的匹配提供了一套清晰而簡(jiǎn)練的語(yǔ)法琼稻。例如吮螺,正則表達(dá)式:
^[hc]?at
可匹配任意以0個(gè)或1個(gè) h 或 c 開頭、后接 at 的字符串帕翻。因此鸠补,此表達(dá)式可以匹配hat、cat和at嘀掸,但不會(huì)匹配bat紫岩。要了解更多,請(qǐng)參考維基百科的regular expression(正則表達(dá)式)條目睬塌。
其他實(shí)用函數(shù)
表5-7中的函數(shù)對(duì)于數(shù)據(jù)管理和處理同樣非常實(shí)用泉蝌,只是它們無法清楚地劃入其他分類中歇万。
[圖片上傳失敗...(image-caf3a5-1577187687248)]
表中的最后一個(gè)例子演示了在輸出時(shí)轉(zhuǎn)義字符的使用方法。 \n 表示新行勋陪, \t 為制表符贪磺, \'
為單引號(hào), \b 為退格诅愚,等等寒锚。(鍵入 ?Quotes 以了解更多。)例如呻粹,代碼:
name <- "Bob"
cat( "Hello", name, "\b.\n", "Isn\'t R", "\t", "GREAT?\n")
可生成:
Hello Bob.
Isn't R GREAT?
請(qǐng)注意第二行縮進(jìn)了一個(gè)空格壕曼。當(dāng) cat 輸出連接后的對(duì)象時(shí),它會(huì)將每一個(gè)對(duì)象都用空格分開等浊。這就是在句號(hào)之前使用退格轉(zhuǎn)義字符( \b )的原因腮郊。不然,生成的結(jié)果將是“Hello Bob .”筹燕。
在數(shù)值轧飞、字符串和向量上使用我們最近學(xué)習(xí)的函數(shù)是直觀而明確的,但是如何將它們應(yīng)用到矩陣和數(shù)據(jù)框上呢撒踪?這就是下一節(jié)的主題过咬。
將函數(shù)應(yīng)用于矩陣和數(shù)據(jù)框
R函數(shù)的諸多有趣特性之一,就是它們可以應(yīng)用到一系列的數(shù)據(jù)對(duì)象上制妄,包括標(biāo)量掸绞、向量、
矩陣耕捞、數(shù)組和數(shù)據(jù)框衔掸。代碼清單5-4提供了一個(gè)示例。
> a <- 5
> sqrt(a)
[1] 2.236068
> b <- c(1.243, 5.654, 2.99)
> round(b)
[1] 1 6 3
> c <- matrix(runif(12), nrow=3)
> c
[,1] [,2] [,3] [,4]
[1,] 0.4205 0.355 0.699 0.323
[2,] 0.0270 0.601 0.181 0.926
[3,] 0.6682 0.319 0.599 0.215
> log(c)
[,1] [,2] [,3] [,4]
[1,] -0.866 -1.036 -0.358 -1.130
[2,] -3.614 -0.508 -1.711 -0.077
[3,] -0.403 -1.144 -0.513 -1.538
> mean(c)
[1] 0.444
請(qǐng)注意俺抽,在代碼清單5-4中對(duì)矩陣 c 求均值的結(jié)果為一個(gè)標(biāo)量(0.444)敞映。函數(shù) mean() 求得的是矩陣中全部12個(gè)元素的均值。但如果希望求的是各行的均值或各列的均值呢磷斧?
R中提供了一個(gè) apply() 函數(shù)振愿,可將一個(gè)任意函數(shù)“應(yīng)用”到矩陣、數(shù)組弛饭、數(shù)據(jù)框的任何維度上冕末。 apply() 函數(shù)的使用格式為:
apply(x, MARGIN, FUN, ...)
其中, x 為數(shù)據(jù)對(duì)象孩哑, MARGIN 是維度的下標(biāo)栓霜, FUN 是由你指定的函數(shù),而 ... 則包括了任何想傳遞給 FUN 的參數(shù)横蜒。在矩陣或數(shù)據(jù)框中胳蛮, MARGIN=1 表示行销凑, MARGIN=2 表示列。請(qǐng)看以下例子仅炊。
> mydata <- matrix(rnorm(30), nrow=6)
> mydata
[,1] [,2] [,3] [,4] [,5]
[1,] 0.71298 1.368 -0.8320 -1.234 -0.790
[2,] -0.15096 -1.149 -1.0001 -0.725 0.506
[3,] -1.77770 0.519 -0.6675 0.721 -1.350
[4,] -0.00132 -0.308 0.9117 -1.391 1.558
[5,] -0.00543 0.378 -0.0906 -1.485 -0.350
[6,] -0.52178 -0.539 -1.7347 2.050 1.569
> apply(mydata, 1, mean)
[1] -0.155 -0.504 -0.511 0.154 -0.310 0.165
> apply(mydata, 2, mean)
[1] -0.2907 0.0449 -0.5688 -0.3442 0.1906
> apply(mydata, 2, mean, trim=0.2)
[1] -0.1699 0.0127 -0.6475 -0.6575 0.2312
首先生成了一個(gè)包含正態(tài)隨機(jī)數(shù)的6×5矩陣?斗幼。然后你計(jì)算了6行的均值?,以及5列的均值?抚垄。最后蜕窿,你計(jì)算了每列的截尾均值(在本例中,截尾均值基于中間60%的數(shù)據(jù)呆馁,最高和最低20%的值均被忽略)?桐经。FUN 可為任意R函數(shù),這也包括你自行編寫的函數(shù)(參見5.4節(jié))浙滤,所以 apply() 是一種很強(qiáng)大的機(jī)制阴挣。 apply() 可把函數(shù)應(yīng)用到數(shù)組的某個(gè)維度上,而 lapply() 和 sapply() 則可將函數(shù)應(yīng)用到列表(list)上纺腊。你將在下一節(jié)中看到 sapply() (它是 lapply() 的更好用的版本)的一個(gè)示例畔咧。
你已經(jīng)擁有了解決5.1節(jié)中數(shù)據(jù)處理問題所需的所有工具,現(xiàn)在揖膜,讓我們小試身手誓沸。
數(shù)據(jù)處理難題的一套解決方案
看起來有點(diǎn)繁瑣,但還是一步步地仔細(xì)看吧
5.1節(jié)中提出的問題是:將學(xué)生的各科考試成績(jī)組合為單一的成績(jī)衡量指標(biāo)壹粟,基于相對(duì)名次(前20%拜隧、下20%、等等)給出從A到F的評(píng)分趁仙,根據(jù)學(xué)生姓氏和名字的首字母對(duì)花名冊(cè)進(jìn)行排序虹蓄。
代碼清單5-6給出了一種解決方案。
# 原始的學(xué)生花名冊(cè)已經(jīng)給出了幸撕,options(digits=2)
# 限定了輸出小數(shù)點(diǎn)后數(shù)字的位數(shù),并且讓輸出更容易閱讀
options(digits = 2)
# 創(chuàng)建數(shù)據(jù)
Student <- c("John Davis", "Angela Williams", "Bullwinkle Moose", "David Jones",
"Janice Markhammer", "Cheryl Cushing", "Reuven Ytzrhak", "Greg Knox", "Joel England",
"Mary Rayburn")
Math <- c(502, 600, 412, 358, 495, 512, 410, 625, 573, 522)
Science <- c(95, 99, 80, 82, 75, 85, 80, 95, 89, 86)
English <- c(25, 22, 18, 15, 20, 28, 15, 30, 37, 18)
roster <- data.frame(Student, Math, Science, English, stringsAsFactors = FALSE)
> roster
Student Math Science English
1 John Davis 502 95 25
2 Angela Williams 600 99 22
3 Bullwinkle Moose 412 80 18
4 David Jones 358 82 15
5 Janice Markhammer 495 75 20
6 Cheryl Cushing 512 85 28
7 Reuven Ytzrhak 410 80 15
8 Greg Knox 625 95 30
9 Joel England 573 89 37
10 Mary Rayburn 522 86 18
# scale()標(biāo)準(zhǔn)化數(shù)據(jù)
z <- scale(roster[ , 2:4])
> z
Math Science English
[1,] 0.013 1.078 0.31
[2,] 1.143 1.591 -0.11
[3,] -1.026 -0.847 -0.67
[4,] -1.649 -0.590 -1.09
[5,] -0.068 -1.489 -0.39
[6,] 0.128 -0.205 0.73
[7,] -1.049 -0.847 -1.09
[8,] 1.432 1.078 1.01
[9,] 0.832 0.308 1.98
[10,] 0.243 -0.077 -0.67
# mean()來計(jì)算各行的均值以獲得綜合得分
score <- apply(z, 1, mean)
> score
[1] 0.47 0.87 -0.85 -1.11 -0.65 0.22 -0.99 1.17 1.04 -0.17
# cbind()將均值列添加到花名冊(cè)中
roster <- cbind(roster, score)
> roster
Student Math Science English score
1 John Davis 502 95 25 0.47
2 Angela Williams 600 99 22 0.87
3 Bullwinkle Moose 412 80 18 -0.85
4 David Jones 358 82 15 -1.11
5 Janice Markhammer 495 75 20 -0.65
6 Cheryl Cushing 512 85 28 0.22
7 Reuven Ytzrhak 410 80 15 -0.99
8 Greg Knox 625 95 30 1.17
9 Joel England 573 89 37 1.04
10 Mary Rayburn 522 86 18 -0.17
# quantile()求學(xué)生綜合得分的百分位數(shù)
y <- quantile(score, c(0.8, 0.6, 0.4, 0.2))
> y
80% 60% 40% 20%
0.91 0.32 -0.36 -0.88
# 使用邏輯運(yùn)算符將學(xué)生的百分位數(shù)排名重編碼為一個(gè)新的類別型成績(jī)變量外臂。
# 下面在數(shù)據(jù)框roster中創(chuàng)建了變量grade 坐儿。
roster$grade[score >= y[1]] <- "A"
roster$grade[score < y[1] & score >= y[2]] <- "B"
roster$grade[score < y[2] & score >= y[3]] <- "C"
roster$grade[score < y[3] & score >= y[4]] <- "D"
roster$grade[score < y[4]] <- "F"
> roster
Student Math Science English score grade
1 John Davis 502 95 25 0.47 B
2 Angela Williams 600 99 22 0.87 B
3 Bullwinkle Moose 412 80 18 -0.85 D
4 David Jones 358 82 15 -1.11 F
5 Janice Markhammer 495 75 20 -0.65 D
6 Cheryl Cushing 512 85 28 0.22 C
7 Reuven Ytzrhak 410 80 15 -0.99 F
8 Greg Knox 625 95 30 1.17 A
9 Joel England 573 89 37 1.04 A
10 Mary Rayburn 522 86 18 -0.17 C
#strsplit()函數(shù)以空格為分隔分割學(xué)生姓名
name <- strsplit((roster$Student), " ")
# sapply()函數(shù)分別取姓名的第一位和第二位,
# "["是一個(gè)可以提取某個(gè)對(duì)象的一部分的函數(shù)
# 在這里它是用來提取列表name各成分中的第一個(gè)或第二個(gè)元素的宋光。
lastname <- sapply(name, "[", 2)
firstname <- sapply(name, "[", 1)
# 將姓和名兩列加入列頭貌矿,并舍棄原來第一列的全名列
roster <- cbind(firstname, lastname, roster[, -1])
> roster
firstname lastname Math Science English score grade
1 John Davis 502 95 25 0.47 B
2 Angela Williams 600 99 22 0.87 B
3 Bullwinkle Moose 412 80 18 -0.85 D
4 David Jones 358 82 15 -1.11 F
5 Janice Markhammer 495 75 20 -0.65 D
6 Cheryl Cushing 512 85 28 0.22 C
7 Reuven Ytzrhak 410 80 15 -0.99 F
8 Greg Knox 625 95 30 1.17 A
9 Joel England 573 89 37 1.04 A
10 Mary Rayburn 522 86 18 -0.17 C
# order() 依姓氏和名字對(duì)數(shù)據(jù)集進(jìn)行排序
roster <- roster[order(lastname, firstname),]
> roster
firstname lastname Math Science English score grade
6 Cheryl Cushing 512 85 28 0.22 C
1 John Davis 502 95 25 0.47 B
9 Joel England 573 89 37 1.04 A
4 David Jones 358 82 15 -1.11 F
8 Greg Knox 625 95 30 1.17 A
5 Janice Markhammer 495 75 20 -0.65 D
3 Bullwinkle Moose 412 80 18 -0.85 D
10 Mary Rayburn 522 86 18 -0.17 C
2 Angela Williams 600 99 22 0.87 B
7 Reuven Ytzrhak 410 80 15 -0.99 F
控制流
在正常情況下,R程序中的語(yǔ)句是從上至下順序執(zhí)行的罪佳。但有時(shí)你可能希望重復(fù)執(zhí)行某些語(yǔ)句逛漫,僅在滿足特定條件的情況下執(zhí)行另外的語(yǔ)句。這就是控制流結(jié)構(gòu)發(fā)揮作用的地方了赘艳。R擁有一般現(xiàn)代編程語(yǔ)言中都有的標(biāo)準(zhǔn)控制結(jié)構(gòu)酌毡。首先你將看到用于條件執(zhí)行的結(jié)構(gòu)克握,接下來是用于循環(huán)執(zhí)行的結(jié)構(gòu)。
為了理解貫穿本節(jié)的語(yǔ)法示例枷踏,請(qǐng)牢記以下概念:
- 語(yǔ)句( statement )是一條單獨(dú)的R語(yǔ)句或一組復(fù)合語(yǔ)句(包含在花括號(hào) { } 中的一組R語(yǔ)句菩暗,使用分號(hào)分隔);
- 條件( cond )是一條最終被解析為真( TRUE )或假( FALSE )的表達(dá)式旭蠕;
- 表達(dá)式( expr )是一條數(shù)值或字符串的求值語(yǔ)句停团;
- 序列( seq )是一個(gè)數(shù)值或字符串序列。
- 在討論過控制流的構(gòu)造后掏熬,我們將學(xué)習(xí)如何編寫函數(shù)佑稠。
重復(fù)和循環(huán)
循環(huán)結(jié)構(gòu)重復(fù)地執(zhí)行一個(gè)或一系列語(yǔ)句,直到某個(gè)條件不為真為止旗芬。循環(huán)結(jié)構(gòu)包括 for 和
while 結(jié)構(gòu)舌胶。
for 結(jié)構(gòu)
for 循環(huán)重復(fù)地執(zhí)行一個(gè)語(yǔ)句,直到某個(gè)變量的值不再包含在序列 seq 中為止岗屏。語(yǔ)法為:
for (var in seq) statement
在下例中:
for (i in 1:10) print("Hello")
單詞Hello被輸出了10次辆琅。
while 結(jié)構(gòu)
while 循環(huán)重復(fù)地執(zhí)行一個(gè)語(yǔ)句,直到條件不為真為止这刷。語(yǔ)法為:
while (cond) statement
作為第二個(gè)例子婉烟,代碼:
i <- 10
while (i > 0) {print("Hello"); i <- i - 1}
又將單詞Hello輸出了10次。請(qǐng)確保括號(hào)內(nèi) while 的條件語(yǔ)句能夠改變暇屋,即讓它在某個(gè)時(shí)刻不再為真——否則循環(huán)將永不停止似袁!在上例中,語(yǔ)句:i <- i – 1
在每步循環(huán)中為對(duì)象 i 減去1咐刨,這樣在十次循環(huán)過后昙衅,它就不再大于0了。反之定鸟,如果在每步循環(huán)都加1的話而涉,R將不停地打招呼。這也是 while 循環(huán)可能較其他循環(huán)結(jié)構(gòu)更危險(xiǎn)的原因联予。
在處理大數(shù)據(jù)集中的行和列時(shí)啼县,R中的循環(huán)可能比較低效費(fèi)時(shí)。只要可能沸久,最好聯(lián)用R中的內(nèi)建數(shù)值/字符處理函數(shù)和 apply 族函數(shù)季眷。
條件執(zhí)行
在條件執(zhí)行結(jié)構(gòu)中,一條或一組語(yǔ)句僅在滿足一個(gè)指定條件時(shí)執(zhí)行卷胯。條件執(zhí)行結(jié)構(gòu)包括if-else 子刮、 ifelse 和 switch 。
if-else 結(jié)構(gòu)
控制結(jié)構(gòu) if-else 在某個(gè)給定條件為真時(shí)執(zhí)行語(yǔ)句窑睁。也可以同時(shí)在條件為假時(shí)執(zhí)行另外的語(yǔ)
句挺峡。語(yǔ)法為:
if (cond) statement
if (cond) statement1 else statement2
示例如下:
if (is.character(grade)) grade <- as.factor(grade)
if (!is.factor(grade)) grade <- as.factor(grade) else print("Grade already
is a factor")
在第一個(gè)實(shí)例中葵孤,如果 grade 是一個(gè)字符向量,它就會(huì)被轉(zhuǎn)換為一個(gè)因子沙郭。在第二個(gè)實(shí)例中佛呻,兩個(gè)語(yǔ)句擇其一執(zhí)行。如果 grade 不是一個(gè)因子(注意符號(hào) ! )病线,它就會(huì)被轉(zhuǎn)換為一個(gè)因子吓著。如果它是一個(gè)因子,就會(huì)輸出一段信息送挑。
ifelse 結(jié)構(gòu)
ifelse 結(jié)構(gòu)是 if-else 結(jié)構(gòu)比較緊湊的向量化版本绑莺,其語(yǔ)法為:
ifelse(cond, statement1, statement2)
若 cond 為 TRUE ,則執(zhí)行第一個(gè)語(yǔ)句惕耕;若 cond 為 FALSE 纺裁,則執(zhí)行第二個(gè)語(yǔ)句。示例如下:
ifelse(score > 0.5, print("Passed"), print("Failed"))
outcome <- ifelse (score > 0.5, "Passed", "Failed")
在程序的行為是二元時(shí)司澎,或者希望結(jié)構(gòu)的輸入和輸出均為向量時(shí)欺缘,請(qǐng)使用 ifelse 。
switch 結(jié)構(gòu)
switch 根據(jù)一個(gè)表達(dá)式的值選擇語(yǔ)句執(zhí)行挤安。語(yǔ)法為:
switch(expr, ...)
其中的 ... 表示與 expr 的各種可能輸出值綁定的語(yǔ)句谚殊。通過觀察代碼清單5-7中的代碼,可以輕松地理解 switch 的工作原理蛤铜。
feelings <- c("sad", "afraid")
for(i in feelings)
print(
switch(i,
happy = "I am glad you are happy",
afraid = "There is nothing to fear",
sad = "Cheer up",
angry = "Calm down now"
)
)
[1] "Cheer up"
[1] "There is nothing to fear"
雖然這個(gè)例子比較幼稚嫩絮,但它展示了 switch 的主要功能。你將在下一節(jié)學(xué)習(xí)如何使用switch 編寫自己的函數(shù)围肥。
用戶自編函數(shù)
R的最大優(yōu)點(diǎn)之一就是用戶可以自行添加函數(shù)剿干。事實(shí)上,R中的許多函數(shù)都是由已有函數(shù)構(gòu)成的穆刻。一個(gè)函數(shù)的結(jié)構(gòu)看起來大致如此:
myfunction <- function(arg1, arg2, ... ){
statements
return(object)
}
函數(shù)中的對(duì)象只在函數(shù)內(nèi)部使用置尔。返回對(duì)象的數(shù)據(jù)類型是任意的,從標(biāo)量到列表皆可氢伟。讓我們看一個(gè)示例撰洗。
mydate <- function(type){
switch(type,
long = format(Sys.time(), "%A %B %d %Y"),
short = format(Sys.time(), "%m-%d-%y"),
cat(type, "is not a recognized type\n")
)
}
> mydate("long")
[1] "星期六 十二月 07 2019"
> mydate("short")
[1] "12-07-19"
> mydate("aoe")
aoe is not a recognized type
請(qǐng)注意,函數(shù) cat() 僅會(huì)在輸入的日期格式類型不匹配 "long" 或 "short" 時(shí)執(zhí)行腐芍。使用一個(gè)表達(dá)式來捕獲用戶的錯(cuò)誤輸入的參數(shù)值通常來說是一個(gè)好主意。
有若干函數(shù)可以用來為函數(shù)添加錯(cuò)誤捕獲和糾正功能试躏。你可以使用函數(shù) warning() 來生成一條錯(cuò)誤提示信息猪勇,用 message() 來生成一條診斷信息,或用 stop() 停止當(dāng)前表達(dá)式的執(zhí)行并提示錯(cuò)誤颠蕴。20.5節(jié)將會(huì)更加詳細(xì)地討論錯(cuò)誤捕捉和調(diào)試泣刹。
在創(chuàng)建好自己的函數(shù)以后助析,你可能希望在每個(gè)會(huì)話中都能直接使用它們。附錄B描述了如何定制R環(huán)境椅您,以使R啟動(dòng)時(shí)自動(dòng)讀取用戶編寫的函數(shù)外冀。我們將在第6章和第8章中看到更多的用戶自編函數(shù)示例。
你可以使用本節(jié)中提供的基本技術(shù)完成很多工作掀泳。第20章的內(nèi)容更加詳細(xì)地涵蓋了控制流和其他編程主題雪隧。第21章涵蓋了如何創(chuàng)建包。如果你想要探索編寫函數(shù)的微妙之處员舵,或編寫可以分發(fā)給他人使用的專業(yè)級(jí)代碼脑沿,個(gè)人推薦閱讀這兩章,然后閱讀兩本優(yōu)秀的書籍马僻,你可在本書末尾的參考文獻(xiàn)部分找到:Venables & Ripley(2000)以及Chambers(2008)庄拇。這兩本書共同提供了大量細(xì)節(jié)和眾多示例。
函數(shù)的編寫就講到這里韭邓,我們將以對(duì)數(shù)據(jù)整合和重塑的討論來結(jié)束本章措近。
整合與重構(gòu)
R中提供了許多用來整合(aggregate)和重塑(reshape)數(shù)據(jù)的強(qiáng)大方法。在整合數(shù)據(jù)時(shí)女淑,往往將多組觀測(cè)替換為根據(jù)這些觀測(cè)計(jì)算的描述性統(tǒng)計(jì)量瞭郑。在重塑數(shù)據(jù)時(shí),則會(huì)通過修改數(shù)據(jù)的結(jié)構(gòu)(行和列)來決定數(shù)據(jù)的組織方式诗力。本節(jié)描述了用來完成這些任務(wù)的多種方式凰浮。
在接下來的兩個(gè)小節(jié)中,我們將使用已包含在R基本安裝中的數(shù)據(jù)框 mtcars 苇本。這個(gè)數(shù)據(jù)集是從Motor Trend雜志(1974)提取的袜茧,它描述了34種車型的設(shè)計(jì)和性能特點(diǎn)(汽缸數(shù)、排量瓣窄、馬力笛厦、每加侖汽油行駛的英里數(shù),等等)俺夕。要了解此數(shù)據(jù)集的更多信息裳凸,請(qǐng)參閱 help(mtcars) 。
轉(zhuǎn)置
轉(zhuǎn)置(反轉(zhuǎn)行和列)也許是重塑數(shù)據(jù)集的眾多方法中最簡(jiǎn)單的一個(gè)了劝贸。使用函數(shù) t() 即可對(duì)一個(gè)矩陣或數(shù)據(jù)框進(jìn)行轉(zhuǎn)置堤框。對(duì)于后者,行名將成為變量(列)名嫉拐。代碼清單5-9展示了一個(gè)例子沃暗。
使用函數(shù) t() 即可對(duì)一個(gè)矩陣或數(shù)據(jù)框進(jìn)行轉(zhuǎn)置。
整合數(shù)據(jù)
在R中使用一個(gè)或多個(gè)by變量和一個(gè)預(yù)先定義好的函數(shù)來折疊(collapse)數(shù)據(jù)是比較容易的。調(diào)用格式為:
aggregate(x, by, FUN)
其中 x 是待折疊的數(shù)據(jù)對(duì)象捌议, by 是一個(gè)變量名組成的列表哼拔,這些變量將被去掉以形成新的觀測(cè),
而 FUN 則是用來計(jì)算描述性統(tǒng)計(jì)量的標(biāo)量函數(shù)瓣颅,它將被用來計(jì)算新觀測(cè)中的值倦逐。
作為一個(gè)示例,我們將根據(jù)汽缸數(shù)和擋位數(shù)整合 mtcars 數(shù)據(jù)宫补,并返回各個(gè)數(shù)值型變量的均
值(見代碼清單5-10)檬姥。
> mtcars
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21 6 160 110 3.9 2.6 16 0 1 4 4
Mazda RX4 Wag 21 6 160 110 3.9 2.9 17 0 1 4 4
Datsun 710 23 4 108 93 3.8 2.3 19 1 1 4 1
Hornet 4 Drive 21 6 258 110 3.1 3.2 19 1 0 3 1
Hornet Sportabout 19 8 360 175 3.1 3.4 17 0 0 3 2
Valiant 18 6 225 105 2.8 3.5 20 1 0 3 1
Duster 360 14 8 360 245 3.2 3.6 16 0 0 3 4
Merc 240D 24 4 147 62 3.7 3.2 20 1 0 4 2
Merc 230 23 4 141 95 3.9 3.1 23 1 0 4 2
Merc 280 19 6 168 123 3.9 3.4 18 1 0 4 4
Merc 280C 18 6 168 123 3.9 3.4 19 1 0 4 4
Merc 450SE 16 8 276 180 3.1 4.1 17 0 0 3 3
Merc 450SL 17 8 276 180 3.1 3.7 18 0 0 3 3
Merc 450SLC 15 8 276 180 3.1 3.8 18 0 0 3 3
Cadillac Fleetwood 10 8 472 205 2.9 5.2 18 0 0 3 4
Lincoln Continental 10 8 460 215 3.0 5.4 18 0 0 3 4
Chrysler Imperial 15 8 440 230 3.2 5.3 17 0 0 3 4
Fiat 128 32 4 79 66 4.1 2.2 19 1 1 4 1
Honda Civic 30 4 76 52 4.9 1.6 19 1 1 4 2
Toyota Corolla 34 4 71 65 4.2 1.8 20 1 1 4 1
Toyota Corona 22 4 120 97 3.7 2.5 20 1 0 3 1
Dodge Challenger 16 8 318 150 2.8 3.5 17 0 0 3 2
AMC Javelin 15 8 304 150 3.1 3.4 17 0 0 3 2
Camaro Z28 13 8 350 245 3.7 3.8 15 0 0 3 4
Pontiac Firebird 19 8 400 175 3.1 3.8 17 0 0 3 2
Fiat X1-9 27 4 79 66 4.1 1.9 19 1 1 4 1
Porsche 914-2 26 4 120 91 4.4 2.1 17 0 1 5 2
Lotus Europa 30 4 95 113 3.8 1.5 17 1 1 5 2
Ford Pantera L 16 8 351 264 4.2 3.2 14 0 1 5 4
Ferrari Dino 20 6 145 175 3.6 2.8 16 0 1 5 6
Maserati Bora 15 8 301 335 3.5 3.6 15 0 1 5 8
Volvo 142E 21 4 121 109 4.1 2.8 19 1 1 4 2
options(digits = 3)
attach(mtcars)
aggdata <- aggregate(mtcars, by = list(cyl, gear), FUN=mean, na.rm = TRUE)
detach(mtcars)
aggdata
Group.1 Group.2 mpg cyl disp hp drat wt qsec vs am gear carb
1 4 3 21.5 4 120 97 3.70 2.46 20.0 1.0 0.00 3 1.00
2 6 3 19.8 6 242 108 2.92 3.34 19.8 1.0 0.00 3 1.00
3 8 3 15.1 8 358 194 3.12 4.10 17.1 0.0 0.00 3 3.08
4 4 4 26.9 4 103 76 4.11 2.38 19.6 1.0 0.75 4 1.50
5 6 4 19.8 6 164 116 3.91 3.09 17.7 0.5 0.50 4 4.00
6 4 5 28.2 4 108 102 4.10 1.83 16.8 0.5 1.00 5 2.00
7 6 5 19.7 6 145 175 3.62 2.77 15.5 0.0 1.00 5 6.00
8 8 5 15.4 8 326 300 3.88 3.37 14.6 0.0 1.00 5 6.00
在結(jié)果中, Group.1 表示汽缸數(shù)量(4守谓、6或8)穿铆, Group.2 代表?yè)跷粩?shù)(3、4或5)斋荞。舉例來說荞雏,擁有4個(gè)汽缸和3個(gè)擋位車型的每加侖汽油行駛英里數(shù)( mpg )均值為21.5。
在使用 aggregate() 函數(shù)的時(shí)候平酿, by 中的變量必須在一個(gè)列表中(即使只有一個(gè)變量)凤优。你可 以 在 列 表 中 為 各 組 聲 明 自 定 義 的 名 稱 , 例 如 by=list(Group.cyl=cyl, Group.gears=gear) 蜈彼。指定的函數(shù)可為任意的內(nèi)建或自編函數(shù)筑辨,這就為整合命令賦予了強(qiáng)大的力量。但說到力量幸逆,沒有什么可以比 reshape2 包更強(qiáng)棍辕。
reshape2 包
reshape2 包是一套重構(gòu)和整合數(shù)據(jù)集的絕妙的萬能工具。由于它的這種萬能特性还绘,可能學(xué)起來會(huì)有一點(diǎn)難度楚昭。我們將慢慢地梳理整個(gè)過程,并使用一個(gè)小型數(shù)據(jù)集作為示例拍顷,這樣每一步發(fā)生了什么就很清晰了抚太。由于 reshape2 包并未包含在R的標(biāo)準(zhǔn)安裝中,在第一次使用它之前需要使用 install.packages("reshape2") 進(jìn)行安裝昔案。
大致說來尿贫,你需要首先將數(shù)據(jù)融合(melt),以使每一行都是唯一的標(biāo)識(shí)符?變量組合踏揣。然后將數(shù)據(jù)重鑄(cast)為你想要的任何形狀庆亡。在重鑄過程中,你可以使用任何函數(shù)對(duì)數(shù)據(jù)進(jìn)行整合捞稿。將使用的數(shù)據(jù)集如表5-8所示身冀。
用處不大感覺钝尸,先不講了。搂根。。