R 學習筆記:dplyr 像操作資料庫一樣操作 R

tidyverse 中包括一個包 dplyr. 正如其描述所說: 操作數據的語法 (a grammar of data manipulation). dplyr 融合了很多在 SQL 資料庫中對數據操作的思想, 使得對於數據表 (tibble) 的操作有邏輯且一致. 這樣的好處在於, 如果是取用資料庫中的分析, 那麼所採取的操作步驟並不會有太大的改變, 使用 dbplyr 包就可以方便的用同樣的函數去處理資料庫中的表格.


選取數據

在 SQL 資料庫中, 我們做任何的操作首先需要選擇數據, 在 R 中同樣也是.

例如我們還拿 iris 的數據來做例子. iris 數據長下面這樣:

head(iris) # Sepal.Length Sepal.Width Petal.Length Petal.Width Species # 1 5.1 3.5 1.4 0.2 setosa # 2 4.9 3.0 1.4 0.2 setosa # 3 4.7 3.2 1.3 0.2 setosa

如果我們想要選擇 Species 列的數據. 在 R 中可以用 data.frame 的選擇數據的方法.

iris$Speciesiris[["Species"]]iris[, "Species"]

那麼假如 iris 是 SQL 資料庫中的一個表呢? 我們大概有以下的辦法:

SELECT Species FROM iris;

dplyr 中提供了 select 函數具有和 SQL 語法邏輯類似的函數 select.

select(iris, Species)

還可以使用很有特色的 %>% 導管運算符.

iris %>% select(Species)

我們可以知道 Species 列中所包含的物種種加詞有如下幾個:

table(iris %>% select(Species)) # setosa versicolor virginica # 50 50 50

下面我們想要只選擇 setosa 物種的數據.

使用 R 默認方法我們會如下做.

setosa <- iris[iris$Species == "setosa", ]

如果使用 SQL 去選擇, 我們可能會寫下如下類似的代碼.

SELECT * FROM iris WHERE Species = "setosa";

利用 dplyr 的 select 函數我們會有如下的代碼.

iris %>% select(everything()) %>% filter(Species == "setosa") # Sepal.Length Sepal.Width Petal.Length Petal.Width Species # 1 5.1 3.5 1.4 0.2 setosa # 2 4.9 3.0 1.4 0.2 setosa # 3 4.7 3.2 1.3 0.2 setosa

這裡 select 實際上沒有起到任何作用, 但是顯示出來如果我們要進行多步驟的操作, %>% 運算符會讓代碼很清晰簡介.

如果我們沒有使用 %>% 則會有下面的代碼.

filter(select(iris, everything()), Species == "setosa")

這樣看起來代碼就多了好多層, 不如一層清晰.

那麼, 我們且用起來 select 函數, 假如我們只需要 setosa 花瓣的信息, 我們可以這麼做.

iris %>% select(Species, starts_with("Petal")) %>% filter(Species == "setosa") # Species Petal.Length Petal.Width # 1 setosa 1.4 0.2 # 2 setosa 1.4 0.2 # 3 setosa 1.3 0.2

又如果, 我們先要看 setosa 所有特徵的寬度, 我們可以這麼做.

iris %>% select(Species, ends_with("Width")) %>% filter(Species == "setosa") # Species Sepal.Width Petal.Width # 1 setosa 3.5 0.2 # 2 setosa 3.0 0.2 # 3 setosa 3.2 0.2

我們也可以使用 select 對列進行重命名, 相當於 SQL 的 AS.

SQL 中代碼類似如下:

SELECT Species AS sp.name FROM iris;

select 函數的代碼可以如下寫.

iris %>% select(sp.name = Species) # sp.name # 1 setosa # 2 setosa # 3 setosa

這裡需要注意的是, 我們使用 select 函數則在結果中只會保留選擇的列.

如果我們只是需要對特定列進行重命名, 而需要保留原來其他的列, 則可以使用 rename 函數.

