本章內容
? 數學和統計函數
? 字符處理函數
? 循環(huán)和條件執(zhí)行
? 自編函數
? 數據整合與重塑
在第4章,我們審視了R中基本的數據集處理方法,本章我們將關注一些高級話題。本章分為三個基本部分。在第一部分中,我們將快速瀏覽R中的多種數學、統計和字符處理函數。為了讓這一部分的內容相互關聯,我們先引入一個能夠使用這些函數解決的數據處理問題。在講解過這些函數以后,再為這個數據處理問題提供一個可能的解決方案。
接下來,我們將講解如何自己編寫函數來完成數據處理和分析任務。首先,我們將探索控制程序流程的多種方式,包括循環(huán)和條件執(zhí)行語句。然后,我們將研究用戶自編函數的結構,以及在編寫完成如何調用它們。
最后,我們將了解數據的整合和概述方法,以及數據集的重塑和重構方法。在整合數據時,你可以使用任何內建或自編函數來獲取數據的概述,所以你在本章前兩部分中學習的內容將會派上用場。
一個數據處理難題
要討論數值和字符處理函數,讓我們首先考慮一個數據處理問題。一組學生參加了數學、科學和英語考試。為了給所有學生確定一個單一的成績衡量指標,需要將這些科目的成績組合起來。
另外,你還想將前20%的學生評定為A,接下來20%的學生評定為B,依次類推。最后,你希望按字母順序對學生排序。數據如表5-1所示。

觀察此數據集,馬上可以發(fā)現一些明顯的障礙。首先,三科考試的成績是無法比較的。由于它們的均值和標準差相去甚遠,所以對它們求平均值是沒有意義的。你在組合這些考試成績之前,必須將其變換為可比較的單元。其次,為了評定等級,你需要一種方法來確定某個學生在前述得分上百分比排名。再次,表示姓名的字段只有一個,這讓排序任務復雜化了。為了正確地將其排序,需要將姓和名拆開。
以上每一個任務都可以巧妙地利用R中的數值和字符處理函數完成。在講解完下一節(jié)中的各種函數之后,我們將考慮一套可行的解決方案,以解決這項數據處理難題。
數值和字符處理函數
本節(jié)我們將綜述R中作為數據處理基石的函數,它們可分為數值(數學、統計、概率)函數和字符處理函數。在闡述過每一類函數以后,我將為你展示如何將函數應用到矩陣和數據框的列(變量)和行(觀測)上(參見5.2.6節(jié))。
數學函數
表5-2列出了常用的數學函數和簡短的用例。


