官术网_书友最值得收藏!

1.5 R語言的基本概念

既然讀者已經安裝了R語言并且選擇了自己喜愛的R語言開發環境,接下來是時候體驗一下,并獲得R語言的一些基礎知識了。本節將介紹R語言的主要構建模塊,這些構建模塊會應用于本書的所有數據挖掘算法之中。更具體地說,通過在交互式控制臺上執行基本操作并保存第一個R語言腳本,讀者將學會如何創建和操作以下構建模塊(見圖1-6)。

向量(Vector):用于存儲有序的一個或多個值。

列表(List):一種數據集合,用于存儲向量或者R語言中其他類型的對象。

數據幀(Dataframe,也稱數據框):可被看作由很多向量組成的二維列表,數據幀中所有向量內元素的數量是相同的。

函數(Function):一組作用于向量、列表和數據幀的指令集合,通過操作這些對象來生成新的結果。

圖1-6

接下來,講解如何使用自定義函數以及如何安裝擴展包來使用R語言的更多功能。如果上述所提及的概念讓讀者感到不知所措,不必擔心,通過后續的學習,讀者一定能對以上構建模塊及其概念達到非常熟悉的程度。

1.5.1 R語言入門

在了解R這門強大語言的入門知識之前,先來看看R語言的基本操作,具體如下。

在R語言控制臺上執行一些基本操作。

創建并保存R語言腳本。

在R語言控制臺執行R語言腳本。

1.通過R語言控制臺交互式地執行腳本

打開你最喜歡的IDE(本書使用RStudio),通過閃爍的光標,能夠找到交互式控制臺。在找到控制臺之后,輸入如下字符并按“Enter”鍵,即可向控制臺提交命令:

2+2

提交的命令的執行結果會在下一行自動輸出:

4

上述命令及輸出只是一個簡單的例子,后續將討論更加復雜的數學運算。

讀者雖然在控制臺上可以很方便地、交互式地測試小代碼塊,但需要注意的是,一旦終止了R語言會話(如關閉IDE),控制臺上執行的所有操作都會丟失。雖然一些IDE(如RStudio)會記錄控制臺歷史操作,但它只是追溯歷史操作的一個線索,而不是一種有效地存儲代碼的方式(見圖1-7)。

圖1-7

后面將介紹存儲控制臺命令歷史記錄的正確方法。同時,為了完整性考慮,在這里說明一下:R語言可以執行所有基本的數學運算,可以使用的運算符包括+、?、*、/、^(冪運算)。

2.創建并保存R語言腳本

R語言腳本是存儲或多或少的R語言代碼的一種文件,其優點是可以存儲和顯示可重復執行,或在腳本外部調用執行的一組結構化的指令(更多詳情見后文)。在IDE中,可以找到用于新建腳本的控件或者按鈕;單擊該控件或按鈕,可生成一個以.R為擴展名的文件;打開該文件,就可以開始編寫R語言代碼了。如果沒有找到類似的控件或按鈕,那么讀者可能需要考慮更換一款IDE,或者嘗試在R語言控制臺上運行以下命令:

file.create("my_first_script.R")

現在就開始在文件中編寫一些代碼,按照慣例,使用“大名鼎鼎”的句子“hello world”(你好,世界)來測試腳本吧。如何才能輸出這兩個神奇的單詞呢?讀者只需告訴R語言執行輸出即可,如圖1-8所示,具體如下:

print("hello world")

圖1-8

再重復一遍,當前這行代碼只是“熱身”,后續會介紹更多復雜的場景,所以讀者不必擔心本書內容過于淺顯。

在往下閱讀之前,請讀者在文件中再添加一行內容,這次要添加的不是代碼,而是注釋:

# my dear interpreter, please do not execute this line, it is just a comment

實際上,注釋與軟件開發是緊密聯系的。讀者可能很輕易地猜到,這樣的注釋語句是不會被解釋器執行的,因為解釋器會忽略所有“#”之后的內容。雖然注釋會被解釋器忽略,但它對程序員來說卻非常寶貴,尤其對于寫完腳本之后一個月,再回頭來看腳本的程序員而言。注釋一般用于標注代碼的基本原理、條件和目標,以便于程序員理解代碼的作用范圍是什么、為什么執行某些操作,以及必須滿足哪些條件才能讓代碼正常運行。

