R語言作為統計學一門語言,一直在小衆領域閃耀着光芒。直到大資料的爆發,R語言變成了一門炙手可熱的資料分析的利器。随着越來越多的工程背景的人的加入,R語言的社群在迅速擴大成長。現在已不僅僅是統計領域,教育,銀行,電商,網際網路….都在使用R語言。
要成為有理想的極客,我們不能停留在文法上,要掌握牢固的數學,機率,統計知識,同時還要有創新精神,把R語言發揮到各個領域。讓我們一起動起來吧,開始R的極客理想。
關于作者:
張丹(Conan), 程式員Java,R,PHP,Javascript
weibo:@Conan_Z
email: [email protected]
轉載請注明出處:
<a href="http://blog.fens.me/r-apply/" target="_blank">http://blog.fens.me/r-apply/</a>
前言
剛開始接觸R語言時,會聽到各種的R語言使用技巧,其中最重要的一條就是不要用循環,效率特别低,要用向量計算代替循環計算。
那麼,這是為什麼呢?原因在于R的循環操作for和while,都是基于R語言本身來實作的,而向量操作是基于底層的C語言函數實作的,從性能上來看,就會有比較明顯的差距了。那麼如何使用C的函數來實作向量計算呢,就是要用到apply的家族函數,包括apply, sapply, tapply, mapply, lapply, rapply, vapply, eapply等。
目錄
apply的家族函數
apply函數
lapply函數
sapply函數
vapply函數
mapply函數
tapply函數
rapply函數
eapply函數
apply函數族是R語言中資料處理的一組核心函數,通過使用apply函數,我們可以實作對資料的循環、分組、過濾、類型控制等操作。但是,由于在R語言中apply函數與其他語言循環體的處理思路是完全不一樣的,是以apply函數族一直是使用者玩不轉一類核心函數。
很多R語言新手,寫了很多的for循環代碼,也不願意多花點時間把apply函數的使用方法了解清楚,最後把R代碼寫的跟C似得,我嚴重鄙視隻會寫for的R程式員。
apply函數本身就是解決資料循環處理的問題,為了面向不同的資料類型,不同的傳回值,apply函數組成了一個函數族,包括了8個功能類似的函數。這其中有些函數很相似,有些也不是太一樣的。
我一般最常用的函數為apply和sapply,下面将分别介紹這8個函數的定義和使用方法。
apply函數是最常用的代替for循環的函數。apply函數可以對矩陣、資料框、數組(二維、多元),按行或列進行循環計算,對子元素進行疊代,并把子元素以參數傳遞的形式給自定義的FUN函數中,并以傳回計算結果。
函數定義:
參數清單:
X:數組、矩陣、資料框
MARGIN: 按行計算或按按列計算,1表示按行,2表示按列
FUN: 自定義的調用函數
…: 更多參數,可選
比如,對一個矩陣的每一行求和,下面就要用到apply做循環了。
下面計算一個稍微複雜點的例子,按行循環,讓資料框的x1列加1,并計算出x1,x2列的均值。
通過這個上面的自定義函數myFUN就實作了,一個常用的循環計算。
如果直接用for循環來實作,那麼代碼如下:
通過for循環的方式,也可以很容易的實作上面計算過程,但是這裡還有一些額外的操作需要自己處理,比如建構循環體、定義結果資料集、并合每次循環的結果到結果資料集。
對于上面的需求,還有第三種實作方法,那就是完成利用了R的特性,通過向量化計算來完成的。
那麼,一行就可以完成整個計算過程了。
接下來,我們需要再比較一下3種操作上面性能上的消耗。
從CPU的耗時來看,用for循環實作的計算是耗時最長的,apply實作的循環耗時很短,而直接使用R語言内置的向量計算的操作幾乎不耗時。通過上面的測試,對同一個計算來說,優先考慮R語言内置的向量計算,必須要用到循環時則使用apply函數,應該盡量避免顯示的使用for,while等操作方法。
lapply函數是一個最基礎循環操作函數之一,用來對list、data.frame資料集進行循環,并傳回和X長度同樣的list結構作為結果集,通過lapply的開頭的第一個字母’l’就可以判斷傳回結果集的類型。
X:list、data.frame資料
比如,計算list中的每個KEY對應該的資料的分位數。
lapply就可以很友善地把list資料集進行循環操作了,還可以用data.frame資料集按列進行循環,但如果傳入的資料集是一個向量或矩陣對象,那麼直接使用lapply就不能達到想要的效果了。
比如,對矩陣的列求和。
lapply會分别循環矩陣中的每個值,而不是按行或按列進行分組計算。
如果對資料框的列求和。
lapply會自動把資料框按列進行分組,再進行計算。
sapply函數是一個簡化版的lapply,sapply增加了2個參數simplify和USE.NAMES,主要就是讓輸出看起來更友好,傳回值為向量,而不是list對象。
simplify: 是否數組化,當值array時,輸出結果按數組進行分組
USE.NAMES: 如果X為字元串,TRUE設定字元串為資料名,FALSE不設定
我們還用上面lapply的計算需求進行說明。
如果simplify=FALSE和USE.NAMES=FALSE,那麼完全sapply函數就等于lapply函數了。
對于simplify為array時,我們可以參考下面的例子,建構一個三維數組,其中二個次元為方陣。
對于字元串的向量,還可以自動生成資料名。
vapply類似于sapply,提供了FUN.VALUE參數,用來控制傳回值的行名,這樣可以讓程式更健壯。
FUN.VALUE: 定義傳回值的行名row.names
比如,對資料框的資料進行累計求和,并對每一行設定行名row.names
通過使用vapply可以直接設定傳回值的行名,這樣子做其實可以節省一行的代碼,讓代碼看起來更順暢,當然如果不願意多記一個函數,那麼也可以直接忽略它,隻用sapply就夠了。
mapply也是sapply的變形函數,類似多變量的sapply,但是參數定義有些變化。第一參數為自定義的FUN函數,第二個參數’…’可以接收多個資料,作為FUN函數的參數調用。
…: 接收多個資料
MoreArgs: 參數清單
SIMPLIFY: 是否數組化,當值array時,輸出結果按數組進行分組
比如,比較3個向量大小,按索引順序取較大的值。
再看一個例子,生成4個符合正态分布的資料集,分别對應的均值和方差為c(1,10,100,1000)。
由于mapply是可以接收多個參數的,是以我們在做資料操作的時候,就不需要把資料先合并為data.frame了,直接一次操作就能計算出結果了。
tapply用于分組的循環計算,通過INDEX參數可以把資料集X進行分組,相當于group by的操作。
X: 向量
INDEX: 用于分組的索引
simplify : 是否數組化,當值array時,輸出結果按數組進行分組
比如,計算不同品種的鸢尾花的花瓣(iris)長度的均值。
對向量x和y進行計算,并以向量t為索引進行分組,求和。
由于tapply隻接收一個向量參考,通過’…’可以把再傳給你FUN其他的參數,那麼我們想去y向量也進行求和,把y作為tapply的第4個參數進行計算。
得到的結果并不符合我們的預期,結果不是把x和y對應的t分組後求和,而是得到了其他的結果。第4個參數y傳入sum時,并不是按照循環一個一個傳進去的,而是每次傳了完整的向量資料,那麼再執行sum時sum(y)=55,是以對于t=0時,x=8 再加上y=55,最後計算結果為63。那麼,我們在使用’…’去傳入其他的參數的時候,一定要看清楚傳遞過程的描述,才不會出現的算法上的錯誤。
rapply是一個遞歸版本的lapply,它隻處理list類型資料,對list的每個元素進行遞歸周遊,如果list包括子元素則繼續周遊。
object:list資料
f: 自定義的調用函數
classes : 比對類型, ANY為所有類型
deflt: 非比對類型的預設值
how: 3種操作方式,當為replace時,則用調用f後的結果替換原list中原來的元素;當為list時,建立一個list,類型比對調用f函數,不比對指派為deflt;當為unlist時,會執行一次unlist(recursive = TRUE)的操作
比如,對一個list的資料進行過濾,把所有數字型numeric的資料進行從小到大的排序。
從結果發現,隻有$z$a的資料進行了排序,檢查$z$b的類型,發現是integer,是不等于numeric的,是以沒有進行排序。
接下來,對字元串類型的資料進行操作,把所有的字元串型加一個字元串’++++’,非字元串類型資料設定為NA。
隻有$x$c為字元串向量,都合并了一個新字元串。那麼,有了rapply就可以對list類型的資料進行友善的資料過濾了。
env: 環境空間
all.names: 比對類型, ANY為所有類型
下面我們定義一個環境空間,然後對環境空間的變量進行循環處理。
計算env環境空間中所有變量的均值。
再計算中目前環境空間中的所有變量的占用記憶體大小。
eapply函數平時很難被用到,但對于R包開發來說,環境空間的使用是必須要掌握的。特别是當R要做為工業化的工具時,對變量的精确控制和管理是非常必要的。
本文全面地介紹了,R語言中的資料循環處理的apply函數族,基本已經可以應對所有的循環處理的情況了。同時,在apply一節中也比較了,3種資料處理方面的性能,R的内置向量計算,要優于apply循環,大幅優于for循環。那麼我們在以後的R的開發和使用過程中,應該更多地把apply函數使用好。
忘掉程式員的思維,換成資料的思維,也許你就一下子開朗了。
本文轉自 h2appy 51CTO部落格,原文連結:http://blog.51cto.com/h2appy/1928502,如需轉載請自行聯系原作者