往期回顧:
R語言實戰(1)——R語言介紹
R語言實戰(2)——創建數據集
R語言實戰(3)——圖形初階
R語言實戰(4) ——數據管理
本期是我們推出《R語言實戰》贈書活動的最後一天啦!
只要你在本文公眾號的R語言實戰系列的第三期--R語言實戰(3) ——圖形初階留言板後臺留言,前五名留言點讚次數最多的,將會獲得《R語言實戰》正版書籍哦,心動不如行動,趕緊轉發朋友圈讓更多朋友為你的留言點讚吧!!
引言:在第4章,我們學習了R中基本的數據集處理方法,本章我們學習一些高級話題。首先,我們將簡單學習R中的多種數學、統計和字符處理函數。接下來,我們將簡略地學習控制流的概念及基本語法。最後,我們將了解數據的整合和概述方法,以及數據集的重塑和重構方法。(後臺回復「R語言實戰」即可獲取二維碼加入R語言實戰學習討論群。)
5-1 一個數據處理難題
假設有一組學生參加了數學、科學和英語考試。我們需要給所有學生確定一個單一的成績衡量指標以及把前20%的學生評定為A,後面20%的學生評定為B,還需要按字母順序對學生排序。這要如何操作呢?
表5-1 學生成績數據
察此數據集,可以發現,首先,三科考試的成績是無法比較的。因為它們的均值和標準差相去甚遠,所以對它們求平均值是沒有意義的。你在組合這些考試成績之前,必須將其變換為可比較的單元。其次,為了評定等級,你需要一種方法來確定某個學生在前述得分上百分比排名。再次,表示姓名的欄位只有一個,為了正確地將其排序,需要將姓和名拆開。但以上每一個任務都可以巧妙地利用R中的數值和字符處理函數完成,接下來讓我們開始本章的學習吧。
5-2 數學和統計函數
5.2.1 數學函數
表5-2 數學函數
5.2.2 統計函數
表5-3 統計函數
均值和標準差的計算:
#簡潔的方式> x <- c(1, 2, 3, 4, 5, 6, 7, 8)> mean(x)[1] 4. 5> sd(x)[1] 2. 449490
#冗長的方法> n <- length(x)> meanx <- sum(x) /n> css <- sum((x - meanx) ^2)> sdx <- sqrt(css / (n-1) )> meanx[1] 4. 5> sdx[1] 2. 4494905.2.3 概率函數
在R中, 概率函數形如 :[dpqr] distribution_abbreviation()
其中第一個字母表示其所指分布的某一方面:
d = 密度函數( density)
p = 分布函數( distribution function)
q = 分位數函數( quantile function)
r = 生成隨機數( 隨機偏差)表5-4 概率分布
比如正態分布的有關函數,如果不指定一個均值和一個標準差,則函數將假定其為標準正態分布(均值為0,標準差為1)。密度函數( dnorm)、分布函數( pnorm)、分位數函數( qnorm)和隨機數生成函數( rnorm)的使用示例如下:
#在區間[–3, 3] 上繪製標準正態曲線, 如圖5-1>x <- pretty(c(-3, 3) , 30) #在區間[–3, 3]上繪製標準正態曲線>y <- dnorm(x)>plot(x, y,type = "l",xlab = "Normal Devi ate",ylab = "Densi ty",yaxs = "i ")
#位於 z=1. 96 左側的標準正態曲線下方面積是多少?>pnorm(1. 96)[1] 0. 97#均值為 500, 標準差為 100 的正態分布的 0. 9 分位點值為多少?>qnorm(. 9, mean=500, sd=100)[1] 628. 16#生成 50 個均值為 50, 標準差為 10 的正態隨機數>rnorm(50, mean=50, sd=10)圖5-1
1. 設定隨機數種子,生成服從正態分布的偽隨機數
#生成服從正態分布的偽隨機數, 函數set. seed() 讓結果可以重現,函數runif() 用來生成0到1區間上服從均勻分布的偽隨機> runif(5)[1] 0. 8725344 0. 3962501 0. 6826534 0. 3667821 0. 9255909> runif(5)[1] 0. 4273903 0. 2641101 0. 3550058 0. 3233044 0. 6584988> set. seed(1234)> runi f(5)[1] 0. 1137034 0. 6222994 0. 6092747 0. 6233794 0. 8609154> set. seed(1234)> runif(5)[1] 0. 1137034 0. 6222994 0. 6092747 0. 6233794 0. 86091542. 生成多元正態數據,mvrnorm( n, mean, sigma)
n是樣本大小, mean為均值向量,而sigma是方差協方差矩陣(或相關矩陣)比如,生成服從多元正態分布的數據:
#第一步, 設定隨機數種子> li brary(MASS)> opti ons(di gi ts=3)> set. seed(1234)#第2步, 指定均值向量、 協方差陣> mean <- c(230. 7, 146. 7, 3. 6)> si gma <- matri x(c(15360. 8, 6721. 2, -47. 1,6721. 2, 4700. 9, -16. 5,-47. 1, -16. 5, 0. 3) , nrow=3, ncol=3)#第3步, 生成數據、> mydata <- mvrnorm(500, mean, si gma)> mydata <- as. data. frame(mydata)> names(mydata) <- c("y", "x1", "x2")第4步, 查看結果> di m(mydata)[1] 500 3> head(mydata, n=10)y x1 x21 98. 8 41. 3 4. 352 244. 5 205. 2 3. 573 375. 7 186. 7 3. 694 -59. 2 11. 2 4. 235 313. 0 111. 0 2. 916 288. 8 185. 1 4. 187 134. 8 165. 0 3. 688 171. 7 97. 4 3. 819 167. 3 101. 0 4. 0110 121. 1 94. 5 3. 765.2.4 字符處理函數
數學和統計函數是用來處理數值型數據的,而字符處理函數是處理文本型數據的。
表5-5 字符處理函數
函 數
描 述
nchar( x)
計算x 中的字符數量
x <- c("ab", "cde", "fghij")
length(x) 返回值為3 (參見表5-7)
nchar(x[3]) 返回值為5substr( x, start, stop)
提取或替換一個字符向量中的子串
x <- "abcdef"
substr(x, 2, 4) 返回值為"bcd"
substr(x, 2, 4) <- "22222" (x 將變成"a222ef" )grep( pattern, x, ignore.
case=FALSE, fixed=FALSE)在x 中搜索某種模式。若fixed=FALSE,則pattern 為一個正則表達式。若
fixed=TRUE,則pattern 為一個文本字符串。返回值為匹配的下標
grep("A",c("b","A","c"),fixed=TRUE) 返回值為2sub( pattern, replacement,
x, ignore.case=FALSE,
fixed=FALSE)在x 中搜索pattern,並以文本replacement 將其替換。若fixed=FALSE,則
pattern 為一個正則表達式。若fixed=TRUE,則pattern 為一個文本字符串。
sub("\\s",".","Hello There") 返回值為Hello.There。注意,"\s" 是一個
用來查找空白的正則表達式;使用"\\s" 而不用"\" 的原因是,後者是R 中的轉義
字符(參見1.3.3 節)strsplit( x, split,
fixed=FALSE)在split 處分割字符向量x 中的元素。若fixed=FALSE,則pattern 為一個正
則表達式。若fixed=TRUE,則pattern 為一個文本字符串
y <- strsplit("abc", "") 將返回一個含有1 個成分、3 個元素的列表,包含
的內容為"a" "b" "c"
unlist(y)[2] 和sapply(y, "[", 2) 均會返回"b"paste(…, sep="")
連接字符串,分隔符為sep
paste("x", 1:3,sep="") 返回值為c("x1", "x2", "x3")
paste("x",1:3,sep="M") 返回值為c("xM1","xM2" "xM3")
paste("Today is", date()) 返回值為Today is Thu Jun 25 14:17:32 2011toupper( x)
大寫轉換
toupper("abc") 返回值為"ABC"tolower( x)
小寫轉換
tolower("ABC") 返回值為"abc"表5-6 其他實用函數#計算
例如:其他使用函數, 舉列子, 還有很多, 感興趣可以去進一步閱讀該書。
#求字符長度>x <- c("ab", "cde", "fghij ")>length(x)[1] 3#提取字符>x <- "abcdef">substr(x, 2, 4)[1] "bcd"5.2.6 將函數應用於矩陣和數據框
1、將函數應用於數據對象:
#求標量5的開方> a <- 5> sqrt(a)[1] 2. 236068#將向量b捨入為指定位的小數> b <- c(1. 243, 5. 654, 2. 99)> round(b)[1] 1 6 3#矩陣的c的log對數和均值> c <- matri x(runi f(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. 4442、將一個函數應用到矩陣的所有行( 列) , MARGIN=1 表示行, MARGIN=2 表示列。
#生成函數> mydata <- matri x(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, tri m=0. 2)[1] -0. 1699 0. 0127 -0. 6475 -0. 6575 0. 23125-3 示例的一種解決方案
5.1 節中提出的問題是:將學生的各科考試成績組合為單一的成績衡量指標,基於相對名次(前20%、下20%、等等)給出從A到F的評分,根據學生姓氏和名字的首字母對花名冊進行排序。下面給出了一種解決方案,一共8步。
步驟1 原始的學生花名冊已經給出了。options(digits=2) 限定了輸出小數點後數字的位數,並且讓輸出更容易閱讀:> options(digits=2)> roster Student Math Science English1 John Davis 502 95 252 Angela Williams 600 99 223 Bullwinkle Moose 412 80 184 David Jones 358 82 155 Janice Markhammer 495 75 206 Cheryl Cushing 512 85 287 Reuven Ytzrhak 410 80 158 Greg Knox 625 95 309 Joel England 573 89 2710 Mary Rayburn 522 86 18步驟2 由於數學、科學和英語考試的分值不同(均值和標準差相去甚遠),在組合之前需要先讓它們變得可以比較。一種方法是將變量進行標準化,這樣每科考試的成績就都是用單位標準差來表示,而不是以原始的尺度來表示了。這個過程可以使用scale() 函數來實現:
> z <- scale(roster[,2:4])> z Math Science English [1,] 0.013 1.078 0.587 [2,] 1.143 1.591 0.037 [3,] -1.026 -0.847 -0.697 [4,] -1.649 -0.590 -1.247 [5,] -0.068 -1.489 -0.330 [6,] 0.128 -0.205 1.137 [7,] -1.049 -0.847 -1.247 [8,] 1.432 1.078 1.504 [9,] 0.832 0.308 0.954[10,] 0.243 -0.077 -0.697步驟3 通過函數mean()來計算各行的均值以獲得綜合得分,並使用函數cbind() 將其添加到花名冊中:
> score <- apply(z, 1, mean)> roster <- cbind(roster, score)> roster Student Math Science English score1 John Davis 502 95 25 0.5592 Angela Williams 600 99 22 0.9243 Bullwinkle Moose 412 80 18 -0.8574 David Jones 358 82 15 -1.1625 Janice Markhammer 495 75 20 -0.6296 Cheryl Cushing 512 85 28 0.3537 Reuven Ytzrhak 410 80 15 -1.0488 Greg Knox 625 95 30 1.3389 Joel England 573 89 27 0.69810 Mary Rayburn 522 86 18 -0.177步驟4 函數quantile()給出了學生綜合得分的百分位數。可以看到,成績為A的分界點為0.74, B的分界點為0.44,等等。
> y <- quantile(roster$score, c(.8,.6,.4,.2))> y 80% 60% 40% 20% 0.74 0.44 -0.36 -0.89步驟5 通過使用邏輯運算符,你可以將學生的百分位數排名重編碼為一個新的類別型成績
變量。下面在數據框roster中創建了變量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 grade1 John Davis 502 95 25 0.559 B2 Angela Williams 600 99 22 0.924 A3 Bullwinkle Moose 412 80 18 -0.857 D4 David Jones 358 82 15 -1.162 F5 Janice Markhammer 495 75 20 -0.629 D6 Cheryl Cushing 512 85 28 0.353 C7 Reuven Ytzrhak 410 80 15 -1.048 F8 Greg Knox 625 95 30 1.338 A9 Joel England 573 89 27 0.698 B10 Mary Rayburn 522 86 18 -0.177 C步 驟 6 你 將 使 用 函 數 strsplit()以 空 格 為 界 把 學 生 姓 名 拆 分 為 姓 氏 和 名 字 。把strsplit() 應用到一個字符串組成的向量上會返回一個列表:
> name <- strsplit((roster$Student), " ")> name[[1]][1] "John" "Davis"[[2]][1] "Angela" "Williams"[[3]][1] "Bullwinkle" "Moose"[[4]][1] "David" "Jones"[[5]][1] "Janice" "Markhammer"[[6]][1] "Cheryl" "Cushing"[[7]][1] "Reuven" "Ytzrhak"[[8]][1] "Greg" "Knox"[[9]][1] "Joel" "England"[[10]][1] "Mary" "Rayburn"步驟7 用函數sapply()提取列表中每個成分的第一個元素,放入一個儲存名字的向量Firstname,並提取每個成分的第二個元素,放入一個儲存姓氏的向量Lastname。"["是一個可以提取某個對象的一部分的函數——在這裡它是用來提取列表name各成分中的第一個或第二個元素的。你將使用cbind() 把它們添加到花名冊中。由於已經不再需要student變量,可以將其丟棄(在下標中使用–1 )。
> Firstname <- sapply(name, "[", 1)> Lastname <- sapply(name, "[", 2)> roster <- cbind(Firstname, Lastname, roster[,-1])> roster Firstname Lastname Math Science English score grade1 John Davis 502 95 25 0.559 B2 Angela Williams 600 99 22 0.924 A3 Bullwinkle Moose 412 80 18 -0.857 D4 David Jones 358 82 15 -1.162 F5 Janice Markhammer 495 75 20 0.629 D6 Cheryl Cushing 512 85 28 0.353 C7 Reuven Ytzrhak 410 80 15 -1.048 F8 Greg Knox 625 95 30 1.338 A9 Joel England 573 89 27 0.698 B10 Mary Rayburn 522 86 18 -0.177 C步驟8 最後,可以使用函數order() 依姓氏和名字對數據集進行排序:
> roster[order(Lastname,Firstname),] Firstname Lastname Math Science English score grade6 Cheryl Cushing 512 85 28 0.35 C1 John Davis 502 95 25 0.56 B9 Joel England 573 89 27 0.70 B4 David Jones 358 82 15 -1.16 F8 Greg Knox 625 95 30 1.34 A5 Janice Markhammer 495 75 20 -0.63 D3 Bullwinkle Moose 412 80 18 -0.86 D10 Mary Rayburn 522 86 18 -0.18 C2 Angela Williams 600 99 22 0.92 A7 Reuven Ytzrhak 410 80 15 -1.05 F5-4 控制流
控制流是為了可以重複復執行某些語句, 或者在滿足特定條件的情況下執行另外的語句。
為了理解貫穿本節的語法示例,請牢記以下概念:
語句( statement)是一條單獨的R語句或一組複合語句(包含在花括號{ } 中的一組R
語句,使用分號分隔);
條件( cond)是一條最終被解析為真( TRUE)或假( FALSE)的表達式;
表達式( expr)是一條數值或字符串的求值語句;
序列( seq)是一個數值或字符串序列。5-4-1、重複和循環,f o r 結構和w h i l e結構
# for循環重複地執行一個語句, 直到某個變量的值不再包含在序列seq中為止, 如單詞Hello被輸出了10次。>for (i in 1: 10) print("Hello")
# while循環重複地執行一個語句, 直到條件不為真為止, 如單詞Hello被輸出了10次。>i <- 10>while (i > 0) {print("Hello"); i <- i - 1}; i <- i - 1}
5-4-2、條件執行1. i f - e l s e結構,制結構if-else在某個給定條件為真時執行語句。
if (cond) statement
if (cond) statement1 else statement2if (is.character(grade)) grade <- as.factor(grade)#如果grade是一個字符向量,它就會被轉換為一個因子
if (!is.factor(grade)) grade <- as.factor(grade) else print("Grade already is a factor")#如果grade不是一個因子(注意符號! ),它就會被轉換為一個因子2. ifelse結構
ifelse結構是if-else結構比較緊湊的向量化版本,其語法為:
ifelse( cond, statement1, statement2)若cond為TRUE,則執行第一個語句;若cond為FALSE,則執行第二個語句。
ifelse(score > 0.5, print("Passed"), print("Failed"))outcome <- ifelse (score > 0.5, "Passed", "Failed")3. switch 結構
switch根據一個表達式的值選擇語句執行。語法為:
switch( expr, ...)> 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"
5-5 整合(aggregate)與重構(reshape)樣本示例的數據主要來自R基本安裝中的數據框mtcars。它描述了34種車型的設計和性能特點(汽缸數、排量、馬力、每加侖汽油行駛的英裡數,等等)。
5-5-1、數據集的轉置(反轉行和列),使用函數t()即可對一個矩陣或數據框進行轉置。
#用R基礎包的基礎函數mtacars來反轉行和列> cars <- mtcars[1: 5, 1: 4]> cars mpg cyl disp hpMazda RX4 21.0 6 160 110Mazda RX4 Wag 21.0 6 160 110Datsun 710 22.8 4 108 93Hornet 4 Drive 21.4 6 258 110Hornet Sportabout 18.7 8 360 175> t(cars) Mazda RX4 Mazda RX4 Wag Datsun 710 Hornet 4 Drive Hornet Sportaboutmpg 21 21 22.8 21.4 18.7cyl 6 6 4.0 6.0 8.0disp 160 160 108.0 258.0 360.0hp 110 110 93.0 110.0 175.05-5-2、整合數據
在R中使用一個或者多個by變量和一個預先定義好的函數來摺疊(collapse)數據。aggregate(x, by, FUN)。x是數據對象,by是一個變量名組成的列表。而FUN則是用來計算描述性統計量的標量函數。
下面的例子將根據汽缸數和擋位數整合mtcars數據,並返回各個數值型變量的均值。
#設置數據為小數點後3位> options(digits=3)#加載mtcars數據>attach(mtcars)#按照cyl和gear> aggdata <-aggregate(mtcars, by=list(cyl,gear), FUN=mean, na.rm=TRUE)> aggdataGroup. 1 Group. 2 mpg cyl disp hp drat wt qsec vs am gear carb1 4 3 21.5 4 120 97 3.70 2.46 20.0 1.0 0.00 3 1.002 6 3 19.8 6 242 108 2.92 3.34 19.8 1.0 0.00 3 1.003 8 3 15.1 8 358 194 3.12 4.10 17.1 0.0 0.00 3 3.084 4 4 26.9 4 103 76 4.11 2.38 19.6 1.0 0.75 4 1.505 6 4 19.8 6 164 116 3.91 3.09 17.7 0.5 0.50 4 4.006 4 5 28.2 4 108 102 4.10 1.83 16.8 0.5 1.00 5 2.007 6 5 19.7 6 145 175 3.62 2.77 15.5 0.0 1.00 5 6.008 8 5 15.4 8 326 300 3.88 3.37 14.6 0.0 1.00 5 6.00注意:在使用aggregate()函數的時候,by中的變量必須在一個列表中(即使只有一個變量),指定的函數可為任意的內建或自編函數。
5-5-3、reshape2 包是一套重構和整合數據集的萬能工具
在使用reshape包時,首先將數據融合(melt),以使每一行都是唯一的標識符變量組合。然後將數據重鑄( cast)為你想要的任何形狀。在重鑄過程中,你可以使用任何函數對數據進行整合。例子中使用的數據集如表5-7所示。
表5-7 原始數據
1. 融合
數據集的融合是指每個測量變量獨佔一行,行中帶有要唯一確定這個測量所需的標識符變量。
#融合數據,melt()函數>install. packages("reshape2")#安裝reshape包>library(reshape2)#加載reshape包>mydata <- data.frame( ID = c(1,1,2,2), Time = c(1,2,1,2), X1 = c(5,3,6,2), X2 = c(6,5,1,4)) #按照表5-7創建數據匡並且賦予給mydata>md <- melt(mydata, id=c("ID","Time"))#按照ID和TIme融合數據並且賦予f誒ms> melt(mydata, id = c("ID","Time")) ID Time variable value1 1 1 X1 52 1 2 X1 33 2 1 X1 64 2 2 X1 25 1 1 X2 66 1 2 X2 57 2 1 X2 18 2 2 X2 42.重鑄
dcast() 函數讀取已融合的數據, 並使用你提供的公式和一個( 可選的) 用於整合數據的函
數將其重塑。調用格式為:newdata < dcast( md, formula, fun. aggregate)示例如下:
> dcast(md,ID ~ variable, mean)#執行整合 ID X1 X21 1 4 5.52 2 4 2.5
> dcast(md, Time ~ variable, mean)#執行整合 Time X1 X21 1 5.5 3.52 2 2.5 4.5
> dcast(md, ID ~ Time, mean)#執行整合 ID 1 21 1 5.5 42 2 3.5 3
> dcast(md, ID + Time ~ variable) # 不執行整合 ID Time X1 X21 1 1 5 62 1 2 3 53 2 1 6 14 2 2 2 4
> dcast(md, ID + variable ~ Time)#不執行整合 ID variable 1 21 1 X1 5 32 1 X2 6 53 2 X1 6 24 2 X2 1 4> dcast(md, ID ~ variable+Time)#不執行整合ID X1_1 X1_2 X2_1 X2_2 1 5 3 6 5 2 6 2 1 4圖5-2 使用函數melt() 和dcast() 重塑數據
小結:本章總結了數種用於處理數據的數學、統計和概率函數。我們學習了如何將這些函數應用到範圍廣泛的數據對象上,其中包括向量、矩陣和數據框。也學習了控制流結構的使用方法:用循環重複執行某些語句,或用分支在滿足某些特定條件時執行另外的語句。最後,我們探索了摺疊、整合以及重構數據的多種方法。在接下來的幾章中,我們將學習多種將數據轉化為信息的統計方法和圖形方法。
後臺回復「R語言實戰」即可獲取二維碼加入R語言實戰學習討論群。