請注意,注釋可以和代碼寫在同一行,如下面的例子所示:

print("hello world") # dear interpreter, please do not execute this comment

在IDE上找到“保存”按鈕,單擊該按鈕就可以保存腳本文件了。保存腳本文件時需要輸入文件名,可以將名字設置為“my_first_script.R”,因為在接下來的內容里會用到它。

3.執行R語言腳本

隨著編碼能力的逐步提升,讀者就更有可能逐步地將數據分析的不同部分存儲到不同的腳本中,并從終端或者一個主腳本中按順序調用它們。因此,從一開始就正確地掌握腳本文件的存儲操作是至關重要的。而且完整地執行腳本是檢查代碼錯誤(即bug)的好方法。將數據分析過程存儲在腳本中,能夠方便其他感興趣的用戶復用代碼,從而驗證讀者的分析結果的正確性——這是非常理想的一大特性。

現在,請讀者嘗試執行之前所創建的腳本。在R語言環境下,通過調用source()函數來執行一個R語言腳本。正如在本書后面部分會更深入介紹的那樣,函數是一組指令的集合,它通常接收一個或多個輸入,并產生一個輸出。函數的輸入被稱為參數,函數的輸出被稱為值。在當前的例子中,將指定一個唯一的參數,即腳本文件參數。當前的例子使用之前保存的文件名作為參數,執行如下命令:

source("my_first_script.R")

執行這個命令的過程是怎樣的呢?可以想象解釋器一邊讀取每行代碼,一邊說:“來看看這個‘my_first_script’文件中的內容。好的,這里面有一條R命令——print("hello world"),執行它看看會發生什么。”雖然上述引用的這些言語是虛構的,但解釋器確實就是這樣運行的。解釋器會查找指定的文件,讀取文件內容,并執行存儲在其中的R語言命令。當前的例子會在控制臺上產生如下輸出:

hello world

現在是時候開始真正學習R語言入門知識了,讓我們先從向量開始吧!

1.5.2 向量

什么是向量?哪里會使用到向量?“向量”這個術語來自代數領域,但在R語言的世界里,向量并沒有那么復雜,讀者可以簡單地將它看成由同一種數據類型的值所組成的有序序列。不同順序的序列是不同的對象,如圖1-9所示。

圖1-9

如何在R語言中創建一個向量呢?答案是可以通過c()函數來創建,具體如下面的語句所示:

c(100,20,40,15,90)

上述語句創建的是常規的向量,只要在R語言控制臺輸出之后,它就失效了。如果想讓該向量在R語言環境中保存下來,需要創建一個變量。可以簡單地通過賦值操作來完成:

vector <- c(100,20,40,15,90)

只要執行這個命令,R語言環境中就會增加一個新的、類型為向量的對象。向量實際上有什么用途呢?作為使用R語言進行開發的基礎,其每一個輸入以及R語言產生的每一個輸出都可以被簡化成一個向量。例如,本書會將數據統計分析的結果存儲在向量中,也會用向量來表示模型所遵循的概率分布。

需要注意的是,到目前為止,雖然讀者在本書中只看到了一個數值向量,但實際上,讀者可以定義包含表1-1所示的所有類型數據的向量。

表1-1

甚至還可以定義混合類型的向量:

mixed_vector <- c(1, TRUE, "text here")

確切地說,混合類型的向量最終會被強制轉換成能包含所有其他類型的向量類型,就像剛剛例子中會全部轉換為字符類型的向量。有關細節不在這里多加討論,以免讀者混淆。

現在,讀者已經知道如何創建一個向量并保存該向量了。那么,如何調用并顯示該向量所存儲的內容呢?一般來說,調用一個對象只需要使用它的名字即可。可以在控制臺上輸入剛剛創建的向量的名字“mixed_vector”并按“Enter”鍵提交,結果如下:

[1] "1" "TRUE" "text here"

1.5.3 列表

既然讀者已經知道了“什么是向量”,就很容易理解列表了,它是包含對象的容器。列表容器內的對象可以是其他列表,甚至可以是數據幀。在R語言環境中,用列表來存儲對象很方便。例如,很多統計函數用列表來存儲運行結果。

請讀者看看如何使用列表,代碼如下:

regression_results <- lm(formula = Sepal.Length ~ Species, data = iris)

