2016-08-26 40 views
4

我對R的標準ifelse語句以及如何創建嵌套的ifelse語句非常熟悉。然而,我想創建一個「更好」的版本,這樣我就不必複製/粘貼ifelse很多次了。R - 如何創建重複的自定義Ifelse函數

把這個嵌套ifelse語句,例如:

df <- data.frame(b = 1:5) 

df$a <- ifelse(df$b == 1,1, 
     ifelse(df$b == 2,2, 
     ifelse(df$b == 3,3,4))) 

相反,我會做的是創建一個函數一樣,我可以這樣調用:

df$a <- myFunction(df$b == 1,1, 
        df$b == 2,2, 
        df$b == 3,3,4) 

我想要的功能能夠獲取我輸入的參數數量,從而知道要包含多少個ifelse語句,然後將這些參數插入正確的位置,直到達到我想要的數量。

仍然有一些重複,但是當創建更長的嵌套ifelse語句時,不必重複那段代碼就可以了,然後嘗試跟蹤paren的結束。

+3

在某些情況下,'之開關可能是你在找什麼。 – lmo

+0

或者手工創建一個符號表(vs'switch')並將其用於匹配 –

+0

也許'ifelse(df $ b> 4,4,df $ b)'?或者看'cut()'函數。 – zx8754

回答

4

我們可以使用Reduce()建立嵌套ifelse()調用所需的解析樹,然後eval()它:

ifelses <- function(...) { 
    ## validate number of args is at least 3 and odd 
    stopifnot(nargs()>=3L); 
    stopifnot(nargs()%%2L==1L); 
    ## precompute the required number of calls and the argument parse tree list 
    num <- (nargs()-1L)%/%2L; 
    cl <- match.call(); 
    ## build up the parse tree of nested ifelse() calls using Reduce(), then eval() it 
    ## terminology (following docs): ifelse(test,yes,no) 
    eval(Reduce(
     function(i,noArg) call('ifelse',cl[[i]],cl[[i+1L]],noArg), 
     seq(2L,by=2L,len=num), ## indexes of "test" args 
     cl[[length(cl)]], ## first (innermost) "no" arg 
     T ## proceed from right-to-left, IOW inside-out wrt parse tree 
    )); 
}; ## end ifelses() 

有用的文檔:

演示:

ifelses(c(F,T,F,F),1:4,c(T,F,F,F),5:8,c(F,T,F,T),9:12,13:16); 
## [1] 5 2 15 12 

OP的例子:

df <- data.frame(b=1:5); 
df$a <- ifelses(df$b==1L,1L,df$b==2L,2L,df$b==3L,3L,4L); 
df; 
## b a 
## 1 1 1 
## 2 2 2 
## 3 3 3 
## 4 4 4 
## 5 5 4 
1

對不起,我無恥的廣告 - 你可以嘗試if_val功能在我的包expss

b = sample(1:7, 10, replace = TRUE) 
if_val(b, 1 ~ 1, 2 ~ 2, 3 ~ 3, other ~ 4) 

還有ifs功能:ifs(b==1 ~ 1, b==2 ~ 2, b==3 ~ 3, TRUE ~ 4)

1

這是一個與查找表合併的工作。你可以用在一個功能,但通常我不會理會:

df <- data.frame(b = 1:5) 

lookupif <- function(df, x, y, else.val = NA, on.col, res.col = "val") { 
lookup <- data.frame(x, y) 
names(lookup)[1] <- res.col 
df <- merge(df, lookup, by.x = on.col, by.y = "y", all.x = TRUE) 
df[is.na(df[[res.col]]), res.col] <- else.val 
df 
} 

lookupif(df, 1:3, 1:3, 4, "b") 
# b val 
#1 1 1 
#2 2 2 
#3 3 3 
#4 4 4 
#5 5 4 
1

dplyr::case_when是級聯的替代嵌套ifelse S,例如

library(dplyr) 

df <- data.frame(b = 1:5) 

df %>% mutate(a = case_when(b == 1 ~ 1, 
          b == 2 ~ 2, 
          b == 3 ~ 3, 
          TRUE ~ 4)) 
#> b a 
#> 1 1 1 
#> 2 2 2 
#> 3 3 3 
#> 4 4 4 
#> 5 5 4 

或只是偷走它,並把它在基本語法:

df$a <- with(df, dplyr::case_when(b == 1 ~ 1, 
            b == 2 ~ 2, 
            b == 3 ~ 3, 
            TRUE ~ 4)) 

返回同樣的事情。

由於它已經儘可能簡單,並且不會犧牲ifelse的多功能性,所以它可能不需要放入函數中,但它可以,如果您願意的話。使用the development version的新rlang NSE語法,

add_cases <- function(.data, .col, ...){ 
    .data %>% mutate(!!.col := case_when(!!!quos(...))) 
} 

df %>% add_cases(.col = 'a', 
       b == 1 ~ 1, 
       b == 2 ~ 2, 
       b == 3 ~ 3, 
       TRUE ~ 4) 
#> b a 
#> 1 1 1 
#> 2 2 2 
#> 3 3 3 
#> 4 4 4 
#> 5 5 4