Menu

Lọc dữ liệu trong R: subset, [ ] và filter của dplyr

Mọi cách giữ lại các hàng và cột bạn cần: mặt nạ logic với ngoặc vuông, subset() trong một dòng, %in%, cái bẫy NA, cùng filter() và select() của dplyr.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Lọc hàng: mặt nạ logic

Để lọc một data frame trong R, đặt điều kiện vào vị trí hàng trong ngoặc vuông: df[condition, ]. Điều kiện được đánh giá thành một vector TRUE/FALSE, mỗi hàng một giá trị, và R giữ lại các hàng TRUE:

Đọc scores[scores$score > 80, ] từ trong ra ngoài: scores$score > 80 tạo ra TRUE FALSE TRUE TRUE FALSE, và ngoặc vuông giữ lại các hàng 1, 3 và 4. Dấu phẩy ở cuối không phải tùy chọn. Ngoặc vuông trên data frame nhận [rows, columns]; để trống vị trí cột nghĩa là "mọi cột". Quên dấu phẩy là bạn đang lấy cột thay vì hàng, một trong những lỗi phổ biến nhất của người mới học R.

Để ý số thứ tự hàng được in ra là 1 3 4, không phải 1 2 3: lọc dữ liệu giữ nguyên nhãn hàng ban đầu. Vô hại, nhưng nhiều người bất ngờ.

Nhiều điều kiện: & và | (không phải &&)

Kết hợp các điều kiện bằng & (AND) và | (OR). Mỗi điều kiện phải là một phép so sánh đầy đủ:

Cái bẫy: R cũng có && và ||, và ở đây chúng không thay thế được cho & và |. Dạng kép dùng cho giá trị đơn lẻ (chúng tồn tại cho điều kiện if); khi nhận cả cột, R hiện đại (4.3+) dừng với lỗi kiểu 'length = 5' in coercion to 'logical(1)', còn R cũ hơn lặng lẽ chỉ so sánh hàng đầu tiên, có lẽ còn tệ hơn. Bên trong ngoặc vuông, luôn dùng & và | đơn. Bài về toán tử giải thích đầy đủ sự khác biệt này.

%in%: so khớp với một tập giá trị

Khi một cột cần khớp với bất kỳ giá trị nào trong vài giá trị, đừng nối == bằng |, hãy dùng %in%:

x %in% set trả về TRUE ở mọi chỗ x là một trong các giá trị của set. Nó dễ đọc hơn status == "pending" | status == "shipped", mở rộng được cho bao nhiêu giá trị cũng được, và phủ định gọn gàng: !(orders$status %in% c("refunded")).

Chọn cột

Vị trí cột trong ngoặc vuông nhận tên hoặc vị trí:

Hai lưu ý. Chọn theo tên vẫn đúng khi thứ tự cột thay đổi; chọn theo vị trí (scores[, c(1, 3)]) sẽ hỏng vào ngày ai đó chèn thêm một cột. Và lấy một cột duy nhất sẽ trả về vector thường, không phải data frame: đó là lý do print() thứ hai hiện 91 88 mà không có bảng bao quanh. Khi cần giữ hình dạng data frame, thêm drop = FALSE: scores[, "score", drop = FALSE].

subset(): cách viết một dòng thân thiện của base R

Base R có sẵn một hàm bao gói xử lý cả hàng lẫn cột trong một lần gọi dễ đọc, không cần $, không phải lo dấu phẩy:

Bên trong subset() bạn viết tên cột trần (score, không phải scores$score); hàm sẽ đánh giá chúng dựa trên data frame. Cơ chế này gọi là đánh giá phi chuẩn (non-standard evaluation), và nó đi kèm một lưu ý đã được ghi trong tài liệu: nó phân giải tên lúc chạy theo những cách hoạt động sai bên trong hàm của chính bạn (một biến tên score trong hàm có thể bị che bởi một cột tên score). Quy tắc kinh nghiệm, lấy thẳng từ ?subset: rất tốt khi làm việc tương tác, nhưng tránh dùng khi lập trình; ở đó hãy dùng chỉ số ngoặc vuông.

