2012-05-07 21 views
18

Tôi đã có một số mã R trông cơ bản như thế này:R ngữ cho chuyển/trường hợp

compute.quantiles <- function(mu, type) { 

    ## 'mu' and 'type' are vectors of the same length 

    var <- ifelse(type=='a', 6.3523 * mu^2, 
     ifelse(type=='b', 234.23 * mu, 
     ifelse(type=='c', {s <- 9.8 * ((mu-0.3)/3)^(6/7)+0.19; mu + mu^2/s}, 
     ifelse(type=='d', 56.345 * mu^1.5, 
     ifelse(type=='e', 0.238986 * mu^2, 
     ifelse(type=='f', mu + 1.1868823 * mu^2, 
     NA)))))) 

    # ...then do something with var... 
} 

Một số mẫu đầu vào & đầu ra:

print(compute.quantiles(2:4, c('c','d','e'))) 
[1] 2.643840 292.777208 3.823776 

đó làm việc một cách chính xác, nhưng nó là loại xấu xí với việc làm tổ sâu, vì vậy tôi tự hỏi nếu có một thành ngữ khác nhau hoạt động tốt hơn. Bất cứ ai có một đề nghị? Nếu switch() chấp nhận một vectơ làm đối số đầu tiên của nó, nó sẽ hoạt động tốt, nhưng nó chỉ lấy một vô hướng.

+0

thể bạn cung cấp một tập dữ liệu nhỏ có thể lặp lại để thử tính năng này? –

+0

@TylerRinker đã hoàn thành. –

+0

Tất cả những câu trả lời đều thông minh, nhưng chúng không gửi thành ngữ. Do đó, câu hỏi thú vị về cách thành ngữ để làm điều này trong R tiếp tục mở. – Pere

Trả lời

6

Tôi nghĩ rằng tôi đã đưa ra một cái gì đó tôi thích tốt hơn:

## Vector-switch 
vswitch <- function(EXPR, ...) { 
    vars <- cbind(...) 
    vars[cbind(seq_along(EXPR), match(EXPR, names(list(...))))] 
} 

compute.quantiles <- function(mu, type) { 
    stopifnot(length(mu) == length(type)) 

    vswitch(type, 
    a = 6.3523 * mu^2, 
    b = 234.23 * mu, 
    c = mu + mu^2/(9.8 * ((mu-0.3)/3)^(6/7)+0.19), 
    d = 56.345 * mu^1.5, 
    e = 0.238986 * mu^2, 
    f = mu + 1.1868823 * mu^2) 
} 

Với mã ma trận-indexing chỉ trong 2 dòng, tôi nghĩ rằng đó là ok cho ngưỡng quá thông minh-mã của tôi. =)

+1

+1 Tôi đã suy ngẫm câu trả lời và suy nghĩ khác của bạn: tại sao không tạo ra hàm 'vswitch' - và bây giờ bạn đã làm! Cùng tên và mọi thứ :) – Tommy

+0

=) [thêm 52 ký tự được thêm để đạt đến ngưỡng đăng ...] –

1

Có lẽ một cái gì đó như thế này là hoàn toàn khả thi:

compute.quantiles <- function(mu, type) { 
    stopifnot(length(mu) == length(type)) 

    vars <- cbind(
    a = 6.3523 * mu^2, 
    b = 234.23 * mu, 
    c = mu + mu^2/(9.8 * ((mu-0.3)/3)^(6/7)+0.19), 
    d = 56.345 * mu^1.5, 
    e = 0.238986 * mu^2, 
    f = mu + 1.1868823 * mu^2) 

    vars[cbind(seq_along(mu), match(type, colnames(vars)))] 
} 

Không chắc rằng sẽ trông quá "cao cấp" cho người đọc tương lai (bao gồm cả bản thân mình) mặc dù.

+0

1+ Nhưng bạn cần thay 'mu [i]' bằng 'mu' ... – Tommy

+1

