最近在學(xué)習(xí)R,主要在看《R語(yǔ)言入門(mén)與實(shí)踐》這本書(shū)(Garrett Grolemund著,馮凌秉譯,人民郵電出版社),做了一些筆記,分享給大家

目錄

這篇筆記的PDF版本:https://pan.baidu.com/s/1PB874TCKAWOP65TM6euFPg
提取碼:08kx
第一部分
第一章 R基礎(chǔ)
1.1 下載、安裝R和RStudio
- R下載地址:https://cran.r-project.org/mirrors.html
- 選擇對(duì)應(yīng)操作系統(tǒng)的版本,下載并安裝即可
- 安裝成功后的運(yùn)行效果:

- RStudio下載地址:https://rstudio.com/products/rstudio/download/#download
- 安裝成功后,運(yùn)行的效果:

1.2 將R當(dāng)成一個(gè)計(jì)算器
- 可以在R中進(jìn)行各種數(shù)學(xué)運(yùn)算,例如:
> 5*(7^2+1)-3
[1] 247
- 優(yōu)先級(jí):冪運(yùn)算 > 乘除運(yùn)算 > 加減運(yùn)算
- 括號(hào)內(nèi)的運(yùn)算將優(yōu)先進(jìn)行,存在多個(gè)括號(hào)時(shí),優(yōu)先計(jì)算最內(nèi)層的括號(hào)
1.3 對(duì)象
- 我們可以使用賦值符號(hào)
<-(=也可以,不過(guò)更推薦<-)將數(shù)值儲(chǔ)存在一個(gè)對(duì)象中,例如:
> a <- 5*(7^2+1)-3
> a
[1] 247
- 對(duì)象名不能以數(shù)字或一些特殊字符開(kāi)頭
- 對(duì)象名區(qū)分大小寫(xiě),例如name和Name是不同的對(duì)象
- 如果把新的內(nèi)容賦值給一個(gè)已存在的變量,則將會(huì)覆蓋掉變量的原始值
- 使用
ls()查看已經(jīng)命名的對(duì)象,使用rm(對(duì)象名)來(lái)刪除某個(gè)對(duì)象 - 【注意】?jī)H輸入
rm()會(huì)刪除所有對(duì)象,慎用!
1.4 函數(shù)
- R中存在各種各樣功能強(qiáng)大的函數(shù),即執(zhí)行某種任務(wù)的語(yǔ)句。例如,mean函數(shù)的作用是計(jì)算算術(shù)平均值,round函數(shù)的作用是四舍五入,sample函數(shù)的作用是從一個(gè)向量中抽取size個(gè)元素并返回,如下:
> mean(1:6)
[1] 3.5
> round(mean(1:6))
[1] 4
> sample(1:6,size=2)
[1] 5 3
- 正如上述例子中的
round(mean(1:6))所展示的,函數(shù)可以嵌套 - 通過(guò)args函數(shù)查看函數(shù)的參數(shù),例如:
> args(sample)
function (x, size, replace = FALSE, prob = NULL)
NULL
- 我們可以根據(jù)需要,自己編寫(xiě)自定義函數(shù),例如下面是一個(gè)計(jì)算t檢驗(yàn)效應(yīng)量的函數(shù)
> #自定義函數(shù):計(jì)算t檢驗(yàn)的效應(yīng)量
> conhens_d <- function(m1, m2, s) {
+ d <- (m1-m2)/s
+ return(d)
+ }
> #調(diào)用自定義函數(shù)
> conhens_d(23,21,2.5)
[1] 0.8
- 一些相關(guān)的信息:
-
conhens_d是函數(shù)名 -
m1、m2、s是函數(shù)中的參數(shù),只在函數(shù)內(nèi)部有效,如果想設(shè)置默認(rèn)值,則可以在參數(shù)后面添加等號(hào)和數(shù)值,例如m1=23 - 花括號(hào)中的代碼是函數(shù)的主體
- 通過(guò)
return()返回值 -
#是注釋符,以#開(kāi)頭的代碼不會(huì)運(yùn)行 - 如果指令太長(zhǎng),對(duì)于一條明顯未完整的指令,敲一下回車(chē)鍵,就可以換行(會(huì)顯示一個(gè)“+”,提示用戶繼續(xù)輸入)
-
1.5 腳本
- 將代碼寫(xiě)在腳本文件中,隨后我們可以保存該文件,以便將來(lái)使用,在RStudio中的腳本窗口存在兩個(gè)運(yùn)行腳本的按鈕:
- Source:運(yùn)行腳本中的全部代碼,對(duì)應(yīng)的語(yǔ)句是
source('腳本名.R')(另一種方法是Ctrl+A然后Ctrl+Enter) - Run:運(yùn)行腳本中的某一行(快捷鍵Crtl+Enter)
- Source:運(yùn)行腳本中的全部代碼,對(duì)應(yīng)的語(yǔ)句是

第二章 R包與幫助系統(tǒng)
2.1 R包
- 許多大學(xué)教授、程序員和統(tǒng)計(jì)學(xué)家使用R設(shè)計(jì)了數(shù)據(jù)分析的工具,并將這些工具無(wú)償提供給他人使用,這些工具就屬于R包。
- R包類(lèi)似于C、C++和JavaScript中的庫(kù)(library),或者Python中的包(package)
- 例如,ggplot2是一個(gè)很受歡迎的可視化包,為了使用這個(gè)包,我們首先需要確保計(jì)算機(jī)已經(jīng)連接互聯(lián)網(wǎng),并在RStudio中的命令行輸入
install.packages()命令來(lái)安裝包
install.packages('ggplot2')
- 但該步驟只是將ggplot2安裝到我們的計(jì)算機(jī)中,想要使用這個(gè)包的函數(shù),首先需要加載這個(gè)包
library('ggplot2')
- 現(xiàn)在,試著輸入
qplot,這是ggplot2包中的一個(gè)函數(shù),輸入后將會(huì)顯示該函數(shù)的源代碼
> qplot
function (x, y, ..., data, facets = NULL, margins = FALSE, geom = "auto",
xlim = c(NA, NA), ylim = c(NA, NA), log = "", main = NULL,
xlab = NULL, ylab = NULL, asp = NA, stat = NULL, position = NULL)
{
...
}
如果未使用library函數(shù)導(dǎo)入ggplot2包便直接輸入
qplot,結(jié)果會(huì)顯示錯(cuò)誤: 找不到對(duì)象'gplot',這是因?yàn)楸仨氁d入一個(gè)包才能使用其中的函數(shù)現(xiàn)在試著輸入如下的代碼,體驗(yàn)一下
qplot的功能吧
> x <- c(-1,-.8,-.6,-.4,-.2,0,.2,.4,.6,.8,1)
> y <- x^3
> qplot(x,y)
-
qplot的含義是“quick plot”,其作用是依據(jù)兩個(gè)相同長(zhǎng)度的向量來(lái)繪制散點(diǎn)圖,對(duì)于上述的例子,結(jié)果如下