IRIS數據集是非常有名的R語言預加載數據集,它包含在每個R語言基礎版本中。上面這行命令表示基于IRIS數據集擬合一個回歸模型(后文會詳細講解回歸模型的細節),該模型試圖解釋特定種類的鳶尾花的萼片長度規律。

現在來看看 regression_results對象,正如之前說過的,它存儲了回歸模型擬合的結果。如果要查看指定對象的類型,可以使用mode()函數,該函數將對象名稱作為參數x的值,如下所示:

mode(x = regression_results)

運行結果如下:

list

1.創建列表

請讀者查看之前的代碼,看看通常都是如何創建列表的呢?在創建列表時,讀者總是會用到賦值運算符“<-”,該運算符在創建向量時也會用到。兩者之間的區別是,創建列表時使用的函數不同。創建列表時不是使用c()函數,而是使用list()函數。如下面例子所示,請讀者嘗試創建兩個向量,然后將它們合并到一個列表中:

first_vector <- c("a","b","c")
  second_vector <- c(1,2,3)
  vector_list <- list(first_vector, second_vector)

2.獲取列表子集

如果讀者想定位列表中的特定對象,應該怎么做呢?這時需要使用[[]]運算符,用來指定要獲取對象的層級。例如,想獲取vector_list列表中的第二個向量,可以使用如下代碼:

vector_list[[2]]

輸入上述代碼并按“Enter”鍵提交,會返回如下結果:

[1] 1 2 3

讀者也許會想,能否獲取列表中的單個對象的單個元素呢?答案是肯定的。例如,想要獲取second_vector中的第三個元素,只需要再使用一次[[]]運算符即可,代碼如下:

vector_list[[2]][[3]]

輸入上述代碼并按“Enter”鍵提交,會返回如下結果:

[1] 3

1.5.4 數據幀

如果滿足下面的規則,數據幀可被簡單地看成列表。

所有組件都是向量,無論是邏輯向量、數值向量,還是字符向量(甚至可以是混合向量)。

所有向量的長度都相同。

根據上述規則,我們可以得出這樣的推斷,即可以將數據幀想象成一個表,該表一般有一定數量的列(以組成這些列的向量來表示)和一定數量的行(行的數量與向量的長度一致),如圖1-10所示。雖然要遵守上述兩條規則,但是規則中并沒有對列的類型做出限制,即數據幀中多個列的類型可以是不同的,例如數據幀中可以存在數值類型和布爾類型的列。

圖1-10

可以想象,數據幀是一種非常方便的數據存儲方式,特別適用于存儲結構化的數據集,如實驗觀察數據或金融交易數據。數據幀允許在每一行中存儲觀察數據,并在每一列中存儲給定觀察數據的屬性。在后文中,讀者將會更深入地理解數據幀。

雖然數據幀是列表的邏輯子集,但是它具有創建和處理數據幀的整套專用函數。

創建一個數據幀類似于創建一個列表,只不過使用的函數有所不同。同樣,創建數據幀的函數被簡單地命名為data.frame(),示例如下:

