2016-08-26 3 views
4

Ich bin ziemlich vertraut mit Rs Standard-ifelse-Anweisung und wie verschachtelte ifelse-Anweisungen zu erstellen. Ich möchte jedoch eine "bessere" Version erstellen, so dass ich ifelse nicht so oft kopieren/einfügen muss.R - So erstellen Sie benutzerdefinierte Ifelse-Funktion, die wiederholt

Nehmen Sie dieses verschachtelte ifelse Anweisung zum Beispiel:

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

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

Statt dessen, was ich möchte eine Funktion zu tun ist, erstellen wie ich so nennen könnte:

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

die Funktion Ich möchte in der Lage zu sein, wie viele Argumente ich eingegeben habe, und so zu wissen, wie viele ifelse-Anweisungen enthalten und dann die Argumente in die richtige Position stecken, bis zu so viele ich will.

Es gibt immer noch einige Wiederholungen, aber beim Erstellen längerer verschachtelter ifelse-Anweisungen wäre es nett, diesen Teil des Codes nicht wiederholen zu müssen, und dann versuchen, die Endung von parens zu verfolgen.

+3

In einigen Fällen' Schalter' sein könnte, was Sie suchen. – lmo

+0

Oder erstellen Sie eine Symbol-Tabelle von Hand (vs 'switch') und verwenden Sie diese für den Abgleich –

+0

Vielleicht' ifelse (df $ b> 4, 4, df $ b) '? Oder siehe 'cut()' Funktion. – zx8754

Antwort

4

Wir können Reduce() verwenden, um die erforderlichen Parse-Baum von verschachtelten ifelse() Anrufe aufzubauen und dann eval() es:

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() 

Nützliche Dokumente:

Demo:

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 

OPs Beispiel:

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

Sorry für schamlose Werbung - können Sie if_val Funktion in meinem Paket versuchen expss

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

Es gibt auch ifs Funktion: ifs(b==1 ~ 1, b==2 ~ 2, b==3 ~ 3, TRUE ~ 4).

1

Dies ist ein Job zum Zusammenführen mit einer Nachschlagetabelle. Sie können, dass in einer Funktion wickeln, aber normalerweise würde ich nicht stören:

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 sind eine Kaskadierung alternativer ifelse s verschachtelt, z.B.

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 

oder es einfach stehlen und es in der Basis-Syntax setzen:

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

, die die gleiche Sache zurückgibt.

Da es schon so einfach wie möglich ist, ohne die Vielseitigkeit von ifelse zu opfern, muss es möglicherweise nicht in eine Funktion gesetzt werden, aber es könnte, wenn Sie möchten. Mit the development version ‚s neue rlang NSE Syntax,

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 
Verwandte Themen