- 如果想更新R包,則可以使用
update.packages()命令,如果想同時(shí)更新多個(gè)R包,只需要把R包名稱(chēng)放入到一個(gè)向量中(第二章會(huì)介紹什么是向量),例如:
update.packages(c('ggplot2','reshape2','dplyr'))
- 如果想同時(shí)安裝多個(gè)R包,也可以使用這樣的方法
2.2 從幫助頁(yè)面獲取幫助
R的函數(shù)非常多,想要記住和學(xué)會(huì)所有函數(shù)幾乎是不可能的,所幸每個(gè)R函數(shù)都有自己的幫助頁(yè)面,在RStudio中輸入
?函數(shù)名即可打開(kāi)相應(yīng)的頁(yè)面例如:
?qplot
- 此時(shí)將打開(kāi)該函數(shù)的幫助頁(yè)面(點(diǎn)擊下圖所示的按鈕可以打開(kāi)單獨(dú)的頁(yè)面)

- 一般來(lái)說(shuō),幫助頁(yè)面包含了以下信息
| 簡(jiǎn)介 | |
|---|---|
| 描述(Description) | 一段簡(jiǎn)短的有關(guān)該函數(shù)功能的描述 |
| 用法(Usage) | 告訴我們?nèi)绾捂I入該函數(shù)和相應(yīng)的參數(shù)名 |
| 參數(shù)(Arguments) | 列出該函數(shù)所包含的所有參數(shù)、各參數(shù)接受的賦值類(lèi)型以及參數(shù)的作用 |
| 相關(guān)細(xì)節(jié)(Details) | 對(duì)函數(shù)的工作原理的進(jìn)一步描述,通常會(huì)提到一些使用函數(shù)時(shí)的注意事項(xiàng) |
| 返回值(Value) | 一段關(guān)于該函數(shù)運(yùn)行后的返回值的簡(jiǎn)短描述 |
| 另請(qǐng)參閱(See also) | 與該函數(shù)相關(guān)的函數(shù)的列表 |
| 示例(Examples) | 確保可以無(wú)錯(cuò)運(yùn)行的代碼示例 |
- 此外,如果我們不小心忘記了某個(gè)函數(shù)的確切名稱(chēng),可以使用
??函數(shù)名的關(guān)鍵詞來(lái)查找相關(guān)的函數(shù),例如可以輸入以下代碼,查看與“plot”相關(guān)的函數(shù)
??plot
2.3 獲取更多的幫助
- Stack Overflow(https://stackoverflow.com/)是一個(gè)非常流行的技術(shù)問(wèn)答網(wǎng)站,可以在這里搜索和上傳你遇到的問(wèn)題

第二部分
第三章 R對(duì)象
3.1 原子型向量
- 原子型向量是R中最基本的數(shù)據(jù)類(lèi)型,是一系列數(shù)值的集合
- 使用
c()將數(shù)值或字符串合并為一個(gè)原子型向量,(c的意思是concatenate,也可以理解為collect、combine)
> x <- c(3,-2,4,7,5,-1,0)
> x
[1] 3 -2 4 7 5 -1 0
- 使用
is.vector()查看某個(gè)對(duì)象是否是向量 - 使用
length()獲取向量的長(zhǎng)度
> is.vector(x)
[1] TRUE
> length(x)
[1] 7
-
向量也支持計(jì)算:
- 當(dāng)對(duì)象y的值為4時(shí),
x+y意味著x中每個(gè)元素加4 - 當(dāng)y也是向量時(shí),
x+y意味著對(duì)應(yīng)索引的元素相加
- 當(dāng)對(duì)象y的值為4時(shí),
原子型向量的6種基本類(lèi)型:
| 簡(jiǎn)介 | |
|---|---|
| double(雙整數(shù)型) | 也稱(chēng)為數(shù)值型(numeric),這個(gè)是R中默認(rèn)的向量類(lèi)型,例如上述例子中的向量x就屬于double |
| integer(整數(shù)型) | 儲(chǔ)存整數(shù)數(shù)值,定義方法:在數(shù)值后加一個(gè)大寫(xiě)的L,例如y=c(1L, 2L, 3L) ,integer可以避免在double中可能出現(xiàn)的浮點(diǎn)誤差問(wèn)題(例如sqrt(2)^2-2的輸出結(jié)果不為0) |
| character(字符型) | 儲(chǔ)存文本,也稱(chēng)字符串(string),定義方法:添加單引號(hào)或雙引號(hào),例如text=c('hello','world')。字符串也可以是數(shù)字,例如text=c('1','2','3'),但character向量無(wú)法進(jìn)行四則運(yùn)算 |
| logical(邏輯型) | 定義方法:大寫(xiě)的TRUE和FALSE,例如logic=c(TRUE, FALSE, TRUE)
|
| complex(復(fù)數(shù)類(lèi)型)和raw(原始類(lèi)型) | conplex向量用于儲(chǔ)存復(fù)數(shù),raw向量用于儲(chǔ)存數(shù)據(jù)的原始字節(jié) |
- 可以使用
typeof()命令查看向量的類(lèi)型 - 向量的常見(jiàn)屬性有name(名稱(chēng))、dim(維度)和class(類(lèi))。例如,對(duì)于上述例子中的向量x,我們可以通過(guò)
names()添加名稱(chēng):
> x <- c(3,-2,4,7,5,-1,0)
> names(x) <- c('one','two','three','four','five','six','seven')
> x
one two three four five six seven
3 -2 4 7 5 -1 0
- 使用
:來(lái)快速定義向量
> x <- 1:6
> x
[1] 1 2 3 4 5 6
- 可以在這個(gè)方法的基礎(chǔ)上靈活變通,例如,如果想要小數(shù)的話:
> x <- (1:6)*0.3
> x
[1] 0.3 0.6 0.9 1.2 1.5 1.8
3.2 矩陣
- 矩陣將數(shù)值儲(chǔ)存在一個(gè)二維數(shù)組中,我們可以使用matrix函數(shù)將向量放置在矩陣中,并通過(guò)參數(shù)
nrow、ncol來(lái)設(shè)置矩陣的行、列的數(shù)量,例如:
> a <- c(1,2,3,4,5,6)
> m <- matrix(a,nrow=2,ncol=3)
> m
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
3.3 數(shù)組
- 使用array函數(shù)來(lái)生成一個(gè)n維的數(shù)組,通過(guò)
dim參數(shù)來(lái)設(shè)置維度大小
> a <- array(c(1:24), dim=c(2,3,4))
> a
, , 1
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
, , 2
[,1] [,2] [,3]
[1,] 7 9 11
[2,] 8 10 12
, , 3
[,1] [,2] [,3]
[1,] 13 15 17
[2,] 14 16 18
, , 4
[,1] [,2] [,3]
[1,] 19 21 23
[2,] 20 22 24
3.4 類(lèi)
- 使用class函數(shù)來(lái)查看對(duì)象的類(lèi),即class屬性(class和type不是一個(gè)東西),例如,對(duì)于上述的數(shù)組a:
> class(a)
[1] "array"
- R使用了一個(gè)特殊的類(lèi)來(lái)表述時(shí)間和日期數(shù)據(jù)
> now <- Sys.time()
> now
[1] "2020-11-03 20:35:47 CST"
> typeof(now)
[1] "double"
> class(now)
[1] "POSIXct" "POSIXt"
- 因子(factor)這個(gè)類(lèi)儲(chǔ)存的是分類(lèi)數(shù)據(jù),例如:
> gender <- factor(c('male','female','female'))
> attributes(gender)
$levels
[1] "female" "male"
$class
[1] "factor"
- factor函數(shù)使得在統(tǒng)計(jì)分析中加入分類(lèi)變量變得簡(jiǎn)單,R時(shí)常會(huì)嘗試將character向量轉(zhuǎn)換成factor,除非有這樣的需要,否則一般應(yīng)該避免這種強(qiáng)制轉(zhuǎn)換(使用
as.character()函數(shù))
3.5 強(qiáng)制轉(zhuǎn)換
- 如果把不同類(lèi)型的數(shù)據(jù)放入同一個(gè)對(duì)象中,R會(huì)將其強(qiáng)制轉(zhuǎn)換為同一類(lèi)型的數(shù)據(jù)(例如,將字符串和數(shù)字放入一個(gè)向量中,數(shù)字會(huì)被轉(zhuǎn)變?yōu)樽址?,將?shù)字和邏輯型放入一個(gè)向量中,TRUE會(huì)被轉(zhuǎn)換為1,F(xiàn)ALSE會(huì)被轉(zhuǎn)換為0)
- 可以使用as函數(shù)家族進(jìn)行強(qiáng)制轉(zhuǎn)換,例如:
> as.character(1)
[1] "1"
> as.logical(1)
[1] TRUE
> as.double(TRUE)
[1] 1
3.6 列表
- 列表(list)儲(chǔ)存的是對(duì)象而非數(shù)值,通過(guò)列表,我們可以將不同類(lèi)型的數(shù)據(jù)(數(shù)字啦、字符串啦、邏輯型啦)放置在一個(gè)集合里
> my_list <- list(1:10, 'text', TRUE)
> my_list
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] "text"
[[3]]
[1] TRUE
3.7 數(shù)據(jù)框
- 數(shù)據(jù)框(data frame)是列表的二維版本。在數(shù)據(jù)框中,每一列是一個(gè)向量(和Excel表格比較相似),并且各個(gè)向量的長(zhǎng)度要相等,例如:
> students <- data.frame(name=c('harry','ron','hermione'), gender=c('male','male','female'),age=c(17,17,17))
> students
name gender age
1 harry male 17
2 ron male 17
3 hermione female 17
- 數(shù)據(jù)框其實(shí)是一個(gè)class為data.frame的列表
- 上述的name和gender實(shí)際上將被存儲(chǔ)為factor,如果不想被強(qiáng)制轉(zhuǎn)換,可以在
data.frame()中添加一個(gè)參數(shù)stringsAsFactors=FALSE - 數(shù)據(jù)框的索引方法:數(shù)據(jù)框名稱(chēng)+美元符號(hào)+列的名稱(chēng),例如
students$gender可以返回students這個(gè)數(shù)據(jù)框中名為gender的一列(非常方便?。?/li>
> students$gender
[1] "male" "male" "female"
- 2021/3/29更新:我發(fā)現(xiàn)data.table比data.frame更好用,推薦大家嘗試(需要先安裝“data.table”包)
3.8 加載和保存數(shù)據(jù)
3.8.1 R基礎(chǔ)包中的數(shù)據(jù)集
- R中的基礎(chǔ)包datasets自帶了很多數(shù)據(jù)集,這些數(shù)據(jù)集沒(méi)有太多的分析價(jià)值,主要的作用是方便我們測(cè)試代碼。可以使用以下命令來(lái)查看這些數(shù)據(jù)集的簡(jiǎn)介:
help(packages='datasets')
- 若想使用某個(gè)數(shù)據(jù)集,,只需要輸入其名稱(chēng)即可(見(jiàn)第十章的筆記)
3.8.2 工作目錄
當(dāng)嘗試加載或保存數(shù)據(jù)時(shí),在沒(méi)有額外指定文件地址的情況下,R都會(huì)在一個(gè)默認(rèn)的文件目錄中進(jìn)行操作,這就是R的工作目錄
可以使用如下代碼查看R的工作目錄:
> getwd()
[1] "C:\Users\韋子謙\Documents"
- 對(duì)于不同的計(jì)算機(jī),默認(rèn)的工作目錄可能是不同的。以及,R無(wú)法讀取中文路徑的文件(至少在我自己的電腦上是這樣的),最好將工作目錄設(shè)置為其他的位置。改變工作目錄的方法之一是setwd函數(shù)
> setwd('D:/R_project')
- 但我自己嘗試的結(jié)果是,每次啟動(dòng)RStudio,之前用
setwd()函數(shù)設(shè)置的工作目錄就會(huì)失效,解決的方法是直接在RStudio的菜單欄里修改工作目錄:“Tools”——“Globe Options”——“Gneral”——“Basic”——“R Sessions”——“Default working directory (when not in a project)”
3.8.3 讀取和寫(xiě)入純文本文件
純文本文件是存儲(chǔ)數(shù)據(jù)的常見(jiàn)格式之一,其將數(shù)據(jù)表存儲(chǔ)在了文本文件中,表中每一行都對(duì)應(yīng)文本中的一行,每一行的不同元素都用一些簡(jiǎn)單的符號(hào)隔開(kāi),即分隔符
常用的分隔符有空格、逗號(hào),等等。每個(gè)文件只會(huì)使用其中一種分隔符
所有的純文本文件都可以存儲(chǔ)為擴(kuò)展名為txt的文件,但有時(shí)候也可以使用特殊的擴(kuò)展名以告訴用戶該文件使用的分隔符。例如,csv(Comma-Separated Values)格式的文件使用逗號(hào)作為分隔符
要加載一個(gè)純文本文件,可以使用read函數(shù),舉個(gè)例子:
data1 <- read.table('data.csv',sep=',',header=TRUE,na.strings='.',skip=3,nrow=5,stringsAsFactors=FALSE)
-
在這里,我們從R的工作目錄加載了一個(gè)名為“data.csv”的數(shù)據(jù)集,并將其內(nèi)容放置在名為
data1的對(duì)象中,涉及到的參數(shù)如下:- sep:該參數(shù)告訴
read.table()我們的文件使用了何種分隔符 - header:是否將第一行的數(shù)據(jù)視為變量名。如果第一行是變量名,則設(shè)置為T(mén)RUE
- na.strings:有時(shí)候,數(shù)據(jù)文件中會(huì)使用一些符號(hào)來(lái)表示缺失值(NA),該參數(shù)會(huì)告訴
read.table()缺失值的位置,read.table()則會(huì)將這些值讀取為NA(在上面這個(gè)例子中,數(shù)據(jù)文件中用于表示缺失值的符號(hào)為“.”) - skip:有時(shí)也我們需要忽略數(shù)據(jù)文件開(kāi)頭的幾行,此時(shí)可以用skip來(lái)指定從第幾行開(kāi)始讀取數(shù)據(jù)
- nrow:告訴
read.table()在讀取了一定數(shù)量的數(shù)據(jù)行后就不再讀?。ㄖ付ǖ男袛?shù)不包括表頭) - stringsAsFactors:有時(shí)候R會(huì)將字符串類(lèi)型轉(zhuǎn)換為因子型,如果不想讓R這么做,則可以將stringsAsFactors設(shè)置為FALSE
- sep:該參數(shù)告訴
當(dāng)需要加載多個(gè)數(shù)據(jù)時(shí),還可以通過(guò)更改全局設(shè)置來(lái)統(tǒng)一設(shè)定一些參數(shù),例如:
options(stringsAsFactors=FALSE)
- read函數(shù)家族
| 默認(rèn)設(shè)置 | 使用情形 | |
|---|---|---|
| read.table | sep=" ", header=FALSE | 通用讀取函數(shù) |
| read.csv | sep=",", header=TRUE | 逗號(hào)分隔值(csv)的文件 |
| read.delim | sep="\t", header=TRUE | 制表符分隔的文件(制表符就是Tab鍵敲出的那個(gè)符號(hào),相當(dāng)于縮進(jìn)) |
| read.csv2 | sep=";", header=TRUE, dec="," | 歐式小數(shù)點(diǎn)符號(hào)的csv文件 |
| read.delim2 | sep="\t", header=TRUE, dec="," | 歐式小數(shù)點(diǎn)符號(hào)的制表符分隔的文件 |
- 在RStudio中,通過(guò)點(diǎn)擊Environment面板的“import Dataset”,也可以將外部文檔的數(shù)據(jù)導(dǎo)入至數(shù)據(jù)框中(該功能相當(dāng)于
read.table()命令的圖形用戶界面)