iris %>% rename(sp.name = Species) # Sepal.Length Sepal.Width Petal.Length Petal.Width sp.name # 1 5.1 3.5 1.4 0.2 setosa # 2 4.9 3.0 1.4 0.2 setosa # 3 4.7 3.2 1.3 0.2 setosa

上面我們看到了 everything 等函數, 還有如下函數在 select 函數中使用.

  • starts_with(match, ignore.case = TRUE, vars = current_vars()): 列名以前綴開始;
  • ends_with(match, ignore.case = TRUE, vars = current_vars()): 列名以後綴結束;
  • contains(match, ignore.case = TRUE, vars = current_vars()): 列名中包含指定字元串;
  • matches(match, ignore.case = TRUE, vars = current_vars()): 列名匹配正則表達式;
  • num_range(prefix, range, width = NULL, vars = current_vars()): prefix 接受一個前綴, range 接受數字序列;
  • one_of(..., vars = current_vars()): 列名包含在其中;
  • everything(vars = current_vars()): 所有的列名.

改變列值

有的時候, 當前的列值並不滿足我們的使用, 我們需要對當前列的值進行處理,例如求 log, 絕對值等, 也可能對多列同時進行處理獲得對應的值來進行下游的分析. 這種情況下我們可以使用 dplyr 中的 mutate 函數和 transmutate 函數. 這兩個函數的區別在於, mutate 函數會保留修改後的列和修改之前的列, 而 transmute 函數則會保留修改後的列而丟棄修改之前的列.

例如, 我們要求鳶尾花的萼片和花瓣的 "面積", 可以假設是橢圓形.

iris %>% mutate( Sepal.area = pi * Sepal.Length * Sepal.Width) %>% select(Species, starts_with("Sepal")) # Species Sepal.Length Sepal.Width Sepal.area # 1 setosa 5.1 3.5 56.07743 # 2 setosa 4.9 3.0 46.18141 # 3 setosa 4.7 3.2 47.24955iris %>% transmute( Sepal.area = pi * Sepal.Length * Sepal.Width) # Sepal.area # 56.07743 # 46.18141 # 47.24955

