前言
apply函數族是R語言中數據處理的一組核心函數,通過使用apply函數,我們可以實現對數據的循環、分組、過濾、類型控制等操作。但是,由於在R語言中apply函數與其他語言循環體的處理思路是完全不一樣的,所以apply函數族一直是初學者玩不轉的一類核心函數。很多R語言新手,寫了很多的for循環代碼,也不願意多花點時間把apply函數的使用方法了解清楚,最後把R代碼寫的跟C似得。
簡介
由於R語言的apply家族函數是用C寫的,所以使用apply進行遍歷的執行效率遠遠高於自己編寫的循環語句。為了面向不同的數據類型,不同的返回值,apply函數組成了一個函數族,包括了8個功能類似的函數,具體如下表所示。下面我們一個一個來介紹。
apply函數
apply函數是最常用的代替for循環的函數。apply函數可以對矩陣、數據框、數組(二維、多維),按行或列進行循環計算,對子元素進行迭代,並把子元素以參數傳遞的形式給自定義的FUN函數中,並返回計算結果。調用格式如下:
apply(X, MARGIN, FUN, ...)
X: 是一個數組(array),也就是說輸入必須都是相同類型的數據,要麼都是數值型,要麼都是字符型。如果是一個混合數據類型的data.frame,那麼就會嘗試用as.matrix強制轉換數據。
MARGIN:表示對行(1)或者是對列(2)應用函數。
FUN: 可是R自帶函數,如mean,sum等。也可以是自己編寫的函數。
... :FUN中的額外參數。
現在假設我們需要對一個矩陣的每一行求和,那麼用apply怎麼實現呢?
x<-matrix(1:12, ncol=3)apply(x, 1, sum)[1] 15 18 21 24一行代碼搞定!當然你說可以使用 rowSums(x)也一樣能得到結果,但是如果稍微複雜點,rowSums函數就不行了。比如說讓數據框的x1列加1,並計算出x1,x2列的均值,這個時候就需要利用apply調用自定義函數了,可以說這才是apply強大的真正原因。
第一步,生成matrix
x <- cbind(x1 = 10, x2 = c(1:4, 2:5)); x x1 x2[1,] 10 1[2,] 10 2[3,] 10 3[4,] 10 4[5,] 10 2[6,] 10 3[7,] 10 4[8,] 10 5第二步,自定義函數myFUN,第一個參數x為數據, 第二、三個參數為自定義參數,可以通過apply的'...'進行傳入。
myFUN <- function(x, c1, c2) { c(sum(x[c1], 1), mean(x[c2])) }第三步,通過apply調用上面自定義的函數
apply(x, 1, myFUN, c1='x1', c2=c('x1', 'x2')) [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8][1,] 11.0 11 11.0 11 11 11.0 11 11.0[2,] 5.5 6 6.5 7 6 6.5 7 7.5是不是很簡單,通過for循環的方式,也可以很容易的實現上面計算過程,但是需要一些額外的操作,比如構建循環體、定義結果數據集、合併每次循環的結果到結果數據集。
lapply函數
lapply函數是一個最基礎循環操作函數之一,用來對list、data.frame數據集進行循環,並返回和X長度同樣的list結構作為結果集,通過lapply的開頭的第一個字母』l』就可以判斷返回結果集的類型。
下面以計算list中的每個元素對應數據的分位數為例,展示該函數的特性。
x <- list(a = 5:20, b = rnorm(6,10,5), c = c(FALSE,FALSE,TRUE,TRUE,TRUE,TRUE))lapply(x, fivenum)$`a`[1] 5.0 8.5 12.5 16.5 20.0
$b[1] 3.772836 11.750169 14.611470 17.395703 23.898115
$c[1] 0 0 1 1 1可以看到,lapply很方便地把list數據集進行循環操作了,此外,它還可以對data.frame數據集按列進行循環,但如果傳入的數據集是一個向量或矩陣對象,那麼直接使用lapply就不能達到想要的效果了,lapply會分別循環矩陣中的每個值,而不是按行或按列進行分組計算。
x <- cbind(x=3, y=c(2:1, 4:5))lapply(data.frame(x), sum)$x[1] 12
$y[1] 12sapply函數
sapply函數是一個簡化版的lapply,sapply增加了2個參數simplify和USE.NAMES,主要就是讓輸出看起來更友好,返回值為向量,而不是list對象。
vapply函數
vapply類似於sapply,提供了FUN.VALUE參數,用來控制返回值的行名,這樣可以讓程序更健壯。
mapply函數
mapply是sapply的變形函數,類似多變量的sapply,但是參數定義有些變化。第一參數為自定義的FUN函數,第二個參數』…』可以接收多個數據,作為FUN函數的參數調用。比如,比較3個向量大小,按索引順序取較大的值。
# 定義3個向量x <- 4:10y <- 10:4z <- round(runif(7, -5, 5))
# 按索引順序取較大的值。mapply(max, x, y, z)[1] 10 9 8 7 8 9 10又比如想生成4個符合正態分布的數據集,分別對應的均值和方差為c(1,10,100,1000)。
# m為均值,v為方差m <- v <- c(1, 10, 100, 1000)
# 生成4組數據,按列分組mapply(rnorm, rep(4,4), m, v)[,1] [,2] [,3] [,4][1,] 1.1321892 11.119809 -12.65840 525.4851[2,] 3.3765492 8.032937 -44.84486 700.5648[3,] 0.1849429 6.717259 157.55199 1430.6167[4,] 1.8121539 15.392016 32.86186 1621.0613tapply函數
tapply用於分組的循環計算,通過INDEX參數可以把數據集X進行分組,相當於group by的操作。例如,計算不同品種的鳶尾花的花瓣(iris)長度的均值。
# 通過iris$Species品種進行分組tapply(iris$Petal.Length, iris$Species, mean) setosa versicolor virginica 1.462 4.260 5.552rapply函數
rapply是一個遞歸版本的lapply,它只處理list類型數據,對list的每個元素進行遞歸遍歷,如果list包括子元素則繼續遍歷。例如,對一個list的數據進行過濾,把所有數字型的數據進行從小到大的排序。
x <- list(a=12, b=1:4, c=c('b', 'a'))y <- data.frame(a=rnorm(10), b=1:10)lst <- list(x=x, y=y)rapply(lst, sort, classes='numeric', how='replace')$`x`$`x`$`a`[1] 12
$`x`$b[1] 1 2 3 4
$`x`$c[1] "b" "a"
$ya b1 -2.28818912 12 -1.11604471 23 -0.71061407 34 -0.47929464 45 -0.45064871 56 -0.32539516 67 -0.09147763 78 0.32032781 89 1.67678507 910 1.71896910 10eapply函數
對一個環境空間中的所有變量進行遍歷。eapply函數平時很難被用到,但對於R包開發來說,環境空間的使用是必須要掌握的。特別是當R要做為工業化的工具時,對變量的精確控制和管理是非常必要的。
總結
從上面這8個函數的參數定義,我們可以發現它們都接收一個函數作為它的參數,在編程的世界裡,這種把函數作為參數傳入的函數稱為高階函數,函數式編程就是指這種高度抽象的編程範式。這種編程範式與面向對象的範式的差異如下圖