- 使用write函數(shù)保存數(shù)據(jù),如下:
| 文件格式 | 函數(shù)及其使用方法 |
|---|---|
| csv | write.csv(r_object, file=filepath, row.names=FALSE) |
| 歐式小數(shù)點(diǎn)符號(hào)的csv | write.csv2(r_object, file=filepath, row.names=FALSE) |
| 制表符分隔 | write.table(r_object, file=filepath, sep="\t", row.names=FALSE) |
- 需要注意的是,write函數(shù)無(wú)法在計(jì)算機(jī)上創(chuàng)建新的文件夾,因此在保存文件時(shí),請(qǐng)確保寫(xiě)入路徑的文件夾都已創(chuàng)建好
- 此外,還可以使用bzfile(bzip2類(lèi)型)、gzfile(gzip類(lèi)型)或xzfile函數(shù)(xz類(lèi)型)來(lái)壓縮文件,用法如下:
write.csv(data1, file=bzfile("data1.csv.bz2"), row.names=FALSE)
- 讀取壓縮文件的方法如下:
read.csv("data1.csv.bz2")
3.8.4 讀取和寫(xiě)入R文件
RDS文件可以存儲(chǔ)單個(gè)R對(duì)象,而RData文件可以存儲(chǔ)多個(gè)R對(duì)象
使用readRDS函數(shù)來(lái)打開(kāi)RDS文件,例如:
data1 <- readRDS("data1.RDS")
- 打開(kāi)RData的方法則更簡(jiǎn)單,只需運(yùn)行l(wèi)oad函數(shù)即可,其中的R對(duì)象會(huì)以其原本的名稱(chēng)加載到工作區(qū)中:
load("data1.RData")
【tips】RData不會(huì)告訴你讀取了多少個(gè)R對(duì)象,也不會(huì)告訴你各個(gè)對(duì)象的具體名稱(chēng),解決的方法之一是在命令行兩側(cè)加上括號(hào),例如
(load("data1.RData")),這會(huì)強(qiáng)制R在讀取文件時(shí)顯示其中所有對(duì)象的名稱(chēng)將對(duì)象保存RDS文件的方法是saveRDS函數(shù),第一個(gè)參數(shù)是R對(duì)象的名稱(chēng),第二個(gè)參數(shù)是目標(biāo)路徑和文件名。保存為RData的方法是save函數(shù),例如可以通過(guò)以下代碼來(lái)保存a、b、c三個(gè)對(duì)象:
save(a,b,c,file="data1.RData")
- RData的缺點(diǎn)在于,我們可能自己都忘了里面保存了啥東西,而且如果當(dāng)前的工作區(qū)存在同名對(duì)象的話,讀取RData會(huì)導(dǎo)致同名對(duì)象被覆蓋,因此保存為RDS文件會(huì)更好一些
- 相比存儲(chǔ)為純文本文件,存儲(chǔ)為R文件的好處在于,因子、日期、時(shí)間或類(lèi)屬性等信息將得到保留,缺點(diǎn)則是,許多軟件程序都無(wú)法讀取R文件,因此其不利于數(shù)據(jù)分享,而且萬(wàn)一哪天你的電腦上沒(méi)有了R,那么可能連打開(kāi)R文件都會(huì)變得困難(我的理解是,如果你的數(shù)據(jù)還沒(méi)分析完,那么可以保存為R文件以便下次繼續(xù)分析,但如果數(shù)據(jù)已經(jīng)處理好了,那么最終導(dǎo)出的結(jié)果最好是純文本文件)
3.8.5 讀取Excel
- 書(shū)中介紹的方法是XLConnect包,不過(guò)我發(fā)現(xiàn)目前RStudio默認(rèn)使用的是readxl包(感覺(jué)通過(guò)選項(xiàng)菜單來(lái)導(dǎo)入文件比寫(xiě)代碼要方便多了……)
3.8.6 從其它程序加載文件
- 有些時(shí)候,我們會(huì)拿到某個(gè)類(lèi)型的數(shù)據(jù),但卻沒(méi)有對(duì)應(yīng)的原始程序,此時(shí)可以嘗試使用以下R包中的函數(shù)來(lái)讀取數(shù)據(jù)
| 文件格式 | 函數(shù) | R包 |
|---|---|---|
| ERSI ArcGIS | read.shapefile | shapefiles |
| MATLAB | readMat | R.matlab |
| minitab | read.mtp | foreign |
| SAS(永久性數(shù)據(jù)集) | read.ssd | foreign |
| SAS(XPORT格式) | read.xport | foreign |
| SPSS | read.spss | foreign |
| Stata | read.dta | foreign |
| Systat | read.systat | foreign |
3.8.7 從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)
- DBI包可以通過(guò)驅(qū)動(dòng)程序連接至數(shù)據(jù)庫(kù),要從某個(gè)數(shù)據(jù)庫(kù)中讀取數(shù)據(jù),首先需要下載其與DBI包相關(guān)聯(lián)的R包,這些包為特定的數(shù)據(jù)庫(kù)程序的原始驅(qū)動(dòng)程序提供了API。例如,對(duì)于MySQL數(shù)據(jù)庫(kù)需要使用RMySQL包,對(duì)于SQLite數(shù)據(jù)庫(kù)需要使用RSQLite包,等等
第四章 R的記號(hào)體系
- R的記號(hào)體系可以幫助我們從R對(duì)象中提取值,一般的形式是先寫(xiě)出對(duì)象名,然后是一個(gè)中括號(hào),例如
x[ , ],在R中有六種索引方式,分別為正整數(shù)、負(fù)整數(shù)、零、空格、邏輯值、名稱(chēng)
4.1 正整數(shù)
- 如下,
students[1,1]返回的值是第一行第一列的元素
> students <- data.frame(name=c('harry','ron','hermione'), gender=c('male','male','female'),age=c(17,17,17))
> students
name gender age
1 harry male 17
2 ron male 17
3 hermione female 17
> students[1,1]
[1] harry
- 如果想取多個(gè)數(shù)值,可以用向量代替數(shù)值作為索引的參數(shù),例如
students[c(1,2),1]或students[1:2,1]返回的是第一、第二行,第一列的兩個(gè)元素
> students[1:2,1]
[1] harry ron
- 如果想提取一整行或一整列:
> students[1,] # 提取第一行
name gender age
1 harry male 17
> students[,1] # 提取第一列
[1] harry ron hermione
- 注意:如果從數(shù)據(jù)框中提取兩行或兩列以上的數(shù)據(jù),將返回一個(gè)新的數(shù)據(jù)框,如果只提取一列,則只會(huì)返回一個(gè)向量
4.2 負(fù)整數(shù)
- 和正整數(shù)索引相反,負(fù)整數(shù)索引將返回負(fù)整數(shù)索引對(duì)應(yīng)的元素之外的部分,例如,
students[-1,1]將返回students中第一行之外的所有元素,即第二、第三行
> students[-1,]
name gender age
2 ron male 17
3 hermione female 17
4.3 零索引
- 如果索引時(shí)使用0,那么返回的是一個(gè)空對(duì)象(沒(méi)啥用處……)
> students[0,0]
data frame with 0 columns and 0 rows
4.4 空格索引
- 其實(shí)就是上文所說(shuō)的提取一整行或一整列的方法,
students[1,]和students[1, ]的效果是一樣的
4.5 邏輯值索引
- 這種方法將提取
TRUE對(duì)應(yīng)的元素,FALSE對(duì)應(yīng)的元素則不會(huì)提取,例如:
> students[c(TRUE, FALSE,TRUE),2]
[1] male female
4.6 名稱(chēng)索引
- 這是從數(shù)據(jù)框中提取列的常用方法
> students[1:3, 'name']
[1] harry ron hermione
4.7 美元符號(hào)和雙中括號(hào)
- 美元符號(hào)可以很方便地提取出數(shù)據(jù)框中的某一列
> students$name
[1] harry ron hermione
- 如果一個(gè)列表中有若干個(gè)子列表,雙中括號(hào)可以直接提取子列表中的元素,而不會(huì)返回一個(gè)列表對(duì)象
> l <- list(c(1,2,3), c(TRUE, FALSE))
> l
[[1]]
[1] 1 2 3
[[2]]
[1] TRUE FALSE
> l[1]
[[1]]
[1] 1 2 3
> l[[1]]
[1] 1 2 3
第五章 對(duì)象改值
5.1 就地改值
- 通過(guò)索引選擇想要更改的元素,然后將新的值通過(guò)
<-賦給該對(duì)象,例如:
> x <- c(1,2,3,4,5)
> x[1] <- 100
> x
[1] 100 2 3 4 5
- 也可以批量改值,例如:
> x[1:3] <- c(100,200,300)
> x
[1] 100 200 300 4 5
- 亦可以添加新的元素,例如:
> x[6] <- 6
> x
[1] 100 200 300 4 5 6
- 這種添加新元素的方法還可以用于在數(shù)據(jù)框中添加新變量(即,新的一列)
> students <- data.frame(name=c('harry','ron','hermione'), gender=c('male','male','female'),age=c(17,17,17))
> students$new <- 1:3
> students
name gender age new
1 harry male 17 1
2 ron male 17 2
3 hermione female 17 3
- 如果想刪除某一列,則賦值為
NULL即可
> students$new <- NULL
> students
name gender age
1 harry male 17
2 ron male 17
3 hermione female 17
- 這些操作都是即時(shí)進(jìn)行的,所以在對(duì)數(shù)據(jù)框進(jìn)行修改之前,最好先保留一份備份
5.2 邏輯取子集
5.2.1 邏輯測(cè)試
- R中的七種邏輯運(yùn)算符
| 運(yùn)算符 | 語(yǔ)法 | 判別 |
|---|---|---|
| > | a>b | a是否大于b? |
| >= | a>=b | a是否大于或等于b? |
| < | a<b | a是否小于b? |
| <= | a<=b | a是否小于或等于b? |
| == | a==b | a是否等于b? |
| != | a!=b | a是否不等于b? |
| %in% | a %in% c(a,b,c) | c(a,b,c) 中是否包含a? |
- 對(duì)于每一個(gè)邏輯運(yùn)算符,R會(huì)將運(yùn)算符兩邊的值或向量進(jìn)行對(duì)比,并返回邏輯值,例如:
> 3 > c(1,2,3,4,5)
[1] TRUE TRUE FALSE FALSE FALSE
【tips】:
%in%是唯一不進(jìn)行一一對(duì)比的運(yùn)算符,其主要是判斷左邊的值是否出現(xiàn)在右邊的對(duì)象中通過(guò)邏輯運(yùn)算符,我們可以很方便地從對(duì)象中提取出我們想要的元素
> a <- c('group1','group2','group1','group2')
> b <- c(0.586, 0.762, 0.462, 0.631)
> a=='group1'
[1] TRUE FALSE TRUE FALSE
> b[a=='group1']
[1] 0.586 0.462
- 而且還可以使用多個(gè)運(yùn)算符
> b[a=='group1' & b>0.5]
[1] 0.586
- 邏輯值取子集非常強(qiáng)大,可以幫助我們快速定位、提取和修改對(duì)象中的元素。在使用這種方法時(shí),我們并不需要知道元素的具體位置,只需要知道如何用邏輯測(cè)試來(lái)描述這些元素即可
5.2.2 布爾運(yùn)算符
- 以下幾個(gè)運(yùn)算符可以將多個(gè)邏輯測(cè)試的結(jié)果整合并輸出為一個(gè)邏輯值(TRUE or FALSE)
| 運(yùn)算符 | 語(yǔ)法 | 判別 |
|---|---|---|
| & | cond1 & cond2 | cond1和cond2是否同時(shí)為真? |
| | | cond1 pipe cond2 | cond1和cond2是否至少有一個(gè)為真? |
| xor | xor(cond1,cond2) | cond1和cond2中是否只有一個(gè)為真? |
| ! | !cond1 | cond1是否為假? |
| any | any(cond1,cond2,cond3, ...) | 所有條件是否至少有一個(gè)為真? |
| all | all(cond1,cond2,cond3, ...) | 所有條件是否同時(shí)為真? |
- 例如:
> any(1>2, 'yes'=='no',2*2==4)
[1] TRUE
5.2.3 缺失信息
- R中的特殊字符
NA代表“not available”,可用于儲(chǔ)存缺失信息。如果運(yùn)算時(shí)存在缺少信息,那么NA將被原封不動(dòng)地保留,例如:
> 1+NA
[1] NA
- 某些情況下,
NA的存在會(huì)使數(shù)據(jù)處理變得棘手,例如當(dāng)我們想計(jì)算一個(gè)向量中所有元素的均值,但向量中存在NA,將會(huì)導(dǎo)致:
> mean(c(1,2,3,NA))
[1] NA
- 此時(shí)可以添加一個(gè)可選參數(shù)
na.rm(大部分R函數(shù)都會(huì)存在這個(gè)參數(shù)),意味著“NA remove”,例如:
> mean(c(1,2,3,NA),na.rm = TRUE)
[1] 2
- 另外,我們還可以通過(guò)
is.na函數(shù)來(lái)判斷是否存在NA:
> is.na(NA)
[1] TRUE
> is.na(c(1,2,3,NA))
[1] FALSE FALSE FALSE TRUE
> any(is.na(c(1,2,3,NA)))
[1] TRUE
第六章 R的環(huán)境系統(tǒng)
6.1 R的環(huán)境系統(tǒng)
- R的環(huán)境系統(tǒng)很像電腦中的系統(tǒng)目錄,是以層級(jí)結(jié)構(gòu)存儲(chǔ)的,其中,全局環(huán)境(globe environment)扮演著關(guān)鍵的作用,命令行中運(yùn)行的所有命令都是在全局環(huán)境中進(jìn)行的,在命令行中創(chuàng)建的所有對(duì)象也被存儲(chǔ)在全局環(huán)境中(見(jiàn)下圖)