Cái bẫy NA

So sánh với NA cho ra NA, không phải FALSE, và lọc bằng ngoặc vuông giữ các hàng có mặt nạ NA thành những hàng toàn NA:

Kết quả đầu tiên chứa một hàng rác toàn NA vì điều kiện của hàng 2 không phải TRUE cũng không phải FALSE. Cách sửa là yêu cầu !is.na() một cách tường minh. subset() và filter() của dplyr đều lặng lẽ bỏ các hàng có điều kiện NA: tiện, nhưng hãy biết là điều đó đang xảy ra. Bài về giá trị thiếu giải thích vì sao NA lan truyền như vậy.

Cách của dplyr: filter() và select()

Package dplyr tách công việc thành hai động từ, filter() cho hàng và select() cho cột, với tên cột trần và không phải lặp lại df$ (code tĩnh; môi trường chạy thử chỉ chạy base R):

library(dplyr)

scores |> filter(score > 80)
scores |> filter(score > 80, team == "red")     # comma = AND
scores |> filter(status %in% c("pending", "shipped"))
scores |> select(name, score)
scores |> filter(team == "red") |> select(name, score)

filter() tự động bỏ các hàng có điều kiện NA và luôn trả về data frame (không bao giờ là vector trần), giải quyết cả hai cái bẫy của base R ở trên. Cái giá là phụ thuộc vào một package và cùng những lưu ý về đánh giá phi chuẩn như subset(); xem bài nhập môn dplyr để đi hết các động từ.

Những điều cần nhớ

  • df[condition, ] là cách làm nền tảng, và dấu phẩy là bắt buộc.
  • Kết hợp điều kiện bằng & và | đơn; && báo lỗi với vector.
  • %in% tốt hơn chuỗi == khi so khớp với một tập giá trị.
  • Chọn cột theo tên, và nhớ rằng một cột đơn sẽ rơi xuống thành vector trừ khi có drop = FALSE.
  • subset() là cách viết một dòng dễ chịu khi làm việc tương tác; ngoặc vuông là cách dùng được khi lập trình.
  • So sánh với NA tạo ra hàng ma khi dùng ngoặc vuông; hãy chặn bằng !is.na().

Tiếp theo: thêm và biến đổi cột, df$new <- ..., ifelse() và mutate() của dplyr.

Câu hỏi thường gặp

Làm sao để lọc các hàng của data frame trong R?

Đặt một điều kiện logic vào vị trí hàng trong ngoặc vuông: df[df$score > 80, ]. Điều kiện tạo ra một vector TRUE/FALSE, và R giữ lại các hàng TRUE. Dấu phẩy rất quan trọng: nó tách phần lọc hàng khỏi phần lọc cột (để trống). subset(df, score > 80) làm điều tương tự mà gõ ít hơn.

Làm sao để lọc với nhiều điều kiện trong R?

Kết hợp các điều kiện bằng & (AND) và | (OR): df[df$score > 80 & df$team == "red", ]. Dùng & đơn, không phải &&: dạng kép chỉ dùng cho giá trị đơn lẻ và báo lỗi với vector trong R hiện đại.

subset() và lọc bằng ngoặc vuông trong R khác nhau thế nào?

Chúng giữ lại cùng các hàng, với hai khác biệt: subset() lặng lẽ bỏ các hàng mà điều kiện là NA (ngoặc vuông giữ chúng thành các hàng toàn NA), và subset() dùng đánh giá phi chuẩn (non-standard evaluation): bạn viết tên cột trần, tiện khi làm việc tương tác nhưng không đáng tin bên trong hàm của chính bạn.

Làm sao để lọc các hàng mà một cột khớp với một danh sách giá trị?

Dùng %in%: df[df$team %in% c("red", "blue"), ] giữ lại các hàng có team là bất kỳ giá trị nào trong danh sách. Nó thay cho một chuỗi phép so sánh == nối bằng |, và khác với ==, nó không bao giờ trả về NA.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu