感謝Robert I.Kabacoff 著作本書,同時感謝高濤、肖楠、陳鋼編譯此書。
最近在學習《R語言實戰(zhàn)》,特將學習過程記錄下來,供各位朋友參考,雖說是筆記,但是90%是書中內(nèi)容,另外10%是自己偶爾冒出的一點點想法的記錄和一些疑問,希望互相探討。末尾有本章的代碼清單下載地址,與各位交流,還是提倡按照書中內(nèi)容把代碼一個個敲出來。
第五章 高級數(shù)據(jù)管理
本章內(nèi)容
數(shù)學和統(tǒng)計函數(shù)
字符處理函數(shù)
循環(huán)和條件執(zhí)行
自編函數(shù)
數(shù)據(jù)整合與重塑
5.1 一個數(shù)據(jù)處理難題
一個難題:
- 三科成績是無法比較的。
- 需要一種方法來確定某個學生在前述得分上百分比排名。
- 表示姓名的字段只有一個,需要將姓和名拆開。
5.2 數(shù)值和字符處理函數(shù)
本節(jié)將學習R中作為數(shù)據(jù)處理基石的函數(shù),它們可分為數(shù)值(數(shù)學、統(tǒng)計、概率)函數(shù)和字符處理函數(shù)。
5.2.1 數(shù)學函數(shù)
書中表5-2列出了常用的數(shù)學函數(shù)和簡短的用例。(P83頁)
值得一提的是round(x, digits = n) 表示將x舍入為指定位的小數(shù)。
signif(x, digits = n)表示將x舍入為指定的有效數(shù)字位數(shù)。
對比如下
round(3.475, digits = 2)
返回值為3.48。(保留兩位小數(shù))
signif(3.475, n= = 2)
返回值為3.5。(保留兩位有效數(shù)字)
表5-2中的示例將數(shù)學函數(shù)應用到了標量(單獨的數(shù)值)上。當這些函數(shù)被應用于數(shù)值向量、矩陣或者數(shù)據(jù)框時,它們會作用于其中每一個獨立的值,然后重新生成一個向量、矩陣或數(shù)據(jù)框。
5.2.2 統(tǒng)計函數(shù)
方差:表示點的離散程度。方差越小,離散程度越低,越接近平均值。公式表達為:
S=[ (x1-x)^2+(x2-x)^2+(x3-x)^2+……+(xn-x)^2]
標準差是方差開根號。
什么是絕對中位差、分位數(shù)、值域、滯后差分、進化中心化、標準化?
代碼清單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中元素的數(shù)量):
(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) summ((x - meanx)^2)對((x - meanx)^2)的所有元素求和,結果為42。
數(shù)據(jù)的標準化
將一組數(shù)據(jù),投射到很小區(qū)間內(nèi)的方法,便于分析。標準化之后的數(shù)據(jù)有這樣的特征:均值為0,標準差為1。
函數(shù)scale()對矩陣或數(shù)據(jù)框的制定了進行均值為0,標準差為1的標準化。
newdata <- scale(mydata
要對每一列進行任意均值和標準差的標準化,可以使用如下代碼:
newdata <- scale(mydata) * SD + M
其中SD表示想要的標準差,M表示想要的均值。注意,在非數(shù)值型的列上用scale()函數(shù)會報錯。如果要對指定列而不是整個數(shù)據(jù)框或矩陣進行標準化,可以使用如下代碼:
newdata <- transform(mydata, myvar = scale(myvar) * 10 + 50)
此句將變量myvar標準化為均值50、標準差為10的變量。
問:是否有能將數(shù)據(jù)落到指定區(qū)間的方法?這種方法與書中的“指定均值和標準差”的方法有何異同?
5.2.3 概率函數(shù)
概率函數(shù)通常用來生成特征已知的模擬數(shù)據(jù),以及在用戶編寫的統(tǒng)計函數(shù)中計算概率值。在R中,概率函數(shù)形如:
[dpqr]distribution_abbreviation()
其中第一個字母表示其所指分布的某一方面:
d = 密度函數(shù)(density)
p = 分布函數(shù)(distribution function)
q = 分位數(shù)函數(shù)(quantile function)
r = 生成隨機數(shù)(隨機偏差)
1. 設定隨機數(shù)種子
每次生成偽隨機數(shù)的時候,函數(shù)都會使用一個不同的種子,可以通過函數(shù)set.seed()顯式指定這個種子,讓結果可以重現(xiàn)(reproducible)。代碼清單5-2給出了一個示例,這里的函數(shù)runif()用來生成0到1區(qū)間上服從均勻分布的偽隨機數(shù)。
設置種子之后,如果種子是同一個數(shù),那么每次產(chǎn)生的隨機數(shù),求和、均值、標準差都相同。這幾個參數(shù)與種子數(shù)之間有什么關系?
注意:如果需要設定種子,那么在每一次生成隨機數(shù)之前都要設定
2. 生成多元正態(tài)數(shù)據(jù)
蒙特卡洛方法:也稱統(tǒng)計模擬方法,是以概率統(tǒng)計理論為指導的一類非常重要的數(shù)值計算方法。是指使用隨機數(shù)(或更常見的偽隨機數(shù))來解決很多計算問題的方法。與它對應的是確定性算法。蒙特卡洛方法百度百科解釋
MASS包可以讓獲取來自給定均值向量和協(xié)方差陣的多元正態(tài)分布的數(shù)據(jù)變得更容易。調用格式為:
mvrnorm(n, mean, sigma)
其中n是你想要的樣本大小,mean為均值向量,而sigma是方差-協(xié)方差矩陣(或相關矩陣)。
5.2.4 字符處理函數(shù)
書中P89-P90頁,表5-6 給出了字符處理函數(shù)表。
請注意,函數(shù)grep()、sub()和strsplit()能夠搜索某個文本字符串(fixed = TRUE)或者某個正則表達式(fixed = FALSE,默認值為FALSE)。正則表達式為文本模式的匹配提供了一套清晰而簡練的語法。正則表達式 - 語法或者正則表達式語法。
例如^[hc]?at
可以匹配任意0個或1個以h或c開頭、后接at的字符串。因此,此表達式可以匹配hat、cat和at,但不會匹配bat。
5.2.5 其他實用函數(shù)
轉義符“\”,\n表示新行,\t為制表符,'為單引號,\b為退格。
5.2.6 將函數(shù)應用于矩陣和數(shù)據(jù)框
R函數(shù)的諸多有趣特性之一就是他們可以應用到一系列的數(shù)據(jù)對象上,包括向量、標量、矩陣、數(shù)組和數(shù)據(jù)框。
R中提供了一個apply()函數(shù),可將一個任意函數(shù)“應用”到矩陣、數(shù)組、數(shù)據(jù)框的任何維度上。其格式為:
apply(x, MARDIN, FUN, …)
其中,x為數(shù)據(jù)對象,MARGIN是維度的下標,F(xiàn)UN是由你指定的函數(shù),而…則包含了想傳遞給FUN的參數(shù)。在矩陣或數(shù)據(jù)框中,MARGIN=1表示行,MARGIN=2表示列。
mydata <- matrix(rnorm(30), nrow = 6)
mydata
apply(mydata, 1, mean)
apply(mydata, 2, mean)
apply(mydata, 2, mean, trim =0.2)
最后一行表示計算每一列的截尾均值,忽略了最高和最低的20%。
apply()可把函數(shù)應用到數(shù)組的某個維度上,而lapply()和aspply()則可將函數(shù)應用到列表(list)上。
5.3 數(shù)據(jù)處理難題的一套解決方案
針對5.1節(jié)提出的問題,本節(jié)給出了一個解決方案。
-
options(digits = 2)
-
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)
-
z <- scale(roster[ , 2:4])
-
score <- apply(z, 1, mean)
-
roster <- cbind(roster, score)
-
y <- quantile(score, c(0.8, 0.6, 0.4, 0.2))
y
-
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"
-
name <- strsplit((roster$Student), " ")
-
name
-
lastname <- sapply(name, "[", 2)
-
firstname <- sapply(name, "[", 1)
-
roster <- cbind(firstname, lastname, roster[, -1])
-
roster <- roster[order(lastname, firstname),]
-
roster
以下是解讀過程:
限定了輸出小數(shù)點后數(shù)字的位數(shù),并且讓輸出更容易閱讀。
輸入學生姓名。
輸入數(shù)學成績。
輸入科學成績。
輸入英語成績。
創(chuàng)建一個數(shù)據(jù)框roster,其中的變量分別是:姓名、數(shù)學成績、科學成績、英語成績。
將數(shù)據(jù)框roster的第二到四列標準化,并保存到z中。標準化的原因是:由于數(shù)學、科學、和英語考試得到分值不同(均值和標準差相去甚遠),在組合之前需要讓他們變得可以比較。標準化就是讓每科的成績都用單位標準差來表示,而不是以原始的尺度來表示了。
求z中所有行的均值,并保存到score中。
將roster和score按列合成為一個矩陣,保存到roster中。
分別求score的20%分位數(shù),40%分位數(shù),60%分位數(shù),80%分位數(shù)。
查看score的20%分位數(shù),40%分位數(shù),60%分位數(shù),80%分位數(shù)。分別是
80% : 0.91
60% : 0.32
40% : -0.36
20% : -0.88
將大于等于80%,即大于等于0.91的成績評為A。
將60%(包含)至80%(不包含),即位于0.32(包含)和0.91(不包含)之間的成績,評為B。
將40%(包含)至60%(不包含),即位于-0.36(包含)和0.32(不包含)之間的成績,評為C。
將20%(包含)至40%(不包含),即位于-0.88(包含)和-0.36(不包含)之間的成績,評為D。
將小于20%,即小于-0.88的成績評為F。
使用strsplit()函數(shù),將原數(shù)據(jù)框中的姓名,按照空格分成兩列,并保存到新的列表name中。
查看新列表name。
選取列表中第二列,保存到一個新變量lastname中。
選取列表中第一列,保存到一個新變量firstname中。
將firstname和lastname添加到花名冊roster中,并將原本第一個變量Student刪除,(roster[, -1])。
使用order()函數(shù)依照firstname和lastname對數(shù)據(jù)框進行排序。
查看數(shù)據(jù)框roster。
5.4 控制流
為了理解貫穿本節(jié)的語法示例,請牢記以下概念:
語句(statement)是一條單獨的R語句或一組符合語句(包含在花括號{}中的一組R語句,使用分號分隔);
條件(cond)是一條最終被解析為真(TRUE)或假(FALSE)得表達式;
表達式(expr)是一條數(shù)值或字符串的求值語句;
序列(seq)是一個數(shù)值或字符串序列。
5.4.1 重復和循環(huán)
循環(huán)結構重復地執(zhí)行一個或一系列語句,知道某個條件不為真為止。循環(huán)結構包括for和while結構。
1. for結構
for循環(huán)重復地執(zhí)行一個語句,直到某個變量不包不再包含在序列seq中為止。語法為:
for(var in seq) statement
在下例中:
for (i in 1:10) print ("Hello")
單詞Hello被輸出了10次。
2. while結構
while循環(huán)重復地執(zhí)行一個語句,直到條件不為真為止。語法為“
while (cond) statement
第二個例子,代碼:
i <- 10
while (i < 0) {pirnt ("Hello"); i <- i - 1}
請確保括號內(nèi)while的條件語句能夠改變,即讓它在某個時刻不再為真——否則循環(huán)將永不停止!
5.4.2 條件執(zhí)行
在條件執(zhí)行結構中,一條或一組語句僅在滿足一個指定條件時執(zhí)行。條件執(zhí)行結構包括if-else、ifelse和switch。
1. if-else結構
控制結構if-else在某個給定條件為真時執(zhí)行語句。也可以同時在條件為假時執(zhí)行另外的語句,語法為:
if (cond) statement
if (cond) statement1 else statement2
示例如下:
if (is.character(grade)) grede <- as.factor(grade)
當grade為字符型向量時,將其轉化為因子。
if (!is.factor(grade)) grade <- as.factor else print ("Grade already is a factor")
當grade不是因子時,將其轉化為因子,當其是因子是,輸出一段信息。
2. ifelse結構
ifelse是if-else比較緊湊的向量化結構。語法為:
ifelse (cond, statement1, statement2)
當條件為真時,執(zhí)行第一條語句,當條件為假時,執(zhí)行第二條語句。
示例如下:
ifelse (score > 0.5, print("Passed"), print("Failed"))
outcome <- ifels (score > 0.5, "Passed", "Failed")
3. switch結構
switch根據(jù)一個表達式的值選擇語句執(zhí)行。語法如下:
switch(expr, …)
其中的…表示與expr的各種可能輸出值綁定的語句。
5.5 用戶自編函數(shù)
R最大的優(yōu)點之一就是用戶可以自行添加函數(shù)。R中許多函數(shù)都是由已有函數(shù)構成的。一個函數(shù)的結構看起來大致如此:
myfunction <- function(arg1, arg2, …){
statements
return(object)
}
在第二個自編函數(shù)的示例中,函數(shù)cat()僅會在輸入的日期格式類型不匹配“l(fā)ong”或“short”時執(zhí)行。使用一個表達式來捕獲錯誤輸入的參數(shù)值通常是一個好主意
有若干函數(shù)可以用來為函數(shù)添加錯誤捕獲和糾正功能。warning()可以生成一條錯誤提示信息。message()可以生成一條診斷信息。stop()可以停止當前表達式的執(zhí)行并提示錯誤。
小提示
一旦開始編寫無論任何長度和復雜度的函數(shù),優(yōu)秀調試工具的重要性都會凸顯出來。R中有許多實用的內(nèi)建調試函數(shù),也有許多用戶貢獻包提供了額外的功能。
關于這個話題,一份優(yōu)秀的參考資料是Duncan Murdoch整理的“Debugging in R”。
5.6 整合與重構
在整合數(shù)據(jù)時,往往將多組觀測替換為根據(jù)這些觀測計算的描述性統(tǒng)計量。在重塑數(shù)據(jù)時,則會通過修改數(shù)據(jù)的結構(行和列)來決定數(shù)據(jù)的組織方式。
5.6.1 轉置
轉置(反轉行和列)也許是重塑數(shù)據(jù)集的眾多方法種最簡單的一個了。使用函數(shù)t()即可對一個矩陣或者數(shù)據(jù)框進行轉置。對于后者,行名將成為變量(列)名。
5.6.2 整合數(shù)據(jù)
在R中使用一個或多個by變量和一個預先定義好的函數(shù)來折疊(collapse)數(shù)據(jù)時比較容易得多。其調用格式為:
aggregate(x, by, FUN)
其中x是待折疊的數(shù)據(jù)對象,by是一個變量名組成的列表,這些變量將被去掉以形成新的觀測,而FUN則是用來計算描述性統(tǒng)計量的標量函數(shù),它將用來計算新觀測中的值。
5.6.3 reshape包
首先將數(shù)據(jù)“融合”(melt),以使每一行都是一個唯一的標識符-變量組合。然后將數(shù)據(jù)“重鑄”為你想要的任何形狀。在重鑄過程中你可以使用任何函數(shù)對數(shù)據(jù)進行整合。
在這個數(shù)據(jù)集中,測量(measurement)是指最后兩列中的值(5、6、3、5、6、1、2、4)。每個測量都能夠被標識符(在本例中,標識符是指ID、Time以及觀測屬于X1還是X2)唯一地確定。
1. 融合
使用以下代碼:
install.package("reshape")
library(reshape)
md <- melt(mydata, id = (c("id", "time")))
2. 重鑄
cast()函數(shù)讀取已融合的數(shù)據(jù),并使用你提供的公式和一個(可選的)用于整合數(shù)據(jù)的函數(shù)將其重塑。調用格式為:
newdata <- cast(md, formula, FUN)
其中的md為已融合的數(shù)據(jù),formula描述了想要的最后結果,而FUN是(可選的)數(shù)據(jù)整合函數(shù)。其接受公式形如:
rowvar1 + rowvar2 + … + ~ colvar1 + colvar2 + …
在這一公式中,rowvar1 + rowvar2 + … 定義了要劃掉的變量集合,以確定各行的內(nèi)容,而colvar1 + colvar2 + … 則定義了要劃掉的、確定各列內(nèi)容的變量集合。
<font color=gray size=3 face="微軟雅黑">作者注:本節(jié)(5.6.3)由于第一個示例代碼就無法得到書中的結果,所以,本節(jié)學習筆記實際上只摘取了書中的內(nèi)容,未理解其中的意義。主要是由于示例的表5-8 不知道何種形式的數(shù)據(jù)集,書中未給出,在嘗試了矩陣、數(shù)據(jù)框、列表創(chuàng)建相同內(nèi)容的數(shù)據(jù)集之后,均失敗。</font>