- 任何時(shí)候,R的活動(dòng)環(huán)境(active environment)都只有一個(gè),其會(huì)將所有的新對(duì)象存儲(chǔ)在當(dāng)前的活動(dòng)環(huán)境中,一般來(lái)說(shuō),全局環(huán)境就是活動(dòng)環(huán)境
- 某些情況下,例如在運(yùn)行函數(shù)時(shí),R的活動(dòng)環(huán)境就變成了運(yùn)行時(shí)環(huán)境(runtime environment),R會(huì)在這個(gè)新的環(huán)境中運(yùn)行該函數(shù),并帶著函數(shù)運(yùn)行的結(jié)果回到調(diào)用該函數(shù)時(shí)的環(huán)境(也就是說(shuō),在函數(shù)內(nèi)部修改外部環(huán)境的對(duì)象時(shí),修改的只是該對(duì)象的副本)
- 當(dāng)調(diào)用某個(gè)對(duì)象時(shí),R首先會(huì)在當(dāng)前環(huán)境中尋找該對(duì)象,如果沒(méi)有找到,就會(huì)進(jìn)入該環(huán)境的父環(huán)境(parent environment)中尋找,例如:
deal <- function(){
print(x)
}
x <- 'hello'
deal()
- 上述例子中,對(duì)象x是在全局環(huán)境中定義的,當(dāng)運(yùn)行deal函數(shù)的時(shí)候,R首先會(huì)在該函數(shù)的活動(dòng)環(huán)境(也就是運(yùn)行時(shí)環(huán)境)中尋找x,但該環(huán)境中并不存在x,因此R進(jìn)入到該運(yùn)行時(shí)環(huán)境的父環(huán)境(也就是全局環(huán)境)中尋找x,這就是為什么函數(shù)內(nèi)部可以調(diào)用函數(shù)外部的對(duì)象,但卻無(wú)法在全局環(huán)境中調(diào)用函數(shù)內(nèi)部的對(duì)象
第三部分
第七章 程序
7.1 策略
-
當(dāng)我們需要將一個(gè)復(fù)雜的任務(wù)編寫(xiě)為R程序時(shí),可以通過(guò)如下三個(gè)策略來(lái)簡(jiǎn)化這個(gè)任務(wù)
- 將復(fù)雜任務(wù)分解成一些簡(jiǎn)單的子任務(wù)
- 使用實(shí)例
- 用通俗的語(yǔ)言描述解決方案,然后將其轉(zhuǎn)換為R代碼
-
通??梢詫程序分解為如下兩種子任務(wù)
- 有序步驟(sequential step):例如河內(nèi)塔問(wèn)題就可以拆解成若干個(gè)步驟:把最大的圓盤(pán)移動(dòng)到目標(biāo)柱子、把第二大的圓盤(pán)移動(dòng)到目標(biāo)柱子……把最小的圓盤(pán)移動(dòng)到目標(biāo)柱子
- 同類(lèi)情況(parallel case):以書(shū)中的簡(jiǎn)化版老虎機(jī)為例,當(dāng)老虎機(jī)轉(zhuǎn)出三個(gè)同樣的符號(hào)時(shí),使用一種算法來(lái)計(jì)算獎(jiǎng)金,當(dāng)轉(zhuǎn)出的符號(hào)都是杠,則使用第二種算法,其余情況則使用第三種,等等
7.2 if和else語(yǔ)句
- 可以用if語(yǔ)句來(lái)統(tǒng)一處理某一類(lèi)的情況,格式如下:
if (this) {
plan A
} else if (that) {
plan B
} else {
plan C
}
- this是某個(gè)邏輯測(cè)試或返回TRUE/FALSE的表達(dá)式,this為T(mén)RUE時(shí)運(yùn)行plan A,如果this為FALSE而that為T(mén)RUE時(shí),運(yùn)行plan B,如果this和that都為FALSE,則屬于else的情況,此時(shí)運(yùn)行plan C
第八章 S3
- 先來(lái)看看下面這個(gè)例子,對(duì)象n的取值為1000000000,當(dāng)n的class(也就是“類(lèi)”)是“numeric”時(shí),print出來(lái)的結(jié)果是1e+09,但當(dāng)n的class被替換為"POSIXct"和"POSIXt"后,print出來(lái)的結(jié)果就變成了一個(gè)時(shí)間
> n <- 1000000000
> class(n)
[1] "numeric"
> n
[1] 1e+09
> class(n) <- c("POSIXct", "POSIXt")
> class(n)
[1] "POSIXct" "POSIXt"
> n
[1] "2001-09-09 09:46:40 CST"
- 在上述例子中,n的取值沒(méi)有變,但是class變了,class的變化之所以會(huì)導(dǎo)致不同的輸出結(jié)果,是因?yàn)镽對(duì)于不同的class有著不同的處理方式
- class是屬性的一種,屬性(attribute)、泛型函數(shù)(generic function)和方法(method),三者共同構(gòu)成了R的S3系統(tǒng),這個(gè)系統(tǒng)掌管著R如何處理具有不同類(lèi)的對(duì)象
8.1 屬性
- 可以使用attributes函數(shù)來(lái)查看一個(gè)對(duì)象的屬性,例如,
gender這個(gè)對(duì)象就有兩個(gè)屬性,一個(gè)是levels屬性,一個(gè)是class屬性
> gender <- factor(c('male','female','female'))
> attributes(gender)
$levels
[1] "female" "male"
$class
[1] "factor"
- 又例如,剛剛提到的n,修改class之后,其屬性如下:
> attributes(n)
$class
[1] "POSIXct" "POSIXt"
- 我們還可以通過(guò)attr函數(shù)來(lái)給對(duì)象添加新的屬性,例如,我們給剛剛定義的n添加一個(gè)叫Hello的屬性,該屬性的值為World
> attr(n, 'Hello') <- "World"
- 現(xiàn)在用attributes函數(shù)來(lái)看看n的屬性,可以發(fā)現(xiàn)新增了我們剛剛添加的屬性
> attributes(n)
$class
[1] "POSIXct"
$Hello
[1] "World"
8.2 泛型函數(shù)
- 在控制臺(tái)中輸入對(duì)象名n,R對(duì)此的反應(yīng)是
print(n),但正如上一節(jié)提到的,n的取值保持不變,當(dāng)n的class改變的時(shí)候,print出來(lái)的內(nèi)容是不同的,之所以print函數(shù)能根據(jù)不同情況/場(chǎng)合完成不同的任務(wù)(例如根據(jù)不同的class給出不同的結(jié)果),是因?yàn)閜rint不是一個(gè)普通的函數(shù),而是一個(gè)泛型函數(shù)
8.3 方法
- 作為一個(gè)泛型函數(shù),
print()的原理大致是這樣的,其首先會(huì)查找一個(gè)對(duì)象的class屬性,然后根據(jù)不同的class分配合理的輸出顯示方式 - 具體而言,當(dāng)調(diào)用
print()時(shí),其會(huì)調(diào)用一個(gè)名為UseMethod的函數(shù)(見(jiàn)第十章的代碼調(diào)試部分的圖示),該函數(shù)會(huì)檢查我們提供給print函數(shù)的第一個(gè)參數(shù)的class屬性,然后將我們提供的待輸出的對(duì)象交給一個(gè)新函數(shù)來(lái)處理。例如,對(duì)于一個(gè)class為POSIXct的對(duì)象,UseMethod會(huì)交給print.POSIXct函數(shù)來(lái)處理,而對(duì)于一個(gè)class為factor的對(duì)象,會(huì)交給print.factor來(lái)處理 - 在這里,print.POSIXct和print.factor被稱(chēng)為print函數(shù)的方法(method),這兩個(gè)函數(shù)本身是普通的R函數(shù),但特別之處在于R會(huì)調(diào)用它們?nèi)ヌ幚砭哂袑?duì)應(yīng)class屬性的對(duì)象
- 可以通過(guò)methods函數(shù)來(lái)查看一個(gè)泛型函數(shù)所支持的方法,例如print函數(shù)支持將近200種方法
> methods(print)
[1] print.acf*
[2] print.anova*
[3] print.aov*
...
[183] print.xgettext*
[184] print.xngettext*
[185] print.xtabs*
8.4 小結(jié)
- 之所以稱(chēng)為S3系統(tǒng),是由于其起源于S語(yǔ)言的第三個(gè)版本(S語(yǔ)言是R語(yǔ)言的前身),許多常見(jiàn)的R函數(shù)都是S3泛型函數(shù),可以支持多種不同的類(lèi)方法函數(shù),S3系統(tǒng)使得R函數(shù)能在不同的場(chǎng)合有不同的表現(xiàn)
- R還有另外兩個(gè)創(chuàng)建class屬性行為的系統(tǒng)——S4系統(tǒng)和R5系統(tǒng),雖然使用難度較大,但也提供了S3系統(tǒng)所沒(méi)有的防護(hù)措施
- 關(guān)于S3的編程方法,可以參考Handley Wickham所著的《advanced R programming》
第九章 循環(huán)
- 循環(huán)(loop)是R用來(lái)重復(fù)完成某個(gè)任務(wù)的方法
9.1 for循環(huán)
- 格式如下,that是一個(gè)對(duì)象集合,對(duì)于出現(xiàn)在that中的每一個(gè)值,R都會(huì)循環(huán)運(yùn)行一遍兩個(gè)大括號(hào)之間的代碼(也就是this)
for (value in that) {
this
}
9.2 while循環(huán)
- 格式如下,當(dāng)condition為T(mén)rue時(shí),R會(huì)反復(fù)循環(huán)兩個(gè)大括號(hào)之間的代碼,直到condition為False或用戶手動(dòng)停止程序(按下Esc鍵或RStudio控制臺(tái)面板上的停止圖標(biāo))
while (condition) {
code
}
9.3 repeat循環(huán)
- 格式如下,R會(huì)反復(fù)循環(huán)repeat中的代碼,直到遇到了break命令
repeat{
code
if (this) {
break
}
}
9.4 小結(jié)
- 循環(huán)并非是在R中實(shí)現(xiàn)重復(fù)性任務(wù)自動(dòng)化的唯一途徑(例如,replicate函數(shù)也可以實(shí)現(xiàn)),遺憾的是,相比其它編程語(yǔ)言,循環(huán)代碼在R中的運(yùn)行速度更慢,但是別擔(dān)心,我們可以使用向量化編程來(lái)提高代碼的運(yùn)行速度!
第十章 代碼提速
10.1 向量化代碼
- 向量化的代碼可以接受一個(gè)含有多個(gè)值的向量作為輸入,并且同時(shí)操作向量中的每個(gè)元素。其使用了R的三大法寶:邏輯測(cè)試,取子集和元素方式執(zhí)行
- 先來(lái)看一個(gè)例子,state.division這個(gè)數(shù)據(jù)集(來(lái)源于R中自帶的datasets包)記載的是美國(guó)各州所在的地區(qū),分別為New England, Middle Atlantic, South Atlantic, East South Central, West South Central, East North Central, West North Central, Mountain, and Pacific。這50個(gè)元素與state.name中的州名相互對(duì)應(yīng)
> state.division
[1] East South Central Pacific Mountain
[4] West South Central Pacific Mountain
[7] New England South Atlantic South Atlantic
[10] South Atlantic Pacific Mountain
[13] East North Central East North Central West North Central
[16] West North Central East South Central West South Central
[19] New England South Atlantic New England
[22] East North Central West North Central East South Central
[25] West North Central Mountain West North Central
[28] Mountain New England Middle Atlantic
[31] Mountain Middle Atlantic South Atlantic
[34] West North Central East North Central West South Central
[37] Pacific Middle Atlantic New England
[40] South Atlantic West North Central East South Central
[43] West South Central Mountain New England
[46] South Atlantic Pacific South Atlantic
[49] East North Central Mountain
9 Levels: New England Middle Atlantic ... Pacific
- 假如我們想計(jì)算“New England”這個(gè)地區(qū)的州的數(shù)量,那么非向量的方法會(huì)是這樣的(其中一種思路):
# 非向量化的代碼
count_loop <- function(){
count <- 0
for (i in 1:length(state.division)) {
if (state.division[i] == "New England") {
count <- count + 1
}
}
return(count)
}
- 在這個(gè)名為count_loop的函數(shù)中,我們定義了一個(gè)名為
count的對(duì)象來(lái)記錄州的數(shù)量,然后通過(guò)for循環(huán)來(lái)遍歷state.division數(shù)據(jù)集中的每個(gè)元素,并通過(guò)條件語(yǔ)句來(lái)判斷某個(gè)元素是否為New England,如果條件為TRUE,則count加1 - 運(yùn)行結(jié)果如下,“New England”有6個(gè)州
> count_loop()
[1] 6
- 向量化的代碼則可以是這樣的(只需要一行代碼即可完成任務(wù)):
# 向量化的代碼
count_set <- function(){
count <- length(state.division[state.division == "New England"])
return(count)
}
- 在這段代碼中,我們先做了一個(gè)邏輯測(cè)試,即判斷state.division數(shù)據(jù)集中有哪些元素的值為“New England”
> state.division == "New England"
[1] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
[11] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
[21] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
[31] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
[41] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
- 然后我們基于返回的邏輯值,對(duì)state.division取子集(subsetting)
> state.division[state.division == "New England"]
[1] New England New England New England New England New England
[6] New England
于是,該子集的長(zhǎng)度就是“New England”地區(qū)的州的數(shù)量啦,整個(gè)過(guò)程都是以元素方式執(zhí)行的
邏輯測(cè)試,取子集和元素方式執(zhí)行,就是R語(yǔ)言的三大法寶,用這個(gè)方法編寫(xiě)向量化的代碼,可以讓代碼飛速運(yùn)行
【tips】這段代碼甚至還可以變得更簡(jiǎn)潔,如下
# 向量化的代碼
count_set <- function(){
count <- sum(state.division == "New England")
return(count)
}
- 當(dāng)然,這里的例子比較簡(jiǎn)單,所以?xún)煞N風(fēng)格的代碼的運(yùn)行效果相差無(wú)幾,但對(duì)于大容量的數(shù)據(jù),向量化代碼的優(yōu)勢(shì)就很明顯了。書(shū)中給出了一個(gè)求1000萬(wàn)個(gè)數(shù)值的絕對(duì)值的例子,結(jié)果是向量化代碼的運(yùn)行速度比非向量化的代碼快30倍(我自己嘗試的結(jié)果是快3倍,或許是和電腦性能有關(guān)系)
10.2 如何編寫(xiě)向量化的代碼
- 有兩種方法:
- 對(duì)于程序中的有序步驟,使用向量化的函數(shù)來(lái)完成
- 對(duì)于同類(lèi)情況,使用邏輯值取子集的方式來(lái)處理,以便一次性處理完一類(lèi)情況中的所有元素(for循環(huán)搭配if條件語(yǔ)句的組合常常能被邏輯值取子集所取代)
10.3 如何在R中編寫(xiě)快速的for循環(huán)
向量化代碼并不意味著for循環(huán)就不應(yīng)該在R中使用了,當(dāng)使用for循環(huán)時(shí),我們應(yīng)該注意改善循環(huán)的效率:1)能放在循環(huán)外的代碼就不要放在循環(huán)內(nèi),2)確保存儲(chǔ)循環(huán)輸出結(jié)果的對(duì)象有足夠的容量
對(duì)于第二點(diǎn),書(shū)中提到一個(gè)例子,將100萬(wàn)個(gè)數(shù)值循環(huán)放入一個(gè)名為output的對(duì)象,第一種方法是先定義一個(gè)長(zhǎng)度為100萬(wàn)的output,然后循環(huán)放入數(shù)值,第二種方法是先定義一個(gè)長(zhǎng)度為1的output,循環(huán)放入數(shù)值,每次循環(huán)都增加output的長(zhǎng)度。結(jié)果是,第一種方法的運(yùn)行時(shí)間不到兩秒鐘,第二種方法的運(yùn)行時(shí)間則長(zhǎng)達(dá)37分鐘。這是因?yàn)椋瑢?duì)于第一種方法,使用的都是同一個(gè)output,而第二種方法則需要不斷地復(fù)制、刪除舊的output,并在內(nèi)存中尋找新的地方存放output的新版本,相當(dāng)于讓R對(duì)output反復(fù)讀寫(xiě)了100萬(wàn)次
【tips】可以運(yùn)用
sys.time()命令來(lái)測(cè)試某個(gè)函數(shù)的運(yùn)行時(shí)間
10.4 調(diào)試R代碼
- 這個(gè)是附錄里的內(nèi)容,方便起見(jiàn)就放在第十章的最后了(附錄中其他的內(nèi)容也已經(jīng)放在了各章節(jié)中合適的位置)
10.4.1 traceback
- 有時(shí)候我們需要在代碼中調(diào)用函數(shù),調(diào)用的函數(shù)往往又會(huì)調(diào)用別的函數(shù),當(dāng)出現(xiàn)錯(cuò)誤時(shí),可能會(huì)難以定位究竟是哪個(gè)函數(shù)出了問(wèn)題
- 這時(shí)候就可以在命令行中輸入
traceback(),該命令會(huì)顯示一個(gè)調(diào)用棧(call stack),即一個(gè)被調(diào)用函數(shù)的有序列表,其中第一行是出錯(cuò)的函數(shù),最底層則是我們最初調(diào)用的函數(shù) - 不過(guò)要注意,出錯(cuò)的原因不一定發(fā)生在第一行的函數(shù)中,但一般來(lái)說(shuō),越上層的函數(shù),嫌疑越大
10.4.2 browser
在某個(gè)函數(shù)內(nèi)添加一行代碼
browser()后,只要調(diào)用了該函數(shù),函數(shù)運(yùn)行到browser()所在的一行時(shí)就會(huì)暫停,此時(shí)R處于一個(gè)新的模式,即瀏覽器模式(browser mode)(見(jiàn)debug部分的圖)-
瀏覽器模式有以下幾個(gè)特性:
- RStudio會(huì)顯示該函數(shù)的源代碼,并突出顯示該函數(shù)暫停時(shí)所在的代碼行
- 此時(shí)的運(yùn)行環(huán)境不再是全局環(huán)境,而是該函數(shù)的運(yùn)行時(shí)環(huán)境,RStudio的環(huán)境面板也將顯示運(yùn)行時(shí)環(huán)境的所有對(duì)象
- 命令行窗口中的命令提示符
>會(huì)被Browse[1]>所取代,我們可以在這里輸入一些命令,來(lái)檢查究竟是哪個(gè)地方出了錯(cuò)誤 - RStudio控制臺(tái)上方將出現(xiàn)“Next”、“Continue”和“Stop”三個(gè)按鈕,作用分別是:運(yùn)行該函數(shù)的下一行代碼、運(yùn)行該函數(shù)剩余的所有代碼后退出瀏覽器模式、立刻中斷并退出瀏覽器模式。此外還可以在命令行輸入命令n、c、s來(lái)實(shí)現(xiàn)對(duì)應(yīng)按鈕的功能(如果函數(shù)中存在名稱(chēng)為n、c、s、Q的對(duì)象,那么輸入這些字母將無(wú)法查看對(duì)象,想要查看的話可以用
get('n')這樣的方法)
10.4.3 斷點(diǎn)
- 打開(kāi)腳本窗口,在函數(shù)中想要暫停的一行的左邊單擊鼠標(biāo)左鍵,將顯示一個(gè)空心紅點(diǎn),即斷點(diǎn)(break point),R會(huì)將斷點(diǎn)識(shí)別為添加
browser()語(yǔ)句的信號(hào),當(dāng)調(diào)用該函數(shù)時(shí),運(yùn)行到斷點(diǎn)所在的地方就會(huì)暫停并進(jìn)入瀏覽器模式(此時(shí)斷點(diǎn)會(huì)變?yōu)閷?shí)心紅點(diǎn))
10.4.4 debug
對(duì)于一個(gè)已經(jīng)存在的函數(shù),可以使用debug函數(shù)來(lái)進(jìn)行調(diào)試(見(jiàn)下圖),其作用相當(dāng)于在函數(shù)第一行添加一句
browser(),調(diào)試完后可以使用undebug()來(lái)將第一行的browser()移除,此外可以使用isdebugged()來(lái)查看一個(gè)函數(shù)是否處于調(diào)試(debugging)模式如果嫌太麻煩,可以用
debugonce()代替debug(),效果是下次運(yùn)行待調(diào)試函數(shù)時(shí)自動(dòng)進(jìn)入瀏覽器模式,并在調(diào)試結(jié)束后解除該函數(shù)的調(diào)試模式

