HOWTO · R

使用 Dplyr 從資料框中移除多個欄位

本文說明如何使用 dplyr 從資料框中移除多個欄位,並透過不同的選取輔助函式與運算子,在一行程式碼中完成操作。

本頁內容

若要使用 dplyr 移除多個欄位,可搭配 dplyr::select() 和補集運算子 -(或 !),保留除了選定移除項目以外的所有欄位。

如何使用 dplyr 移除多個欄位

若要移除的欄位固定,可直接指定名稱;若名稱存放在字元向量中,可使用 all_of() 或 any_of();連續欄位可用範圍選取;名稱模式可用輔助函式;若規則取決於資料,則可使用 where()。將轉換後的資料用於後續操作前,請先確認結果中的欄位名稱。

設定 R 工作階段與範例資料

dplyr 提供 select() 及下文會用到的 tidyselect 輔助函式。在 select() 中,未加引號的欄位名稱會被視為運算式;存放在物件中的名稱則必須是字元字串。以下範例展示 dplyr 語法;這個獨立的 base R 範例則示範相同的按名稱排除方式,不需要安裝套件。

請執行以下範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

records <- data.frame(
  id = 1:3,
  name = c("Ada", "Lin", "Sam"),
  score = c(91, 84, 88),
  debug_note = c("first", "second", "third"),
  group = c("A", "B", "A")
)

to_remove <- c("id", "debug_note")
remaining <- records[setdiff(names(records), to_remove)]
cat(paste(names(remaining), collapse = ", "), "\n", sep = "")
name, score, group

直接指定欄位名稱來移除

若要移除的欄位已事先確定,可在 select() 中直接寫出未加引號的名稱。例如,select(records, -c(id, debug_note)) 會移除這兩個欄位並保留其他欄位。也可以寫成 select(records, -id, -debug_note);兩種寫法表示相同的排除操作。

另一種等效寫法是對聯集取補集:select(records, -(id | debug_note))。運算子 | 會合併選取項目,前面的 - 則排除合併後的欄位集合。請選擇最容易看出預期欄位集合的寫法。也可以按位置排除欄位,例如 select(records, -c(1, 4));但欄位位置取決於欄位順序,因此若輸入資料結構可能改變,建議使用欄位名稱。

使用字元向量中的名稱移除欄位

若名稱來自設定、使用者選擇或先前的計算結果,可將名稱存成字串,再透過輔助函式傳入。select(records, -all_of(to_remove)) 要求指定的每個名稱都必須存在。若名稱缺漏代表預期結構與實際結構不符,這種寫法就很有用。

若名稱可能不存在,而預期操作是「移除其中實際存在的欄位」,請使用 select(records, -any_of(to_remove))。any_of() 讓移除操作能容許資料結構有所差異;它不會移除清單以外的欄位。若要建立可靠的資料處理流程,請依欄位缺漏應視為問題,還是可接受的常態,選擇合適的寫法。

移除一段連續欄位

冒號運算子會選取包含起點與終點的連續序列。select(records, -(name:score)) 會依資料目前的欄位順序,排除從 name 到 score 的欄位。select(records, -(2:4)) 則依位置執行相同操作。若範圍端點名稱穩定,使用名稱範圍很方便;位置範圍較簡潔,但欄位重新排列後,選到的欄位可能不同。這兩種語法都不表示依字母順序排列。

依欄位名稱模式移除欄位

使用 starts_with()、ends_with() 或 contains(),依共用前綴、後綴或子字串選取欄位名稱,再排除選取結果。以下範例使用一個為各個輔助函式準備的獨立資料框:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

pattern_records <- data.frame(
  id = 1:2,
  name = c("Ada", "Lin"),
  raw_payload = c("x", "y"),
  tmp_cache = c("p", "q"),
  score_old = c(7, 8),
  temp_flag = c(TRUE, FALSE),
  measure_1 = c(10, 20),
  measure_2 = c(30, 40),
  group = c("A", "B")
)

欄位名稱為 id、name、raw_payload、tmp_cache、score_old、temp_flag、measure_1、measure_2 和 group 時,select(pattern_records, -starts_with("raw_")) 會保留 id、name、tmp_cache、score_old、temp_flag、measure_1、measure_2 和 group。select(pattern_records, -starts_with(c("raw_", "tmp_"))) 也會移除 tmp_cache,並保留 id、name、score_old、temp_flag、measure_1、measure_2 和 group。select(pattern_records, -ends_with("_old")) 會保留 id、name、raw_payload、tmp_cache、temp_flag、measure_1、measure_2 和 group;select(pattern_records, -contains("temp")) 則會保留 id、name、raw_payload、tmp_cache、score_old、measure_1、measure_2 和 group。

這些輔助函式預設不區分大小寫;若大小寫必須影響名稱判斷,請設定 ignore.case = FALSE。若要使用正規表示式,可寫成 select(pattern_records, -matches("^measure_[0-9]+$"));這會移除 measure_1 和 measure_2,並保留 id、name、raw_payload、tmp_cache、score_old、temp_flag 和 group。此模式會比對以 measure_ 開頭、後接一個以上數字,且後面沒有其他字元的名稱。matches() 彈性很高,但只有在所有維護程式碼的人都能清楚理解該模式時才適合使用。

依數字名稱範圍移除欄位

若欄位名稱有一致的前綴和數字後綴,可使用 num_range() 選取一段後綴序列,無須逐一寫出完整名稱。使用上文列出的 pattern_records 欄位時,select(pattern_records, -num_range("measure_", 1:2)) 會移除 measure_1 和 measure_2,並保留 id、name、raw_payload、tmp_cache、score_old、temp_flag 和 group。第一個引數是共用前綴,第二個引數是數字序列。若後綴使用固定補零格式,例如名稱為 measure_01,請設定 width,如 width = 2。這個輔助函式依數字後綴選取欄位,不會根據欄位中儲存的值進行選取。

依述詞移除欄位

where() 會對每個欄位套用述詞,並選取回傳 TRUE 的欄位;若要移除這些欄位,請對選取結果取補集。例如,select(records, -where(is.character)) 會移除所有字元欄位。若要移除的欄位群組是由資料特性而非特定名稱決定,這種方式就很適合。

請選擇適用於所有受檢欄位的述詞。像 mean() 這類數值摘要函式不適用於任意混合型別的資料,而範圍過廣的規則也可能移除超出預期的欄位。若只想移除部分數值欄位,可結合型別檢查與適用於這些欄位的條件;也可以直接指定欄位名稱。

排解 select() 問題與查找說明

若未加命名空間的 select() 呼叫結果不如預期,可能是其他已載入的套件遮蔽了該函式。試試 dplyr::select(data, ...);如果這樣能正常運作,請使用命名空間前綴,或調整套件載入方式,確保呼叫到預期的函式。

dplyr select() 參考文件說明欄位選取方式,並連結至相關輔助函式。tidyselect 選取語言參考文件則解釋選取運算式與運算子。在 R 中查詢說明時,可使用 ?select,或在說明窗格中搜尋不帶括號的 select。

一般而言,請用清楚且合適的名稱、向量、範圍、模式或述詞選出要移除的欄位,再取其補集。這樣能讓程式碼的邏輯清楚表達哪些欄位會保留,也更容易在資料結構改變時調整選取方式。