對數據做變換是這些函數的一個主要用途。例如,你經常會在進一步分析之前將收入這種存在明顯偏倚的變量取對數。數學函數也被用作公式中的一部分,用于繪圖函數(例如 x 對 sin(x) )和在輸出結果之前對數值做格式化。
表5-2中的示例將數學函數應用到了標量(單獨的數值)上。當這些函數被應用于數值向量、矩陣或數據框時,它們會作用于每一個獨立的值。例如, sqrt(c(4, 16, 25)) 的返回值為 c(2,4, 5) 。
統計函數
常用的統計函數如表5-3所示,其中許多函數都擁有可以影響輸出結果的可選參數。舉例來說:y <- mean(x)提供了對象 x 中元素的算術平均數,而:z <- mean(x, trim =0.05, na.rm=TRUE則提供了截尾平均數,即丟棄了最大5%和最小5%的數據和所有缺失值后的算術平均數。請使用help() 了解以上每個函數和其參數的用法。

要了解這些函數的實戰(zhàn)應用,請參考代碼清單5-1。這段代碼演示了計算某個數值向量的均值和標準差的兩種方式。
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 )的計算過程是很有啟發(fā)性的:
(1) x 等于 c(1, 2, 3, 4, 5, 6, 7, 8) , x 的平均值等于4.5( length(x) 返回了 x 中元素的數量);
(2) (x – meanx) 從 x 的每個元素中減去了4.5,結果為 c(-3.5, -2.5, -1.5, -0.5, 0.5,1.5, 2.5, 3.5) ;
(3) (x – meanx)^2 將 (x - meanx) 的每個元素求平方,結果為 c(12.25, 6.25, 2.25,0.25, 0.25, 2.25, 6.25, 12.25) ;
(4) sum((x - meanx)^2) 對 (x - meanx)^2) 的所有元素求和,結果為42。
R中公式的寫法和類似MATLAB的矩陣運算語言有著許多共同之處。(我們將在附錄E中具體關注解決矩陣代數問題的方法。)
數據的標準化
默認情況下,函數 scale() 對矩陣或數據框的指定列進行均值為0、標準差為1的標準化:
newdata <- scale(mydata)
要對每一列進行任意均值和標準差的標準化,可以使用如下的代碼:
newdata <- scale(mydata)*SD + M
其中的 M 是想要的均值, SD 為想要的標準差。在非數值型的列上使用 scale() 函數將會報錯。
要對指定列而不是整個矩陣或數據框進行標準化,你可以使用這樣的代碼:
newdata <- transform(mydata, myvar = scale(myvar)*10+50)
此句將變量 myvar 標準化為均值50、標準差為10的變量。你將在5.3節(jié)數據處理問題的解決方法中用到 scale() 函數。
概率函數
你可能在疑惑為何概率函數未和統計函數列在一起。(你真的對此有些困惑,對吧?)雖然根據定義,概率函數也屬于統計類,但是它們非常獨特,應獨立設一節(jié)進行講解。概率函數通常用來生成特征已知的模擬數據,以及在用戶編寫的統計函數中計算概率值。
在R中,概率函數形如 :
[dpqr]distribution_abbreviation()
其中第一個字母表示其所指分布的某一方面:
d = 密度函數(density)
p = 分布函數(distribution function)
q = 分位數函數(quantile function)
r = 生成隨機數(隨機偏差)
常用的概率函數列于表5-4中。

我們不妨先看看正態(tài)分布的有關函數,以了解這些函數的使用方法。如果不指定一個均值和一個標準差,則函數將假定其為標準正態(tài)分布(均值為0,標準差為1)。密度函數( dnorm )、分布函數( pnorm )、分位數函數( qnorm )和隨機數生成函數( 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)]
如果讀者對 plot() 函數的選項不熟悉,請不要擔心。這些選項在第11章中有詳述。 pretty()在本章稍后的表5-7中進行了解釋。
設定隨機數種子
在每次生成偽隨機數的時候,函數都會使用一個不同的種子,因此也會產生不同的結果。你可以通過函數 set.seed() 顯式指定這個種子,讓結果可以重現(reproducible)。代碼清單5-2給出了一個示例。這里的函數 runif() 用來生成0到1區(qū)間上服從均勻分布的偽隨機數。
> 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
通過手動設定種子,就可以重現你的結果了。這種能力有助于我們創(chuàng)建會在未來取用的,以及可與他人分享的示例。
生成多元正態(tài)數據
在模擬研究和蒙特卡洛方法中,你經常需要獲取來自給定均值向量和協方差陣的多元正態(tài)分布的數據。 MASS 包中的 mvrnorm() 函數可以讓這個問題變得很容易。其調用格式為:
mvrnorm(n, mean, sigma)
其中 n 是你想要的樣本大小, mean 為均值向量,而 sigma 是方差?協方差矩陣(或相關矩陣)。代
碼清單5-3從一個參數如下所示的三元正態(tài)分布中抽取500個觀測。

...
字符處理函數
數學和統計函數是用來處理數值型數據的,而字符處理函數可以從文本型數據中抽取信息,或者為打印輸出和生成報告重設文本的格式。舉例來說,你可能希望將某人的姓和名連接在一起,并保證姓和名的首字母大寫,抑或想統計可自由回答的調查反饋信息中含有穢語的實例
(instance)數量。一些最有用的字符處理函數見表5-6。

