dplyr biến đổi với các giá trị có điều kiện


87

Trong khung dữ liệu lớn ("myfile") có bốn cột, tôi phải thêm cột thứ năm với các giá trị có điều kiện dựa trên bốn cột đầu tiên.

Thích các câu trả lời có dplyrmutate, chủ yếu là do tốc độ của nó trong các tập dữ liệu lớn.

Khung dữ liệu của tôi trông như thế này:

  V1 V2 V3 V4
1  1  2  3  5
2  2  4  4  1
3  1  4  1  1
4  4  5  1  3
5  5  5  5  4
...

Các giá trị của cột thứ năm (V5) dựa trên một số quy tắc có điều kiện:

if (V1==1 & V2!=4) {
  V5 <- 1
} else if (V2==4 & V3!=1) {
  V5 <- 2
} else {
  V5 <- 0
}

Bây giờ tôi muốn sử dụng mutatehàm để sử dụng các quy tắc này trên tất cả các hàng (để tránh vòng lặp chậm). Một cái gì đó như thế này (và vâng, tôi biết nó không hoạt động theo cách này!):

myfile <- mutate(myfile, if (V1==1 & V2!=4){V5 = 1}
    else if (V2==4 & V3!=1){V5 = 2}
    else {V5 = 0})

Đây sẽ là kết quả:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

Làm thế nào để làm điều này trong dplyr?


Sẽ rất hữu ích nếu bạn nêu V1..4 là số nguyên (không phải thừa số, logic, chuỗi hoặc float)? và bạn có quan tâm đến việc xử lý chính xác không NA, ( NaN, +Inf, -Inf)?
smci

Nếu tốc độ dường như là một vấn đề cho việc thích hơn dplyr, thì tốt hơn tôi nên sử dụng data.table.
Valentin ngày

Câu trả lời:


105

Thử đi:

myfile %>% mutate(V5 = (V1 == 1 & V2 != 4) + 2 * (V2 == 4 & V3 != 1))

cho:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

hoặc cái này:

myfile %>% mutate(V5 = ifelse(V1 == 1 & V2 != 4, 1, ifelse(V2 == 4 & V3 != 1, 2, 0)))

cho:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

Ghi chú

Đề nghị bạn đặt tên tốt hơn cho khung dữ liệu của mình. myfile làm cho nó có vẻ như thể nó chứa một tên tệp.

Ở trên đã sử dụng đầu vào này:

myfile <- 
structure(list(V1 = c(1L, 2L, 1L, 4L, 5L), V2 = c(2L, 4L, 4L, 
5L, 5L), V3 = c(3L, 4L, 1L, 1L, 5L), V4 = c(5L, 1L, 1L, 3L, 4L
)), .Names = c("V1", "V2", "V3", "V4"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5"))

Cập nhật 1 Kể từ khi đăng ban đầu dplyr đã thay đổi %.%thành %>%vì vậy đã sửa đổi câu trả lời cho phù hợp.

Bản cập nhật 2 dplyr hiện có case_whencung cấp giải pháp khác:

myfile %>% 
       mutate(V5 = case_when(V1 == 1 & V2 != 4 ~ 1, 
                             V2 == 4 & V3 != 1 ~ 2,
                             TRUE ~ 0))

Tôi đã thử giải pháp thứ hai của bạn. Tôi gặp lỗi này: Lỗi trong mutate_impl (.data, names_dots (...), environment ()): REAL () chỉ có thể được áp dụng cho 'số', không phải 'lôgic'. Bạn có biết chuyện gì đang xảy ra không?
rdatasculptor

5
Tôi phát hiện ra một cách mà cho phép bạn không tổ ifelsebáo cáo:myfile %>% mutate(V5 = ifelse(V1 == 1 & V2 != 4, 1, 0), V5 = ifelse(V2 == 4 & V3 != 1, 2, V5))
Alex

31

Với dplyr 0.7.2, bạn có thể sử dụng case_whenchức năng rất hữu ích :

x=read.table(
 text="V1 V2 V3 V4
 1  1  2  3  5
 2  2  4  4  1
 3  1  4  1  1
 4  4  5  1  3
 5  5  5  5  4")
x$V5 = case_when(x$V1==1 & x$V2!=4 ~ 1,
                 x$V2==4 & x$V3!=1 ~ 2,
                 TRUE ~ 0)

Được thể hiện bằng dplyr::mutate, nó cho:

x = x %>% mutate(
     V5 = case_when(
         V1==1 & V2!=4 ~ 1,
         V2==4 & V3!=1 ~ 2,
         TRUE ~ 0
     )
)

Xin lưu ý rằng NAnó không được đối xử đặc biệt, vì nó có thể gây hiểu lầm. Hàm sẽ NAchỉ trả về khi không có điều kiện nào được khớp. Nếu bạn đặt một dòng với TRUE ~ ..., như tôi đã làm trong ví dụ của mình, thì giá trị trả về sẽ không bao giờ là như vậy NA.

Do đó, bạn phải nói rõ ràng case_whenđể đặt NAnó ở đâu bằng cách thêm một câu như is.na(x$V1) | is.na(x$V3) ~ NA_integer_. Gợi ý: dplyr::coalesce()đôi khi hàm có thể thực sự hữu ích!

Hơn nữa, xin lưu ý rằng NAmình sẽ thường không làm việc, bạn cần phải đặt đặc biệt NAgiá trị: NA_integer_, NA_character_hoặc NA_real_.


1
Điều này nhanh hơn đáng kể so với originFactor.
Fato39

12

Có vẻ như derivedFactortừ mosaicgói được thiết kế cho điều này. Trong ví dụ này, nó sẽ giống như sau:

library(mosaic)
myfile <- mutate(myfile, V5 = derivedFactor(
    "1" = (V1==1 & V2!=4),
    "2" = (V2==4 & V3!=1),
    .method = "first",
    .default = 0
    ))

(Nếu bạn muốn kết quả là số thay vì thừa số, hãy bọc dấu derivedFactorbằng as.numeric.)

Lưu ý rằng .defaulttùy chọn kết hợp với .method = "first"đặt điều kiện "else" - cách tiếp cận này được mô tả trong tệp trợ giúp cho derivedFactor.


Bạn cũng có thể ngăn kết quả là một yếu tố bằng cách sử dụng .asFactor = Ftùy chọn hoặc bằng cách sử dụng hàm (tương tự) derivedVariabletrong cùng một gói.
Jake Fisher

Có vẻ như recodetừ dplyr 0.5 sẽ làm điều này. Tôi vẫn chưa điều tra nó. Xem blog.rstudio.org/2016/06/27/dplyr-0-5-0
Jake Fisher

Điều này là chậm đối với dữ liệu của tôi với 1e6 hàng.
Fato39,

3
@ Fato39 Vâng, nhóm mosaic::derivedFactorchức năng rất chậm. Nếu bạn tìm ra lý do tại sao, vui lòng trả lời câu hỏi SO của tôi về nó: stackoverflow.com/questions/33787691/… . Tôi rất vui khi thấy từ nhận xét khác của bạn dplyr::case_whennhanh hơn - Tôi sẽ phải chuyển sang nhận xét đó.
Jake Fisher

Tôi đang thử lệnh sau, library (mosaic) VENEZ.FINAL2 <- mutate (VENEZ, SEX = originFactor ("M" = (CATEGORY == "BULL" & CATEGORY! = "SIRE"), "F" = ( CATEGORY == "COW" & CATEGORY! = "HEIFER"), .method = "first", .default = "NA")) nhưng không hiệu quả, chỉ cần giải quyết điều kiện VENEZ.FINAL2 <- mutate (VENEZ, SEX = originFactor ("M" = (CATEGORY == "BULL Bạn có thể giúp tôi không? Cảm ơn rất nhiều!
Johanna Ramirez
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.