a_data_frame <- data.frame(first_attribute =c("alpha","beta","
gamma"),second_attribute = c(14,20,11))

請注意:對于每一個向量(即每一列),將運算符“=”之前的字符作為列的名稱。另外,還需要注意以下兩點。

如果不給向量指定名稱,那么向量就會被隨機地賦予一個難記且非常不友好的名稱,在上述示例中,第一個向量會被命名為“c..alpha....beta....gamma..”,而第二個向量會被命名為“c.14..20..11..”。所以,強烈建議讀者在創建數據幀時,給向量指定名稱。

可以指定包含空格的列名,如將第一個向量的名稱改為“first attribute”(取代原來的first_attribute),這時只需要在名稱上添加雙引號即可,示例如下:

a_data_frame <- data.frame("first attribute" ...)

實際上,并不推薦第二種指定包含空格的列名的做法,因為在后續的代碼中再次使用這個向量時,該做法會讓事情變得復雜,從而引發很多令人煩惱的后果。

那么,如何選擇并展示數據幀的一列呢?答案是使用符號“$”,具體如下:

a_data_frame$second_attribute
[1] 14 20 11

可以用類似的方法向數據幀中添加一列:

a_data_frame$third_attribute <- c(TRUE,FALSE,FALSE)

1.5.5 函數

簡單來說,函數是用來操作和處理向量、列表或數據幀的方法。這個定義也許不夠嚴謹,但它突出了一個重點,即函數會接收一些輸入參數,這些參數可以是向量(甚至只包含一個元素)、列表或數據幀,然后該函數會輸出一個結果,通常也是向量、列表或數據幀。

執行文件系統操作及其他特定任務的函數則是例外,在其他語言中,這些例外通常會被稱為過程。例如1.5.1節中遇到過的file.create()函數。

R語言最受贊賞的特性之一就是可以很容易地查看其所有可用函數的定義,只需要使用函數的唯一名稱作為命令提交即可,無須輸入括號。現在,請讀者嘗試查看mode()函數的定義,代碼如下:

mode
 
function (x)
 {
  if (is.expression(x))
   return("expression")
 if (is.call(x))
   return(switch(deparse(x[[1L]])[1L], `(` = "(", "call"))
 if (is.name(x))
   "name"
else switch(tx <- typeof(x), double = , integer = "numeric",
   closure = , builtin = , special = "function", tx)
}
<bytecode: 0x102264c98>
<environment: namespace:base>

本書不打算深入探討mode()函數的細節內容,但需要讀者注意函數定義的一些結構元素。

這里的function()表示mode()函數本身。

對mode()函數的唯一參數x進行了明確說明。

在function()調用之后的所有內容都用花括號括起來,花括號里面的內容是函數體,它包含對輸入參數的所有計算和操作。

在R語言中,上述函數定義的結構元素是定義一個函數所需要的最少元素,可簡化表示如下:

function_name <- function(arguments){
    [function body]
}

既然已經知道了函數定義的原理,現在就定義一個簡單的函數,即給任意輸入的數字加上2:

adding_two <- function(the_number){
the_number + 2
}

這個函數可行嗎?當然可行。 為了測試這個函數,必須先執行聲明函數定義的兩行代碼,然后就可以使用讀者的自定義函數了:

adding_two( the_number = 4)
[1] 6

繼續介紹更加復雜但與函數更相關的概念——函數內的賦值。設想讀者正在編寫一個函數,并將函數結果存儲在一個名為“function_result”的向量中,讀者編寫的代碼可能如下所示:

my_func <- function(x){
function_result <- x / 2
}

假如x的參數值為4,當執行這個函數時,函數的輸出應該是一個值為2、名稱為“function_result”的對象。

現在請讀者根據前面的知識,嘗試輸出這個對象:

function_result

執行結果為:

Error: object function_result not found

為什么會這樣呢?上述錯誤輸出是由函數內賦值的規則所控制的,規則的具體內容如下。

函數內部能夠查看并使用函數外部定義的變量。

函數內部定義的變量只能在函數內部使用。

那么,如何將function_result對象從函數內部輸出呢?通常有以下兩種方式。

使用<<-運算符——通常稱之為超賦值運算符。

使用assign()函數。

使用超賦值運算符來重新編寫上述函數,具體如下:

my_func <- function(x){
  function_result <<- x / 2
}

如果試著運行它,會發現function_result對象出現在IDE的變量資源管理器中。需要說明的是,從函數內導出對象和將對象作為函數的返回結果是不一樣的,如下面的函數所示:

my_func <- function(x){
  function_result <- x / 2
  function_result
}

如果再次嘗試運行my_func(4),控制臺會輸出如下結果:

[1] 2

但在IDE的變量資源管理器中卻找不到function_result對象,這是為什么呢?原因是,在上面的函數中,function_result對象的值成了函數的返回值。不管怎樣,與上述第一個超賦值運算符的例子類似,這個對象現在是通過標準賦值運算符定義的。

主站蜘蛛池模板: 鄂温| 天祝| 德保县| 呼图壁县| 腾冲县| 鄂托克旗| 长丰县| 且末县| 井陉县| 北碚区| 基隆市| 惠安县| 宁远县| 平罗县| 长白| 固安县| 新田县| 德保县| 灯塔市| 永春县| 浦江县| 濉溪县| 菏泽市| 金寨县| 如皋市| 义乌市| 吉水县| 肇州县| 海宁市| 宁晋县| 宽城| 武隆县| 清徐县| 古交市| 仪陇县| 来安县| 潞城市| 梅州市| 高邮市| 台南县| 璧山县|