Tại sao `[` tốt hơn` tập hợp con?


400

Khi tôi cần lọc data.frame, nghĩa là trích xuất các hàng đáp ứng một số điều kiện nhất định, tôi thích sử dụng subsethàm:

subset(airquality, Month == 8 & Temp > 90)

Thay vì [chức năng:

airquality[airquality$Month == 8 & airquality$Temp > 90, ]

Có hai lý do chính cho sở thích của tôi:

  1. Tôi thấy mã đọc tốt hơn, từ trái sang phải. Ngay cả những người không biết gì về R cũng có thể nói những gì subsettuyên bố trên đang làm.

  2. Vì các cột có thể được gọi là các biến trong selectbiểu thức, tôi có thể lưu một vài tổ hợp phím. Trong ví dụ của tôi ở trên, tôi chỉ phải gõ airqualitymột lần với subset, nhưng ba lần với [.

Vì vậy, tôi đã sống hạnh phúc, sử dụng subsetở mọi nơi vì nó ngắn hơn và đọc tốt hơn, thậm chí ủng hộ vẻ đẹp của nó cho các lập trình viên R của tôi. Nhưng hôm qua thế giới của tôi tan vỡ. Trong khi đọc subsettài liệu, tôi nhận thấy phần này:

Cảnh báo

Đây là một chức năng tiện lợi dành cho sử dụng tương tác. Để lập trình, tốt hơn là sử dụng các hàm tập hợp tiêu chuẩn như [và đặc biệt là việc đánh giá không chuẩn của tập hợp đối số có thể có các hậu quả không lường trước được.

Ai đó có thể giúp làm rõ ý nghĩa của các tác giả?

Đầu tiên, ý nghĩa của việc " sử dụng tương tác " là gì? Tôi biết phiên tương tác là gì, trái ngược với tập lệnh chạy ở chế độ BATCH nhưng tôi không thấy sự khác biệt của nó.

Sau đó, bạn có thể vui lòng giải thích " đánh giá không chuẩn của tập hợp đối số " và tại sao nó nguy hiểm, có thể cung cấp một ví dụ không?


14
Nó hơi ít (nhưng ít hơn so với tập hợp con) để sử dụng với,with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker

7
Bạn cũng có thể xem Cirlces 8.2.31 và 8.2.32 của 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf
Patrick Burns

9
Thay vào đó, hãy thử data.table, cú pháp mặc định giống như airquality [Tháng == 8 & Temp> 90,] - rất dễ đọc và nhanh hơn nhiều.
Stian Håklev

3
ĐỒNG Ý. Vì vậy, nếu tập hợp con không tốt để sử dụng - còn [so với dplyr :: filter () thì sao?
userJT

4
Đối với những người thắc mắc, dplyr::filtercó cùng một vấn đề. Tức là nếu môi trường xảy ra có một biến với tên đó, nó sẽ sử dụng nó thay vì biến trong khung dữ liệu. Làm cho việc gỡ lỗi khó hiểu!
Deleet

Câu trả lời:


241

Câu hỏi này đã được trả lời rất tốt trong các bình luận của @James, chỉ ra một lời giải thích tuyệt vời của Hadley Wickham về sự nguy hiểm của subset(và các chức năng như nó) [ở đây] . Đi đọc đi!

Đây là một bài đọc hơi dài, vì vậy có thể hữu ích khi ghi lại ở đây ví dụ mà Hadley sử dụng để giải quyết trực tiếp nhất câu hỏi "điều gì có thể sai?":

Hadley gợi ý ví dụ sau: giả sử chúng ta muốn tập hợp con và sau đó sắp xếp lại khung dữ liệu bằng các hàm sau:

scramble <- function(x) x[sample(nrow(x)), ]

subscramble <- function(x, condition) {
  scramble(subset(x, condition))
}

subscramble(mtcars, cyl == 4)

Điều này trả về lỗi:

Lỗi trong eval (expr, envir, thùng): không tìm thấy đối tượng 'trụ'

bởi vì R không còn "biết" nơi tìm đối tượng được gọi là 'trụ'. Ông cũng chỉ ra những thứ thực sự kỳ quái có thể xảy ra nếu tình cờ có một vật thể gọi là 'hình trụ' trong môi trường toàn cầu:

cyl <- 4
subscramble(mtcars, cyl == 4)

cyl <- sample(10, 100, rep = T)
subscramble(mtcars, cyl == 4)

(Chạy chúng và tự mình xem, điều đó thật điên rồ.)


2
Tôi có thể có một số câu hỏi cho người mới để làm rõ? Khi chúng ta viết subset(mtcars, cyl == 4)(ở cấp cao nhất), R tìm xi lanh ở đâu? Nếu nó nhìn vào mtcarsđối tượng được truyền tới subset(), thì nó không thể tìm thấy cylngay cả khi scramblenó nằm trong một chức năng khác, vì mtcarsvẫn còn được truyền cho nó? Nếu câu hỏi của tôi không có ý nghĩa, bạn có thể giải thích thêm về lý do tại sao R không thể tìm thấy nữa cyl. Cảm ơn!
Heisenberg

4
@Anh Bên trong subset.data.frame, điều chúng tôi đang cố gắng đánh giá vào thời điểm đó chỉ là condition. Điều đó không tồn tại trong mtcars. Vì vậy, subset.data.framesử dụng enclos = parent.frame()để đảm bảo rằng conditionđược đánh giá chính xác như cyl == 4. Nhưng sau đó, chúng tôi đã xuất hiện trở lại khung kèm theo, và bây giờ khi R tìm kiếm cylnó không còn nhìn vào bên trong mtcarsnữa. Nếu chúng tôi không sử dụng enclos, một cái gì đó như subset(mtcars,cyl == a)sẽ không hoạt động.
Joran

có ai biết tại sao tập hợp con () sẽ không thực hiện phương thức [,] nhanh hơn và an toàn hơn đằng sau hậu trường không?
Người hâm mộ số một của Bjork

1
@MikePalmice Nó làm. Dòng cuối cùng subset.data.framex[r, vars, drop = drop]. Vấn đề là làm thế nào để có được từ không được trích dẫn subsetselectđối số đến một cái gì đó mà bạn có thể chuyển đến một cách hợp lệ [.data.frame.
joran

@joran hiểu rồi, cảm ơn. Bạn nghĩ thế nào về việc có nên sử dụng bộ lọc của dplyr thay vì []?
Người hâm mộ số một của Bjork

30

Cũng [nhanh hơn:

require(microbenchmark)        
microbenchmark(subset(airquality, Month == 8 & Temp > 90),airquality[airquality$Month == 8 & airquality$Temp > 90,])
    Unit: microseconds
                                                           expr     min       lq   median       uq     max neval
                     subset(airquality, Month == 8 & Temp > 90) 301.994 312.1565 317.3600 349.4170 500.903   100
     airquality[airquality$Month == 8 & airquality$Temp > 90, ] 234.807 239.3125 244.2715 271.7885 340.058   100

36
Có và không. Tôi nghĩ rằng sự khác biệt thời gian bạn đang thấy là do hai điều. 1) một chi phí nhỏ (<100 micro giây) và 2) subsetkhông giống như [loại bỏ các hàng nơi bộ lọc ước tính NA. Làm điều này và bạn sẽ thấy rằng cả hai đều nhanh như vậy khi so sánh "khá":x <- do.call(rbind, rep(list(airquality), 100)); microbenchmark(subset(x, Month == 8 & Temp > 90),{ i <- x$Month == 8 & x$Temp > 90; x[!is.na(i) & i ,] })
flodel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.