Và liên quan đến "quá nâng cao" - bạn có thể xem xét nhận xét giải thích những gì đang diễn ra ... Đừng sợ các bình luận :) – Tommy

+0

... và sử dụng 'seq_along (mu)' thay vì '1: length (mu) 'để bạn xử lý vectơ có độ dài bằng không ... – Tommy

0

Tôi không thể cưỡng lại việc thêm câu trả lời khác bằng cách tiếp cận hoàn toàn khác. Đây rồi.

## Sort of a cross between tapply() and ave() 
tswitch <- function(x, INDEX, ...) { 
    l <- substitute(list(...)) 
    s <- split(x, INDEX) 
    pf <- parent.frame() 
    split(x, INDEX) <- lapply(names(s), function(n) 
    eval(l[[n]], list(x=s[[n]]), pf) 
) 
    x 
} 

compute.quantiles <- function(mu, type) { 
    stopifnot(length(mu) == length(type)) 

    tswitch(mu, type, 
    a = 6.3523 * x^2, 
    b = 234.23 * x, 
    c = x + x^2/(9.8 * ((x-0.3)/3)^(6/7)+0.19), 
    d = 56.345 * x^1.5, 
    e = 0.238986 * x^2, 
    f = x + 1.1868823 * x^2) 
} 

Và đầu vào mẫu & đầu ra:

> compute.quantiles(2:4, c('c','d','e')) 
[1] 2.643840 292.777208 3.823776 

Ưu điểm để thực hiện này là nó chỉ tính các giá trị cụ thể length(mu) mà cần phải được tính toán. Ngược lại, phương pháp vswitch ở trên tính giá trị length(mu) * M, trong đó M là số "trường hợp" trong nút chuyển. Vì vậy, nếu tính toán là tốn kém, hoặc nếu dữ liệu lớn, phiên bản này có thể là một chiến thắng.

3

Dưới đây là một cách tiếp cận khác:

library(data.table) 
# Case selection table: 
dtswitch <- data.table(type=letters[1:6], 
         result=c("6.3523 * mu^2", 
           "234.23 * mu", 
           "{s <- 9.8 * ((mu-0.3)/3)^(6/7)+0.19; mu + mu^2/s}", 
           "56.345 * mu^1.5", 
           "0.238986 * mu^2", 
           "mu + 1.1868823 * mu^2"), 
         key="type") 

# Data to which you want the cases applied: 
compute <- data.table(type=letters[3:5],mu=2:4,key="type") 

# Join the data table with the case selection table, and evaluate the results: 
dtswitch[compute,list(mu,result=eval(parse(text=result)))] 
#> type mu  result 
#>1: c 2 2.643840 
#>2: d 3 292.777208 
#>3: e 4 3.823776 

Thay vì tạo bảng dtswitch trong mã R, bạn có thể lưu trữ nó trong một bảng tính bên ngoài hoặc cơ sở dữ liệu và sau đó tải nó vào R. Có thể có ích nếu bạn có nhiều trường hợp khác nhau hoặc chúng thay đổi thường xuyên và bạn muốn kiểm soát chúng từ vị trí trung tâm.

2

Việc triển khai của Ken Williams là vswitch không hoạt động tốt đối với một số loại đầu vào. Tôi nghĩ cái này là linh hoạt hơn:

vswitch <- function(expr, ...) { 
    lookup <- list(...) 
    vec <- as.character(expr) 
    vec[is.na(vec)] <- "NA" 
    unname(do.call(c, lookup[vec])) 
} 

Để sử dụng nó với các giá trị tra cứu số, bạn cần phải trích dẫn hoặc backquote họ:

num_vec <- c(1, 3, 2, 2, 1) 
vswitch(num_vec, `1` = 10, `2` = 25, `3` = 50) 
## [1] 10 50 25 25 10 

Với tra cứu nhân vật:

char_vec <- letters[num_vec] 
vswitch(char_vec, a = "Albert", b = "Bertrand", c = "Charles") 
## [1] "Albert" "Charles" "Bertrand" "Bertrand" "Albert"