10.4.5 trace
- 可以使用trace函數(shù)來(lái)將
browser()語(yǔ)句添加至函數(shù)中的任意位置,例如以下代碼可以將browser()添加至名為“deal”的函數(shù)的第四行:
trace("deal",browser,at=4)
- 也可以用trace函數(shù)在函數(shù)中插入browser函數(shù)之外的函數(shù),但這么做的時(shí)候務(wù)必確保有充分的理由
- 調(diào)試結(jié)束后,使用
untrace("deal")來(lái)將函數(shù)恢復(fù)正常
10.4.6 recover
- 使用方法和
browser()一樣,區(qū)別是可以輸入棧的編號(hào)來(lái)進(jìn)入調(diào)用棧的任意層級(jí),從而進(jìn)入某個(gè)函數(shù)的運(yùn)行時(shí)環(huán)境進(jìn)行調(diào)試 - 通過(guò)添加全局選項(xiàng),可以使R在每次報(bào)錯(cuò)時(shí)自動(dòng)運(yùn)行
recover()命令:
options(error=recover)
- 可以通過(guò)以下代碼來(lái)取消這一全局設(shè)置:
options(error=NULL)
?
----------2020.12.06更新----------
添加了第6、7、9、10章的筆記
----------2020.12.07更新----------
添加了第8章的筆記
----------2020.12.14更新----------
添加了第2章和附錄部分的內(nèi)容,修正了諸多表述和錯(cuò)字
----------2020.12.2更新----------
添加了目錄