可以用於 mutate 和 transmute 函數還有許多輔助函數, 有些是 base 包中的常規函數, 有些是 dplyr 包中的提供的函數.

  • log(), log2(), log10(): 對值求 log;
  • lead(), lag(): 返回序列中當前位置前第幾個值或後第幾個值;
  • row_number(): 結果等於 rank(ties.method = "first"), 即對於相同的數值的排名按照先後順序排;
  • min_rank(): 結果等於 `rank(ties.method = "min"), 即對於相同的數值的排名都取最小的排名;
  • dense_rank(): 結果類似於 min_rank(), 差別在於填充了由於 min_rank 造成的排名空隙;
  • percent_rank(): 把 min_rank() 的值轉換為 0 到 1 區間;
  • cume_dist(): 計算比當前值還小的值的比例, 相當於計算 density;
  • ntile(): 把數據分成若干塊, 看每個數據在具體拿一個塊;
  • cumsum(), cummean(), cummin(), cummax(), cumany(), cumall(): 累積地 (cumulative) 計算和 (sum), 均值 (mean), 最小值 (min), 最大值 (max), 任何為真 (any), 所有為真 (all);
  • na_if(): 把特定地值轉換為 NA;
  • coalesce(): 找出若干列中第一個不為 NA 的值;
  • if_else(): 向量化的 ifelse 函數的效果.
  • recode: 把一系列值轉換為其他值
  • case_when: 多條件選擇.

theseq <- c(5, 1, 3, 2, 2, NA)lead(theseq, 1) # 最後一位缺少的補 NA # [1] 1 3 2 2 NA NAlag(theseq, 1) # 第一位缺少的補 NA # [1] NA 5 1 3 2 2row_number(theseq) # [1] 5 1 4 2 3 NAmin_rank(theseq) # [1] 5 1 4 2 2 NAdense_rank(theseq) # [1] 4 1 3 2 2 NAcume_dist(theseq) # [1] 1.0 0.2 0.8 0.6 0.6 NAntile(theseq, 3) # [1] 3 1 2 1 2 NA cumsum(theseq) # [1] 5 6 9 11 13 NAna_if(theseq, 2) # [1] 5 1 3 NA NA NAcoalesce(theseq, c(NA, 1, 2, NA, 4, 5)) # [1] 5 1 3 2 2 5recode(theseq, "2" ="two", "5"="five") # [1] "five" NA NA "two" "two" NAcase_when( theseq %% 2 == 0 ~ "even", theseq %% 2 == 1 ~ "odd" ) # [1] "odd" "odd" "odd" "even" "even" NA


分割-應用-整合

Hadley Wickham 在其關於 plyr 包的文章中抽象出了數據分析中的範式: 分割-應用-整合 (split-apply-combine). 這個範式在 dplyr 包中的實現就是 group_by 和 summarize 函數.

拿我們的 iris 數據來說, 如果我想要知道每個物種的每一種屬性的均值, 就可以使用 group_bysummarize 函數來獲得.

iris %>% group_by(Species) %>% summarize( Sepal.Width=mean(Sepal.Width), Sepal.Length=mean(Sepal.Length), Petal.Length=mean(Petal.Length), Petal.Width=mean(Petal.Width) ) # Species Sepal.Width Sepal.Length Petal.Length Petal.Width # <fctr> <dbl> <dbl> <dbl> <dbl> # 1 setosa 3.428 5.006 1.462 0.246 # 2 versicolor 2.770 5.936 4.260 1.326 # 3 virginica 2.974 6.588 5.552 2.026

同樣, 也有眾多的函數可以用於 summarize 函數.

  • mean(), median(), max(), min(), sd(),

    IQR(), mad() 等統計函數.
  • first(), last(), nth(): 返回第幾位的值.
  • n(): 計算數據的數量, 相當於 length() 函數.
  • n_distinct(): 計算非重複數據的數量, 相當於 length(unique(x)).
  • any(), all(): 邏輯計算函數.

排序

在分析的時候需要對數據進行排序, dplyr 提供了 arrange 函數. 例如我們按照 Petal.Width 對數據進行從小到大排序.

iris %>% arrange(Petal.Width) # Sepal.Length Sepal.Width Petal.Length Petal.Width Species # 1 4.9 3.1 1.5 0.1 setosa # 2 4.8 3.0 1.4 0.1 setosa # 3 4.3 3.0 1.1 0.1 setosa

配合 desc 函數可以實現從大到小排序.

iris %>% arrange(desc(Petal.Length), Petal.Width) # Sepal.Length Sepal.Width Petal.Length Petal.Width Species # 1 7.7 2.6 6.9 2.3 virginica # 2 7.7 2.8 6.7 2.0 virginica # 3 7.7 3.8 6.7 2.2 virginica


總結

綜上, 我們了解到 dplyr 包提供了眾多規範地對數據表格進行操作的函數, 這些函數一方面方便了我們達到數據處理的目的, 另一方面使得代碼邏輯清晰.

  • 選擇數據表的列: select, rename
  • select 只會選擇你指定的列
  • rename 則會改變列名, 並選擇其他所有的列
  • 選擇數據表的行: filter
  • 改變數據表的列: mutate, transmute
  • mutate 會保留改變前和改變後的列
  • transmute 則只會保留改變後的列, 而扔掉改變前的列
  • 通過 group_bysummarize 函數可以把數據進行分組進行分析

推薦閱讀:

R初學者如何自助
R語言可視化學習筆記之基因組數據可視化
一技之長——我的大數據學習之路
請用正確的姿勢維護新手的熱情和自信----學慣用R操作資料庫MySQL以及項目模塊化的應用

TAG:R编程语言 | 生物信息学 |