R學(xué)習(xí)往期回顧: R學(xué)習(xí):R for Data Science 循環(huán)-迭代(for while)) R學(xué)習(xí):R for Data Science 向量(1) R學(xué)習(xí):R for Data Science 向量(2) R學(xué)習(xí) 從Tidyverse學(xué)起,入門R語(yǔ)言 dplyr合并數(shù)據(jù) R學(xué)習(xí) 流程控制 if,else,ifelse R學(xué)習(xí) 從Tidyverse學(xué)起,入門R語(yǔ)言(tidyr和stringr) R學(xué)習(xí) 從Tidyverse學(xué)起,入門R語(yǔ)言(tibble,readr和dplyr) R學(xué)習(xí):數(shù)據(jù)框的基本操作 R學(xué)習(xí):R for Data Science(五) R學(xué)習(xí):R for Data Science(四) R學(xué)習(xí):R for Data Science(三) R學(xué)習(xí):R for Data Science(二) R學(xué)習(xí):R for Data Science(一) for循環(huán)與函數(shù)式編程for 循環(huán)在 R 中不像在其他語(yǔ)言中那么重要,因?yàn)?R 是一門函數(shù)式編程語(yǔ)言。這意味著可以先將 for 循環(huán)包裝在函數(shù)中,然后再調(diào)用這個(gè)函數(shù),而不是直接使用 for 循環(huán) library(tidyverse) df <- tibble( a = rnorm(10), b = rnorm(10), c = rnorm(10), d = rnorm(10) ) 假設(shè)想要計(jì)算每列的均值。你可以使用 for 循環(huán)來完成這個(gè)任務(wù) output <- vector("double", length(df)) for (i in seq_along(df)) { output[[i]] <- mean(df[[i]]) } output 將這段代碼提取出來,轉(zhuǎn)換成一個(gè)函數(shù): col_mean <- function(df) { output <- vector("double", length(df)) for (i in seq_along(df)) { output[i] <- mean(df[[i]]) } output } 還可以算每列的中位數(shù)和標(biāo)準(zhǔn)差 col_median <- function(df) { output <- vector("double", length(df)) for (i in seq_along(df)) { output[i] <- median(df[[i]]) } output } col_sd <- function(df) { output <- vector("double", length(df)) for (i in seq_along(df)) { output[i] <- sd(df[[i]]) } output } 通過添加支持函數(shù)應(yīng)用到每列的一個(gè)參數(shù),我們可以使用同一個(gè)函數(shù)完成與 col_mean()、col_median() 和 col_sd() 函數(shù)相同的操作: col_summary <- function(df, fun) { out <- vector("double", length(df)) for (i in seq_along(df)) { out[i] <- fun(df[[i]]) } out } col_summary(df, median) col_summary(df, mean) 將函數(shù)作為參數(shù)傳入另一個(gè)函數(shù)的這種做法是一種非常強(qiáng)大的功能,它是促使 R 成為函數(shù)式編程語(yǔ)言的因素之一。 使用 purrr 函數(shù)代替 for 循環(huán)的目的是將常見的列表處理問題分解為獨(dú)立的幾個(gè)部分。 · 如果你面臨的是一個(gè)非常復(fù)雜的問題,那么如何將其分解為幾個(gè)可行的子問題,然后循序漸進(jìn)地解決,直至完成最終的解決方案?使用 purrr,你可以解決很多子問題,然后再通過管道操作將這些問題的結(jié)果組合起來。 映射函數(shù)先對(duì)向量進(jìn)行循環(huán)、然后對(duì)其每個(gè)元素進(jìn)行一番處理,最后保存結(jié)果。這種模式太普遍了,因此 purrr 包提供了一個(gè)函數(shù)族來替你完成這種操作。每種類型的輸出都有一個(gè)相應(yīng)的函數(shù): 每個(gè)函數(shù)都使用一個(gè)向量作為輸入,并對(duì)向量的每個(gè)元素應(yīng)用一個(gè)函數(shù),然后返回和輸入向量同樣長(zhǎng)度(同樣名稱)的一個(gè)新向量。向量的類型由映射函數(shù)的后綴決定。 我們可以使用這些函數(shù)來執(zhí)行與最后一個(gè) for 循環(huán)相同的操作。因?yàn)槟切┱瘮?shù)返回的是雙精度數(shù),所以我們需要使用 map_dbl() 函數(shù): map_dbl(df, median) map_dbl(df, mean) map_*() 和 col_summary() 具有以下幾點(diǎn)區(qū)別 · 所有 purrr 函數(shù)都是用 C 實(shí)現(xiàn)的。這使得它們的速度非??欤珷奚艘恍┛勺x性 · 第二個(gè)參數(shù)(即 .f,要應(yīng)用的函數(shù))可以是一個(gè)公式、一個(gè)字符向量或一個(gè)整型向量 · 映射函數(shù)還可以保留名稱 快捷方式 對(duì)于參數(shù) .f,你可以使用幾種快捷方式來減少輸入量。假設(shè)你想對(duì)某個(gè)數(shù)據(jù)集中的每個(gè)分組都擬合一個(gè)線性模型。以下這個(gè)簡(jiǎn)單示例將 mtcars 數(shù)據(jù)集拆分成 3 個(gè)部分(按照氣缸的值分類),并對(duì)每個(gè)部分?jǐn)M合一個(gè)線性模型: models <- mtcars %>% split(.$cyl) %>% map(function(df) lm(mpg ~ wt, data = df)) 因?yàn)?R 中創(chuàng)建匿名函數(shù)的語(yǔ)法比較繁瑣,所以 purrr 提供了一種更方便的快捷方式——單側(cè)公式: models <- mtcars %>% split(.$cyl) %>% map(~lm(mpg ~ wt, data = .)) 我們?cè)谝陨鲜纠惺褂昧?. 作為一個(gè)代詞:它表示當(dāng)前列表元素(與 for 循環(huán)中用 i 表示當(dāng)前索引是一樣的) 需要提取出 R平方 這樣的摘要統(tǒng)計(jì)量 需要先運(yùn)行 summary() 函數(shù),然后提取出結(jié)果中的 r.squared。我們可以使用匿名函數(shù)的快捷方式來完成這個(gè)操作: models %>% map(summary) %>% map_dbl(~.$r.squared) 因?yàn)樘崛∶煞值倪@種操作非常普遍,所以 purrr 提供了一種更為簡(jiǎn)潔的快捷方式:使用字符串 models %>% map(summary) %>% map_dbl("r.squared") 后面需要有apply()家族函數(shù)的基礎(chǔ)知識(shí),為了循序漸進(jìn),后面我們將會(huì)介紹apply家族,下回見。 |
|