請注意,函數 grep() 、 sub() 和 strsplit() 能夠搜索某個文本字符串( fixed=TRUE )或某個正則表達式( fixed=FALSE ,默認值為 FALSE )。正則表達式為文本模式的匹配提供了一套清晰而簡練的語法。例如,正則表達式:
^[hc]?at
可匹配任意以0個或1個 h 或 c 開頭、后接 at 的字符串。因此,此表達式可以匹配hat、cat和at,但不會匹配bat。要了解更多,請參考維基百科的regular expression(正則表達式)條目。
其他實用函數
表5-7中的函數對于數據管理和處理同樣非常實用,只是它們無法清楚地劃入其他分類中。
[圖片上傳失敗...(image-caf3a5-1577187687248)]
表中的最后一個例子演示了在輸出時轉義字符的使用方法。 \n 表示新行, \t 為制表符, \'為單引號, \b 為退格,等等。(鍵入 ?Quotes 以了解更多。)例如,代碼:
name <- "Bob"
cat( "Hello", name, "\b.\n", "Isn\'t R", "\t", "GREAT?\n")
可生成:
Hello Bob.
Isn't R GREAT?
請注意第二行縮進了一個空格。當 cat 輸出連接后的對象時,它會將每一個對象都用空格分開。這就是在句號之前使用退格轉義字符( \b )的原因。不然,生成的結果將是“Hello Bob .”。
在數值、字符串和向量上使用我們最近學習的函數是直觀而明確的,但是如何將它們應用到矩陣和數據框上呢?這就是下一節(jié)的主題。
將函數應用于矩陣和數據框
R函數的諸多有趣特性之一,就是它們可以應用到一系列的數據對象上,包括標量、向量、
矩陣、數組和數據框。代碼清單5-4提供了一個示例。
> 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
請注意,在代碼清單5-4中對矩陣 c 求均值的結果為一個標量(0.444)。函數 mean() 求得的是矩陣中全部12個元素的均值。但如果希望求的是各行的均值或各列的均值呢?
R中提供了一個 apply() 函數,可將一個任意函數“應用”到矩陣、數組、數據框的任何維度上。 apply() 函數的使用格式為:
apply(x, MARGIN, FUN, ...)
其中, x 為數據對象, MARGIN 是維度的下標, FUN 是由你指定的函數,而 ... 則包括了任何想傳遞給 FUN 的參數。在矩陣或數據框中, MARGIN=1 表示行, MARGIN=2 表示列。請看以下例子。
> 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
首先生成了一個包含正態(tài)隨機數的6×5矩陣?。然后你計算了6行的均值?,以及5列的均值?。最后,你計算了每列的截尾均值(在本例中,截尾均值基于中間60%的數據,最高和最低20%的值均被忽略)?。FUN 可為任意R函數,這也包括你自行編寫的函數(參見5.4節(jié)),所以 apply() 是一種很強大的機制。 apply() 可把函數應用到數組的某個維度上,而 lapply() 和 sapply() 則可將函數應用到列表(list)上。你將在下一節(jié)中看到 sapply() (它是 lapply() 的更好用的版本)的一個示例。
你已經擁有了解決5.1節(jié)中數據處理問題所需的所有工具,現在,讓我們小試身手。
數據處理難題的一套解決方案
看起來有點繁瑣,但還是一步步地仔細看吧
5.1節(jié)中提出的問題是:將學生的各科考試成績組合為單一的成績衡量指標,基于相對名次(前20%、下20%、等等)給出從A到F的評分,根據學生姓氏和名字的首字母對花名冊進行排序。
代碼清單5-6給出了一種解決方案。
# 原始的學生花名冊已經給出了,options(digits=2)
# 限定了輸出小數點后數字的位數,并且讓輸出更容易閱讀
options(digits = 2)
# 創(chuàng)建數據
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()標準化數據
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()來計算各行的均值以獲得綜合得分
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()將均值列添加到花名冊中
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()求學生綜合得分的百分位數
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
# 使用邏輯運算符將學生的百分位數排名重編碼為一個新的類別型成績變量。
# 下面在數據框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()函數以空格為分隔分割學生姓名
name <- strsplit((roster$Student), " ")
# sapply()函數分別取姓名的第一位和第二位,
# "["是一個可以提取某個對象的一部分的函數
# 在這里它是用來提取列表name各成分中的第一個或第二個元素的。
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() 依姓氏和名字對數據集進行排序
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程序中的語句是從上至下順序執(zhí)行的。但有時你可能希望重復執(zhí)行某些語句,僅在滿足特定條件的情況下執(zhí)行另外的語句。這就是控制流結構發(fā)揮作用的地方了。R擁有一般現代編程語言中都有的標準控制結構。首先你將看到用于條件執(zhí)行的結構,接下來是用于循環(huán)執(zhí)行的結構。
為了理解貫穿本節(jié)的語法示例,請牢記以下概念:
- 語句( statement )是一條單獨的R語句或一組復合語句(包含在花括號 { } 中的一組R語句,使用分號分隔);
- 條件( cond )是一條最終被解析為真( TRUE )或假( FALSE )的表達式;
- 表達式( expr )是一條數值或字符串的求值語句;
- 序列( seq )是一個數值或字符串序列。
- 在討論過控制流的構造后,我們將學習如何編寫函數。
重復和循環(huán)
循環(huán)結構重復地執(zhí)行一個或一系列語句,直到某個條件不為真為止。循環(huán)結構包括 for 和
while 結構。
for 結構
for 循環(huán)重復地執(zhí)行一個語句,直到某個變量的值不再包含在序列 seq 中為止。語法為:
for (var in seq) statement
在下例中:
for (i in 1:10) print("Hello")
單詞Hello被輸出了10次。
while 結構
while 循環(huán)重復地執(zhí)行一個語句,直到條件不為真為止。語法為:
while (cond) statement
作為第二個例子,代碼:
i <- 10
while (i > 0) {print("Hello"); i <- i - 1}
又將單詞Hello輸出了10次。請確保括號內 while 的條件語句能夠改變,即讓它在某個時刻不再為真——否則循環(huán)將永不停止!在上例中,語句:i <- i – 1在每步循環(huán)中為對象 i 減去1,這樣在十次循環(huán)過后,它就不再大于0了。反之,如果在每步循環(huán)都加1的話,R將不停地打招呼。這也是 while 循環(huán)可能較其他循環(huán)結構更危險的原因。
在處理大數據集中的行和列時,R中的循環(huán)可能比較低效費時。只要可能,最好聯用R中的內建數值/字符處理函數和 apply 族函數。
條件執(zhí)行
在條件執(zhí)行結構中,一條或一組語句僅在滿足一個指定條件時執(zhí)行。條件執(zhí)行結構包括if-else 、 ifelse 和 switch 。
if-else 結構
控制結構 if-else 在某個給定條件為真時執(zhí)行語句。也可以同時在條件為假時執(zhí)行另外的語
句。語法為:
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")
在第一個實例中,如果 grade 是一個字符向量,它就會被轉換為一個因子。在第二個實例中,兩個語句擇其一執(zhí)行。如果 grade 不是一個因子(注意符號 ! ),它就會被轉換為一個因子。如果它是一個因子,就會輸出一段信息。
ifelse 結構
ifelse 結構是 if-else 結構比較緊湊的向量化版本,其語法為:
ifelse(cond, statement1, statement2)
若 cond 為 TRUE ,則執(zhí)行第一個語句;若 cond 為 FALSE ,則執(zhí)行第二個語句。示例如下:
ifelse(score > 0.5, print("Passed"), print("Failed"))
outcome <- ifelse (score > 0.5, "Passed", "Failed")
在程序的行為是二元時,或者希望結構的輸入和輸出均為向量時,請使用 ifelse 。
switch 結構
switch 根據一個表達式的值選擇語句執(zhí)行。語法為:
switch(expr, ...)
其中的 ... 表示與 expr 的各種可能輸出值綁定的語句。通過觀察代碼清單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"
雖然這個例子比較幼稚,但它展示了 switch 的主要功能。你將在下一節(jié)學習如何使用switch 編寫自己的函數。
用戶自編函數
R的最大優(yōu)點之一就是用戶可以自行添加函數。事實上,R中的許多函數都是由已有函數構成的。一個函數的結構看起來大致如此:
myfunction <- function(arg1, arg2, ... ){
statements
return(object)
}
函數中的對象只在函數內部使用。返回對象的數據類型是任意的,從標量到列表皆可。讓我們看一個示例。
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
請注意,函數 cat() 僅會在輸入的日期格式類型不匹配 "long" 或 "short" 時執(zhí)行。使用一個表達式來捕獲用戶的錯誤輸入的參數值通常來說是一個好主意。
有若干函數可以用來為函數添加錯誤捕獲和糾正功能。你可以使用函數 warning() 來生成一條錯誤提示信息,用 message() 來生成一條診斷信息,或用 stop() 停止當前表達式的執(zhí)行并提示錯誤。20.5節(jié)將會更加詳細地討論錯誤捕捉和調試。
在創(chuàng)建好自己的函數以后,你可能希望在每個會話中都能直接使用它們。附錄B描述了如何定制R環(huán)境,以使R啟動時自動讀取用戶編寫的函數。我們將在第6章和第8章中看到更多的用戶自編函數示例。
你可以使用本節(jié)中提供的基本技術完成很多工作。第20章的內容更加詳細地涵蓋了控制流和其他編程主題。第21章涵蓋了如何創(chuàng)建包。如果你想要探索編寫函數的微妙之處,或編寫可以分發(fā)給他人使用的專業(yè)級代碼,個人推薦閱讀這兩章,然后閱讀兩本優(yōu)秀的書籍,你可在本書末尾的參考文獻部分找到:Venables & Ripley(2000)以及Chambers(2008)。這兩本書共同提供了大量細節(jié)和眾多示例。
函數的編寫就講到這里,我們將以對數據整合和重塑的討論來結束本章。
整合與重構
R中提供了許多用來整合(aggregate)和重塑(reshape)數據的強大方法。在整合數據時,往往將多組觀測替換為根據這些觀測計算的描述性統計量。在重塑數據時,則會通過修改數據的結構(行和列)來決定數據的組織方式。本節(jié)描述了用來完成這些任務的多種方式。
在接下來的兩個小節(jié)中,我們將使用已包含在R基本安裝中的數據框 mtcars 。這個數據集是從Motor Trend雜志(1974)提取的,它描述了34種車型的設計和性能特點(汽缸數、排量、馬力、每加侖汽油行駛的英里數,等等)。要了解此數據集的更多信息,請參閱 help(mtcars) 。
轉置
轉置(反轉行和列)也許是重塑數據集的眾多方法中最簡單的一個了。使用函數 t() 即可對一個矩陣或數據框進行轉置。對于后者,行名將成為變量(列)名。代碼清單5-9展示了一個例子。
使用函數 t() 即可對一個矩陣或數據框進行轉置。
整合數據
在R中使用一個或多個by變量和一個預先定義好的函數來折疊(collapse)數據是比較容易的。調用格式為:
aggregate(x, by, FUN)
其中 x 是待折疊的數據對象, by 是一個變量名組成的列表,這些變量將被去掉以形成新的觀測,
而 FUN 則是用來計算描述性統計量的標量函數,它將被用來計算新觀測中的值。
作為一個示例,我們將根據汽缸數和擋位數整合 mtcars 數據,并返回各個數值型變量的均
值(見代碼清單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
在結果中, Group.1 表示汽缸數量(4、6或8), Group.2 代表擋位數(3、4或5)。舉例來說,擁有4個汽缸和3個擋位車型的每加侖汽油行駛英里數( mpg )均值為21.5。
在使用 aggregate() 函數的時候, by 中的變量必須在一個列表中(即使只有一個變量)。你可 以 在 列 表 中 為 各 組 聲 明 自 定 義 的 名 稱 , 例 如 by=list(Group.cyl=cyl, Group.gears=gear) 。指定的函數可為任意的內建或自編函數,這就為整合命令賦予了強大的力量。但說到力量,沒有什么可以比 reshape2 包更強。
reshape2 包
reshape2 包是一套重構和整合數據集的絕妙的萬能工具。由于它的這種萬能特性,可能學起來會有一點難度。我們將慢慢地梳理整個過程,并使用一個小型數據集作為示例,這樣每一步發(fā)生了什么就很清晰了。由于 reshape2 包并未包含在R的標準安裝中,在第一次使用它之前需要使用 install.packages("reshape2") 進行安裝。
大致說來,你需要首先將數據融合(melt),以使每一行都是唯一的標識符?變量組合。然后將數據重鑄(cast)為你想要的任何形狀。在重鑄過程中,你可以使用任何函數對數據進行整合。將使用的數據集如表5-8所示。

用處不大感覺,先不講了。。。