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